Files
2026-05-04 08:06:34 +03:00

93 lines
2.6 KiB
Python

import os
import json
import struct
import signal
import select
import threading
def _pty_set_winsize(fd, rows, cols):
import fcntl
import termios
winsz = struct.pack("HHHH", int(rows), int(cols), 0, 0)
fcntl.ioctl(fd, termios.TIOCSWINSZ, winsz)
def terminal_session(ws):
"""PTY + bash, обмен через WebSocket (как ttyd, но на том же порту что и Flask)."""
import pty
pid, master_fd = pty.fork()
if pid == 0:
os.environ.setdefault("TERM", "xterm-256color")
os.environ.setdefault("SHELL", "/bin/bash")
try:
os.execvp("/bin/bash", ["/bin/bash"])
except OSError:
os.execvp("/bin/sh", ["/bin/sh"])
os._exit(127)
relay_done = threading.Event()
ws_lock = threading.Lock()
def relay_out():
try:
while not relay_done.is_set():
r, _, _ = select.select([master_fd], [], [], 0.25)
if master_fd not in r:
continue
try:
chunk = os.read(master_fd, 65536)
except OSError:
break
if not chunk:
break
try:
with ws_lock:
ws.send(chunk)
except Exception:
break
finally:
relay_done.set()
t = threading.Thread(target=relay_out, daemon=True)
t.start()
try:
while True:
msg = ws.receive()
if msg is None:
break
if isinstance(msg, str) and msg.startswith("{"):
try:
j = json.loads(msg)
if j.get("type") == "resize":
r, c = int(j.get("rows") or 24), int(j.get("cols") or 80)
if r > 0 and c > 0:
_pty_set_winsize(master_fd, r, c)
continue
except (json.JSONDecodeError, TypeError, ValueError, KeyError):
pass
if isinstance(msg, str):
msg = msg.encode("utf-8", errors="replace")
try:
os.write(master_fd, msg)
except OSError:
break
finally:
relay_done.set()
try:
os.close(master_fd)
except OSError:
pass
try:
os.kill(pid, signal.SIGTERM)
except ProcessLookupError:
pass
try:
os.waitpid(pid, 0)
except ChildProcessError:
pass
t.join(timeout=1.5)