Added remote process control

This commit is contained in:
Ylian Saint-Hilaire 2018-04-11 13:49:05 -07:00
parent d05f086a0e
commit fb55e44edf
14 changed files with 448 additions and 47 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -30,6 +30,7 @@ function createMeshCore(agent) {
var net = require('net');
var fs = require('fs');
var rtc = require('ILibWebRTC');
var processManager = require('process-manager');
var SMBiosTables = require('smbios');
var amtMei = null, amtLms = null, amtLmsState = 0;
var amtMeiConnected = 0, amtMeiTmpState = null;
@ -327,39 +328,55 @@ function createMeshCore(agent) {
// If this is a console command, parse it and call the console handler
switch (data.action) {
case 'msg': {
if (data.type == 'console') { // Process a console command
if (data.value && data.sessionid) {
var args = splitArgs(data.value);
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
switch (data.type) {
case 'console': { // Process a console command
if (data.value && data.sessionid) {
var args = splitArgs(data.value);
processConsoleCommand(args[0].toLowerCase(), parseArgs(args), data.rights, data.sessionid);
}
break;
}
case 'tunnel': {
if (data.value != null) { // Process a new tunnel connection request
// Create a new tunnel object
var xurl = getServerTargetUrlEx(data.value);
if (xurl != null) {
var woptions = http.parseUri(xurl);
woptions.rejectUnauthorized = 0;
//sendConsoleText(JSON.stringify(woptions));
var tunnel = http.request(woptions);
tunnel.upgrade = onTunnelUpgrade;
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
tunnel.sessionid = data.sessionid;
tunnel.rights = data.rights;
tunnel.state = 0;
tunnel.url = xurl;
tunnel.protocol = 0;
tunnel.tcpaddr = data.tcpaddr;
tunnel.tcpport = data.tcpport;
tunnel.end();
// Put the tunnel in the tunnels list
var index = nextTunnelIndex++;;
tunnel.index = index;
tunnels[index] = tunnel;
sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
}
}
break;
}
case 'ps': {
if (data.sessionid) {
processManager.getProcesses(function (plist) { mesh.SendCommand({ "action": "msg", "type": "ps", "value": JSON.stringify(plist), "sessionid": data.sessionid }); });
}
break;
}
case 'pskill': {
sendConsoleText(JSON.stringify(data));
try { process.kill(data.value); } catch (e) { sendConsoleText(JSON.stringify(e)); }
break;
}
}
else if ((data.type == 'tunnel') && (data.value != null)) { // Process a new tunnel connection request
// Create a new tunnel object
var xurl = getServerTargetUrlEx(data.value);
if (xurl != null) {
var woptions = http.parseUri(xurl);
woptions.rejectUnauthorized = 0;
//sendConsoleText(JSON.stringify(woptions));
var tunnel = http.request(woptions);
tunnel.upgrade = onTunnelUpgrade;
tunnel.onerror = function (e) { sendConsoleText('ERROR: ' + JSON.stringify(e)); }
tunnel.sessionid = data.sessionid;
tunnel.rights = data.rights;
tunnel.state = 0;
tunnel.url = xurl;
tunnel.protocol = 0;
tunnel.tcpaddr = data.tcpaddr;
tunnel.tcpport = data.tcpport;
tunnel.end();
// Put the tunnel in the tunnels list
var index = nextTunnelIndex++;;
tunnel.index = index;
tunnels[index] = tunnel;
sendConsoleText('New tunnel connection #' + index + ': ' + tunnel.url + ', rights: ' + tunnel.rights, data.sessionid);
}
}
break;
}
case 'wakeonlan': {
// Send wake-on-lan on all interfaces for all MAC addresses in data.macs array. The array is a list of HEX MAC addresses.
@ -819,7 +836,7 @@ function createMeshCore(agent) {
var response = null;
switch (cmd) {
case 'help': { // Displays available commands
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios.';
response = 'Available commands: help, info, args, print, type, dbget, dbset, dbcompact, eval, parseuri, httpget,\r\nwslist, wsconnect, wssend, wsclose, notify, ls, ps, kill, amt, netinfo, location, power, wakeonlan, scanwifi,\r\nscanamt, setdebug, smbios, rawsmbios.';
break;
}
case 'setdebug': {
@ -827,6 +844,23 @@ function createMeshCore(agent) {
else { if (args['_'][0] == '*') { console.setDestination(1); } else { console.setDestination(parseInt(args['_'][0]), sessionid); } }
break;
}
case 'ps': {
processManager.getProcesses(function (plist) {
var x = '';
for (var i in plist) { x += i + ', ' + plist[i].cmd + ((plist[i].user) ? (', ' + plist[i].user):'') + '\r\n'; }
sendConsoleText(x, sessionid);
});
break;
}
case 'kill': {
if ((args['_'].length < 1)) {
response = 'Proper usage: kill [pid]'; // Display correct command usage
} else {
process.kill(parseInt(args['_'][0]));
response = 'Killed process ' + args['_'][0] + '.';
}
break;
}
case 'smbios': {
if (SMBiosTables != null) {
SMBiosTables.get(function (data) {

View File

@ -0,0 +1,102 @@
function UserSessions()
{
this._ObjectID = 'UserSessions';
if (process.platform == 'win32') {
this._marshal = require('_GenericMarshal');
this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
this._kernel32.CreateMethod('GetLastError');
this._wts = this._marshal.CreateNativeProxy('Wtsapi32.dll');
this._wts.CreateMethod('WTSEnumerateSessionsA');
this._wts.CreateMethod('WTSQuerySessionInformationA');
this._wts.CreateMethod('WTSFreeMemory');
this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
this.InfoClass =
{
'WTSInitialProgram': 0,
'WTSApplicationName': 1,
'WTSWorkingDirectory': 2,
'WTSOEMId': 3,
'WTSSessionId': 4,
'WTSUserName': 5,
'WTSWinStationName': 6,
'WTSDomainName': 7,
'WTSConnectState': 8,
'WTSClientBuildNumber': 9,
'WTSClientName': 10,
'WTSClientDirectory': 11,
'WTSClientProductId': 12,
'WTSClientHardwareId': 13,
'WTSClientAddress': 14,
'WTSClientDisplay': 15,
'WTSClientProtocolType': 16,
'WTSIdleTime': 17,
'WTSLogonTime': 18,
'WTSIncomingBytes': 19,
'WTSOutgoingBytes': 20,
'WTSIncomingFrames': 21,
'WTSOutgoingFrames': 22,
'WTSClientInfo': 23,
'WTSSessionInfo': 24,
'WTSSessionInfoEx': 25,
'WTSConfigInfo': 26,
'WTSValidationInfo': 27,
'WTSSessionAddressV4': 28,
'WTSIsRemoteSession': 29
};
this.getSessionAttribute = function getSessionAttribute(sessionId, attr)
{
var buffer = this._marshal.CreatePointer();
var bytesReturned = this._marshal.CreateVariable(4);
if (this._wts.WTSQuerySessionInformationA(0, sessionId, attr, buffer, bytesReturned).Val == 0)
{
throw ('Error calling WTSQuerySessionInformation: ' + this._kernel32.GetLastError.Val);
}
var retVal = buffer.Deref().String;
this._wts.WTSFreeMemory(buffer.Deref());
return (retVal);
};
this.Current = function Current()
{
var retVal = {};
var pinfo = this._marshal.CreatePointer();
var count = this._marshal.CreateVariable(4);
if (this._wts.WTSEnumerateSessionsA(0, 0, 1, pinfo, count).Val == 0)
{
throw ('Error calling WTSEnumerateSessionsA: ' + this._kernel32.GetLastError().Val);
}
for (var i = 0; i < count.toBuffer().readUInt32LE() ; ++i)
{
var info = pinfo.Deref().Deref(i * (this._marshal.PointerSize == 4 ? 12 : 24), this._marshal.PointerSize == 4 ? 12 : 24);
var j = { SessionId: info.toBuffer().readUInt32LE() };
j.StationName = info.Deref(this._marshal.PointerSize == 4 ? 4 : 8, this._marshal.PointerSize).Deref().String;
j.State = this.SessionStates[info.Deref(this._marshal.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE()];
if (j.State == 'Active') {
j.Username = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSUserName);
j.Domain = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSDomainName);
}
retVal[j.SessionId] = j;
}
this._wts.WTSFreeMemory(pinfo.Deref());
return (retVal);
};
}
else
{
this.Current = function Current()
{
return ({});
}
}
}
module.exports = new UserSessions();

View File

@ -0,0 +1,102 @@
function UserSessions()
{
this._ObjectID = 'UserSessions';
if (process.platform == 'win32') {
this._marshal = require('_GenericMarshal');
this._kernel32 = this._marshal.CreateNativeProxy('Kernel32.dll');
this._kernel32.CreateMethod('GetLastError');
this._wts = this._marshal.CreateNativeProxy('Wtsapi32.dll');
this._wts.CreateMethod('WTSEnumerateSessionsA');
this._wts.CreateMethod('WTSQuerySessionInformationA');
this._wts.CreateMethod('WTSFreeMemory');
this.SessionStates = ['Active', 'Connected', 'ConnectQuery', 'Shadow', 'Disconnected', 'Idle', 'Listening', 'Reset', 'Down', 'Init'];
this.InfoClass =
{
'WTSInitialProgram': 0,
'WTSApplicationName': 1,
'WTSWorkingDirectory': 2,
'WTSOEMId': 3,
'WTSSessionId': 4,
'WTSUserName': 5,
'WTSWinStationName': 6,
'WTSDomainName': 7,
'WTSConnectState': 8,
'WTSClientBuildNumber': 9,
'WTSClientName': 10,
'WTSClientDirectory': 11,
'WTSClientProductId': 12,
'WTSClientHardwareId': 13,
'WTSClientAddress': 14,
'WTSClientDisplay': 15,
'WTSClientProtocolType': 16,
'WTSIdleTime': 17,
'WTSLogonTime': 18,
'WTSIncomingBytes': 19,
'WTSOutgoingBytes': 20,
'WTSIncomingFrames': 21,
'WTSOutgoingFrames': 22,
'WTSClientInfo': 23,
'WTSSessionInfo': 24,
'WTSSessionInfoEx': 25,
'WTSConfigInfo': 26,
'WTSValidationInfo': 27,
'WTSSessionAddressV4': 28,
'WTSIsRemoteSession': 29
};
this.getSessionAttribute = function getSessionAttribute(sessionId, attr)
{
var buffer = this._marshal.CreatePointer();
var bytesReturned = this._marshal.CreateVariable(4);
if (this._wts.WTSQuerySessionInformationA(0, sessionId, attr, buffer, bytesReturned).Val == 0)
{
throw ('Error calling WTSQuerySessionInformation: ' + this._kernel32.GetLastError.Val);
}
var retVal = buffer.Deref().String;
this._wts.WTSFreeMemory(buffer.Deref());
return (retVal);
};
this.Current = function Current()
{
var retVal = {};
var pinfo = this._marshal.CreatePointer();
var count = this._marshal.CreateVariable(4);
if (this._wts.WTSEnumerateSessionsA(0, 0, 1, pinfo, count).Val == 0)
{
throw ('Error calling WTSEnumerateSessionsA: ' + this._kernel32.GetLastError().Val);
}
for (var i = 0; i < count.toBuffer().readUInt32LE() ; ++i)
{
var info = pinfo.Deref().Deref(i * (this._marshal.PointerSize == 4 ? 12 : 24), this._marshal.PointerSize == 4 ? 12 : 24);
var j = { SessionId: info.toBuffer().readUInt32LE() };
j.StationName = info.Deref(this._marshal.PointerSize == 4 ? 4 : 8, this._marshal.PointerSize).Deref().String;
j.State = this.SessionStates[info.Deref(this._marshal.PointerSize == 4 ? 8 : 16, 4).toBuffer().readUInt32LE()];
if (j.State == 'Active') {
j.Username = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSUserName);
j.Domain = this.getSessionAttribute(j.SessionId, this.InfoClass.WTSDomainName);
}
retVal[j.SessionId] = j;
}
this._wts.WTSFreeMemory(pinfo.Deref());
return (retVal);
};
}
else
{
this.Current = function Current()
{
return ({});
}
}
}
module.exports = new UserSessions();

View File

@ -0,0 +1,96 @@
/*
Copyright 2018 Intel Corporation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
var GM = require('_GenericMarshal');
function processManager() {
this._ObjectID = 'processManager';
switch (process.platform) {
case 'win32':
this._kernel32 = GM.CreateNativeProxy('kernel32.dll');
this._kernel32.CreateMethod('GetLastError');
this._kernel32.CreateMethod('CreateToolhelp32Snapshot');
this._kernel32.CreateMethod('Process32First');
this._kernel32.CreateMethod('Process32Next');
break;
case 'linux':
this._childProcess = require('child_process');
break;
default:
throw (process.platform + ' not supported');
}
this.getProcesses = function getProcesses(callback) {
switch (process.platform) {
case 'win32':
var h = this._kernel32.CreateToolhelp32Snapshot(2, 0), info = GM.CreateVariable(304), retVal = {};
info.toBuffer().writeUInt32LE(304, 0);
var nextProcess = this._kernel32.Process32First(h, info);
while (nextProcess.Val) {
retVal[info.Deref(8, 4).toBuffer().readUInt32LE(0)] = { cmd: info.Deref(GM.PointerSize == 4 ? 36 : 44, 260).String };
nextProcess = this._kernel32.Process32Next(h, info);
}
if (callback) { callback.apply(this, [retVal]); }
break;
case 'linux':
if (!this._psp) { this._psp = {}; }
var p = this._childProcess.execFile("/bin/ps", ["ps", "-uxa"], { type: this._childProcess.SpawnTypes.TERM });
this._psp[p.pid] = p;
p.Parent = this;
p.ps = '';
p.callback = callback;
p.args = [];
for (var i = 1; i < arguments.length; ++i) { p.args.push(arguments[i]); }
p.on('exit', function onGetProcesses() {
delete this.Parent._psp[this.pid];
var retVal = {}, lines = this.ps.split('\x0D\x0A'), key = {}, keyi = 0;
for (var i in lines) {
var tokens = lines[i].split(' '), tokenList = [];
for (var x in tokens) {
if (i == 0 && tokens[x]) { key[tokens[x]] = keyi++; }
if (i > 0 && tokens[x]) { tokenList.push(tokens[x]); }
}
if ((i > 0) && (tokenList[key.PID])) {
retVal[tokenList[key.PID]] = { user: tokenList[key.USER], cmd: tokenList[key.COMMAND] };
}
}
if (this.callback) {
this.args.unshift(retVal);
this.callback.apply(this.parent, this.args);
}
});
p.stdout.on('data', function (chunk) { this.parent.ps += chunk.toString(); });
break;
default:
throw ('Enumerating processes on ' + process.platform + ' not supported');
}
};
this.getProcessInfo = function getProcessInfo(pid) {
switch (process.platform) {
case 'linux':
var status = require('fs').readFileSync('/proc/' + pid + '/status'), lines = status.toString().split('\n'), info = {};
for (var i in lines) {
var tokens = lines[i].split(':');
if (tokens.length > 1) { tokens[1] = tokens[1].trim(); }
info[tokens[0]] = tokens[1];
}
return info;
default:
throw ('getProcessInfo() not supported for ' + process.platform);
}
};
}
module.exports = new processManager();

View File

@ -651,9 +651,10 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
if ((command.meshid.split('/').length != 3) || (command.meshid.split('/')[1] != domain.id)) return; // Invalid domain, operation only valid for current domain
if (obj.common.validateString(command.devicename, 1, 256) == false) break; // Check device name
if (obj.common.validateString(command.hostname, 1, 256) == false) break; // Check hostname
if (obj.common.validateString(command.amtusername, 1, 16) == false) break; // Check username
if (obj.common.validateString(command.amtpassword, 1, 16) == false) break; // Check password
if (obj.common.validateInt(command.amttls, 0, 1) == false) break; // Check TLS flag
if (obj.common.validateString(command.amtusername, 0, 16) == false) break; // Check username
if (obj.common.validateString(command.amtpassword, 0, 16) == false) break; // Check password
if (command.amttls == '0') { command.amttls = 0; } else if (command.amttls == '1') { command.amttls = 1; } // Check TLS flag
if ((command.amttls != 1) && (command.amttls != 0)) break;
// Get the mesh
var mesh = obj.parent.meshes[command.meshid];
@ -667,7 +668,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain) {
obj.parent.crypto.randomBytes(48, function (err, buf) {
// create the new node
var nodeid = 'node/' + domain.id + '/' + buf.toString('base64').replace(/\+/g, '@').replace(/\//g, '$');;
var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: parseInt(command.amttls) } };
var device = { type: 'node', mtype: 1, _id: nodeid, meshid: command.meshid, name: command.devicename, host: command.hostname, domain: domain.id, intelamt: { user: command.amtusername, pass: command.amtpassword, tls: command.amttls } };
obj.db.Set(device);
// Event the new node

View File

@ -1,6 +1,6 @@
{
"name": "meshcentral",
"version": "0.1.6-a",
"version": "0.1.6-c",
"keywords": [
"Remote Management",
"Intel AMT",

View File

@ -513,4 +513,12 @@ a {
.notification:hover {
background-color: #EFE8B6;
}
.deskToolsBar {
padding:3px;
}
.deskToolsBar:hover {
background-color: #EFE8B6;
}

View File

@ -360,12 +360,21 @@
<div id="DeskParent">
<canvas id="Desk" width="640" height="200" style="width:100%;-ms-touch-action:none;margin-left:0px" oncontextmenu="return false" onmousedown="dmousedown(event)" onmouseup="dmouseup(event)" onmousemove="dmousemove(event)"></canvas>
</div>
<div id="DeskTools" style="position:absolute;width:400px;height:100%;background-color:gray;top:0;right:0;border-left:2px solid lightgray;display:none">
<a id="DeskToolsRefreshButton" style="float:right;padding:3px;cursor:pointer" onclick="refreshDeskTools()">Refresh</a>
<div id="DeskToolsBar" style="position:absolute;padding:3px;border-radius: 3px 3px 0px 0px;top:5px;left:4px;bottom:26px;background-color:lightgray;cursor:pointer">Processes</div>
<div style="position:absolute;top:26px;left:4px;right:4px;bottom:4px;background-color:lightgray;text-align:left">
<div style="border-bottom:1px solid darkgray;padding:3px"><a style=width:50px;padding-right:5px;float:left;cursor:pointer title="Sort by process id" onclick=sortProcess(0)>PID</a><a style=cursor:pointer title="Sort by name" onclick=sortProcess(1)>Name</a></div>
<div id="DeskToolsProcesses" style="overflow-y:scroll;position:absolute;top:24px;bottom:0px;width:100%"></div>
</div>
</div>
</td>
</tr>
<tr id=deskarea4>
<td style="padding-top:2px;padding-bottom:2px;background:#C0C0C0">
<div style="float:right;text-align:right">
<select id="termdisplays" style="display:none" onchange="deskSetDisplay(event)" onclick="deskGetDisplayNumbers(event)"></select>&nbsp;
<input id="DeskToolsButton" type="button" value="Tools" title="Toggle tools view" onkeypress="return false" onkeydown="return false" onclick="toggleDeskTools()">&nbsp;
</div>
<div>
&nbsp;
@ -969,11 +978,13 @@
if (index != -1) {
// Node was found, dispatch the message
if (message.type == 'console') { p15consoleReceive(nodes[index], message.value); } // This is a console message.
if (message.type == 'notify') { // This is a notification message.
else if (message.type == 'notify') { // This is a notification message.
var n = { text:message.value };
if (message.nodeid != null) { n.nodeid = message.nodeid; }
if (message.tag != null) { n.tag = message.tag; }
addNotification(n);
} else if (message.type == 'ps') {
showDeskToolsProcesses(message);
}
}
} else {
@ -2823,6 +2834,10 @@
// Request the power timeline
if ((powerTimelineNode != currentNode._id) && (powerTimelineReq != currentNode._id)) { powerTimelineReq = currentNode._id; meshserver.send({ action: 'powertimeline', nodeid: currentNode._id }); }
// Reset the desktop tools
QV('DeskTools', false);
showDeskToolsProcesses();
}
setupDesktop(); // Always refresh the desktop, even if we are on the same device, we need to do some canvas switching.
if (!panel) panel = 10;
@ -3168,15 +3183,16 @@
// Show and enable the right buttons
function updateDesktopButtons() {
var mesh = meshes[currentNode.meshid];
var deskState = ((desktop != null) && (desktop.state != 0));
var deskState = 0;
if (desktop != null) { deskState = desktop.State; }
// Show the right buttons
QV('disconnectbutton1span', (deskState == true));
QV('connectbutton1span', (deskState == false) && (mesh.mtype == 2));
QV('connectbutton1hspan', (deskState == false) && (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))));
QV('disconnectbutton1span', (deskState != 0));
QV('connectbutton1span', (deskState == 0) && (mesh.mtype == 2));
QV('connectbutton1hspan', (deskState == 0) && (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))));
// Show the right settings
QV('d7amtkvm', (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == false) || (desktop.contype == 2)));
QV('d7amtkvm', (currentNode.intelamt != null && ((currentNode.intelamt.ver != null) || (mesh.mtype == 1))) && ((deskState == 0) || (desktop.contype == 2)));
QV('d7meshkvm', (mesh.mtype == 2) && ((deskState == false) || (desktop.contype == 1)));
// Enable buttons
@ -3184,6 +3200,11 @@
QE('connectbutton1', online);
var hwonline = ((currentNode.conn & 6) != 0); // If CIRA (2) or AMT (4) connected, enable hardware terminal
QE('connectbutton1h', hwonline);
QE('deskSaveBtn', deskState == 3);
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (deskState != 0) && (desktopsettings.showfocus));
QE('DeskCAD', deskState == 3);
QE('DeskToolsButton', online);
if (online == false) QV('DeskTools', false);
}
// Debug
@ -3232,12 +3253,9 @@
var xstate = state;
if ((xstate == 3) && (xdesktop.contype == 2)) { xstate++; }
var str = StatusStrs[xstate];
if (desktop.webRtcActive == true) { str += ', WebRTC'; }
if ((desktop != null) && (desktop.webRtcActive == true)) { str += ', WebRTC'; }
//if (desktop.m.stopInput == true) { str += ', Loopback'; }
QH('deskstatus', str);
QE('deskSaveBtn', state == 3);
QV('deskFocusBtn', (desktop != null) && (desktop.contype == 2) && (state != 0) && (desktopsettings.showfocus));
QE('DeskCAD', state == 3);
switch (state) {
case 0:
// Disconnect and clean up the remote desktop
@ -3364,6 +3382,44 @@
desktop.m.sendcad();
}
// Show process dialogs
function toggleDeskTools() {
if (xxdialogMode) return;
if (QS('DeskTools').display == 'none') {
QV('DeskTools', true);
Q('DeskTools').nodeid = currentNode._id;
refreshDeskTools();
} else {
QV('DeskTools', false);
}
}
// Refresh all of the desktop tool panels
function refreshDeskTools() {
QV('DeskToolsRefreshButton', false);
setTimeout(refreshDeskToolsEx, 500);
meshserver.send({ action: 'msg', type:'ps', nodeid: currentNode._id });
}
function refreshDeskToolsEx() { QV('DeskToolsRefreshButton', true); }
var deskTools = { sort: 1, msg: null };
function sortProcess(sort) { deskTools.sort = sort; showDeskToolsProcesses(deskTools.msg); }
function sortProcessPid(a, b) { if (a.p > b.p) return 1; if (a.p < b.p) return (-1); return 0; }
function sortProcessName(a, b) { if (a.d > b.d) return 1; if (a.d < b.d) return (-1); return 0; }
function showDeskToolsProcesses(message) {
deskTools.msg = message;
if (message == null) { QH('DeskToolsProcesses', ''); return; }
if (Q('DeskTools').nodeid != message.nodeid) return;
var p = [], processes = null;
try { processes = JSON.parse(message.value); } catch (e) { }
if (processes != null) {
for (var pid in processes) { p.push( { p:parseInt(pid), c:processes[pid].cmd, d:processes[pid].cmd.toLowerCase(), u: processes[pid].user } ); }
if (deskTools.sort == 0) { p.sort(sortProcessPid); } else if (deskTools.sort == 1) { p.sort(sortProcessName); }
var x = '';
for (var i in p) { if (p[i].p != 0) { x += '<div class=deskToolsBar><div style=width:50px;float:left;text-align:right;padding-right:5px>' + p[i].p + '</div><a style=float:right;padding-right:5px;cursor:pointer title="Stop process" onclick=stopProcess(' + p[i].p + ',"' + p[i].c + '")><img width=10 height=10 src="images/trash.png"></a><div style=float:right;padding-right:5px>' + (p[i].u?p[i].u:'') + '</div><div>' + p[i].c + '</div></div>'; } }
QH('DeskToolsProcesses', x);
}
}
// Toggle mouse and keyboard input
function toggleKvmControl() { putstore('DeskControl', (Q("DeskControl").checked?1:0)); }
@ -3393,6 +3449,8 @@
function dmousemove(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked) desktop.m.mousemove(e) }
function dmousewheel(e) { if (!xxdialogMode && desktop != null && Q('DeskControl').checked) { desktop.m.mousewheel(e); haltEvent(e); return true; } return false; }
function drotate(x) { if (!xxdialogMode && desktop != null) { desktop.m.setRotation(desktop.m.rotation + x); deskAdjust(); deskAdjust(); } }
function stopProcess(id, name) { setDialogMode(2, "Process Control", 3, stopProcessEx, 'Stop process #' + id + ' "' + name + '"?', id); }
function stopProcessEx(buttons, tag) { meshserver.send({ action: 'msg', type:'pskill', nodeid: currentNode._id, value: tag }); setTimeout(refreshDeskTools, 300); }
//
// TERMINAL