domotz-remote-pawn
Version:
Domotz Agent
208 lines (190 loc) • 7.32 kB
JavaScript
/** This file is part of Domotz Agent.
* Copyright (C) 2016 Domotz Ltd
*
* Domotz Agent is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Domotz Agent is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Domotz Agent. If not, see <http://www.gnu.org/licenses/>.
*
* Created by Tommaso Latini <tommaso@domotz.com> on 11/09/15.
*/
var factory = function (q, fs, childProcess, cpu, resourceLocator) {
var myConsole = resourceLocator.log.decorateLogs();
var env = process.env;
var serviceDomotz = env.DOMOTZ_SS_LINK;
var logDir = env.DOMOTZ_LOG_DIR;
function kill(pid, signal) {
signal = signal !== undefined ? signal : 'SIGTERM';
pid = pid || process.pid;
myConsole.warn(' Send Signal %s to %s', signal, pid);
try {
return process.kill(pid, signal);
} catch (e) {
myConsole.error('Error killing process ' + e.message);
}
}
function gracefulShutdown() {
var delay = 500;
if (
resourceLocator.utils.platform.isNetgearRouter() ||
(resourceLocator.utils.sentryManager && resourceLocator.utils.sentryManager.isEnabled())
) {
delay = 5000;
}
if (process.platform === 'win32') {
resourceLocator.log.flush();
} else {
kill(process.pid, 'SIGHUP'); // Flush Log
}
try {
terminateJamDriver();
if (resourceLocator.stallWatcher) {
resourceLocator.stallWatcher.kill();
}
storeHttpCache();
storeRTDData();
storeDHCPData();
} catch (e) {
myConsole.error(' An error happened during shutdown procedure:');
myConsole.error(e.stack);
}
myConsole.warn('Graceful Shutdown within %d ms', delay);
setTimeout(function () {
process.exit(0);
}, delay);
}
function terminateJamDriver() {
if (resourceLocator.jamDriver) {
return resourceLocator.jamDriver.killDeviceJammer();
}
}
function storeRTDData() {
if (resourceLocator.networkTools.rtd) {
resourceLocator.networkTools.rtd.storeToFS();
}
}
function storeDHCPData() {
if (resourceLocator.DHCPRequestCounters) {
resourceLocator.DHCPRequestCounters.storeToFS();
}
}
function storeHttpCache() {
if (resourceLocator.httpOutCache) {
resourceLocator.httpOutCache.storeCache();
}
}
/**
* DEPRECATED --> use gracefulShutdown instead
* Used when there was also zabbix proxy.
* In order to avoid memory leak it can be called once per day by the
* suicide event.
*/
function restart() {
myConsole.warn(' Rebooting services');
var cmd = serviceDomotz,
args = ['restart'],
options = {
detached: true,
stdio: [
'ignore', // out and err below
fs.openSync(logDir + '/reboot.log', 'w'),
fs.openSync(logDir + '/reboot_error.log', 'w'),
],
};
if (resourceLocator.utils.platform.needsSudo()) {
cmd = 'sudo';
args.unshift(serviceDomotz);
}
childProcess.spawn(cmd, args, options).unref();
}
/**
* Same arguments as child_process.spawn. It returns a promise, hiding
* the event emitter behind!
*
* @param command
* @param args
* @param options
* @returns {*|promise}
*/
function spawn(command, args, options) {
if (!command || typeof command !== 'string') {
throw new Error('Command must be a string. command: <' + command + '> type=' + typeof command);
}
var deferred = q.defer();
cpu.schedule(deferred, command, args, options);
return deferred.promise;
}
/**
* Same arguments as child_process.exec except for the callback.
* It returns a promise!
* This function originally exploited the exec method of child_process.
* We rewrite it because exec it's not efficient, insofar it opens an
* interpreter (by default a shell) and then the command for 2 process in
* total.
*
* @param command
* @param options
* @returns {*|promise}
*
*/
function exec(command, options) {
var isCountDiscoveryEnabled = process.env.ENABLE_COMMAND_SUFFIX_COMMENT;
var commandSuffixComment = '';
if (isCountDiscoveryEnabled && options && options.suffix_comment) {
commandSuffixComment = ' #' + options.suffix_comment;
}
var argv = (command + commandSuffixComment).trim().split(' ');
return spawn(argv[0], argv.slice(1), options);
}
/**
* This is a special case of the spawn() functionality for spawning
* Node processes. In addition to having all the methods in a normal
* ChildProcess instance, the returned object has a communication channel
* built-in. See child.send(message, [sendHandle]) for details.
*
* N.B. THESE CHILD NODES ARE STILL WHOLE NEW INSTANCES OF V8.
* ASSUME AT LEAST 30MS STARTUP AND 10MB MEMORY FOR EACH NEW NODE.
* THAT IS, YOU CANNOT CREATE MANY THOUSANDS OF THEM !!
*
* TODO: Embed this type of call to external process in the scheduler
*
* @param modulePath: The module to run in the child
* @param args: Array List of string arguments
* @param options: Object
* --> cwd: String Current working directory of the child process
* --> env: Object Environment key-value pairs
* --> execPath: String Executable used to create the child process
* --> execArgv: Array List of string arguments passed to the
* executable (Default: process.execArgv)
* --> silent: Boolean
* If true, stdin, stdout, and stderr of the child
* will be piped to the parent,
*
* If false they will be inherited from the parent,
* see the "pipe" and "inherit" options for spawn()'s
* stdio for more details (default is false)
* @returns {*|{send, kill, on}}
*/
function fork(modulePath, args, options) {
var child = childProcess.fork(modulePath, args, options);
myConsole.info(' Forking Module --> %s. Args: %s. Options: %s.', modulePath, args.join(' '), JSON.stringify(options), { pid: child.pid });
return child;
}
return {
kill: kill,
gracefulShutdown: gracefulShutdown,
restart: restart,
exec: exec,
spawn: spawn,
fork: fork,
};
};
module.exports.factory = factory;