mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-06 00:13:33 +00:00
1146 lines
61 KiB
JavaScript
1146 lines
61 KiB
JavaScript
/*
|
|
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.
|
|
*/
|
|
|
|
|
|
function extractFileName(filePath)
|
|
{
|
|
if (typeof (filePath) == 'string')
|
|
{
|
|
var tokens = filePath.split('\\').join('/').split('/');
|
|
var name;
|
|
|
|
while ((name = tokens.pop()) == '');
|
|
return (name);
|
|
}
|
|
else
|
|
{
|
|
return(filePath.newName)
|
|
}
|
|
}
|
|
function extractFileSource(filePath)
|
|
{
|
|
return (typeof (filePath) == 'string' ? filePath : filePath.source);
|
|
}
|
|
|
|
function parseServiceStatus(token)
|
|
{
|
|
var j = {};
|
|
var serviceType = token.Deref(0, 4).IntVal;
|
|
j.isFileSystemDriver = ((serviceType & 0x00000002) == 0x00000002);
|
|
j.isKernelDriver = ((serviceType & 0x00000001) == 0x00000001);
|
|
j.isSharedProcess = ((serviceType & 0x00000020) == 0x00000020);
|
|
j.isOwnProcess = ((serviceType & 0x00000010) == 0x00000010);
|
|
j.isInteractive = ((serviceType & 0x00000100) == 0x00000100);
|
|
switch (token.Deref((1 * 4), 4).toBuffer().readUInt32LE())
|
|
{
|
|
case 0x00000005:
|
|
j.state = 'CONTINUE_PENDING';
|
|
break;
|
|
case 0x00000006:
|
|
j.state = 'PAUSE_PENDING';
|
|
break;
|
|
case 0x00000007:
|
|
j.state = 'PAUSED';
|
|
break;
|
|
case 0x00000004:
|
|
j.state = 'RUNNING';
|
|
break;
|
|
case 0x00000002:
|
|
j.state = 'START_PENDING';
|
|
break;
|
|
case 0x00000003:
|
|
j.state = 'STOP_PENDING';
|
|
break;
|
|
case 0x00000001:
|
|
j.state = 'STOPPED';
|
|
break;
|
|
}
|
|
var controlsAccepted = token.Deref((2 * 4), 4).toBuffer().readUInt32LE();
|
|
j.controlsAccepted = [];
|
|
if ((controlsAccepted & 0x00000010) == 0x00000010)
|
|
{
|
|
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDADD');
|
|
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDREMOVE');
|
|
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDENABLE');
|
|
j.controlsAccepted.push('SERVICE_CONTROL_NETBINDDISABLE');
|
|
}
|
|
if ((controlsAccepted & 0x00000008) == 0x00000008) { j.controlsAccepted.push('SERVICE_CONTROL_PARAMCHANGE'); }
|
|
if ((controlsAccepted & 0x00000002) == 0x00000002) { j.controlsAccepted.push('SERVICE_CONTROL_PAUSE'); j.controlsAccepted.push('SERVICE_CONTROL_CONTINUE'); }
|
|
if ((controlsAccepted & 0x00000100) == 0x00000100) { j.controlsAccepted.push('SERVICE_CONTROL_PRESHUTDOWN'); }
|
|
if ((controlsAccepted & 0x00000004) == 0x00000004) { j.controlsAccepted.push('SERVICE_CONTROL_SHUTDOWN'); }
|
|
if ((controlsAccepted & 0x00000001) == 0x00000001) { j.controlsAccepted.push('SERVICE_CONTROL_STOP'); }
|
|
if ((controlsAccepted & 0x00000020) == 0x00000020) { j.controlsAccepted.push('SERVICE_CONTROL_HARDWAREPROFILECHANGE'); }
|
|
if ((controlsAccepted & 0x00000040) == 0x00000040) { j.controlsAccepted.push('SERVICE_CONTROL_POWEREVENT'); }
|
|
if ((controlsAccepted & 0x00000080) == 0x00000080) { j.controlsAccepted.push('SERVICE_CONTROL_SESSIONCHANGE'); }
|
|
j.pid = token.Deref((7 * 4), 4).toBuffer().readUInt32LE();
|
|
return (j);
|
|
}
|
|
|
|
function serviceManager()
|
|
{
|
|
this._ObjectID = 'service-manager';
|
|
if (process.platform == 'win32')
|
|
{
|
|
this.GM = require('_GenericMarshal');
|
|
this.proxy = this.GM.CreateNativeProxy('Advapi32.dll');
|
|
this.proxy.CreateMethod('OpenSCManagerA');
|
|
this.proxy.CreateMethod('EnumServicesStatusExA');
|
|
this.proxy.CreateMethod('OpenServiceA');
|
|
this.proxy.CreateMethod('QueryServiceStatusEx');
|
|
this.proxy.CreateMethod('QueryServiceConfigA');
|
|
this.proxy.CreateMethod('QueryServiceConfig2A');
|
|
this.proxy.CreateMethod('ControlService');
|
|
this.proxy.CreateMethod('StartServiceA');
|
|
this.proxy.CreateMethod('CloseServiceHandle');
|
|
this.proxy.CreateMethod('CreateServiceA');
|
|
this.proxy.CreateMethod('ChangeServiceConfig2A');
|
|
this.proxy.CreateMethod('DeleteService');
|
|
this.proxy.CreateMethod('AllocateAndInitializeSid');
|
|
this.proxy.CreateMethod('CheckTokenMembership');
|
|
this.proxy.CreateMethod('FreeSid');
|
|
|
|
this.proxy2 = this.GM.CreateNativeProxy('Kernel32.dll');
|
|
this.proxy2.CreateMethod('GetLastError');
|
|
|
|
this.isAdmin = function isAdmin() {
|
|
var NTAuthority = this.GM.CreateVariable(6);
|
|
NTAuthority.toBuffer().writeInt8(5, 5);
|
|
var AdministratorsGroup = this.GM.CreatePointer();
|
|
var admin = false;
|
|
|
|
if (this.proxy.AllocateAndInitializeSid(NTAuthority, 2, 32, 544, 0, 0, 0, 0, 0, 0, AdministratorsGroup).Val != 0)
|
|
{
|
|
var member = this.GM.CreateInteger();
|
|
if (this.proxy.CheckTokenMembership(0, AdministratorsGroup.Deref(), member).Val != 0)
|
|
{
|
|
if (member.toBuffer().readUInt32LE() != 0) { admin = true; }
|
|
}
|
|
this.proxy.FreeSid(AdministratorsGroup.Deref());
|
|
}
|
|
return admin;
|
|
};
|
|
this.getProgramFolder = function getProgramFolder()
|
|
{
|
|
if (require('os').arch() == 'x64')
|
|
{
|
|
// 64 bit Windows
|
|
if (this.GM.PointerSize == 4)
|
|
{
|
|
return process.env['ProgramFiles(x86)']; // 32 Bit App
|
|
}
|
|
return process.env['ProgramFiles']; // 64 bit App
|
|
}
|
|
|
|
// 32 bit Windows
|
|
return process.env['ProgramFiles'];
|
|
};
|
|
this.getServiceFolder = function getServiceFolder() { return this.getProgramFolder() + '\\mesh'; };
|
|
|
|
this.enumerateService = function () {
|
|
var machineName = this.GM.CreatePointer();
|
|
var dbName = this.GM.CreatePointer();
|
|
var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0001 | 0x0004);
|
|
|
|
var bytesNeeded = this.GM.CreatePointer();
|
|
var servicesReturned = this.GM.CreatePointer();
|
|
var resumeHandle = this.GM.CreatePointer();
|
|
//var services = this.proxy.CreateVariable(262144);
|
|
var success = this.proxy.EnumServicesStatusExA(handle, 0, 0x00000030, 0x00000003, 0x00, 0x00, bytesNeeded, servicesReturned, resumeHandle, 0x00);
|
|
if (bytesNeeded.IntVal <= 0) {
|
|
throw ('error enumerating services');
|
|
}
|
|
var sz = bytesNeeded.IntVal;
|
|
var services = this.GM.CreateVariable(sz);
|
|
this.proxy.EnumServicesStatusExA(handle, 0, 0x00000030, 0x00000003, services, sz, bytesNeeded, servicesReturned, resumeHandle, 0x00);
|
|
console.log("servicesReturned", servicesReturned.IntVal);
|
|
|
|
var ptrSize = dbName._size;
|
|
var blockSize = 36 + (2 * ptrSize);
|
|
blockSize += ((ptrSize - (blockSize % ptrSize)) % ptrSize);
|
|
var retVal = [];
|
|
for (var i = 0; i < servicesReturned.IntVal; ++i) {
|
|
var token = services.Deref(i * blockSize, blockSize);
|
|
var j = {};
|
|
j.name = token.Deref(0, ptrSize).Deref().String;
|
|
j.displayName = token.Deref(ptrSize, ptrSize).Deref().String;
|
|
j.status = parseServiceStatus(token.Deref(2 * ptrSize, 36));
|
|
retVal.push(j);
|
|
}
|
|
this.proxy.CloseServiceHandle(handle);
|
|
return (retVal);
|
|
}
|
|
this.getService = function (name)
|
|
{
|
|
var serviceName = this.GM.CreateVariable(name);
|
|
var ptr = this.GM.CreatePointer();
|
|
var bytesNeeded = this.GM.CreateVariable(ptr._size);
|
|
var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0001 | 0x0004 | 0x0020 | 0x0010);
|
|
if (handle.Val == 0) { throw ('could not open ServiceManager'); }
|
|
var h = this.proxy.OpenServiceA(handle, serviceName, 0x0001 | 0x0004 | 0x0020 | 0x0010 | 0x00010000);
|
|
if (h.Val != 0) {
|
|
var success = this.proxy.QueryServiceStatusEx(h, 0, 0, 0, bytesNeeded);
|
|
var status = this.GM.CreateVariable(bytesNeeded.toBuffer().readUInt32LE());
|
|
success = this.proxy.QueryServiceStatusEx(h, 0, status, status._size, bytesNeeded);
|
|
if (success != 0)
|
|
{
|
|
var retVal = { _ObjectID: 'service-manager.service' }
|
|
require('events').EventEmitter.call(retVal);
|
|
|
|
retVal.close = function ()
|
|
{
|
|
if(this._service && this._scm)
|
|
{
|
|
this._proxy.CloseServiceHandle(this._service);
|
|
this._proxy.CloseServiceHandle(this._scm);
|
|
this._service = this._scm = null;
|
|
}
|
|
};
|
|
|
|
retVal.on('~', retVal.close);
|
|
retVal.status = parseServiceStatus(status);
|
|
retVal._scm = handle;
|
|
retVal._service = h;
|
|
retVal._GM = this.GM;
|
|
retVal._proxy = this.proxy;
|
|
retVal.name = name;
|
|
|
|
retVal.appLocation = function ()
|
|
{
|
|
var reg = require('win-registry');
|
|
var imagePath = reg.QueryKey(reg.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services\\' + this.name, 'ImagePath').toString();
|
|
var ret = imagePath.split('.exe')[0] + '.exe';
|
|
if (ret.startsWith('"')) { ret = ret.substring(1); }
|
|
return (ret);
|
|
};
|
|
|
|
|
|
retVal.appWorkingDirectory = function ()
|
|
{
|
|
var tokens = this.appLocation().split('\\');
|
|
tokens.pop();
|
|
return (tokens.join('\\'));
|
|
};
|
|
retVal.isRunning = function ()
|
|
{
|
|
var bytesNeeded = this._GM.CreateVariable(this._GM.PointerSize);
|
|
this._proxy.QueryServiceStatusEx(this._service, 0, 0, 0, bytesNeeded);
|
|
var st = this._GM.CreateVariable(bytesNeeded.toBuffer().readUInt32LE());
|
|
if(this._proxy.QueryServiceStatusEx(this._service, 0, st, st._size, bytesNeeded).Val != 0)
|
|
{
|
|
var state = parseServiceStatus(st);
|
|
return (state.state == 'RUNNING');
|
|
}
|
|
return (false);
|
|
};
|
|
|
|
retVal.stop = function () {
|
|
if (this.status.state == 'RUNNING') {
|
|
var newstate = this._GM.CreateVariable(36);
|
|
var success = this._proxy.ControlService(this._service, 0x00000001, newstate);
|
|
if (success == 0) {
|
|
throw (this.name + '.stop() failed');
|
|
}
|
|
}
|
|
else {
|
|
throw ('cannot call ' + this.name + '.stop(), when current state is: ' + this.status.state);
|
|
}
|
|
}
|
|
retVal.start = function () {
|
|
if (this.status.state == 'STOPPED') {
|
|
var success = this._proxy.StartServiceA(this._service, 0, 0);
|
|
if (success == 0) {
|
|
throw (this.name + '.start() failed');
|
|
}
|
|
}
|
|
else {
|
|
throw ('cannot call ' + this.name + '.start(), when current state is: ' + this.status.state);
|
|
}
|
|
}
|
|
var query_service_configa_DWORD = this.GM.CreateVariable(4);
|
|
this.proxy.QueryServiceConfigA(h, 0, 0, query_service_configa_DWORD);
|
|
if (query_service_configa_DWORD.toBuffer().readUInt32LE() > 0)
|
|
{
|
|
var query_service_configa = this.GM.CreateVariable(query_service_configa_DWORD.toBuffer().readUInt32LE());
|
|
if(this.proxy.QueryServiceConfigA(h, query_service_configa, query_service_configa._size, query_service_configa_DWORD).Val != 0)
|
|
{
|
|
var val = query_service_configa.Deref(this.GM.PointerSize == 4 ? 28 : 48, this.GM.PointerSize).Deref().String;
|
|
Object.defineProperty(retVal, 'user', { value: val });
|
|
}
|
|
}
|
|
|
|
|
|
var failureactions = this.GM.CreateVariable(8192);
|
|
var bneeded = this.GM.CreateVariable(4);
|
|
if (this.proxy.QueryServiceConfig2A(h, 2, failureactions, 8192, bneeded).Val != 0)
|
|
{
|
|
var cActions = failureactions.toBuffer().readUInt32LE(this.GM.PointerSize == 8 ? 24 : 12);
|
|
retVal.failureActions = {};
|
|
retVal.failureActions.resetPeriod = failureactions.Deref(0, 4).toBuffer().readUInt32LE(0);
|
|
retVal.failureActions.actions = [];
|
|
for(var act = 0 ; act < cActions; ++act)
|
|
{
|
|
var action = failureactions.Deref(this.GM.PointerSize == 8 ? 32 : 16, this.GM.PointerSize).Deref().Deref(act*8,8).toBuffer();
|
|
switch(action.readUInt32LE())
|
|
{
|
|
case 0:
|
|
retVal.failureActions.actions.push({ type: 'NONE' });
|
|
break;
|
|
case 1:
|
|
retVal.failureActions.actions.push({ type: 'SERVICE_RESTART' });
|
|
break;
|
|
case 2:
|
|
retVal.failureActions.actions.push({ type: 'REBOOT' });
|
|
break;
|
|
default:
|
|
retVal.failureActions.actions.push({ type: 'OTHER' });
|
|
break;
|
|
}
|
|
retVal.failureActions.actions.peek().delay = action.readUInt32LE(4);
|
|
}
|
|
}
|
|
return (retVal);
|
|
}
|
|
else {
|
|
|
|
}
|
|
}
|
|
|
|
this.proxy.CloseServiceHandle(handle);
|
|
throw ('could not find service: ' + name);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.isAdmin = function isAdmin()
|
|
{
|
|
return (require('user-sessions').isRoot());
|
|
}
|
|
if (process.platform == 'darwin')
|
|
{
|
|
this.getService = function (name)
|
|
{
|
|
var ret = { name: name, close: function () { }};
|
|
if(!require('fs').existsSync('/Library/LaunchDaemons/' + name + '.plist'))
|
|
{
|
|
// Before we throw in the towel, let's enumerate all the plist files, and see if one has a matching label
|
|
var files = require('fs').readdirSync('/Library/LaunchDaemons');
|
|
for (var file in files)
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("cat /Library/LaunchDaemons/" + files[file] + " | tr '\n' '\.' | awk '{ split($0, a, \"<key>Label</key>\"); split(a[2], b, \"</string>\"); split(b[1], c, \"<string>\"); print c[2]; }'\nexit\n");
|
|
child.waitExit();
|
|
if(child.stdout.str.trim() == name)
|
|
{
|
|
ret.name = files[file].endsWith('.plist') ? files[file].substring(0, files[file].length - 6) : files[file];
|
|
Object.defineProperty(ret, 'alias', { value: name });
|
|
break;
|
|
}
|
|
}
|
|
if (ret.name == name) { throw (' LaunchDaemon (' + name + ') NOT FOUND'); }
|
|
}
|
|
ret.label = function label()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("cat /Library/LaunchDaemons/" + this.name + ".plist | tr '\n' '\.' | awk '{ split($0, a, \"<key>Label</key>\"); split(a[2], b, \"</string>\"); split(b[1], c, \"<string>\"); print c[2]; }'\nexit\n");
|
|
child.waitExit();
|
|
return (child.stdout.str.trim());
|
|
}
|
|
if (!ret.alias) { Object.defineProperty(ret, 'alias', { value: ret.label() }); }
|
|
ret.label = null;
|
|
ret.getPID = function getPID()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("launchctl list | grep '" + this.alias + "' | awk '{ if($3==\"" + this.alias + "\"){print $1;}}'\nexit\n");
|
|
child.waitExit();
|
|
return (parseInt(child.stdout.str.trim()));
|
|
};
|
|
ret.isRunning = function isRunning()
|
|
{
|
|
return (this.getPID() > 0);
|
|
};
|
|
ret.isMe = function isMe()
|
|
{
|
|
return (this.getPID() == process.pid);
|
|
};
|
|
ret.appWorkingDirectory = function appWorkingDirectory()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("cat /Library/LaunchDaemons/" + this.name + ".plist | tr '\n' '\.' | awk '{ split($0, a, \"<key>WorkingDirectory</key>\"); split(a[2], b, \"</string>\"); split(b[1], c, \"<string>\"); print c[2]; }'\nexit\n");
|
|
child.waitExit();
|
|
child.stdout.str = child.stdout.str.trim();
|
|
|
|
return (child.stdout.str.endsWith('/') ? child.stdout.str.substring(0, child.stdout.str.length - 1) : child.stdout.str);
|
|
};
|
|
ret.appLocation = function appLocation()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("cat /Library/LaunchDaemons/" + this.name + ".plist | tr '\n' '\.' | awk '{ split($0, a, \"<key>ProgramArguments</key>\"); split(a[2], b, \"</string>\"); split(b[1], c, \"<string>\"); print c[2]; }'\nexit\n");
|
|
child.waitExit();
|
|
return (child.stdout.str.trim());
|
|
};
|
|
ret.start = function start()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('launchctl start ' + this.alias + '\nexit\n');
|
|
child.waitExit();
|
|
};
|
|
ret.stop = function stop()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('launchctl stop ' + this.alias + '\nexit\n');
|
|
child.waitExit();
|
|
};
|
|
ret.restart = function restart()
|
|
{
|
|
this.stop();
|
|
this.start();
|
|
};
|
|
return (ret);
|
|
};
|
|
}
|
|
if(process.platform == 'linux')
|
|
{
|
|
this.getService = function (name, platform)
|
|
{
|
|
if (!platform) { platform = this.getServiceType(); }
|
|
var ret = { name: name, close: function () { }};
|
|
switch(platform)
|
|
{
|
|
case 'init':
|
|
case 'upstart':
|
|
if (require('fs').existsSync('/etc/init.d/' + name)) { platform = 'init'; }
|
|
if (require('fs').existsSync('/etc/init/' + name + '.conf')) { platform = 'upstart'; }
|
|
if ((platform == 'init' && require('fs').existsSync('/etc/init.d/' + name)) ||
|
|
(platform == 'upstart' && require('fs').existsSync('/etc/init/' + name + '.conf')))
|
|
{
|
|
ret.appWorkingDirectory = function appWorkingDirectory()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
if (appWorkingDirectory.platform == 'init')
|
|
{
|
|
child.stdin.write("cat /etc/init.d/" + this.name + " | grep 'SCRIPT=' | awk -F= '{ len=split($2, a, \"/\"); print substr($2,0,length($2)-length(a[len])); }'\nexit\n");
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write("cat /etc/init/" + this.name + ".conf | grep 'chdir ' | awk '{print $2}'\nexit\n");
|
|
}
|
|
child.waitExit();
|
|
return (child.stdout.str.trim());
|
|
};
|
|
ret.appWorkingDirectory.platform = platform;
|
|
ret.appLocation = function appLocation()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
if(appLocation.platform == 'init')
|
|
{
|
|
child.stdin.write("cat /etc/init.d/" + this.name + " | grep 'SCRIPT=' | awk -F= '{print $2}'\nexit\n");
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write("cat /etc/init/" + this.name + ".conf | grep 'exec ' | awk '{print $2}'\nexit\n");
|
|
}
|
|
child.waitExit();
|
|
return (child.stdout.str.trim());
|
|
};
|
|
ret.appLocation.platform = platform;
|
|
ret.isMe = function isMe()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
if (isMe.platform == 'upstart')
|
|
{
|
|
child.stdin.write("initctl status " + this.name + " | awk '{print $2}' | awk -F, '{print $4}'\nexit\n");
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write("service " + this.name + " status | awk '{print $2}' | awk -F, '{print $4}'\nexit\n");
|
|
}
|
|
child.waitExit();
|
|
return (parseInt(child.stdout.str.trim()) == process.pid);
|
|
};
|
|
ret.isMe.platform = platform;
|
|
ret.isRunning = function isRunning()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
if (isRunning.platform == 'upstart')
|
|
{
|
|
child.stdin.write("initctl status " + this.name + " | awk '{print $2}' | awk -F, '{print $1}'\nexit\n");
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write("service " + this.name + " status | awk '{print $2}' | awk -F, '{print $1}'\nexit\n");
|
|
}
|
|
child.waitExit();
|
|
return (child.stdout.str.trim() == 'start/running');
|
|
};
|
|
ret.isRunning.platform = platform;
|
|
ret.start = function start()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
if (start.platform == 'upstart')
|
|
{
|
|
child.stdin.write('initctl start ' + this.name + '\nexit\n');
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write('service ' + this.name + ' start\nexit\n');
|
|
}
|
|
child.waitExit();
|
|
};
|
|
ret.start.platform = platform;
|
|
ret.stop = function stop()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
if (stop.platform == 'upstart')
|
|
{
|
|
child.stdin.write('initctl stop ' + this.name + '\nexit\n');
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write('service ' + this.name + ' stop\nexit\n');
|
|
}
|
|
child.waitExit();
|
|
};
|
|
ret.stop.platform = platform;
|
|
ret.restart = function restart()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
if (restart.platform == 'upstart')
|
|
{
|
|
child.stdin.write('initctl restart ' + this.name + '\nexit\n');
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write('service ' + this.name + ' restart\nexit\n');
|
|
}
|
|
child.waitExit();
|
|
};
|
|
ret.restart.platform = platform;
|
|
ret.status = function status()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout._str = '';
|
|
child.stdout.on('data', function (chunk) { this._str += chunk.toString(); });
|
|
if (status.platform == 'upstart')
|
|
{
|
|
child.stdin.write('initctl status ' + this.name + '\nexit\n');
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write('service ' + this.name + ' status\nexit\n');
|
|
}
|
|
child.waitExit();
|
|
return (child.stdout._str);
|
|
};
|
|
ret.status.platform = platform;
|
|
return (ret);
|
|
}
|
|
else
|
|
{
|
|
throw (platform + ' Service (' + name + ') NOT FOUND');
|
|
}
|
|
break;
|
|
case 'systemd':
|
|
if (require('fs').existsSync('/lib/systemd/system/' + name + '.service') ||
|
|
require('fs').existsSync('/usr/lib/systemd/system/' + name + '.service'))
|
|
{
|
|
ret.appWorkingDirectory = function appWorkingDirectory()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
if (require('fs').existsSync('/lib/systemd/system/' + this.name + '.service')) {
|
|
child.stdin.write("cat /lib/systemd/system/" + this.name + ".service | grep 'WorkingDirectory=' | awk -F= '{ print $2 }'\n\exit\n");
|
|
}
|
|
else {
|
|
child.stdin.write("cat /usr/lib/systemd/system/" + this.name + ".service | grep 'WorkingDirectory=' | awk -F= '{ print $2 }'\n\exit\n");
|
|
}
|
|
child.waitExit();
|
|
return (child.stdout.str.trim());
|
|
};
|
|
ret.appLocation = function ()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
if (require('fs').existsSync('/lib/systemd/system/' + this.name + '.service'))
|
|
{
|
|
child.stdin.write("cat /lib/systemd/system/" + this.name + ".service | grep 'ExecStart=' | awk -F= '{ split($2, a, \" \"); print a[1] }'\n\exit\n");
|
|
}
|
|
else
|
|
{
|
|
child.stdin.write("cat /usr/lib/systemd/system/" + this.name + ".service | grep 'ExecStart=' | awk -F= '{ split($2, a, \" \"); print a[1] }'\n\exit\n");
|
|
}
|
|
child.waitExit();
|
|
return (child.stdout.str.trim());
|
|
};
|
|
ret.isMe = function isMe()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("systemctl status " + this.name + " | grep 'Main PID:' | awk '{print $3}'\nexit\n");
|
|
child.waitExit();
|
|
return (parseInt(child.stdout.str.trim()) == process.pid);
|
|
};
|
|
ret.isRunning = function isRunning()
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.str = '';
|
|
child.stdout.on('data', function (chunk) { this.str += chunk.toString(); });
|
|
child.stdin.write("systemctl status " + this.name + " | grep 'Active:' | awk '{print $2}'\nexit\n");
|
|
child.waitExit();
|
|
return (child.stdout.str.trim() == 'active');
|
|
};
|
|
ret.start = function start() {
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('systemctl start ' + this.name + '\nexit\n');
|
|
child.waitExit();
|
|
};
|
|
ret.stop = function stop() {
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('systemctl stop ' + this.name + '\nexit\n');
|
|
child.waitExit();
|
|
};
|
|
ret.restart = function restart() {
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('systemctl restart ' + this.name + '\nexit\n');
|
|
child.waitExit();
|
|
};
|
|
ret.status = function status() {
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout._str = '';
|
|
child.stdout.on('data', function (chunk) { this._str += chunk.toString(); });
|
|
child.stdin.write('systemctl status ' + this.name + '\nexit\n');
|
|
child.waitExit();
|
|
return (child.stdout._str);
|
|
};
|
|
return (ret);
|
|
}
|
|
else
|
|
{
|
|
throw (platform + ' Service (' + name + ') NOT FOUND');
|
|
}
|
|
break;
|
|
default:
|
|
throw ('Unknown Service Platform: ' + platform);
|
|
break;
|
|
}
|
|
};
|
|
}
|
|
}
|
|
this.installService = function installService(options)
|
|
{
|
|
if (!options.target) { options.target = options.name; }
|
|
if (!options.displayName) { options.displayName = options.name; }
|
|
|
|
if (process.platform == 'win32')
|
|
{
|
|
if (!this.isAdmin()) { throw ('Installing as Service, requires admin'); }
|
|
|
|
// Before we start, we need to copy the binary to the right place
|
|
var folder = this.getServiceFolder();
|
|
if (!require('fs').existsSync(folder)) { require('fs').mkdirSync(folder); }
|
|
if (!require('fs').existsSync(folder + '\\' + options.name)) { require('fs').mkdirSync(folder + '\\' + options.name); }
|
|
|
|
require('fs').copyFileSync(options.servicePath, folder + '\\' + options.name + '\\' + options.target + '.exe');
|
|
options.servicePath = folder + '\\' + options.name + '\\' + options.target + '.exe';
|
|
|
|
var servicePath = this.GM.CreateVariable('"' + options.servicePath + '"');
|
|
var handle = this.proxy.OpenSCManagerA(0x00, 0x00, 0x0002);
|
|
if (handle.Val == 0) { throw ('error opening SCManager'); }
|
|
var serviceName = this.GM.CreateVariable(options.name);
|
|
var displayName = this.GM.CreateVariable(options.displayName);
|
|
var allAccess = 0x000F01FF;
|
|
var serviceType;
|
|
|
|
|
|
switch (options.startType) {
|
|
case 'AUTO_START':
|
|
serviceType = 0x02; // Automatic
|
|
break;
|
|
case 'DEMAND_START':
|
|
default:
|
|
serviceType = 0x03; // Manual
|
|
break;
|
|
case 'DISABLED':
|
|
serviceType = 0x04; // Disabled
|
|
break;
|
|
}
|
|
|
|
var h = this.proxy.CreateServiceA(handle, serviceName, displayName, allAccess, 0x10 | 0x100, serviceType, 0, servicePath, 0, 0, 0, 0, 0);
|
|
if (h.Val == 0) { this.proxy.CloseServiceHandle(handle); throw ('Error Creating Service: ' + this.proxy2.GetLastError().Val); }
|
|
if (options.description)
|
|
{
|
|
var dsc = this.GM.CreateVariable(options.description);
|
|
var serviceDescription = this.GM.CreateVariable(this.GM.PointerSize);
|
|
dsc.pointerBuffer().copy(serviceDescription.Deref(0, this.GM.PointerSize).toBuffer());
|
|
|
|
if (this.proxy.ChangeServiceConfig2A(h, 1, serviceDescription).Val == 0)
|
|
{
|
|
console.log('unable to set description...');
|
|
}
|
|
}
|
|
if (options.failureRestart == null || options.failureRestart > 0)
|
|
{
|
|
var delay = options.failureRestart == null ? 5000 : options.failureRestart; // Delay in milliseconds
|
|
var actions = this.GM.CreateVariable(3 * 8); // 3*sizeof(SC_ACTION)
|
|
actions.Deref(0, 4).toBuffer().writeUInt32LE(1); // SC_ACTION[0].type
|
|
actions.Deref(4, 4).toBuffer().writeUInt32LE(delay); // SC_ACTION[0].delay
|
|
actions.Deref(8, 4).toBuffer().writeUInt32LE(1); // SC_ACTION[1].type
|
|
actions.Deref(12, 4).toBuffer().writeUInt32LE(delay); // SC_ACTION[1].delay
|
|
actions.Deref(16, 4).toBuffer().writeUInt32LE(1); // SC_ACTION[2].type
|
|
actions.Deref(20, 4).toBuffer().writeUInt32LE(delay); // SC_ACTION[2].delay
|
|
|
|
var failureActions = this.GM.CreateVariable(40); // sizeof(SERVICE_FAILURE_ACTIONS)
|
|
failureActions.Deref(0, 4).toBuffer().writeUInt32LE(7200); // dwResetPeriod: 2 Hours
|
|
failureActions.Deref(this.GM.PointerSize == 8 ? 24 : 12, 4).toBuffer().writeUInt32LE(3);// cActions: 3
|
|
actions.pointerBuffer().copy(failureActions.Deref(this.GM.PointerSize == 8 ? 32 : 16, this.GM.PointerSize).toBuffer());
|
|
if (this.proxy.ChangeServiceConfig2A(h, 2, failureActions).Val == 0)
|
|
{
|
|
console.log('Unable to set FailureActions...');
|
|
}
|
|
}
|
|
this.proxy.CloseServiceHandle(h);
|
|
this.proxy.CloseServiceHandle(handle);
|
|
|
|
if (options.files)
|
|
{
|
|
for(var i in options.files)
|
|
{
|
|
if (options.files[i]._buffer)
|
|
{
|
|
console.log('writing ' + extractFileName(options.files[i]));
|
|
require('fs').writeFileSync(folder + '\\' + options.name + '\\' + extractFileName(options.files[i]), options.files[i]._buffer);
|
|
}
|
|
else
|
|
{
|
|
console.log('copying ' + extractFileSource(options.files[i]));
|
|
require('fs').copyFileSync(extractFileSource(options.files[i]), folder + '\\' + options.name + '\\' + extractFileName(options.files[i]));
|
|
}
|
|
}
|
|
}
|
|
if (options.parameters)
|
|
{
|
|
var reg = require('win-registry');
|
|
var imagePath = reg.QueryKey(reg.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services\\' + options.name, 'ImagePath');
|
|
imagePath += (' ' + options.parameters.join(' '));
|
|
reg.WriteKey(reg.HKEY.LocalMachine, 'SYSTEM\\CurrentControlSet\\Services\\' + options.name, 'ImagePath', imagePath);
|
|
}
|
|
}
|
|
if(process.platform == 'linux')
|
|
{
|
|
if (!this.isAdmin()) { console.log('Installing a Service requires root'); throw ('Installing as Service, requires root'); }
|
|
var parameters = options.parameters ? options.parameters.join(' ') : '';
|
|
var conf;
|
|
if (!options.servicePlatform) { options.servicePlatform = this.getServiceType(); }
|
|
|
|
switch (options.servicePlatform)
|
|
{
|
|
case 'init':
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/')) { require('fs').mkdirSync('/usr/local/mesh_services'); }
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/' + options.name)) { require('fs').mkdirSync('/usr/local/mesh_services/' + options.name); }
|
|
|
|
require('fs').copyFileSync(options.servicePath, '/usr/local/mesh_services/' + options.name + '/' + options.target);
|
|
console.log('copying ' + options.servicePath);
|
|
|
|
var m = require('fs').statSync('/usr/local/mesh_services/' + options.name + '/' + options.target).mode;
|
|
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
|
|
require('fs').chmodSync('/usr/local/mesh_services/' + options.name + '/' + options.target, m);
|
|
|
|
// The following is the init.d script I wrote. Rather than having to deal with escaping the thing, I just Base64 encoded it to prevent issues.
|
|
conf = require('fs').createWriteStream('/etc/init.d/' + options.name, { flags: 'wb' });
|
|
conf.write(Buffer.from('IyEvYmluL3NoCgoKU0NSSVBUPS91c3IvbG9jYWwvbWVzaF9zZXJ2aWNlcy9YWFhYWC9ZWVlZWQpSVU5BUz1yb290CgpQSURGSUxFPS92YXIvcnVuL1hYWFhYLnBpZApMT0dGSUxFPS92YXIvbG9nL1hYWFhYLmxvZwoKc3RhcnQoKSB7CiAgaWYgWyAtZiAiJFBJREZJTEUiIF0gJiYga2lsbCAtMCAkKGNhdCAiJFBJREZJTEUiKSAyPi9kZXYvbnVsbDsgdGhlbgogICAgZWNobyAnU2VydmljZSBhbHJlYWR5IHJ1bm5pbmcnID4mMgogICAgcmV0dXJuIDEKICBmaQogIGVjaG8gJ1N0YXJ0aW5nIHNlcnZpY2XigKYnID4mMgogIGxvY2FsIENNRD0iJFNDUklQVCB7e1BBUk1TfX0gJj4gXCIkTE9HRklMRVwiICYgZWNobyBcJCEiCiAgbG9jYWwgQ01EUEFUSD0kKGVjaG8gJFNDUklQVCB8IGF3ayAneyBsZW49c3BsaXQoJDAsIGEsICIvIik7IHByaW50IHN1YnN0cigkMCwgMCwgbGVuZ3RoKCQwKS1sZW5ndGgoYVtsZW5dKSk7IH0nKQogIGNkICRDTURQQVRICiAgc3UgLWMgIiRDTUQiICRSVU5BUyA+ICIkUElERklMRSIKICBlY2hvICdTZXJ2aWNlIHN0YXJ0ZWQnID4mMgp9CgpzdG9wKCkgewogIGlmIFsgISAtZiAiJFBJREZJTEUiIF07IHRoZW4KICAgIGVjaG8gJ1NlcnZpY2Ugbm90IHJ1bm5pbmcnID4mMgogICAgcmV0dXJuIDEKICBlbHNlCglwaWQ9JCggY2F0ICIkUElERklMRSIgKQoJaWYga2lsbCAtMCAkcGlkIDI+L2Rldi9udWxsOyB0aGVuCiAgICAgIGVjaG8gJ1N0b3BwaW5nIHNlcnZpY2XigKYnID4mMgogICAgICBraWxsIC0xNiAkcGlkCiAgICAgIGVjaG8gJ1NlcnZpY2Ugc3RvcHBlZCcgPiYyCgllbHNlCgkgIGVjaG8gJ1NlcnZpY2Ugbm90IHJ1bm5pbmcnCglmaQoJcm0gLWYgJCJQSURGSUxFIgogIGZpCn0KcmVzdGFydCgpewoJc3RvcAoJc3RhcnQKfQpzdGF0dXMoKXsKCWlmIFsgLWYgIiRQSURGSUxFIiBdCgl0aGVuCgkJcGlkPSQoIGNhdCAiJFBJREZJTEUiICkKCQlpZiBraWxsIC0wICRwaWQgMj4vZGV2L251bGw7IHRoZW4KCQkJZWNobyAiWFhYWFggc3RhcnQvcnVubmluZywgcHJvY2VzcyAkcGlkIgoJCWVsc2UKCQkJZWNobyAnWFhYWFggc3RvcC93YWl0aW5nJwoJCWZpCgllbHNlCgkJZWNobyAnWFhYWFggc3RvcC93YWl0aW5nJwoJZmkKCn0KCgpjYXNlICIkMSIgaW4KCXN0YXJ0KQoJCXN0YXJ0CgkJOzsKCXN0b3ApCgkJc3RvcAoJCTs7CglyZXN0YXJ0KQoJCXN0b3AKCQlzdGFydAoJCTs7CglzdGF0dXMpCgkJc3RhdHVzCgkJOzsKCSopCgkJZWNobyAiVXNhZ2U6IHNlcnZpY2UgWFhYWFgge3N0YXJ0fHN0b3B8cmVzdGFydHxzdGF0dXN9IgoJCTs7CmVzYWMKZXhpdCAwCgo=', 'base64').toString().split('XXXXX').join(options.name).split('YYYYY').join(options.target).replace('{{PARMS}}', parameters));
|
|
conf.end();
|
|
|
|
m = require('fs').statSync('/etc/init.d/' + options.name).mode;
|
|
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
|
|
require('fs').chmodSync('/etc/init.d/' + options.name, m);
|
|
switch (options.startType)
|
|
{
|
|
case 'BOOT_START':
|
|
case 'SYSTEM_START':
|
|
case 'AUTO_START':
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('update-rc.d ' + options.name + ' defaults\nexit\n');
|
|
child.waitExit();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case 'upstart':
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/')) { require('fs').mkdirSync('/usr/local/mesh_services'); }
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/' + options.name)) { require('fs').mkdirSync('/usr/local/mesh_services/' + options.name); }
|
|
|
|
require('fs').copyFileSync(options.servicePath, '/usr/local/mesh_services/' + options.name + '/' + options.target);
|
|
console.log('copying ' + options.servicePath);
|
|
|
|
var m = require('fs').statSync('/usr/local/mesh_services/' + options.name + '/' + options.target).mode;
|
|
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
|
|
require('fs').chmodSync('/usr/local/mesh_services/' + options.name + '/' + options.target, m);
|
|
|
|
conf = require('fs').createWriteStream('/etc/init/' + options.name + '.conf', { flags: 'wb' });
|
|
switch (options.startType)
|
|
{
|
|
case 'BOOT_START':
|
|
case 'SYSTEM_START':
|
|
case 'AUTO_START':
|
|
conf.write('start on runlevel [2345]\n');
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
conf.write('stop on runlevel [016]\n\n');
|
|
if (options.failureRestart == null || options.failureRestart > 0)
|
|
{
|
|
conf.write('respawn\n\n');
|
|
}
|
|
conf.write('chdir /usr/local/mesh_services/' + options.name + '\n');
|
|
conf.write('exec /usr/local/mesh_services/' + options.name + '/' + options.target + ' ' + parameters + '\n\n');
|
|
conf.end();
|
|
break;
|
|
case 'systemd':
|
|
var serviceDescription = options.description ? options.description : 'MeshCentral Agent';
|
|
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/')) { require('fs').mkdirSync('/usr/local/mesh_services'); }
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/' + options.name)) { require('fs').mkdirSync('/usr/local/mesh_services/' + options.name); }
|
|
|
|
console.log('copying ' + options.servicePath);
|
|
require('fs').copyFileSync(options.servicePath, '/usr/local/mesh_services/' + options.name + '/' + options.target);
|
|
|
|
var m = require('fs').statSync('/usr/local/mesh_services/' + options.name + '/' + options.target).mode;
|
|
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
|
|
require('fs').chmodSync('/usr/local/mesh_services/' + options.name + '/' + options.target, m);
|
|
|
|
if (require('fs').existsSync('/lib/systemd/system'))
|
|
{
|
|
conf = require('fs').createWriteStream('/lib/systemd/system/' + options.name + '.service', { flags: 'wb' });
|
|
}
|
|
else if (require('fs').existsSync('/usr/lib/systemd/system'))
|
|
{
|
|
conf = require('fs').createWriteStream('/usr/lib/systemd/system/' + options.name + '.service', { flags: 'wb' });
|
|
}
|
|
else
|
|
{
|
|
throw ('unknown location for systemd configuration files');
|
|
}
|
|
|
|
conf.write('[Unit]\nDescription=' + serviceDescription + '\n');
|
|
conf.write('[Service]\n');
|
|
conf.write('WorkingDirectory=/usr/local/mesh_services/' + options.name + '\n');
|
|
conf.write('ExecStart=/usr/local/mesh_services/' + options.name + '/' + options.target + ' ' + parameters + '\n');
|
|
conf.write('StandardOutput=null\n');
|
|
if (options.failureRestart == null || options.failureRestart > 0)
|
|
{
|
|
conf.write('Restart=on-failure\n');
|
|
if (options.failureRestart == null)
|
|
{
|
|
conf.write('RestartSec=3\n');
|
|
}
|
|
else
|
|
{
|
|
conf.write('RestartSec=' + (options.failureRestart / 1000) + '\n');
|
|
}
|
|
}
|
|
switch (options.startType)
|
|
{
|
|
case 'BOOT_START':
|
|
case 'SYSTEM_START':
|
|
case 'AUTO_START':
|
|
conf.write('[Install]\n');
|
|
conf.write('WantedBy=multi-user.target\n');
|
|
conf.write('Alias=' + options.name + '.service\n'); break;
|
|
this._update = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
|
|
this._update._moduleName = options.name;
|
|
this._update.stdout.on('data', function (chunk) { });
|
|
this._update.stdin.write('systemctl enable ' + options.name + '.service\n');
|
|
this._update.stdin.write('exit\n');
|
|
this._update.waitExit();
|
|
default:
|
|
break;
|
|
}
|
|
conf.end();
|
|
|
|
break;
|
|
default: // unknown platform service type
|
|
break;
|
|
}
|
|
}
|
|
if(process.platform == 'darwin')
|
|
{
|
|
if (!this.isAdmin()) { throw ('Installing as Service, requires root'); }
|
|
|
|
// Mac OS
|
|
var stdoutpath = (options.stdout ? ('<key>StandardOutPath</key>\n<string>' + options.stdout + '</string>') : '');
|
|
var autoStart = (options.startType == 'AUTO_START' ? '<true/>' : '<false/>');
|
|
var params = ' <key>ProgramArguments</key>\n';
|
|
params += ' <array>\n';
|
|
params += (' <string>/usr/local/mesh_services/' + options.name + '/' + options.target + '</string>\n');
|
|
if(options.parameters)
|
|
{
|
|
for(var itm in options.parameters)
|
|
{
|
|
params += (' <string>' + options.parameters[itm] + '</string>\n');
|
|
}
|
|
}
|
|
params += ' </array>\n';
|
|
|
|
var plist = '<?xml version="1.0" encoding="UTF-8"?>\n';
|
|
plist += '<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n';
|
|
plist += '<plist version="1.0">\n';
|
|
plist += ' <dict>\n';
|
|
plist += ' <key>Label</key>\n';
|
|
plist += (' <string>' + options.name + '</string>\n');
|
|
plist += (params + '\n');
|
|
plist += ' <key>WorkingDirectory</key>\n';
|
|
plist += (' <string>/usr/local/mesh_services/' + options.name + '</string>\n');
|
|
plist += (stdoutpath + '\n');
|
|
plist += ' <key>RunAtLoad</key>\n';
|
|
plist += (autoStart + '\n');
|
|
plist += ' <key>KeepAlive</key>\n';
|
|
if(options.failureRestart == null || options.failureRestart > 0)
|
|
{
|
|
plist += ' <true/>\n';
|
|
}
|
|
else
|
|
{
|
|
plist += ' <false/>\n';
|
|
}
|
|
if(options.failureRestart != null)
|
|
{
|
|
plist += ' <key>ThrottleInterval</key>\n';
|
|
plist += ' <integer>' + (options.failureRestart / 1000) + '</integer>\n';
|
|
}
|
|
|
|
plist += ' </dict>\n';
|
|
plist += '</plist>';
|
|
|
|
if (!require('fs').existsSync('/usr/local/mesh_services')) { require('fs').mkdirSync('/usr/local/mesh_services'); }
|
|
if (!require('fs').existsSync('/Library/LaunchDaemons/' + options.name + '.plist'))
|
|
{
|
|
if (!require('fs').existsSync('/usr/local/mesh_services/' + options.name)) { require('fs').mkdirSync('/usr/local/mesh_services/' + options.name); }
|
|
if (options.binary)
|
|
{
|
|
require('fs').writeFileSync('/usr/local/mesh_services/' + options.name + '/' + options.target, options.binary);
|
|
}
|
|
else
|
|
{
|
|
require('fs').copyFileSync(options.servicePath, '/usr/local/mesh_services/' + options.name + '/' + options.target);
|
|
}
|
|
require('fs').writeFileSync('/Library/LaunchDaemons/' + options.name + '.plist', plist);
|
|
var m = require('fs').statSync('/usr/local/mesh_services/' + options.name + '/' + options.target).mode;
|
|
m |= (require('fs').CHMOD_MODES.S_IXUSR | require('fs').CHMOD_MODES.S_IXGRP);
|
|
require('fs').chmodSync('/usr/local/mesh_services/' + options.name + '/' + options.target, m);
|
|
}
|
|
else
|
|
{
|
|
throw ('Service: ' + options.name + ' already exists');
|
|
}
|
|
}
|
|
|
|
if (options.files)
|
|
{
|
|
for (var i in options.files)
|
|
{
|
|
if (options.files[i]._buffer)
|
|
{
|
|
console.log('writing ' + extractFileName(options.files[i]));
|
|
require('fs').writeFileSync('/usr/local/mesh_services/' + options.name + '/' + extractFileName(options.files[i]), options.files[i]._buffer);
|
|
}
|
|
else
|
|
{
|
|
console.log('copying ' + extractFileSource(options.files[i]));
|
|
require('fs').copyFileSync(extractFileSource(options.files[i]), '/usr/local/mesh_services/' + options.name + '/' + extractFileName(options.files[i]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
this.uninstallService = function uninstallService(name)
|
|
{
|
|
if (!this.isAdmin()) { throw ('Uninstalling a service, requires admin'); }
|
|
|
|
if (typeof (name) == 'object') { name = name.name; }
|
|
var service = this.getService(name);
|
|
var servicePath = service.appLocation();
|
|
|
|
if (process.platform == 'win32')
|
|
{
|
|
if (service.status.state == undefined || service.status.state == 'STOPPED')
|
|
{
|
|
try
|
|
{
|
|
require('fs').unlinkSync(servicePath);
|
|
}
|
|
catch (e)
|
|
{
|
|
}
|
|
if (this.proxy.DeleteService(service._service) == 0)
|
|
{
|
|
throw ('Uninstall Service for: ' + name + ', failed with error: ' + this.proxy2.GetLastError());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw ('Cannot uninstall service: ' + name + ', because it is: ' + service.status.state);
|
|
}
|
|
service.close();
|
|
service = null;
|
|
}
|
|
else if(process.platform == 'linux')
|
|
{
|
|
switch (this.getServiceType())
|
|
{
|
|
case 'init':
|
|
case 'upstart':
|
|
if (require('fs').existsSync('/etc/init.d/' + name))
|
|
{
|
|
// init.d service
|
|
this._update = require('child_process').execFile('/bin/sh', ['sh']);
|
|
this._update.stdout.on('data', function (chunk) { });
|
|
this._update.stdin.write('service ' + name + ' stop\n');
|
|
this._update.stdin.write('update-rc.d -f ' + name + ' remove\n');
|
|
this._update.stdin.write('exit\n');
|
|
this._update.waitExit();
|
|
try {
|
|
require('fs').unlinkSync('/etc/init.d/' + name);
|
|
require('fs').unlinkSync(servicePath);
|
|
console.log(name + ' uninstalled');
|
|
}
|
|
catch (e) {
|
|
console.log(name + ' could not be uninstalled', e)
|
|
}
|
|
}
|
|
if (require('fs').existsSync('/etc/init/' + name + '.conf'))
|
|
{
|
|
// upstart service
|
|
this._update = require('child_process').execFile('/bin/sh', ['sh']);
|
|
this._update.stdout.on('data', function (chunk) { });
|
|
this._update.stdin.write('service ' + name + ' stop\n');
|
|
this._update.stdin.write('exit\n');
|
|
this._update.waitExit();
|
|
try {
|
|
require('fs').unlinkSync('/etc/init/' + name + '.conf');
|
|
require('fs').unlinkSync(servicePath);
|
|
console.log(name + ' uninstalled');
|
|
}
|
|
catch (e) {
|
|
console.log(name + ' could not be uninstalled', e)
|
|
}
|
|
}
|
|
break;
|
|
case 'systemd':
|
|
this._update = require('child_process').execFile('/bin/sh', ['sh'], { type: require('child_process').SpawnTypes.TERM });
|
|
this._update.stdout.on('data', function (chunk) { });
|
|
this._update.stdin.write('systemctl stop ' + name + '.service\n');
|
|
this._update.stdin.write('systemctl disable ' + name + '.service\n');
|
|
this._update.stdin.write('exit\n');
|
|
this._update.waitExit();
|
|
try
|
|
{
|
|
require('fs').unlinkSync(servicePath);
|
|
if (require('fs').existsSync('/lib/systemd/system/' + name + '.service')) { require('fs').unlinkSync('/lib/systemd/system/' + name + '.service'); }
|
|
if (require('fs').existsSync('/usr/lib/systemd/system/' + name + '.service')) { require('fs').unlinkSync('/usr/lib/systemd/system/' + name + '.service'); }
|
|
console.log(name + ' uninstalled');
|
|
}
|
|
catch (e)
|
|
{
|
|
console.log(name + ' could not be uninstalled', e)
|
|
}
|
|
break;
|
|
default: // unknown platform service type
|
|
break;
|
|
}
|
|
}
|
|
else if(process.platform == 'darwin')
|
|
{
|
|
if (require('fs').existsSync('/Library/LaunchDaemons/' + name + '.plist'))
|
|
{
|
|
var child = require('child_process').execFile('/bin/sh', ['sh']);
|
|
child.stdout.on('data', function (chunk) { });
|
|
child.stdin.write('launchctl stop ' + name + '\n');
|
|
child.stdin.write('launchctl unload /Library/LaunchDaemons/' + name + '.plist\n');
|
|
child.stdin.write('exit\n');
|
|
child.waitExit();
|
|
|
|
try
|
|
{
|
|
require('fs').unlinkSync(servicePath);
|
|
require('fs').unlinkSync('/Library/LaunchDaemons/' + name + '.plist');
|
|
}
|
|
catch(e)
|
|
{
|
|
throw ('Error uninstalling service: ' + name + ' => ' + e);
|
|
}
|
|
|
|
try
|
|
{
|
|
require('fs').rmdirSync('/usr/local/mesh_services/' + name);
|
|
}
|
|
catch(e)
|
|
{}
|
|
}
|
|
else
|
|
{
|
|
throw ('Service: ' + name + ' does not exist');
|
|
}
|
|
}
|
|
}
|
|
if(process.platform == 'linux')
|
|
{
|
|
this.getServiceType = function getServiceType()
|
|
{
|
|
var platform = require('process-manager').getProcessInfo(1).Name;
|
|
if (platform == 'init')
|
|
{
|
|
if(require('fs').existsSync('/etc/init'))
|
|
{
|
|
platform = 'upstart';
|
|
}
|
|
}
|
|
return (platform);
|
|
};
|
|
}
|
|
}
|
|
|
|
module.exports = serviceManager;
|
|
module.exports.manager = new serviceManager(); |