Removed task support from agent (for now).
Ylian Saint-Hilaire committed
Aug 21, 2021 at 23:07 UTC
fbe37b294e9a55d73560486da1e192a55aa11d77
2 files changed
+2
-455
agents/meshcore.js
+2
-1
@@ -640,7 +640,8 @@ function getIpLocationDataEx(func) {
640
}
641
642
// Setup script task. Allows running scripts at scheduled intervals
643
-var scriptTask = require('scripttask');
643
+var scriptTask = null;
644
+try { scriptTask = require('scripttask'); } catch (ex) { }
645
646
// Remove all Gateway MAC addresses for interface list. This is useful because the gateway MAC is not always populated reliably.
647
function clearGatewayMac(str) {
agents/modules_meshcore/scripttask.js
deleted
-454
@@ -1,454 +0,0 @@
1
-/**
2
-* @description MeshCentral ScriptTask plugin
3
-* @author Ryan Blenis
4
-* @copyright
5
-* @license Apache-2.0
6
-*/
7
-
8
-'use strict';
9
-var mesh = require('MeshAgent');
10
-var db = require('SimpleDataStore').Shared();
11
-var pendingDownload = [];
12
-var debug_flag = false;
13
-var runningJobs = [];
14
-var runningJobPIDs = {};
15
-
16
-var dbg = function (str, sessionid) {
17
- if (debug_flag || sessionid) { sendConsoleText(str, sessionid); } sendConsoleText
18
- if (debug_flag !== true) return;
19
- var logStream = require('fs').createWriteStream('scripttask.txt', { 'flags': 'a' });
20
- // use {'flags': 'a'} to append and {'flags': 'w'} to erase and write a new file
21
- logStream.write(new Date().toLocaleString() + ': ' + str + '\n');
22
- logStream.end('\n');
23
-}
24
-
25
-Array.prototype.remove = function (from, to) {
26
- var rest = this.slice((to || from) + 1 || this.length);
27
- this.length = (from < 0) ? this.length + from : from;
28
- return this.push.apply(this, rest);
29
-};
30
-
31
-function processCommand(args, rights, sessionid) {
32
- switch (args.cmd) {
33
- case 'trigger':
34
- var jObj = {
35
- jobId: args.jobId,
36
- scriptId: args.scriptId,
37
- replaceVars: args.replaceVars,
38
- scriptHash: args.scriptHash,
39
- dispatchTime: args.dispatchTime
40
- };
41
- //dbg('jObj args is ' + JSON.stringify(jObj), sessionid);
42
- var sObj = getScriptFromCache(jObj.scriptId);
43
- //dbg('sobj = ' + JSON.stringify(sObj) + ', shash = ' + jObj.scriptHash, sessionid);
44
- if ((sObj == null) ||( sObj.contentHash != jObj.scriptHash)) {
45
- // Get from the server, then run
46
- //dbg('Getting and caching script '+ jObj.scriptId, sessionid);
47
- mesh.SendCommand({ 'action': 'scripttask', 'task': 'get', 'scriptId': jObj.scriptId, 'sessionid': sessionid });
48
- pendingDownload.push(jObj);
49
- } else {
50
- // Ready to run
51
- runScript(sObj, jObj, sessionid);
52
- }
53
- break;
54
- case 'set':
55
- var sObj = args.script;
56
- cacheScript(sObj);
57
- var setRun = [];
58
- if (pendingDownload.length) {
59
- pendingDownload.forEach(function (pd, k) {
60
- if ((pd.scriptId == sObj._id) && (pd.scriptHash == sObj.contentHash)) {
61
- if (setRun.indexOf(pd) === -1) {
62
- runScript(sObj, pd, sessionid);
63
- setRun.push(pd);
64
- }
65
- pendingDownload.remove(k);
66
- }
67
- });
68
- }
69
- break;
70
- case 'clear':
71
- clearScriptCache();
72
- mesh.SendCommand({ 'action': 'scripttask', 'task': 'clear', 'sessionid': sessionid });
73
- return 'Cache cleared. All pending tasks cleared.';
74
- case 'clearcache':
75
- clearScriptCache();
76
- return 'The script cache has been cleared';
77
- case 'debug':
78
- debug_flag = (debug_flag) ? false : true;
79
- var str = (debug_flag) ? 'on' : 'off';
80
- return 'Debugging is now ' + str;
81
- case 'pending':
82
- var ret = '';
83
- if (pendingDownload.length == 0) return "No jobs pending script download";
84
- pendingDownload.forEach(function (pd, k) {
85
- ret += 'Task ' + k + ': ' + 'TaskID: ' + pd.jobId + ' ScriptID: ' + pd.scriptId;
86
- });
87
- return ret;
88
- default:
89
- if (sessionid == null) return;
90
- if (args['_'][0] == null) { return 'Supported task commands: pending, trigger, clear, clearcache, debug'; }
91
- return 'Unknown action: ' + args['_'][0] + ' with data ' + JSON.stringify(args);
92
- }
93
-}
94
-
95
-function finalizeJob(job, retVal, errVal, sessionid) {
96
- if (errVal != null && errVal.stack != null) errVal = errVal.stack;
97
- runningJobs.remove(runningJobs.indexOf(job.jobId));
98
- if (typeof runningJobPIDs[job.jobId] != 'undefined') delete runningJobPIDs[job.jobId];
99
- mesh.SendCommand({
100
- 'action': 'scripttask',
101
- 'task': 'complete',
102
- 'jobId': job.jobId,
103
- 'scriptId': job.scriptId,
104
- 'retVal': retVal,
105
- 'errVal': errVal,
106
- 'dispatchTime': job.dispatchTime, // Include original run time (long running tasks could have tried a re-send)
107
- 'sessionid': sessionid
108
- });
109
-}
110
-
111
-//@TODO Test powershell on *nix devices with and without powershell installed
112
-function runPowerShell(sObj, jObj, sessionid) {
113
- if (process.platform != 'win32') return runPowerShellNonWin(sObj, jObj);
114
- const fs = require('fs');
115
- var rand = Math.random().toString(32).replace('0.', '');
116
-
117
- var oName = 'st' + rand + '.txt';
118
- var pName = 'st' + rand + '.ps1';
119
- var pwshout = '', pwsherr = '', cancontinue = false;
120
- try {
121
- fs.writeFileSync(pName, sObj.content);
122
- var outstr = '', errstr = '';
123
- var child = require('child_process').execFile(process.env['windir'] + '\\system32\\WindowsPowerShell\\v1.0\\powershell.exe', ['-NoLogo']);
124
- child.xxsessionid = sessionid;
125
- child.stderr.on('data', function (chunk) { errstr += chunk; });
126
- child.stdout.on('data', function (chunk) { });
127
- runningJobPIDs[jObj.jobId] = child.pid;
128
- child.stdin.write('.\\' + pName + ' | Out-File ' + oName + ' -Encoding UTF8\r\n');
129
- child.on('exit', function (procRetVal, procRetSignal) {
130
- dbg('Exiting with ' + procRetVal + ', Signal: ' + procRetSignal);
131
- if (errstr != '') {
132
- finalizeJob(jObj, null, errstr, this.xxsessionid);
133
- try {
134
- fs.unlinkSync(oName);
135
- fs.unlinkSync(pName);
136
- } catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
137
- return;
138
- }
139
- if (procRetVal == 1) {
140
- finalizeJob(jObj, null, 'Process terminated unexpectedly.', this.xxsessionid);
141
- try {
142
- fs.unlinkSync(oName);
143
- fs.unlinkSync(pName);
144
- } catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
145
- return;
146
- }
147
- try {
148
- outstr = fs.readFileSync(oName, 'utf8').toString();
149
- } catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
150
- if (outstr) {
151
- //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
152
- try { outstr = outstr.trim(); } catch (e) { }
153
- } else {
154
- outstr = (procRetVal) ? 'Failure' : 'Success';
155
- }
156
- dbg('Output is: ' + outstr, this.xxsessionid);
157
- finalizeJob(jObj, outstr, this.xxsessionid);
158
- try {
159
- fs.unlinkSync(oName);
160
- fs.unlinkSync(pName);
161
- } catch (e) { }
162
- });
163
- child.stdin.write('exit\r\n');
164
- //child.waitExit(); // this was causing the event loop to stall on long-running scripts, switched to '.on exit'
165
- } catch (e) {
166
- dbg('Error block was (PowerShell): ' + e, sessionid);
167
- finalizeJob(jObj, null, e, sessionid);
168
- }
169
-}
170
-
171
-function runPowerShellNonWin(sObj, jObj, sessionid) {
172
- const fs = require('fs');
173
- var rand = Math.random().toString(32).replace('0.', '');
174
- var path = '';
175
- var pathTests = [
176
- '/usr/local/mesh',
177
- '/tmp',
178
- '/usr/local/mesh_services/meshagent',
179
- '/var/tmp'
180
- ];
181
- pathTests.forEach(function (p) {
182
- if (path == '' && fs.existsSync(p)) { path = p; }
183
- });
184
- dbg('Path chosen is: ' + path, sessionid);
185
- path = path + '/';
186
-
187
- var oName = 'st' + rand + '.txt';
188
- var pName = 'st' + rand + '.ps1';
189
- var pwshout = '', pwsherr = '', cancontinue = false;
190
- try {
191
- var childp = require('child_process').execFile('/bin/sh', ['sh']);
192
- childp.stderr.on('data', function (chunk) { pwsherr += chunk; });
193
- childp.stdout.on('data', function (chunk) { pwshout += chunk; });
194
- childp.stdin.write('which pwsh' + '\n');
195
- childp.stdin.write('exit\n');
196
- childp.waitExit();
197
- } catch (e) { finalizeJob(jObj, null, "Couldn't determine pwsh in env: " + e, sessionid); }
198
- if (pwsherr != '') {
199
- finalizeJob(jObj, null, "PowerShell env determination error: " + pwsherr, sessionid);
200
- return;
201
- }
202
- if (pwshout.trim() != '') {
203
- cancontinue = true;
204
- }
205
- if (cancontinue === false) { finalizeJob(jObj, null, "PowerShell is not installed"); return; }
206
- try {
207
- fs.writeFileSync(path + pName, '#!' + pwshout + '\n' + sObj.content.split('\r\n').join('\n').split('\r').join('\n'));
208
- var outstr = '', errstr = '';
209
- var child = require('child_process').execFile('/bin/sh', ['sh']);
210
- child.xxsessionid = sessionid;
211
- child.stderr.on('data', function (chunk) { errstr += chunk; });
212
- child.stdout.on('data', function (chunk) { });
213
- runningJobPIDs[jObj.jobId] = child.pid;
214
- child.stdin.write('cd ' + path + '\n');
215
- child.stdin.write('chmod a+x ' + pName + '\n');
216
- child.stdin.write('./' + pName + ' > ' + oName + '\n');
217
- child.on('exit', function (procRetVal, procRetSignal) {
218
- if (errstr != '') {
219
- finalizeJob(jObj, null, errstr);
220
- try {
221
- fs.unlinkSync(path + oName);
222
- fs.unlinkSync(path + pName);
223
- } catch (e) { dbg('Could not unlink files, error was: ' + e + ' for path ' + path, this.xxsessionid); }
224
- return;
225
- }
226
- if (procRetVal == 1) {
227
- finalizeJob(jObj, null, 'Process terminated unexpectedly.');
228
- try {
229
- fs.unlinkSync(path + oName);
230
- fs.unlinkSync(path + pName);
231
- } catch (e) { dbg('Could not unlink files1, error was: ' + e + ' for path ' + path, this.xxsessionid); }
232
- return;
233
- }
234
- try {
235
- outstr = fs.readFileSync(path + oName, 'utf8').toString();
236
- } catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
237
- if (outstr) {
238
- //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
239
- try { outstr = outstr.trim(); } catch (e) { }
240
- } else {
241
- outstr = (procRetVal) ? 'Failure' : 'Success';
242
- }
243
- dbg('Output is: ' + outstr);
244
- finalizeJob(jObj, outstr);
245
- try {
246
- fs.unlinkSync(path + oName);
247
- fs.unlinkSync(path + pName);
248
- } catch (e) { dbg('Could not unlink files2, error was: ' + e + ' for path ' + path, this.xxsessionid); }
249
- });
250
- child.stdin.write('exit\n');
251
- } catch (e) {
252
- dbg('Error block was (PowerShellNonWin): ' + e, sessionid);
253
- finalizeJob(jObj, null, e, sessionid);
254
- }
255
-}
256
-
257
-function runBat(sObj, jObj, sessionid) {
258
- if (process.platform != 'win32') { finalizeJob(jObj, null, 'Platform not supported.', sessionid); return; }
259
- const fs = require('fs');
260
- var rand = Math.random().toString(32).replace('0.', '');
261
- var oName = 'st' + rand + '.txt';
262
- var pName = 'st' + rand + '.bat';
263
- try {
264
- fs.writeFileSync(pName, sObj.content);
265
- var outstr = '', errstr = '';
266
- var child = require('child_process').execFile(process.env['windir'] + '\\system32\\cmd.exe');
267
- child.xxsessionid = sessionid;
268
- child.stderr.on('data', function (chunk) { errstr += chunk; });
269
- child.stdout.on('data', function (chunk) { });
270
- runningJobPIDs[jObj.jobId] = child.pid;
271
- child.stdin.write(pName + ' > ' + oName + '\r\n');
272
- child.stdin.write('exit\r\n');
273
- child.on('exit', function (procRetVal, procRetSignal) {
274
- if (errstr != '') {
275
- try {
276
- fs.unlinkSync(oName);
277
- fs.unlinkSync(pName);
278
- } catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
279
- finalizeJob(jObj, null, errstr, this.xxsessionid);
280
- return;
281
- }
282
- if (procRetVal == 1) {
283
- try {
284
- fs.unlinkSync(oName);
285
- fs.unlinkSync(pName);
286
- } catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
287
- finalizeJob(jObj, null, 'Process terminated unexpectedly.', this.xxsessionid);
288
- return;
289
- }
290
- try {
291
- outstr = fs.readFileSync(oName, 'utf8').toString();
292
- } catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
293
- if (outstr) {
294
- //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
295
- try { outstr = outstr.trim(); } catch (e) { }
296
- } else {
297
- outstr = (procRetVal) ? 'Failure' : 'Success';
298
- }
299
- dbg('Output is: ' + outstr);
300
- try {
301
- fs.unlinkSync(oName);
302
- fs.unlinkSync(pName);
303
- } catch (e) { dbg('Could not unlink files, error was: ' + e, this.xxsessionid); }
304
- finalizeJob(jObj, outstr, null, this.xxsessionid);
305
- });
306
- } catch (e) {
307
- dbg('Error block was (BAT): ' + e, sessionid);
308
- finalizeJob(jObj, null, e, sessionid);
309
- }
310
-}
311
-
312
-function runBash(sObj, jObj, sessionid) {
313
- if (process.platform == 'win32') {
314
- finalizeJob(jObj, null, 'Platform not supported.', sessionid);
315
- return;
316
- }
317
- //dbg('proc is ' + JSON.stringify(process));
318
- const fs = require('fs');
319
- var path = '';
320
- var pathTests = [
321
- '/usr/local/mesh',
322
- '/tmp',
323
- '/usr/local/mesh_services/meshagent',
324
- '/var/tmp'
325
- ];
326
- pathTests.forEach(function (p) {
327
- if (path == '' && fs.existsSync(p)) { path = p; }
328
- });
329
- dbg('Path chosen is: ' + path, sessionid);
330
- path = path + '/';
331
- //var child = require('child_process');
332
- //child.execFile(process.env['windir'] + '\\system32\\cmd.exe', ['/c', 'RunDll32.exe user32.dll,LockWorkStation'], { type: 1 });
333
-
334
- var rand = Math.random().toString(32).replace('0.', '');
335
- var oName = 'st' + rand + '.txt';
336
- var pName = 'st' + rand + '.sh';
337
- try {
338
- fs.writeFileSync(path + pName, sObj.content);
339
- var outstr = '', errstr = '';
340
- var child = require('child_process').execFile('/bin/sh', ['sh']);
341
- child.xxsessionid = sessionid;
342
- child.stderr.on('data', function (chunk) { errstr += chunk; });
343
- child.stdout.on('data', function (chunk) { });
344
- runningJobPIDs[jObj.jobId] = child.pid;
345
- child.stdin.write('cd ' + path + '\n');
346
- child.stdin.write('chmod a+x ' + pName + '\n');
347
- child.stdin.write('./' + pName + ' > ' + oName + '\n');
348
- child.stdin.write('exit\n');
349
- child.on('exit', function (procRetVal, procRetSignal) {
350
- if (errstr != '') {
351
- try {
352
- fs.unlinkSync(path + oName);
353
- fs.unlinkSync(path + pName);
354
- } catch (e) { dbg('Could not unlink files, error was: ' + e + ' for path ' + path, this.xxsessionid); }
355
- finalizeJob(jObj, null, errstr, this.xxsessionid);
356
- return;
357
- }
358
- if (procRetVal == 1) {
359
- try {
360
- fs.unlinkSync(path + oName);
361
- fs.unlinkSync(path + pName);
362
- } catch (e) { dbg('Could not unlink files1, error was: ' + e + ' for path ' + path, this.xxsessionid); }
363
- finalizeJob(jObj, null, 'Process terminated unexpectedly.', this.xxsessionid);
364
- return;
365
- }
366
- try {
367
- outstr = fs.readFileSync(path + oName, 'utf8').toString();
368
- } catch (e) { outstr = (procRetVal) ? 'Failure' : 'Success'; }
369
- if (outstr) {
370
- //outstr = outstr.replace(/[^\x20-\x7E]/g, '');
371
- try { outstr = outstr.trim(); } catch (e) { }
372
- } else {
373
- outstr = (procRetVal) ? 'Failure' : 'Success';
374
- }
375
- dbg('Output is: ' + outstr);
376
- try {
377
- fs.unlinkSync(path + oName);
378
- fs.unlinkSync(path + pName);
379
- } catch (e) { dbg('Could not unlink files2, error was: ' + e + ' for path ' + path, this.xxsessionid); }
380
- finalizeJob(jObj, outstr, null, this.xxsessionid);
381
- });
382
- } catch (e) {
383
- dbg('Error block was (bash): ' + e, sessionid);
384
- finalizeJob(jObj, null, e, sessionid);
385
- }
386
-}
387
-
388
-function jobIsRunning(jObj) {
389
- if (runningJobs.indexOf(jObj.jobId) === -1) return false;
390
- return true;
391
-}
392
-
393
-function runScript(sObj, jObj, sessionid) {
394
- // get current processes and clean running jobs if they are no longer running (computer fell asleep, user caused process to stop, etc.)
395
- if ((process.platform != 'linux') && runningJobs.length) { // linux throws errors here in the meshagent for some reason
396
- require('process-manager').getProcesses(function (plist) {
397
- dbg('Got process list', sessionid);
398
- dbg('There are currently ' + runningJobs.length + ' running jobs.', sessionid);
399
- if (runningJobs.length) {
400
- runningJobs.forEach(function (jobId, idx) {
401
- dbg('Checking for running job: ' + jobId + ' with PID ' + runningJobPIDs[jobId], sessionid);
402
- if (typeof plist[runningJobPIDs[jobId]] == 'undefined' || typeof plist[runningJobPIDs[jobId]].cmd != 'string') {
403
- dbg('Found job with no process. Removing running status.', sessionid);
404
- delete runningJobPIDs[jobId];
405
- runningJobs.remove(runningJobs.indexOf(idx));
406
- //dbg('RunningJobs: ' + JSON.stringify(runningJobs));
407
- //dbg('RunningJobsPIDs: ' + JSON.stringify(runningJobPIDs));
408
- }
409
- });
410
- }
411
- });
412
- }
413
- if (jobIsRunning(jObj)) { dbg('Job already running job id [' + jObj.jobId + ']. Skipping.', sessionid); return; }
414
- if (jObj.replaceVars != null) {
415
- Object.getOwnPropertyNames(jObj.replaceVars).forEach(function (key) {
416
- var val = jObj.replaceVars[key];
417
- sObj.content = sObj.content.replace(new RegExp('#' + key + '#', 'g'), val);
418
- dbg('replacing var ' + key + ' with ' + val, sessionid);
419
- });
420
- sObj.content = sObj.content.replace(new RegExp('#(.*?)#', 'g'), 'VAR_NOT_FOUND');
421
- }
422
- runningJobs.push(jObj.jobId);
423
- dbg('Running Script ' + sObj._id, sessionid);
424
- switch (sObj.filetype) {
425
- case 'ps1': runPowerShell(sObj, jObj, sessionid); break;
426
- case 'bat': runBat(sObj, jObj, sessionid); break;
427
- case 'bash': runBash(sObj, jObj, sessionid); break;
428
- default: dbg('Unknown filetype: ' + sObj.filetype, sessionid);
429
- break;
430
- }
431
-}
432
-function getScriptFromCache(id) {
433
- var script = db.Get('scriptTask_script_' + id);
434
- if (script == '' || script == null) return null;
435
- try { script = JSON.parse(script); } catch (e) { return null; }
436
- return script;
437
-}
438
-function cacheScript(sObj) {
439
- db.Put('scriptTask_script_' + sObj._id, sObj);
440
-}
441
-function clearScriptCache() {
442
- db.Keys.forEach(function (k) {
443
- if (k.indexOf('scriptTask_script_') === 0) {
444
- db.Put(k, null);
445
- db.Delete(k);
446
- }
447
- });
448
-}
449
-
450
-function sendConsoleText(text, sessionid) {
451
- require('MeshAgent').SendCommand({ 'action': 'msg', 'type': 'console', 'value': (typeof text == 'string') ? text : JSON.stringify(text), 'sessionid': sessionid });
452
-}
453
-
454
-module.exports = { processCommand: processCommand };
\ No newline at end of file