mirror of
https://github.com/Ylianst/MeshCentral.git
synced 2024-11-22 12:52:50 +03:00
Removed task support from agent (for now).
This commit is contained in:
parent
08f9b0666f
commit
fbe37b294e
@ -640,7 +640,8 @@ function getIpLocationDataEx(func) {
|
||||
}
|
||||
|
||||
// Setup script task. Allows running scripts at scheduled intervals
|
||||
var scriptTask = require('scripttask');
|
||||
var scriptTask = null;
|
||||
try { scriptTask = require('scripttask'); } catch (ex) { }
|
||||
|
||||
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
|
||||
function clearGatewayMac(str) {
|
||||
|
@ -1,454 +0,0 @@
|
||||
/**
|
||||
* @description MeshCentral ScriptTask plugin
|
||||
* @author Ryan Blenis
|
||||
* @copyright
|
||||
* @license Apache-2.0
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
var mesh = require('MeshAgent');
|
||||
var db = require('SimpleDataStore').Shared();
|
||||
var pendingDownload = [];
|
||||
var debug_flag = false;
|
||||
var runningJobs = [];
|
||||
var runningJobPIDs = {};
|
||||
|
||||
var dbg = function (str, sessionid) {
|
||||
if (debug_flag || sessionid) { sendConsoleText(str, sessionid); } sendConsoleText
|
||||
if (debug_flag !== true) return;
|
||||
var logStream = require('fs').createWriteStream('scripttask.txt', { 'flags': 'a' });
|
||||
// use {'flags': 'a'} to append and {'flags': 'w'} to erase and write a new file
|
||||
logStream.write(new Date().toLocaleString() + ': ' + str + '\n');
|
||||
logStream.end('\n');
|
||||
}
|
||||
|
||||
Array.prototype.remove = function (from, to) {
|
||||
var rest = this.slice((to || from) + 1 || this.length);
|
||||
this.length = (from < 0) ? this.length + from : from;
|
||||
return this.push.apply(this, rest);
|
||||
};
|
||||
|
||||
function processCommand(args, rights, sessionid) {
|
||||
switch (args.cmd) {
|
||||
case 'trigger':
|
||||
var jObj = {
|
||||
jobId: args.jobId,
|
||||
scriptId: args.scriptId,
|
||||
replaceVars: args.replaceVars,
|
||||
scriptHash: args.scriptHash,
|
||||
dispatchTime: args.dispatchTime
|
||||
};
|
||||
//dbg('jObj args is ' + JSON.stringify(jObj), sessionid);
|
||||
var sObj = getScriptFromCache(jObj.scriptId);
|
||||
//dbg('sobj = ' + JSON.stringify(sObj) + ', shash = ' + jObj.scriptHash, sessionid);
|
||||
if ((sObj == null) ||( sObj.contentHash != jObj.scriptHash)) {
|
||||
// Get from the server, then run
|
||||
//dbg('Getting and caching script '+ jObj.scriptId, sessionid);
|
||||
mesh.SendCommand({ 'action': 'scripttask', 'task': 'get', 'scriptId': jObj.scriptId, 'sessionid': sessionid });
|
||||
pendingDownload.push(jObj);
|
||||
} else {
|
||||
// Ready to run
|
||||
runScript(sObj, jObj, sessionid);
|
||||
}
|
||||
break;
|
||||
case 'set':
|
||||
var sObj = args.script;
|
||||
cacheScript(sObj);
|
||||
var setRun = [];
|
||||
if (pendingDownload.length) {
|
||||
pendingDownload.forEach(function (pd, k) {
|
||||
if ((pd.scriptId == sObj._id) && (pd.scriptHash == sObj.contentHash)) {
|
||||
if (setRun.indexOf(pd) === -1) {
|
||||
runScript(sObj, pd, sessionid);
|
||||
setRun.push(pd);
|
||||
}
|
||||
pendingDownload.remove(k);
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
case 'clear':
|
||||
clearScriptCache();
|
||||
mesh.SendCommand({ 'action': 'scripttask', 'task': 'clear', 'sessionid': sessionid });
|
||||
return 'Cache cleared. All pending tasks cleared.';
|
||||
case 'clearcache':
|
||||
clearScriptCache();
|
||||
return 'The script cache has been cleared';
|
||||
case 'debug':
|
||||
debug_flag = (debug_flag) ? false : true;
|
||||
var str = (debug_flag) ? 'on' : 'off';
|
||||
return 'Debugging is now ' + str;
|
||||
case 'pending':
|
||||
var ret = '';
|
||||
if (pendingDownload.length == 0) return "No jobs pending script download";
|
||||
pendingDownload.forEach(function (pd, k) {
|
||||
ret += 'Task ' + k + ': ' + 'TaskID: ' + pd.jobId + ' ScriptID: ' + pd.scriptId;
|
||||
});
|
||||
return ret;
|
||||
default:
|
||||
if (sessionid == null) return;
|
||||
if (args['_'][0] == null) { return 'Supported task commands: pending, trigger, clear, clearcache, debug'; }
|
||||
return 'Unknown action: ' + args['_'][0] + ' with data ' + JSON.stringify(args);
|
||||
}
|
||||
}
|
||||
|
||||
function finalizeJob(job, retVal, errVal, sessionid) {
|
||||
if (errVal != null && errVal.stack != null) errVal = errVal.stack;
|
||||
runningJobs.remove(runningJobs.indexOf(job.jobId));
|
||||
if (typeof runningJobPIDs[job.jobId] != 'undefined') delete runningJobPIDs[job.jobId];
|
||||
mesh.SendCommand({
|
||||
'action': 'scripttask',
|
||||
'task': 'complete',
|
||||
'jobId': job.jobId,
|
||||
'scriptId': job.scriptId,
|
||||
'retVal': retVal,
|
||||
'errVal': errVal,
|
||||
'dispatchTime': job.dispatchTime, // Include original run time (long running tasks could have tried a re-send)
|
||||
'sessionid': sessionid
|
||||
});
|
||||
}
|
||||
|
||||
//@TODO Test powershell on *nix devices with and without powershell installed
|
||||
function runPowerShell(sObj, jObj, sessionid) {
|
||||
if (process.platform != 'win32') return runPowerShellNonWin(sObj, jObj);
|
||||
const fs = require('fs');
|
||||
var rand = Math.random().toString(32).replace('0.', '');
|
||||
|
||||
var oName = 'st' + rand + '.txt';
|
||||
var pName = 'st' + rand + '.ps1';
|
||||
var pwshout = '', pwsherr = '', cancontinue = false;
|
||||
try {
|
||||
fs.writeFileSync(pName, sObj.content);
|
||||
var outstr = '', errstr = '';
|
||||
var child = require('child_process').execFile(process.env['windir'] + '\\system32\\WindowsPowerShell\\v1.0\\powershell.exe', ['-NoLogo']);
|
||||
child.xxsessionid = sessionid;
|
||||
child.stderr.on('data', function (chunk) { errstr += chunk; });
|
||||
child.stdout.on('data', function (chunk) { });
|
||||
runningJobPIDs[jObj.jobId] = child.pid;
|
||||
child.stdin.write('.\\' + pName + ' | Out-File ' + oName + ' -Encoding UTF8\r\n');
|
||||
child.on('exit', function (procRetVal, procRetSignal) {
|
||||
dbg('Exiting with ' + procRetVal + ', Signal: ' + procRetSignal);
|
||||
if (errstr != '') {
|
||||
finalizeJob(jObj, null, errstr, this.xxsessionid);
|
||||
try {
|
||||
fs.unlinkSync(oName);
|
||||
fs.unlinkSync(pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
|
||||
return;
|
||||
}
|
||||
if (procRetVal == 1) {
|
||||
finalizeJob(jObj, null, 'Process terminated unexpectedly.', this.xxsessionid);
|
||||
try {
|
||||
fs.unlinkSync(oName);
|
||||
fs.unlinkSync(pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
|
||||
return;
|
||||
}
|
||||
try {
|
||||
outstr = fs.readFileSync(oName, 'utf8').toString();
|
||||
} catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
|
||||
if (outstr) {
|
||||
//outstr = outstr.replace(/[^\x20-\x7E]/g, '');
|
||||
try { outstr = outstr.trim(); } catch (e) { }
|
||||
} else {
|
||||
outstr = (procRetVal) ? 'Failure' : 'Success';
|
||||
}
|
||||
dbg('Output is: ' + outstr, this.xxsessionid);
|
||||
finalizeJob(jObj, outstr, this.xxsessionid);
|
||||
try {
|
||||
fs.unlinkSync(oName);
|
||||
fs.unlinkSync(pName);
|
||||
} catch (e) { }
|
||||
});
|
||||
child.stdin.write('exit\r\n');
|
||||
//child.waitExit(); // this was causing the event loop to stall on long-running scripts, switched to '.on exit'
|
||||
} catch (e) {
|
||||
dbg('Error block was (PowerShell): ' + e, sessionid);
|
||||
finalizeJob(jObj, null, e, sessionid);
|
||||
}
|
||||
}
|
||||
|
||||
function runPowerShellNonWin(sObj, jObj, sessionid) {
|
||||
const fs = require('fs');
|
||||
var rand = Math.random().toString(32).replace('0.', '');
|
||||
var path = '';
|
||||
var pathTests = [
|
||||
'/usr/local/mesh',
|
||||
'/tmp',
|
||||
'/usr/local/mesh_services/meshagent',
|
||||
'/var/tmp'
|
||||
];
|
||||
pathTests.forEach(function (p) {
|
||||
if (path == '' && fs.existsSync(p)) { path = p; }
|
||||
});
|
||||
dbg('Path chosen is: ' + path, sessionid);
|
||||
path = path + '/';
|
||||
|
||||
var oName = 'st' + rand + '.txt';
|
||||
var pName = 'st' + rand + '.ps1';
|
||||
var pwshout = '', pwsherr = '', cancontinue = false;
|
||||
try {
|
||||
var childp = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
childp.stderr.on('data', function (chunk) { pwsherr += chunk; });
|
||||
childp.stdout.on('data', function (chunk) { pwshout += chunk; });
|
||||
childp.stdin.write('which pwsh' + '\n');
|
||||
childp.stdin.write('exit\n');
|
||||
childp.waitExit();
|
||||
} catch (e) { finalizeJob(jObj, null, "Couldn't determine pwsh in env: " + e, sessionid); }
|
||||
if (pwsherr != '') {
|
||||
finalizeJob(jObj, null, "PowerShell env determination error: " + pwsherr, sessionid);
|
||||
return;
|
||||
}
|
||||
if (pwshout.trim() != '') {
|
||||
cancontinue = true;
|
||||
}
|
||||
if (cancontinue === false) { finalizeJob(jObj, null, "PowerShell is not installed"); return; }
|
||||
try {
|
||||
fs.writeFileSync(path + pName, '#!' + pwshout + '\n' + sObj.content.split('\r\n').join('\n').split('\r').join('\n'));
|
||||
var outstr = '', errstr = '';
|
||||
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.xxsessionid = sessionid;
|
||||
child.stderr.on('data', function (chunk) { errstr += chunk; });
|
||||
child.stdout.on('data', function (chunk) { });
|
||||
runningJobPIDs[jObj.jobId] = child.pid;
|
||||
child.stdin.write('cd ' + path + '\n');
|
||||
child.stdin.write('chmod a+x ' + pName + '\n');
|
||||
child.stdin.write('./' + pName + ' > ' + oName + '\n');
|
||||
child.on('exit', function (procRetVal, procRetSignal) {
|
||||
if (errstr != '') {
|
||||
finalizeJob(jObj, null, errstr);
|
||||
try {
|
||||
fs.unlinkSync(path + oName);
|
||||
fs.unlinkSync(path + pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e + ' for path ' + path, this.xxsessionid); }
|
||||
return;
|
||||
}
|
||||
if (procRetVal == 1) {
|
||||
finalizeJob(jObj, null, 'Process terminated unexpectedly.');
|
||||
try {
|
||||
fs.unlinkSync(path + oName);
|
||||
fs.unlinkSync(path + pName);
|
||||
} catch (e) { dbg('Could not unlink files1, error was: ' + e + ' for path ' + path, this.xxsessionid); }
|
||||
return;
|
||||
}
|
||||
try {
|
||||
outstr = fs.readFileSync(path + oName, 'utf8').toString();
|
||||
} catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
|
||||
if (outstr) {
|
||||
//outstr = outstr.replace(/[^\x20-\x7E]/g, '');
|
||||
try { outstr = outstr.trim(); } catch (e) { }
|
||||
} else {
|
||||
outstr = (procRetVal) ? 'Failure' : 'Success';
|
||||
}
|
||||
dbg('Output is: ' + outstr);
|
||||
finalizeJob(jObj, outstr);
|
||||
try {
|
||||
fs.unlinkSync(path + oName);
|
||||
fs.unlinkSync(path + pName);
|
||||
} catch (e) { dbg('Could not unlink files2, error was: ' + e + ' for path ' + path, this.xxsessionid); }
|
||||
});
|
||||
child.stdin.write('exit\n');
|
||||
} catch (e) {
|
||||
dbg('Error block was (PowerShellNonWin): ' + e, sessionid);
|
||||
finalizeJob(jObj, null, e, sessionid);
|
||||
}
|
||||
}
|
||||
|
||||
function runBat(sObj, jObj, sessionid) {
|
||||
if (process.platform != 'win32') { finalizeJob(jObj, null, 'Platform not supported.', sessionid); return; }
|
||||
const fs = require('fs');
|
||||
var rand = Math.random().toString(32).replace('0.', '');
|
||||
var oName = 'st' + rand + '.txt';
|
||||
var pName = 'st' + rand + '.bat';
|
||||
try {
|
||||
fs.writeFileSync(pName, sObj.content);
|
||||
var outstr = '', errstr = '';
|
||||
var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe');
|
||||
child.xxsessionid = sessionid;
|
||||
child.stderr.on('data', function (chunk) { errstr += chunk; });
|
||||
child.stdout.on('data', function (chunk) { });
|
||||
runningJobPIDs[jObj.jobId] = child.pid;
|
||||
child.stdin.write(pName + ' > ' + oName + '\r\n');
|
||||
child.stdin.write('exit\r\n');
|
||||
child.on('exit', function (procRetVal, procRetSignal) {
|
||||
if (errstr != '') {
|
||||
try {
|
||||
fs.unlinkSync(oName);
|
||||
fs.unlinkSync(pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
|
||||
finalizeJob(jObj, null, errstr, this.xxsessionid);
|
||||
return;
|
||||
}
|
||||
if (procRetVal == 1) {
|
||||
try {
|
||||
fs.unlinkSync(oName);
|
||||
fs.unlinkSync(pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
|
||||
finalizeJob(jObj, null, 'Process terminated unexpectedly.', this.xxsessionid);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
outstr = fs.readFileSync(oName, 'utf8').toString();
|
||||
} catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
|
||||
if (outstr) {
|
||||
//outstr = outstr.replace(/[^\x20-\x7E]/g, '');
|
||||
try { outstr = outstr.trim(); } catch (e) { }
|
||||
} else {
|
||||
outstr = (procRetVal) ? 'Failure' : 'Success';
|
||||
}
|
||||
dbg('Output is: ' + outstr);
|
||||
try {
|
||||
fs.unlinkSync(oName);
|
||||
fs.unlinkSync(pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
|
||||
finalizeJob(jObj, outstr, null, this.xxsessionid);
|
||||
});
|
||||
} catch (e) {
|
||||
dbg('Error block was (BAT): ' + e, sessionid);
|
||||
finalizeJob(jObj, null, e, sessionid);
|
||||
}
|
||||
}
|
||||
|
||||
function runBash(sObj, jObj, sessionid) {
|
||||
if (process.platform == 'win32') {
|
||||
finalizeJob(jObj, null, 'Platform not supported.', sessionid);
|
||||
return;
|
||||
}
|
||||
//dbg('proc is ' + JSON.stringify(process));
|
||||
const fs = require('fs');
|
||||
var path = '';
|
||||
var pathTests = [
|
||||
'/usr/local/mesh',
|
||||
'/tmp',
|
||||
'/usr/local/mesh_services/meshagent',
|
||||
'/var/tmp'
|
||||
];
|
||||
pathTests.forEach(function (p) {
|
||||
if (path == '' && fs.existsSync(p)) { path = p; }
|
||||
});
|
||||
dbg('Path chosen is: ' + path, sessionid);
|
||||
path = path + '/';
|
||||
//var child = require('child_process');
|
||||
//child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
|
||||
|
||||
var rand = Math.random().toString(32).replace('0.', '');
|
||||
var oName = 'st' + rand + '.txt';
|
||||
var pName = 'st' + rand + '.sh';
|
||||
try {
|
||||
fs.writeFileSync(path + pName, sObj.content);
|
||||
var outstr = '', errstr = '';
|
||||
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
||||
child.xxsessionid = sessionid;
|
||||
child.stderr.on('data', function (chunk) { errstr += chunk; });
|
||||
child.stdout.on('data', function (chunk) { });
|
||||
runningJobPIDs[jObj.jobId] = child.pid;
|
||||
child.stdin.write('cd ' + path + '\n');
|
||||
child.stdin.write('chmod a+x ' + pName + '\n');
|
||||
child.stdin.write('./' + pName + ' > ' + oName + '\n');
|
||||
child.stdin.write('exit\n');
|
||||
child.on('exit', function (procRetVal, procRetSignal) {
|
||||
if (errstr != '') {
|
||||
try {
|
||||
fs.unlinkSync(path + oName);
|
||||
fs.unlinkSync(path + pName);
|
||||
} catch (e) { dbg('Could not unlink files, error was: ' + e + ' for path ' + path, this.xxsessionid); }
|
||||
finalizeJob(jObj, null, errstr, this.xxsessionid);
|
||||
return;
|
||||
}
|
||||
if (procRetVal == 1) {
|
||||
try {
|
||||
fs.unlinkSync(path + oName);
|
||||
fs.unlinkSync(path + pName);
|
||||
} catch (e) { dbg('Could not unlink files1, error was: ' + e + ' for path ' + path, this.xxsessionid); }
|
||||
finalizeJob(jObj, null, 'Process terminated unexpectedly.', this.xxsessionid);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
outstr = fs.readFileSync(path + oName, 'utf8').toString();
|
||||
} catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
|
||||
if (outstr) {
|
||||
//outstr = outstr.replace(/[^\x20-\x7E]/g, '');
|
||||
try { outstr = outstr.trim(); } catch (e) { }
|
||||
} else {
|
||||
outstr = (procRetVal) ? 'Failure' : 'Success';
|
||||
}
|
||||
dbg('Output is: ' + outstr);
|
||||
try {
|
||||
fs.unlinkSync(path + oName);
|
||||
fs.unlinkSync(path + pName);
|
||||
} catch (e) { dbg('Could not unlink files2, error was: ' + e + ' for path ' + path, this.xxsessionid); }
|
||||
finalizeJob(jObj, outstr, null, this.xxsessionid);
|
||||
});
|
||||
} catch (e) {
|
||||
dbg('Error block was (bash): ' + e, sessionid);
|
||||
finalizeJob(jObj, null, e, sessionid);
|
||||
}
|
||||
}
|
||||
|
||||
function jobIsRunning(jObj) {
|
||||
if (runningJobs.indexOf(jObj.jobId) === -1) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
function runScript(sObj, jObj, sessionid) {
|
||||
// get current processes and clean running jobs if they are no longer running (computer fell asleep, user caused process to stop, etc.)
|
||||
if ((process.platform != 'linux') && runningJobs.length) { // linux throws errors here in the meshagent for some reason
|
||||
require('process-manager').getProcesses(function (plist) {
|
||||
dbg('Got process list', sessionid);
|
||||
dbg('There are currently ' + runningJobs.length + ' running jobs.', sessionid);
|
||||
if (runningJobs.length) {
|
||||
runningJobs.forEach(function (jobId, idx) {
|
||||
dbg('Checking for running job: ' + jobId + ' with PID ' + runningJobPIDs[jobId], sessionid);
|
||||
if (typeof plist[runningJobPIDs[jobId]] == 'undefined' || typeof plist[runningJobPIDs[jobId]].cmd != 'string') {
|
||||
dbg('Found job with no process. Removing running status.', sessionid);
|
||||
delete runningJobPIDs[jobId];
|
||||
runningJobs.remove(runningJobs.indexOf(idx));
|
||||
//dbg('RunningJobs: ' + JSON.stringify(runningJobs));
|
||||
//dbg('RunningJobsPIDs: ' + JSON.stringify(runningJobPIDs));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
if (jobIsRunning(jObj)) { dbg('Job already running job id [' + jObj.jobId + ']. Skipping.', sessionid); return; }
|
||||
if (jObj.replaceVars != null) {
|
||||
Object.getOwnPropertyNames(jObj.replaceVars).forEach(function (key) {
|
||||
var val = jObj.replaceVars[key];
|
||||
sObj.content = sObj.content.replace(new RegExp('#' + key + '#', 'g'), val);
|
||||
dbg('replacing var ' + key + ' with ' + val, sessionid);
|
||||
});
|
||||
sObj.content = sObj.content.replace(new RegExp('#(.*?)#', 'g'), 'VAR_NOT_FOUND');
|
||||
}
|
||||
runningJobs.push(jObj.jobId);
|
||||
dbg('Running Script ' + sObj._id, sessionid);
|
||||
switch (sObj.filetype) {
|
||||
case 'ps1': runPowerShell(sObj, jObj, sessionid); break;
|
||||
case 'bat': runBat(sObj, jObj, sessionid); break;
|
||||
case 'bash': runBash(sObj, jObj, sessionid); break;
|
||||
default: dbg('Unknown filetype: ' + sObj.filetype, sessionid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
function getScriptFromCache(id) {
|
||||
var script = db.Get('scriptTask_script_' + id);
|
||||
if (script == '' || script == null) return null;
|
||||
try { script = JSON.parse(script); } catch (e) { return null; }
|
||||
return script;
|
||||
}
|
||||
function cacheScript(sObj) {
|
||||
db.Put('scriptTask_script_' + sObj._id, sObj);
|
||||
}
|
||||
function clearScriptCache() {
|
||||
db.Keys.forEach(function (k) {
|
||||
if (k.indexOf('scriptTask_script_') === 0) {
|
||||
db.Put(k, null);
|
||||
db.Delete(k);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function sendConsoleText(text, sessionid) {
|
||||
require('MeshAgent').SendCommand({ 'action': 'msg', 'type': 'console', 'value': (typeof text == 'string') ? text : JSON.stringify(text), 'sessionid': sessionid });
|
||||
}
|
||||
|
||||
module.exports = { processCommand: processCommand };
|
Loading…
Reference in New Issue
Block a user