tellki-node-windows
Version:
Support for Tellki Agent Windows service.
156 lines (137 loc) • 4.55 kB
JavaScript
// Handle input parameters
var Logger = require('./eventlog'),
optimist = require('optimist'),
max = 60,
p = require('path'),
argv = optimist
.demand('file')
.alias('f','file')
.describe('file','The absolute path of the script to be run as a process.')
.check(function(argv){
require('fs').existsSync(p.resolve(argv.f),function(exists){
return exists;
});
})
.demand('log')
.alias('l','log')
.describe('log','The descriptive name of the log for the process')
.default('eventlog','APPLICATION')
.alias('e','eventlog')
.describe('eventlog','The event log container. This must be APPLICATION or SYSTEM.')
.default('maxretries',-1)
.alias('m','maxretries')
.describe('maxretries','The maximim number of times the process will be auto-restarted.')
.default('maxrestarts',5)
.alias('r','maxrestarts')
.describe('maxrestarts','The maximim number of times the process should be restarted within a '+max+' second period shutting down.')
.default('wait',1)
.alias('w','wait')
.describe('wait','The number of seconds between each restart attempt.')
.check(function(argv){
return argv.w >= 0;
})
.default('grow',.25)
.alias('g','grow')
.describe('grow','A percentage growth rate at which the wait time is increased.')
.check(function(argv){
return (argv.g >= 0 && argv.g <= 1);
})
.default('abortonerror','no')
.alias('a','abortonerror')
.describe('abortonerror','Do not attempt to restart the process if it fails with an error,')
.check(function(argv){
return ['y','n','yes','no'].indexOf(argv.a.trim().toLowerCase()) >= 0;
})
.argv,
log = new Logger(argv.e == undefined ? argv.l : {source:argv.l,eventlog:argv.e}),
fork = require('child_process').fork,
script = p.resolve(argv.f),
wait = argv.w*1000,
grow = argv.g+1,
attempts = 0,
startTime = null,
starts = 0,
child = null
forcekill = false;
if (typeof argv.m === 'string'){
argv.m = parseInt(argv.m);
}
// Set the absolute path of the file
argv.f = p.resolve(argv.f);
// Hack to force the wrapper process to stay open by launching a ghost socket server
var server = require('net').createServer().listen();
/**
* @method monitor
* Monitor the process to make sure it is running
*/
var monitor = function(exit){
if(!child.pid||exit){
// If the number of periodic starts exceeds the max, kill the process
if (starts >= argv.r){
if (new Date().getTime()-(max*1000) <= startTime.getTime()){
log.error('Too many restarts within the last '+max+' seconds. Please check the script.');
process.exit();
}
}
setTimeout(function(){
wait = wait * grow;
attempts += 1;
if (attempts > argv.m && argv.m >= 0){
log.error('Too many restarts. '+argv.f+' will not be restarted because the maximum number of total restarts has been exceeded.');
process.exit();
} else {
launch();
}
},wait);
} else {
attempts = 0;
wait = argv.w * 1000;
}
};
/**
* @method launch
* A method to start a process.
*/
var launch = function(){
if (forcekill){
return;
}
log.info('Starting '+argv.f);
// Set the start time if it's null
if (startTime == null) {
startTime = startTime || new Date();
setTimeout(function(){
startTime = null;
starts = 0;
},(max*1000)+1);
}
starts += 1;
// Fork the child process
child = fork(script,{env:process.env});
// When the child dies, attempt to restart based on configuration
child.on('exit',function(code){
log.warn(argv.f+' stopped running.');
// If an error is thrown and the process is configured to exit, then kill the parent.
if (code !== 0 && argv.a == "yes"){
log.error(argv.f+' exited with error code '+code);
process.exit();
//server.unref();
}
// Monitor the process
monitor(true);
});
};
var killkid = function(){
forcekill = true;
child.kill();
/*if (child.pid) {
require('child_process').exec('taskkill /F /PID '+child.pid,function(){
process.exit(0);
});
}*/
}
process.on('exit',killkid);
process.on("SIGINT", killkid);
process.on("SIGTERM", killkid);
// Launch the process
launch();