Merge pull request #751 from TotallyNotElite/terminalimprovements

Various (linux) terminal improvements
This commit is contained in:
Ylian Saint-Hilaire 2019-12-22 12:40:06 -08:00 committed by GitHub
commit bf7dfce58f
4 changed files with 119 additions and 55 deletions

View File

@ -1184,8 +1184,7 @@ function createMeshCore(agent) {
} }
}); });
} }
} catch (e) } catch (e) {
{
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest); MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
this.end(); this.end();
@ -1202,15 +1201,33 @@ function createMeshCore(agent) {
{ {
try try
{ {
if (fs.existsSync('/usr/bin/python') && fs.existsSync('/bin/bash')) { var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
this.httprequest.process = childProcess.execFile('/usr/bin/python', ['python', '-c', "import pty; pty.spawn([\"/bin/bash\"])"], { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null }); // Start as active user var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
if (process.platform == 'linux') { this.httprequest.process.stdin.write("export TERM='xterm'\nalias ls='ls --color=auto'\nclear\n"); } var script = fs.existsSync('/usr/bin/script') ? '/usr/bin/script' : false;
} else if (fs.existsSync('/bin/bash')) { var python = fs.existsSync('/usr/bin/python') ? '/usr/bin/python' : false;
this.httprequest.process = childProcess.execFile('/bin/bash', ['bash', '-i'], { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null }); // Start as active user var shell = bash || sh;
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: { HISTCONTROL: "ignoreboth", TERM: "xterm" } };
var setupcommands = " alias ls='ls --color=auto'\n";
if (shell == sh) setupcommands += "stty erase ^H\n"
if (script && shell) {
this.httprequest.process = childProcess.execFile(script, ['script', '--return', '--quiet', '-c', '"' + shell + '"', '/dev/null'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
} else if (python && shell) {
this.httprequest.process = childProcess.execFile(python, ['python', '-c', 'import pty; pty.spawn(["' + shell + '"])'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
} else if (bash) {
options.type = childProcess.SpawnTypes.TERM;
this.httprequest.process = childProcess.execFile(bash, ['bash', '-i'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
} else if (sh) {
options.type = childProcess.SpawnTypes.TERM;
this.httprequest.process = childProcess.execFile(sh, ['sh'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands + "PS1='$ '\n"); }
} else { } else {
this.httprequest.process = childProcess.execFile('/bin/sh', ['sh'], { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8)?require('user-sessions').consoleUid():null }); // Start as active user MeshServerLog("Failed to start remote terminal session, no shell found");
if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); } return;
} }
} catch (e) { } catch (e) {
MeshServerLog("Failed to start remote terminal session, " + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest); MeshServerLog("Failed to start remote terminal session, " + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
@ -2967,4 +2984,4 @@ try {
} }
} catch (ex) { } catch (ex) {
require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException2: " + ex }); require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException2: " + ex });
} }

129
agents/meshcore.min.js vendored
View File

@ -768,13 +768,20 @@ function createMeshCore(agent) {
//sendConsoleText('setClip: ' + JSON.stringify(data)); //sendConsoleText('setClip: ' + JSON.stringify(data));
if (typeof data.data == 'string') { if (typeof data.data == 'string') {
MeshServerLog('Setting clipboard content, ' + data.data.length + ' byte(s)', data); MeshServerLog('Setting clipboard content, ' + data.data.length + ' byte(s)', data);
if (typeof data.data == 'string') { if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard
if (require('MeshAgent').isService) { require('clipboard').dispatchWrite(data.data); } else { require("clipboard")(data.data); } // Set the clipboard mesh.SendCommand({ "action": "msg", "type": "setclip", "sessionid": data.sessionid, "success": true });
mesh.SendCommand({ "action": "msg", "type": "setclip", "sessionid": data.sessionid, "success": true });
}
} }
break; break;
} }
case 'userSessions': {
// Send back current user sessions list, this is Windows only.
//sendConsoleText('userSessions: ' + JSON.stringify(data));
if (process.platform != 'win32') break;
var p = require('user-sessions').enumerateUsers();
p.sessionid = data.sessionid;
p.then(function (u) { mesh.SendCommand({ 'action': 'msg', 'type': 'userSessions', 'sessionid': u.sessionid, 'data': u }); });
break;
}
default: default:
// Unknown action, ignore it. // Unknown action, ignore it.
break; break;
@ -1088,6 +1095,7 @@ function createMeshCore(agent) {
// Handle tunnel data // Handle tunnel data
if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user) if (this.httprequest.protocol == 0) { // 1 = Terminal (admin), 2 = Desktop, 5 = Files, 6 = PowerShell (admin), 7 = Plugin Data Exchange, 8 = Terminal (user), 9 = PowerShell (user)
// Take a look at the protocol // Take a look at the protocol
if ((data.length > 3) && (data[0] == '{')) { onTunnelControlData(data, this); return; }
this.httprequest.protocol = parseInt(data); this.httprequest.protocol = parseInt(data);
if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; } if (typeof this.httprequest.protocol != 'number') { this.httprequest.protocol = 0; }
if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) { if ((this.httprequest.protocol == 1) || (this.httprequest.protocol == 6) || (this.httprequest.protocol == 8) || (this.httprequest.protocol == 9)) {
@ -1143,29 +1151,40 @@ function createMeshCore(agent) {
else else
{ {
// Logged in user // Logged in user
var username = require('user-sessions').getUsername(require('user-sessions').consoleUid()); var userPromise = require('user-sessions').enumerateUsers();
if (require('win-virtual-terminal').supported) userPromise.that = this;
userPromise.then(function (u)
{ {
// ConPTY PseudoTerminal var that = this.that;
this.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (this.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [80, 25] } }); if (u.Active.length > 0)
} {
else var username = u.Active[0].Username;
{ if (require('win-virtual-terminal').supported)
// Legacy Terminal {
this.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (this.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [80, 25] } }); // ConPTY PseudoTerminal
} that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-virtual-terminal', script: getJSModule('win-virtual-terminal') }], launch: { module: 'win-virtual-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [80, 25] } });
this.httprequest._dispatcher.ws = this; }
this.httprequest._dispatcher.on('connection', function (c) else
{ {
console.log('client connected'); // Legacy Terminal
this.ws._term = c; that.httprequest._dispatcher = require('win-dispatcher').dispatch({ user: username, modules: [{ name: 'win-terminal', script: getJSModule('win-terminal') }], launch: { module: 'win-terminal', method: (that.httprequest.protocol == 9 ? 'StartPowerShell' : 'Start'), args: [80, 25] } });
c.pipe(this.ws, { dataTypeSkip: 1 }); }
this.ws.pipe(c, { dataTypeSkip: 1, end: false }); that.httprequest._dispatcher.ws = that;
this.ws.prependListener('end', function () { this.httprequest._term.end(function () { console.log("Terminal was closed"); }); }); that.httprequest._dispatcher.on('connection', function (c)
}); {
console.log('client connected');
this.ws._term = c;
c.pipe(this.ws, { dataTypeSkip: 1 });
this.ws.pipe(c, { dataTypeSkip: 1, end: false });
this.ws.prependListener('end', function ()
{
if (this.httprequest._term) { this.httprequest._term.end(function () { console.log("Terminal was closed"); }); }
});
});
}
});
} }
} catch (e) } catch (e) {
{
MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest); MeshServerLog('Failed to start remote terminal session, ' + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() })); this.write(JSON.stringify({ ctrlChannel: '102938', type: 'console', msg: e.toString() }));
this.end(); this.end();
@ -1182,15 +1201,33 @@ function createMeshCore(agent) {
{ {
try try
{ {
if (fs.existsSync('/usr/bin/python') && fs.existsSync('/bin/bash')) { var bash = fs.existsSync('/bin/bash') ? '/bin/bash' : false;
this.httprequest.process = childProcess.execFile('/usr/bin/python', ['python', '-c', "import pty; pty.spawn([\"/bin/bash\"])"], { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null }); // Start as active user var sh = fs.existsSync('/bin/sh') ? '/bin/sh' : false;
if (process.platform == 'linux') { this.httprequest.process.stdin.write("export TERM='xterm'\nalias ls='ls --color=auto'\nclear\n"); } var script = fs.existsSync('/usr/bin/script') ? '/usr/bin/script' : false;
} else if (fs.existsSync('/bin/bash')) { var python = fs.existsSync('/usr/bin/python') ? '/usr/bin/python' : false;
this.httprequest.process = childProcess.execFile('/bin/bash', ['bash', '-i'], { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null }); // Start as active user var shell = bash || sh;
if (process.platform == 'linux') { this.httprequest.process.stdin.write("alias ls='ls --color=auto'\nclear\n"); }
var options = { uid: (this.httprequest.protocol == 8) ? require('user-sessions').consoleUid() : null, env: { HISTCONTROL: "ignoreboth", TERM: "xterm" } };
var setupcommands = " alias ls='ls --color=auto'\n";
if (shell == sh) setupcommands += "stty erase ^H\n"
if (script && shell) {
this.httprequest.process = childProcess.execFile(script, ['script', '--return', '--quiet', '-c', '"' + shell + '"', '/dev/null'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
} else if (python && shell) {
this.httprequest.process = childProcess.execFile(python, ['python', '-c', 'import pty; pty.spawn(["' + shell + '"])'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
} else if (bash) {
options.type = childProcess.SpawnTypes.TERM;
this.httprequest.process = childProcess.execFile(bash, ['bash', '-i'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands); }
} else if (sh) {
options.type = childProcess.SpawnTypes.TERM;
this.httprequest.process = childProcess.execFile(sh, ['sh'], options); // Start as active user
if (process.platform == 'linux') { this.httprequest.process.stdin.write(setupcommands + "PS1='$ '\n"); }
} else { } else {
this.httprequest.process = childProcess.execFile('/bin/sh', ['sh'], { type: childProcess.SpawnTypes.TERM, uid: (this.httprequest.protocol == 8)?require('user-sessions').consoleUid():null }); // Start as active user MeshServerLog("Failed to start remote terminal session, no shell found");
if (process.platform == 'linux') { this.httprequest.process.stdin.write("stty erase ^H\nalias ls='ls --color=auto'\nPS1='\\u@\\h:\\w\\$ '\nclear\n"); } return;
} }
} catch (e) { } catch (e) {
MeshServerLog("Failed to start remote terminal session, " + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest); MeshServerLog("Failed to start remote terminal session, " + e.toString() + ' (' + this.httprequest.remoteaddr + ')', this.httprequest);
@ -1257,8 +1294,13 @@ function createMeshCore(agent) {
return; return;
} }
// Look for a TSID
var tsid = null;
if ((this.httprequest.xoptions != null) && (typeof this.httprequest.xoptions.tsid == 'number')) { tsid = this.httprequest.xoptions.tsid; }
require('MeshAgent')._tsid = tsid;
// Remote desktop using native pipes // Remote desktop using native pipes
this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(require('MeshAgent')._tsid == null ? undefined : require('MeshAgent')._tsid), tunnel: this }; this.httprequest.desktop = { state: 0, kvm: mesh.getRemoteDesktopStream(tsid), tunnel: this };
this.httprequest.desktop.kvm.parent = this.httprequest.desktop; this.httprequest.desktop.kvm.parent = this.httprequest.desktop;
this.desktop = this.httprequest.desktop; this.desktop = this.httprequest.desktop;
@ -1739,7 +1781,12 @@ function createMeshCore(agent) {
return; return;
} }
if (obj.type == 'close') { if (obj.type == 'options') {
// These are additional connection options passed in the control channel.
//sendConsoleText('options: ' + JSON.stringify(obj));
delete obj.type;
ws.httprequest.xoptions = obj;
} else if (obj.type == 'close') {
// We received the close on the websocket // We received the close on the websocket
//sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close'); //sendConsoleText('Tunnel #' + ws.tunnel.index + ' WebSocket control close');
try { ws.close(); } catch (e) { } try { ws.close(); } catch (e) { }
@ -1871,7 +1918,7 @@ function createMeshCore(agent) {
var response = null; var response = null;
switch (cmd) { switch (cmd) {
case 'help': { // Displays available commands case 'help': { // Displays available commands
var fin = '', f = '', availcommands = 'help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper'; var fin = '', f = '', availcommands = 'version,help,info,osinfo,args,print,type,dbkeys,dbget,dbset,dbcompact,eval,parseuri,httpget,nwslist,plugin,wsconnect,wssend,wsclose,notify,ls,ps,kill,amt,netinfo,location,power,wakeonlan,setdebug,smbios,rawsmbios,toast,lock,users,sendcaps,openurl,amtreset,amtccm,amtacm,amtdeactivate,amtpolicy,getscript,getclip,setclip,log,av,cpuinfo,sysinfo,apf,scanwifi,scanamt,wallpaper';
if (process.platform == 'win32') if (process.platform == 'win32')
{ {
availcommands += ',safemode,wpfhwacceleration'; availcommands += ',safemode,wpfhwacceleration';
@ -1885,6 +1932,9 @@ function createMeshCore(agent) {
response = 'Available commands: \r\n' + fin + '.'; response = 'Available commands: \r\n' + fin + '.';
break; break;
} }
case 'version':
response = 'Mesh Agent Version: ' + process.versions.meshAgent;
break;
case 'wpfhwacceleration': case 'wpfhwacceleration':
if (process.platform != 'win32') { throw ('wpfhwacceleration setting is only supported on Windows'); } if (process.platform != 'win32') { throw ('wpfhwacceleration setting is only supported on Windows'); }
if (args['_'].length != 1) if (args['_'].length != 1)
@ -1979,10 +2029,7 @@ function createMeshCore(agent) {
var v = []; var v = [];
for(var i in u) for(var i in u)
{ {
if(u[i].State == 'Active' || u[i].State == 'Connected') if(u[i].State == 'Active') { v.push({ tsid: i, type: u[i].StationName, user: u[i].Username, domain: u[i].Domain }); }
{
v.push({ tsid: i, type: u[i].StationName, user: u[i].Username });
}
} }
sendConsoleText(JSON.stringify(v, null, 1), this.sessionid); sendConsoleText(JSON.stringify(v, null, 1), this.sessionid);
}); });
@ -2937,4 +2984,4 @@ try {
} }
} catch (ex) { } catch (ex) {
require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException2: " + ex }); require('MeshAgent').SendCommand({ "action": "msg", "type": "console", "value": "uncaughtException2: " + ex });
} }

View File

@ -73,7 +73,7 @@ var CreateAmtRemoteTerminal = function (divid, options) {
} }
obj.xxStateChange = function (newstate) { obj.xxStateChange = function (newstate) {
if ((newstate == 3) && (options != null) && (options.xterm == true)) { obj.TermSendKeys('stty rows ' + obj.height + ' cols ' + obj.width + '\nclear\n'); } if ((newstate == 3) && (options != null) && (options.xterm == true)) { obj.TermSendKeys(' stty rows ' + obj.height + ' cols ' + obj.width + '\n clear\n'); }
} }
obj.ProcessData = function (str) { obj.ProcessData = function (str) {
@ -807,4 +807,4 @@ var CreateAmtRemoteTerminal = function (divid, options) {
obj.DivElement.style['height'] = ''; obj.DivElement.style['height'] = '';
if ((options != null) && (options.width != null) && (options.height != null)) { obj.Init(options.width, options.height); } else { obj.Init(); } if ((options != null) && (options.width != null) && (options.height != null)) { obj.Init(options.width, options.height); } else { obj.Init(); }
return obj; return obj;
} }

View File

@ -2930,7 +2930,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
archive.on('error', function (err) { throw err; }); archive.on('error', function (err) { throw err; });
// Set the archive name // Set the archive name
res.attachment(domain.title + '-Backup-' + new Date().toLocaleDateString().replace('/', '-').replace('/', '-') + '.zip'); res.attachment((domain.title ? domain.title : "Meshcentral") + '-Backup-' + new Date().toLocaleDateString().replace('/', '-').replace('/', '-') + '.zip');
// Pipe archive data to the file // Pipe archive data to the file
archive.pipe(res); archive.pipe(res);