Add dynamic resizing support to xterm.js terminal

TotallyNotElite committed Jan 25, 2020 at 12:08 UTC 2ee4aebc15576c7202ed7e2d6d049d170cccfbb1
2 files changed +24 -18
agents/meshcore.js
+19 -16
@@ -39,8 +39,7 @@ var MESHRIGHT_LIMITEDINPUT = 4096;
39
40 function createMeshCore(agent) {
41 var obj = {};
42 - if (process.platform == 'win32' && require('user-sessions').isRoot())
43 - {
42 + if (process.platform == 'win32' && require('user-sessions').isRoot()) {
43 // Check the Agent Uninstall MetaData for correctness, as the installer may have written an incorrect value
44 try {
45 var writtenSize = 0, actualSize = Math.floor(require('fs').statSync(process.execPath).size / 1024);
@@ -1213,9 +1212,10 @@ function createMeshCore(agent) {
1212 }
1213 }
1214 } catch (ex) { }
1216 -
1215 var python = fs.existsSync('/usr/bin/python') ? '/usr/bin/python' : false;
1216 var shell = bash || sh;
1217 + var pty = python || script;
1218 +
1219 var env = { HISTCONTROL: 'ignoreboth', TERM: 'xterm' };
1220 if (this.httprequest.xoptions) {
1221 if (this.httprequest.xoptions.rows) { env.LINES = ('' + this.httprequest.xoptions.rows); }
@@ -1224,7 +1224,8 @@ function createMeshCore(agent) {
1224 var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: env };
1225 var setupcommands = ' alias ls=\'ls --color=auto\'\n';
1226 if (shell == sh) setupcommands += ' stty erase ^H\n';
1227 - setupcommands += ' clear\n';
1227 + // Dynamic resizing is only supported in PTYs
1228 + 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'
1229
1230 if (script && shell && process.platform == 'linux') {
1231 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) {
1251 this.end();
1252 return;
1253 }
1254 + this.httprequest.process.pty = pty;
1255 this.httprequest.process.tunnel = this;
1256 this.httprequest.process.on('exit', function (ecode, sig) { this.tunnel.end(); });
1257 + this.httprequest.process.stdin.write(" clear\n");
1258 this.httprequest.process.stderr.on('data', function (chunk) { this.parent.tunnel.write(chunk); });
1259 this.httprequest.process.stdout.pipe(this, { dataTypeSkip: 1 }); // 0 = Binary, 1 = Text.
1260 this.pipe(this.httprequest.process.stdin, { dataTypeSkip: 1, end: false }); // 0 = Binary, 1 = Text.
@@ -1783,16 +1786,17 @@ function createMeshCore(agent) {
1786 break;
1787 }
1788 case 'termsize': {
1786 - if (ws.httprequest.protocol == 1) { // Terminal
1787 - // Indicates a change in terminal size
1788 - if (process.platform == 'win32') {
1789 - if (ws.httprequest._term == null) return;
1790 - //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
1791 - // TODO
1792 - } else {
1793 - if (ws.httprequest.process == null) return;
1794 - //sendConsoleText('Linux-TermSize: ' + obj.cols + 'x' + obj.rows);
1795 - // TODO
1789 + // Indicates a change in terminal size
1790 + if (process.platform == 'win32') {
1791 + if (ws.httprequest._term == null) return;
1792 + //sendConsoleText('Win32-TermSize: ' + obj.cols + 'x' + obj.rows);
1793 + // TODO
1794 + } else {
1795 + if (ws.httprequest.process == null || !ws.httprequest.process.pty) return;
1796 + // ILibDuktape_ChildProcess kill doesn't support sending signals
1797 + if (fs.existsSync("/bin/kill")) {
1798 + // We need to send signal to the child of the process, since the child is the shell
1799 + childProcess.execFile('/bin/bash', ['bash', "-c", "kill -SIGWINCH $(pgrep -P " + ws.httprequest.process.pid + ")"]);
1800 }
1801 }
1802 break;
@@ -2226,8 +2230,7 @@ function createMeshCore(agent) {
2230 break;
2231 }
2232 case 'ps': {
2229 - processManager.getProcesses(function (plist)
2230 - {
2233 + processManager.getProcesses(function (plist) {
2234 var x = '';
2235 for (var i in plist) { x += i + ((plist[i].user) ? (', ' + plist[i].user) : '') + ', ' + plist[i].cmd + '\r\n'; }
2236 sendConsoleText(x, sessionid);
views/xterm.handlebars
+5 -2
@@ -91,7 +91,6 @@
91 // When the user resizes the window, re-fit
92 window.onresize = function () {
93 if (termfit != null) { termfit.fit(); }
94 - if (resizeTimer == null) { resizeTimer = setTimeout(sendResize, 200); }
94 }
95
96 // Update the terminal status and buttons
@@ -143,8 +142,12 @@
142 if (termfit) { term.loadAddon(termfit); }
143 term.open(Q('terminal'));
144 term.onData(function (data) { if (tunnel != null) { tunnel.sendText(data); } })
146 - term.onResize(function (size) { if (resizeTimer == null) { resizeTimer = setTimeout(sendResize, 200); } });
145 if (termfit) { termfit.fit(); }
146 + term.onResize(function (size) {
147 + // Despam resize
148 + if (resizeTimer) clearTimeout(resizeTimer);
149 + resizeTimer = setTimeout(sendResize, 200);
150 + });
151
152 // Setup a terminal tunnel to the agent
153 tunnel = CreateAgentRedirect(meshserver, CreateRemoteTunnel(tunnelUpdate), serverPublicNamePort, authCookie, authRelayCookie, domainUrl);