Use pty to properly capture wsssh stdin/stdout/stderr

parent 784d08d2
...@@ -29,6 +29,8 @@ import os ...@@ -29,6 +29,8 @@ import os
import threading import threading
import uuid import uuid
import subprocess import subprocess
import pty
import select
from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
...@@ -184,40 +186,47 @@ def connect_terminal(client_id): ...@@ -184,40 +186,47 @@ def connect_terminal(client_id):
username = request.form.get('username', 'root') username = request.form.get('username', 'root')
request_id = str(uuid.uuid4()) request_id = str(uuid.uuid4())
command = ['wsssh', '-p', str(args.port), f'{username}@{client_id}.{args.domain}'] command = ['wsssh', '-p', str(args.port), f'{username}@{client_id}.{args.domain}']
# Spawn wsssh process # Spawn wsssh process with pty
master, slave = pty.openpty()
proc = subprocess.Popen( proc = subprocess.Popen(
command, command,
stdout=subprocess.PIPE, stdin=slave,
stdin=subprocess.PIPE, stdout=slave,
stderr=subprocess.STDOUT stderr=slave,
preexec_fn=os.setsid
) )
os.close(slave)
# Start a thread to read output # Start a thread to read output
output_buffer = [] output_buffer = []
def read_output(): def read_output():
output_buffer.append('Process started, reading output...\r\n') output_buffer.append('Process started, reading output...\r\n')
while proc.poll() is None: while proc.poll() is None:
try: r, w, e = select.select([master], [], [], 0.1)
data = proc.stdout.read(1) # Read byte by byte for real-time if master in r:
if data: try:
output_buffer.append(data.decode('utf-8', errors='ignore')) data = os.read(master, 1024)
except: if data:
break output_buffer.append(data.decode('utf-8', errors='ignore'))
except:
break
# Read any remaining data # Read any remaining data
try: try:
data = proc.stdout.read() data = os.read(master, 1024)
if data: while data:
output_buffer.append(data.decode('utf-8', errors='ignore')) output_buffer.append(data.decode('utf-8', errors='ignore'))
data = os.read(master, 1024)
except: except:
pass pass
output_buffer.append('\r\nProcess finished.\r\n') output_buffer.append('\r\nProcess finished.\r\n')
os.close(master)
thread = threading.Thread(target=read_output, daemon=True) thread = threading.Thread(target=read_output, daemon=True)
thread.start() thread.start()
if proc.poll() is not None: if proc.poll() is not None:
output_buffer.append(f'\r\nProcess failed to start, exit code: {proc.returncode}\r\n') output_buffer.append(f'\r\nProcess failed to start, exit code: {proc.returncode}\r\n')
active_terminals[request_id] = {'client_id': client_id, 'username': username, 'proc': proc, 'output_buffer': output_buffer} active_terminals[request_id] = {'client_id': client_id, 'username': username, 'proc': proc, 'output_buffer': output_buffer, 'master': master}
return jsonify({'request_id': request_id, 'command': ' '.join(command)}) return jsonify({'request_id': request_id, 'command': ' '.join(command)})
...@@ -229,9 +238,12 @@ def terminal_data(client_id): ...@@ -229,9 +238,12 @@ def terminal_data(client_id):
data = request.form.get('data') data = request.form.get('data')
if request_id in active_terminals: if request_id in active_terminals:
proc = active_terminals[request_id]['proc'] proc = active_terminals[request_id]['proc']
master = active_terminals[request_id]['master']
if proc.poll() is None: # Process is still running if proc.poll() is None: # Process is still running
proc.stdin.write(data.encode()) try:
proc.stdin.flush() os.write(master, data.encode())
except:
pass
return 'OK' return 'OK'
else: else:
request_id = request.args.get('request_id') request_id = request.args.get('request_id')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment