code-server-like ssh clash
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -39,6 +39,7 @@ def read_and_forward_pty_output(fd=None, pid=None, room_id=None,namespace=None):
|
|||||||
"""
|
"""
|
||||||
max_read_bytes = 1024 * 20
|
max_read_bytes = 1024 * 20
|
||||||
timeout=0.1
|
timeout=0.1
|
||||||
|
try:
|
||||||
while True:
|
while True:
|
||||||
socketio.sleep(timeout)
|
socketio.sleep(timeout)
|
||||||
timeout=min(timeout*2, 0.4)
|
timeout=min(timeout*2, 0.4)
|
||||||
@@ -48,8 +49,21 @@ def read_and_forward_pty_output(fd=None, pid=None, room_id=None,namespace=None):
|
|||||||
try:
|
try:
|
||||||
child_process = psutil.Process(pid)
|
child_process = psutil.Process(pid)
|
||||||
except psutil.NoSuchProcess as err:
|
except psutil.NoSuchProcess as err:
|
||||||
|
# Process already terminated, clean up any zombie
|
||||||
|
try:
|
||||||
|
os.waitpid(pid, os.WNOHANG)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return
|
return
|
||||||
if child_process.status() not in ('running', 'sleeping'):
|
if child_process.status() not in ('running', 'sleeping'):
|
||||||
|
# Process is terminated or in other state, clean up
|
||||||
|
try:
|
||||||
|
child_process.wait(timeout=1)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
os.waitpid(pid, os.WNOHANG)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
return
|
return
|
||||||
if fd:
|
if fd:
|
||||||
timeout_sec = 0
|
timeout_sec = 0
|
||||||
@@ -68,6 +82,13 @@ def read_and_forward_pty_output(fd=None, pid=None, room_id=None,namespace=None):
|
|||||||
# the key for different visitor to get different terminal (instead of mixing up)
|
# the key for different visitor to get different terminal (instead of mixing up)
|
||||||
# is to let the background task push pty response to each one's own (default) ROOM!
|
# is to let the background task push pty response to each one's own (default) ROOM!
|
||||||
namespace.emit("pty_output", {"output": output}, room=room_id)
|
namespace.emit("pty_output", {"output": output}, room=room_id)
|
||||||
|
finally:
|
||||||
|
# Clean up file descriptor if it's open
|
||||||
|
if fd:
|
||||||
|
try:
|
||||||
|
os.close(fd)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
class VSCLikeNameSpace(Namespace):
|
class VSCLikeNameSpace(Namespace):
|
||||||
def on_connect(self):
|
def on_connect(self):
|
||||||
@@ -156,15 +177,40 @@ class VSCLikeNameSpace(Namespace):
|
|||||||
set_winsize(fd, data["rows"], data["cols"])
|
set_winsize(fd, data["rows"], data["cols"])
|
||||||
|
|
||||||
def on_disconnect(self):
|
def on_disconnect(self):
|
||||||
|
child_pid = session.get('terminal_config', {}).get('child_pid')
|
||||||
|
if child_pid:
|
||||||
try:
|
try:
|
||||||
child_process = psutil.Process(session.get('terminal_config', {}).get('child_pid'))
|
child_process = psutil.Process(child_pid)
|
||||||
except psutil.NoSuchProcess as err:
|
|
||||||
disconnect()
|
|
||||||
session['terminal_config'] = TERM_INIT_CONFIG
|
|
||||||
return
|
|
||||||
if child_process.status() in ('running', 'sleeping'):
|
if child_process.status() in ('running', 'sleeping'):
|
||||||
# if visitor just close the browser tab then left alone the pty here
|
# if visitor just close the browser tab then left alone the pty here
|
||||||
# it should be terminated by the parent process after
|
# it should be terminated by the parent process after
|
||||||
child_process.terminate()
|
child_process.terminate()
|
||||||
current_app.logger.debug('user left the pty alone, terminated')
|
# Wait for the process to terminate and collect its exit status
|
||||||
|
child_process.wait(timeout=2)
|
||||||
|
current_app.logger.debug('user left the pty alone, terminated and waited')
|
||||||
|
except psutil.NoSuchProcess as err:
|
||||||
|
# Process already terminated, try to wait anyway to clean up any zombie
|
||||||
|
try:
|
||||||
|
os.waitpid(child_pid, os.WNOHANG)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except psutil.TimeoutExpired:
|
||||||
|
# If process didn't terminate in time, kill it forcefully
|
||||||
|
child_process.kill()
|
||||||
|
try:
|
||||||
|
child_process.wait(timeout=1)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
except Exception as err:
|
||||||
|
current_app.logger.error(f'Error terminating process: {err}')
|
||||||
|
finally:
|
||||||
|
# Close the file descriptor if it's open
|
||||||
|
fd = session.get('terminal_config', {}).get('fd')
|
||||||
|
if fd:
|
||||||
|
try:
|
||||||
|
os.close(fd)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
# Reset session config
|
||||||
|
session['terminal_config'] = TERM_INIT_CONFIG
|
||||||
current_app.logger.debug('Client disconnected')
|
current_app.logger.debug('Client disconnected')
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="notificationsContainer"></div>
|
<div id="notificationsContainer"></div>
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.34.0/min/vs/loader.js"></script>
|
<script src="static/cdnback/js/monaco-editor/0.34.0/min/vs/loader.js"></script>
|
||||||
<script src="/vsc-like/static/js/code-like-extension.js"></script>
|
<script src="/vsc-like/static/js/code-like-extension.js"></script>
|
||||||
<script src="/vsc-like/static/js/file.js"></script>
|
<script src="/vsc-like/static/js/file.js"></script>
|
||||||
<script src="/vsc-like/static/js/terminal.js"></script>
|
<script src="/vsc-like/static/js/terminal.js"></script>
|
||||||
|
|||||||
Reference in New Issue
Block a user