From 69846d598208887a9ee0f1a8ce017bbad7a1deb7 Mon Sep 17 00:00:00 2001 From: TotallyNotElite <1yourexperiment@protonmail.com> Date: Sat, 25 Jan 2020 12:08:01 +0100 Subject: [PATCH] Add dynamic resizing support to xterm.js terminal --- agents/meshcore.js | 35 +++++++++++++++++++---------------- views/xterm.handlebars | 7 +++++-- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/agents/meshcore.js b/agents/meshcore.js index 70e86f47..8338dc5d 100644 --- a/agents/meshcore.js +++ b/agents/meshcore.js @@ -39,8 +39,7 @@ var MESHRIGHT_LIMITEDINPUT = 4096; function createMeshCore(agent) { var obj = {}; - if (process.platform == 'win32' && require('user-sessions').isRoot()) - { + if (process.platform == 'win32' && require('user-sessions').isRoot()) { // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value try { var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024); @@ -1213,9 +1212,10 @@ function createMeshCore(agent) { } } } catch (ex) { } - var python = fs.existsSync('/usr/bin/python') ? '/usr/bin/python' : false; var shell = bash || sh; + var pty = python || script; + var env = { HISTCONTROL: 'ignoreboth', TERM: 'xterm' }; if (this.httprequest.xoptions) { if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); } @@ -1224,7 +1224,8 @@ function createMeshCore(agent) { var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env }; var setupcommands = ' alias ls=\'ls --color=auto\'\n'; if (shell == sh) setupcommands += ' stty erase ^H\n'; - setupcommands += ' clear\n'; + // Dynamic resizing is only supported in PTYs + if (pty) setupcommands += ' mcresize() { old=$(stty -g);stty raw -echo min 0 time 5;printf \'\\0337\\033[r\\033[999;999H\\033[6n\\0338\' > /dev/tty;IFS=\'[;R\' read -r _ rows cols _ < /dev/tty;stty "$old";stty cols "$cols" rows "$rows"; };trap mcresize SIGWINCH;\n' if (script && shell && process.platform == 'linux') { this.httprequest.process = childProcess.execFile(script, ['script', '--return', '--quiet', '-c', '"' + shell + '"', '/dev/null'], options); // Start as active user @@ -1250,8 +1251,10 @@ function createMeshCore(agent) { this.end(); return; } + this.httprequest.process.pty = pty; this.httprequest.process.tunnel = this; this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); }); + this.httprequest.process.stdin.write(" clear\n"); this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); }); this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text. this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text. @@ -1783,16 +1786,17 @@ function createMeshCore(agent) { break; } case 'termsize': { - if (ws.httprequest.protocol == 1) { // Terminal - // Indicates a change in terminal size - if (process.platform == 'win32') { - if (ws.httprequest._term == null) return; - //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows); - // TODO - } else { - if (ws.httprequest.process == null) return; - //sendConsoleText('Linux-TermSize: ' + obj.cols + 'x' + obj.rows); - // TODO + // Indicates a change in terminal size + if (process.platform == 'win32') { + if (ws.httprequest._term == null) return; + //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows); + // TODO + } else { + if (ws.httprequest.process == null || !ws.httprequest.process.pty) return; + // ILibDuktape_ChildProcess kill doesn't support sending signals + if (fs.existsSync("/bin/kill")) { + // We need to send signal to the child of the process, since the child is the shell + childProcess.execFile('/bin/bash', ['bash', "-c", "kill -SIGWINCH $(pgrep -P " + ws.httprequest.process.pid + ")"]); } } break; @@ -2226,8 +2230,7 @@ function createMeshCore(agent) { break; } case 'ps': { - processManager.getProcesses(function (plist) - { + processManager.getProcesses(function (plist) { var x = ''; for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; } sendConsoleText(x, sessionid); diff --git a/views/xterm.handlebars b/views/xterm.handlebars index fc1c9156..bdc7cd73 100644 --- a/views/xterm.handlebars +++ b/views/xterm.handlebars @@ -91,7 +91,6 @@ // When the user resizes the window, re-fit window.onresize = function () { if (termfit != null) { termfit.fit(); } - if (resizeTimer == null) { resizeTimer = setTimeout(sendResize, 200); } } // Update the terminal status and buttons @@ -143,8 +142,12 @@ if (termfit) { term.loadAddon(termfit); } term.open(Q('terminal')); term.onData(function (data) { if (tunnel != null) { tunnel.sendText(data); } }) - term.onResize(function (size) { if (resizeTimer == null) { resizeTimer = setTimeout(sendResize, 200); } }); if (termfit) { termfit.fit(); } + term.onResize(function (size) { + // Despam resize + if (resizeTimer) clearTimeout(resizeTimer); + resizeTimer = setTimeout(sendResize, 200); + }); // Setup a terminal tunnel to the agent tunnel = CreateAgentRedirect(meshserver, CreateRemoteTunnel(tunnelUpdate), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);