mirror of
https://github.com/Ylianst/MeshAgent
synced 2025-12-06 00:13:33 +00:00
Documented code for agent-installer
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -15,8 +15,15 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// This is a helper utility that is used by the Mesh Agent to install itself
|
||||
// as a background service, on all platforms that the agent supports.
|
||||
//
|
||||
|
||||
try
|
||||
{
|
||||
// This peroperty is a polyfill for an Array, to fetch the specified element if it exists, removing the surrounding quotes if they are there
|
||||
Object.defineProperty(Array.prototype, 'getParameterEx',
|
||||
{
|
||||
value: function (name, defaultValue)
|
||||
@@ -34,6 +41,8 @@ try
|
||||
return (defaultValue);
|
||||
}
|
||||
});
|
||||
|
||||
// This property is a polyfill for an Array, to fetch the specified element if it exists
|
||||
Object.defineProperty(Array.prototype, 'getParameter',
|
||||
{
|
||||
value: function (name, defaultValue)
|
||||
@@ -46,6 +55,7 @@ catch(x)
|
||||
{ }
|
||||
try
|
||||
{
|
||||
// This property is a polyfill for an Array, to fetch the index of the specified element, if it exists
|
||||
Object.defineProperty(Array.prototype, 'getParameterIndex',
|
||||
{
|
||||
value: function (name)
|
||||
@@ -66,6 +76,7 @@ catch(x)
|
||||
{ }
|
||||
try
|
||||
{
|
||||
// This property is a polyfill for an Array, to remove the specified element, if it exists
|
||||
Object.defineProperty(Array.prototype, 'deleteParameter',
|
||||
{
|
||||
value: function (name)
|
||||
@@ -82,6 +93,7 @@ catch(x)
|
||||
{ }
|
||||
try
|
||||
{
|
||||
// This property is a polyfill for an Array, to to fetch the value YY of an element XX in the format --XX=YY, if it exists
|
||||
Object.defineProperty(Array.prototype, 'getParameterValue',
|
||||
{
|
||||
value: function (i)
|
||||
@@ -95,6 +107,7 @@ try
|
||||
catch(x)
|
||||
{ }
|
||||
|
||||
// This function performs some checks on the parameter structure, to make sure the minimum set of requried elements are present
|
||||
function checkParameters(parms)
|
||||
{
|
||||
var msh = _MSH();
|
||||
@@ -104,6 +117,7 @@ function checkParameters(parms)
|
||||
|
||||
if (msh.fileName != null)
|
||||
{
|
||||
// This converts the --fileName parameter of the installer, to the --target=XXX format required by service-manager.js
|
||||
var i = parms.getParameterIndex('fileName');
|
||||
if(i>=0)
|
||||
{
|
||||
@@ -116,6 +130,7 @@ function checkParameters(parms)
|
||||
{
|
||||
if(msh.meshServiceName != null)
|
||||
{
|
||||
// This adds the specified service name, to be consumed by service-manager.js
|
||||
parms.push('--meshServiceName="' + msh.meshServiceName + '"');
|
||||
}
|
||||
else
|
||||
@@ -129,6 +144,8 @@ function checkParameters(parms)
|
||||
catch(xx)
|
||||
{
|
||||
}
|
||||
|
||||
// The default is 'Mesh Agent' for Windows, and 'meshagent' for everything else...
|
||||
if(tmp != (process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'))
|
||||
{
|
||||
parms.push('--meshServiceName="' + tmp + '"');
|
||||
@@ -136,6 +153,8 @@ function checkParameters(parms)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is the entry point for installing the service
|
||||
function installService(params)
|
||||
{
|
||||
process.stdout.write('...Installing service');
|
||||
@@ -145,6 +164,7 @@ function installService(params)
|
||||
var targetx = params.getParameterIndex('target');
|
||||
if (targetx >= 0)
|
||||
{
|
||||
// Let's remove any embedded spaces in 'target' as that can mess up some OSes
|
||||
target = params.getParameterValue(targetx);
|
||||
params.splice(targetx, 1);
|
||||
target = target.split(' ').join('');
|
||||
@@ -157,6 +177,7 @@ function installService(params)
|
||||
proxyFile = proxyFile.split('.exe').join('.proxy');
|
||||
try
|
||||
{
|
||||
// Add this parameter, so the agent instance will be embedded with the Windows User that installed the service
|
||||
params.push('--installedByUser="' + require('win-registry').usernameToUserKey(require('user-sessions').getProcessOwnerName(process.pid).name) + '"');
|
||||
}
|
||||
catch(exc)
|
||||
@@ -165,6 +186,7 @@ function installService(params)
|
||||
}
|
||||
else
|
||||
{
|
||||
// On Linux, the --installedByUser property is populated with the UID of the user that is installing the service
|
||||
var u = require('user-sessions').tty();
|
||||
var uid = 0;
|
||||
try
|
||||
@@ -178,6 +200,9 @@ function installService(params)
|
||||
proxyFile += '.proxy';
|
||||
}
|
||||
|
||||
|
||||
// We're going to create the OPTIONS object to hand to service-manager.js. We're going to populate all the properties we can, using
|
||||
// values that were passed into the installer, using default values for the ones that aren't specified.
|
||||
var options =
|
||||
{
|
||||
name: params.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'),
|
||||
@@ -191,8 +216,12 @@ function installService(params)
|
||||
options.description = params.getParameter('description', options.name + ' background service'); params.deleteParameter('description');
|
||||
|
||||
if (process.platform == 'win32') { options.companyName = ''; }
|
||||
if (require('fs').existsSync(proxyFile)) { options.files = [{ source: proxyFile, newName: options.target + '.proxy' }]; }
|
||||
|
||||
// If a .proxy file was found, we'll include it in the list of files to be copied when installing the agent
|
||||
if (require('fs').existsSync(proxyFile)) { options.files = [{ source: proxyFile, newName: options.target + '.proxy' }]; }
|
||||
|
||||
|
||||
// if '--copy-msh' is specified, we will try to copy the .msh configuration file found in the current working directory
|
||||
var i;
|
||||
if ((i = params.indexOf('--copy-msh="1"')) >= 0)
|
||||
{
|
||||
@@ -209,6 +238,7 @@ function installService(params)
|
||||
options.installInPlace = true;
|
||||
}
|
||||
|
||||
// We're going to specify what folder the agent should be installed into
|
||||
if (global._workingpath != null && global._workingpath != '' && global._workingpath != '/')
|
||||
{
|
||||
for (i = 0; i < options.parameters.length; ++i)
|
||||
@@ -224,13 +254,14 @@ function installService(params)
|
||||
options.parameters.push('--installPath="' + global._workingpath + '"');
|
||||
}
|
||||
}
|
||||
|
||||
if ((i = options.parameters.getParameterIndex('installPath')) >= 0)
|
||||
{
|
||||
options.installPath = options.parameters.getParameterValue(i);
|
||||
options.installInPlace = false;
|
||||
options.parameters.splice(i, 1);
|
||||
}
|
||||
|
||||
// If companyName was specified, we're going to move it into the structure
|
||||
if ((i = options.parameters.getParameterIndex('companyName')) >= 0)
|
||||
{
|
||||
options.companyName = options.parameters.getParameterValue(i);
|
||||
@@ -239,10 +270,12 @@ function installService(params)
|
||||
|
||||
try
|
||||
{
|
||||
// Let's actually install the service
|
||||
require('service-manager').manager.installService(options);
|
||||
process.stdout.write(' [DONE]\n');
|
||||
if(process.platform == 'win32')
|
||||
{
|
||||
// On Windows, we're going to enable this service to be runnable from SafeModeWithNetworking
|
||||
require('win-bcd').enableSafeModeService(options.name);
|
||||
}
|
||||
}
|
||||
@@ -252,6 +285,9 @@ function installService(params)
|
||||
process.exit();
|
||||
}
|
||||
var svc = require('service-manager').manager.getService(options.name);
|
||||
|
||||
// macOS needs a LaunchAgent to help with some usages that need to run from within the user session,
|
||||
// so we can setup ourselves to accomplish that.
|
||||
if (process.platform == 'darwin')
|
||||
{
|
||||
svc.load();
|
||||
@@ -274,6 +310,7 @@ function installService(params)
|
||||
}
|
||||
}
|
||||
|
||||
// For Windows, we're going to add an INBOUND UDP rule for WebRTC Data
|
||||
if(process.platform == 'win32')
|
||||
{
|
||||
var loc = svc.appLocation();
|
||||
@@ -293,6 +330,8 @@ function installService(params)
|
||||
require('win-firewall').addFirewallRule(rule);
|
||||
process.stdout.write(' [DONE]\n');
|
||||
}
|
||||
|
||||
// Let's try to start the service that we just installed
|
||||
process.stdout.write(' -> Starting service...');
|
||||
try
|
||||
{
|
||||
@@ -304,15 +343,18 @@ function installService(params)
|
||||
process.stdout.write(' [ERROR]\n');
|
||||
}
|
||||
|
||||
if (process.platform == 'win32') { svc.close(); }
|
||||
// On Windows we should explicitly close the service manager when we are done, instead of relying on the Garbage Collection, so the service object isn't unnecessarily locked
|
||||
if (process.platform == 'win32') { svc.close(); }
|
||||
if (parseInt(params.getParameter('__skipExit', 0)) == 0)
|
||||
{
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
// The last step in uninstalling a service
|
||||
function uninstallService3(params)
|
||||
{
|
||||
// macOS has a LaunchAgent, that we need to uninstall
|
||||
if (process.platform == 'darwin')
|
||||
{
|
||||
process.stdout.write(' -> Uninstalling launch agent...');
|
||||
@@ -328,16 +370,20 @@ function uninstallService3(params)
|
||||
process.stdout.write(' [ERROR]\n');
|
||||
}
|
||||
}
|
||||
|
||||
if (params != null && !params.includes('_stop'))
|
||||
{
|
||||
// Since we are done uninstalling a previously installed service, we can continue with installation
|
||||
installService(params);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We are going to stop here, if we are only intending to uninstall the service
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2 in service uninstallation
|
||||
function uninstallService2(params, msh)
|
||||
{
|
||||
var secondaryagent = false;
|
||||
@@ -345,16 +391,20 @@ function uninstallService2(params, msh)
|
||||
var dataFolder = null;
|
||||
var appPrefix = null;
|
||||
var uninstallOptions = null;
|
||||
var serviceName = params.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
|
||||
var serviceName = params.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'); // get the service name, using the provided defaults if not specified
|
||||
|
||||
// Remove the .msh file if present
|
||||
try { require('fs').unlinkSync(msh); } catch (mshe) { }
|
||||
if ((i = params.indexOf('__skipBinaryDelete')) >= 0)
|
||||
{
|
||||
// We will skip deleting of the actual binary, if this option was provided.
|
||||
// This will happen if we try to install the service to a location where we are running the installer from.
|
||||
params.splice(i, 1);
|
||||
uninstallOptions = { skipDeleteBinary: true };
|
||||
}
|
||||
if (params && params.includes('--_deleteData="1"'))
|
||||
{
|
||||
// This will facilitate cleanup of the files associated with the agent
|
||||
dataFolder = params.getParameterEx('_workingDir', null);
|
||||
appPrefix = params.getParameterEx('_appPrefix', null);
|
||||
}
|
||||
@@ -362,17 +412,22 @@ function uninstallService2(params, msh)
|
||||
process.stdout.write(' -> Uninstalling previous installation...');
|
||||
try
|
||||
{
|
||||
// Let's actually try to uninstall the service
|
||||
require('service-manager').manager.uninstallService(serviceName, uninstallOptions);
|
||||
process.stdout.write(' [DONE]\n');
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
// For Windows, we can remove the entry to enable this service to be runnable from SafeModeWithNetworking
|
||||
require('win-bcd').disableSafeModeService(serviceName);
|
||||
}
|
||||
|
||||
// Lets try to cleanup the uninstalled service
|
||||
if (dataFolder && appPrefix)
|
||||
{
|
||||
process.stdout.write(' -> Deleting agent data...');
|
||||
if (process.platform != 'win32')
|
||||
{
|
||||
// On Non-Windows platforms, we're going to cleanup using the shell
|
||||
var levelUp = dataFolder.split('/');
|
||||
levelUp.pop();
|
||||
levelUp = levelUp.join('/');
|
||||
@@ -408,6 +463,7 @@ function uninstallService2(params, msh)
|
||||
}
|
||||
else
|
||||
{
|
||||
// On Windows, we're going to spawn a command shell to cleanup
|
||||
var levelUp = dataFolder.split('\\');
|
||||
levelUp.pop();
|
||||
levelUp = levelUp.join('\\');
|
||||
@@ -452,6 +508,7 @@ function uninstallService2(params, msh)
|
||||
|
||||
if(secondaryagent)
|
||||
{
|
||||
// If a secondary agent was found, remove the CRON job for it
|
||||
process.stdout.write(' -> removing secondary agent from task scheduler...');
|
||||
var p = require('task-scheduler').delete(serviceName + 'Diagnostic/periodicStart');
|
||||
p._params = params;
|
||||
@@ -470,9 +527,14 @@ function uninstallService2(params, msh)
|
||||
uninstallService3(params);
|
||||
}
|
||||
}
|
||||
|
||||
// First step in service uninstall
|
||||
function uninstallService(params)
|
||||
{
|
||||
// Before we uninstall, we need to fetch the service from service-manager.js
|
||||
var svc = require('service-manager').manager.getService(params.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'));
|
||||
|
||||
// We can calculate what the .msh file location is, based on the appLocation of the service
|
||||
var msh = svc.appLocation();
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
@@ -483,6 +545,7 @@ function uninstallService(params)
|
||||
msh = msh + '.msh';
|
||||
}
|
||||
|
||||
// Let's try to stop the service if we think it might be running
|
||||
if (svc.isRunning == null || svc.isRunning())
|
||||
{
|
||||
process.stdout.write(' -> Stopping Service...');
|
||||
@@ -504,6 +567,7 @@ function uninstallService(params)
|
||||
{
|
||||
if (process.platform == 'darwin')
|
||||
{
|
||||
// macOS requries us to unload the service
|
||||
svc.unload();
|
||||
}
|
||||
else
|
||||
@@ -520,11 +584,14 @@ function uninstallService(params)
|
||||
uninstallService2(params, msh);
|
||||
}
|
||||
}
|
||||
|
||||
// A previous service installation was found, so lets do some extra processing
|
||||
function serviceExists(loc, params)
|
||||
{
|
||||
process.stdout.write(' [FOUND: ' + loc + ']\n');
|
||||
if(process.platform == 'win32')
|
||||
{
|
||||
// On Windows, we need to cleanup the firewall rules associated with our install path
|
||||
process.stdout.write(' -> Checking firewall rules for previous installation... [0%]');
|
||||
var p = require('win-firewall').getFirewallRulesAsync({ program: loc, noResult: true, minimal: true, timeout: 15000 });
|
||||
p.on('progress', function (c)
|
||||
@@ -533,6 +600,7 @@ function serviceExists(loc, params)
|
||||
});
|
||||
p.on('rule', function (r)
|
||||
{
|
||||
// Remove firewall entries for our install path
|
||||
require('win-firewall').removeFirewallRule(r.DisplayName);
|
||||
});
|
||||
p.finally(function ()
|
||||
@@ -547,23 +615,26 @@ function serviceExists(loc, params)
|
||||
}
|
||||
}
|
||||
|
||||
// Entry point for -fulluninstall
|
||||
function fullUninstall(jsonString)
|
||||
{
|
||||
var parms = JSON.parse(jsonString);
|
||||
if (parseInt(parms.getParameter('verbose', 0)) == 0)
|
||||
{
|
||||
console.setDestination(console.Destinations.DISABLED);
|
||||
console.setDestination(console.Destinations.DISABLED); // IF verbose is disabled(default), we will no-op console.log
|
||||
}
|
||||
else
|
||||
{
|
||||
console.setInfoLevel(1);
|
||||
console.setInfoLevel(1); // IF verbose is specified, we will show info level 1 messages
|
||||
}
|
||||
parms.push('_stop');
|
||||
parms.push('_stop'); // Since we are intending to halt after uninstalling the service, we specify this, since we are re-using the uninstall code with the installer.
|
||||
|
||||
checkParameters(parms);
|
||||
checkParameters(parms); // Perform some checks on the passed in parameters
|
||||
|
||||
var name = parms.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
|
||||
var name = parms.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'); // Set the service name, using the defaults if not specified
|
||||
|
||||
|
||||
// Check for a previous installation of the service
|
||||
try
|
||||
{
|
||||
process.stdout.write('...Checking for previous installation of "' + name + '"');
|
||||
@@ -579,22 +650,26 @@ function fullUninstall(jsonString)
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// No previous installation was found, so we can just exit
|
||||
process.stdout.write(' [NONE]\n');
|
||||
process.exit();
|
||||
}
|
||||
serviceExists(loc, parms);
|
||||
}
|
||||
|
||||
// Entry point for -fullinstall
|
||||
function fullInstall(jsonString)
|
||||
{
|
||||
// Perform some checks on the specified parameters
|
||||
var parms = JSON.parse(jsonString);
|
||||
checkParameters(parms);
|
||||
|
||||
var loc = null;
|
||||
var i;
|
||||
var name = parms.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent');
|
||||
var name = parms.getParameter('meshServiceName', process.platform == 'win32' ? 'Mesh Agent' : 'meshagent'); // Set the service name, using defaults if not specified
|
||||
if (process.platform != 'win32') { name = name.split(' ').join('_'); }
|
||||
|
||||
// No-op console.log() if verbose is not specified, otherwise set the verbosity level to level 1
|
||||
if (parseInt(parms.getParameter('verbose', 0)) == 0)
|
||||
{
|
||||
console.setDestination(console.Destinations.DISABLED);
|
||||
@@ -604,6 +679,7 @@ function fullInstall(jsonString)
|
||||
console.setInfoLevel(1);
|
||||
}
|
||||
|
||||
// Check for a previous installation of the service
|
||||
try
|
||||
{
|
||||
process.stdout.write('...Checking for previous installation of "' + name + '"');
|
||||
@@ -617,15 +693,16 @@ function fullInstall(jsonString)
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
// No previous installation was found, so we can continue with installation
|
||||
process.stdout.write(' [NONE]\n');
|
||||
installService(parms);
|
||||
return;
|
||||
}
|
||||
if (process.execPath == loc)
|
||||
{
|
||||
parms.push('__skipBinaryDelete');
|
||||
parms.push('__skipBinaryDelete'); // If the installer is running from the installed service path, skip deleting the binary
|
||||
}
|
||||
serviceExists(loc, parms);
|
||||
serviceExists(loc, parms); // Previous installation was found, so we need to do some extra processing before we continue with installation
|
||||
}
|
||||
|
||||
|
||||
@@ -635,6 +712,8 @@ module.exports =
|
||||
fullUninstall: fullUninstall
|
||||
};
|
||||
|
||||
|
||||
// Legacy Windows Helper function, to perform a self-update
|
||||
function sys_update(isservice, b64)
|
||||
{
|
||||
// This is run on the 'updated' agent.
|
||||
@@ -778,6 +857,7 @@ function sys_update(isservice, b64)
|
||||
});
|
||||
}
|
||||
|
||||
// Another Windows Legacy Helper for Self-Update, that shows the updater version
|
||||
function agent_updaterVersion(updatePath)
|
||||
{
|
||||
var ret = 0;
|
||||
@@ -807,6 +887,8 @@ function agent_updaterVersion(updatePath)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
|
||||
// Windows Helper to clear firewall entries
|
||||
function win_clearfirewall(passthru)
|
||||
{
|
||||
process.stdout.write('Clearing firewall rules... [0%]');
|
||||
@@ -829,6 +911,8 @@ function win_clearfirewall(passthru)
|
||||
return (p);
|
||||
}
|
||||
}
|
||||
|
||||
// Windows Helper for enumerating Firewall Rules associated with our binary
|
||||
function win_checkfirewall()
|
||||
{
|
||||
process.stdout.write('Checking firewall rules... [0%]');
|
||||
@@ -850,6 +934,8 @@ function win_checkfirewall()
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
// Windows Helper for setting a firewall rule entry
|
||||
function win_setfirewall()
|
||||
{
|
||||
var p = win_clearfirewall(true);
|
||||
@@ -873,6 +959,7 @@ function win_setfirewall()
|
||||
|
||||
}
|
||||
|
||||
// Windows Helper, for performing SelfUpdate on Console Mode Agent
|
||||
function win_consoleUpdate()
|
||||
{
|
||||
// This is run from the 'old' agent, to copy the 'updated' agent.
|
||||
@@ -889,12 +976,15 @@ function win_consoleUpdate()
|
||||
require('child_process')._execve(process.execPath.split('.exe').join('.update.exe'), args);
|
||||
}
|
||||
|
||||
|
||||
// Legacy Helper for Windows Self-Update. Shouldn't really be used anymore, but is still here for Legacy Support
|
||||
module.exports.update = sys_update;
|
||||
module.exports.updaterVersion = agent_updaterVersion;
|
||||
|
||||
if (process.platform == 'win32')
|
||||
{
|
||||
module.exports.consoleUpdate = win_consoleUpdate;
|
||||
module.exports.clearfirewall = win_clearfirewall;
|
||||
module.exports.setfirewall = win_setfirewall;
|
||||
module.exports.checkfirewall = win_checkfirewall;
|
||||
module.exports.consoleUpdate = win_consoleUpdate; // Windows Helper, for performing SelfUpdate on Console Mode Agent
|
||||
module.exports.clearfirewall = win_clearfirewall; // Windows Helper, to clear firewall entries
|
||||
module.exports.setfirewall = win_setfirewall; // Windows Helper, to set firewall entries
|
||||
module.exports.checkfirewall = win_checkfirewall; // Windows Helper, to check firewall rules
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user