Skip to content

Commit

Permalink
Remove optimist in favor of @author.io/arg to avoid
Browse files Browse the repository at this point in the history
This avoids a security issue on minimist, which is an indirect dependency
  • Loading branch information
forty committed Jul 29, 2020
1 parent 55f4940 commit 76f8a46
Show file tree
Hide file tree
Showing 2 changed files with 118 additions and 82 deletions.
198 changes: 117 additions & 81 deletions lib/wrapper.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,124 @@
// Handle input parameters
var Logger = require('./eventlog'),
optimist = require('optimist'),
Args = require('@author.io/args'),
net = require('net'),
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;
});
})
.describe('scriptoptions','The options to be sent to the script.')
.alias('d','cwd')
.describe('cwd','The absolute path of the current working directory of the script to be run as a process.')
// .check(function(argv){
// require('fs').existsSync(p.resolve(argv.d),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;
})
.default('stopparentfirst', 'no')
.alias('s', 'stopparentfirst')
.describe('stopparentfirst', 'Allow the script to exit using a shutdown message.')
.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
child = null,
forcekill = false;

if (argv.d){
if (!require('fs').existsSync(p.resolve(argv.d))){
console.warn(argv.d+' not found.');
argv.d = process.cwd();
Args.configure({
file: {
type: 'string',
required: true,
alias: 'f',
description: 'The absolute path of the script to be run as a process.',
validate: function(value){
require('fs').existsSync(p.resolve(value),function(exists){
return exists;
});
}
},
scriptoptions: {
type: 'string',
description: 'The options to be sent to the script.'
},
cwd: {
type: 'string',
description: 'The absolute path of the current working directory of the script to be run as a process.',
alias: 'd',
validate: function(value){
require('fs').existsSync(p.resolve(value),function(exists){
return exists;
});
}
},
log: {
type: 'string',
required: true,
alias: 'l',
description: 'The descriptive name of the log for the process'
},
eventlog: {
type: 'string',
alias: 'e',
description: 'The event log container. This must be APPLICATION or SYSTEM.',
defaults: 'APPLICATION'
},
maxretries: {
type: 'number',
alias: 'm',
description: 'The maximim number of times the process will be auto-restarted.',
defaults: -1
},
maxrestarts: {
type: 'number',
alias: 'r',
description: 'The maximim number of times the process should be restarted within a '+max+' second period shutting down.',
defaults: 5
},
wait: {
type: 'number',
alias: 'w',
description: 'The number of seconds between each restart attempt.',
defaults: 1,
validate: function(value){
return value >= 0;
}
},
grow: {
type: 'number',
alias: 'g',
description: 'A percentage growth rate at which the wait time is increased.',
defaults: .25,
validate: function(value){
return value >= 0 && value <= 1;
}
},
abortonerror: {
type: 'string',
alias: 'a',
description: 'Do not attempt to restart the process if it fails with an error.',
defaults: 'no',
options: ['y','n','yes','no']
},
stopparentfirst: {
type: 'string',
alias: 's',
decribe: 'Allow the script to exit using a shutdown message.',
defaults: 'no',
options: ['y','n','yes','no']
}
});
Args.disallowUnrecognized();
Args.enforceRules();



var argv = Args.data,
log = new Logger(argv.eventlog == undefined ? argv.log : {source:argv.log,eventlog:argv.eventlog}),
script = p.resolve(argv.file),
wait = argv.wait*1000,
grow = argv.grow+1;

if (argv.cwd){
if (!require('fs').existsSync(p.resolve(argv.cwd))){
console.warn(argv.cwd+' not found.');
argv.cwd = process.cwd();
}
argv.d = p.resolve(argv.d);
argv.cwd = p.resolve(argv.cwd);
}

if (typeof argv.m === 'string'){
argv.m = parseInt(argv.m);
if (typeof argv.maxretries === 'string'){
argv.maxretries = parseInt(argv.maxretries);
}

// Set the absolute path of the file
argv.f = p.resolve(argv.f);
argv.file = p.resolve(argv.file);

// Hack to force the wrapper process to stay open by launching a ghost socket server
var server = net.createServer().listen();
Expand All @@ -100,7 +136,7 @@ var monitor = function() {
if(!child || !child.pid) {

// If the number of periodic starts exceeds the max, kill the process
if (starts >= argv.r){
if (starts >= argv.maxrestarts){
if (new Date().getTime()-(max*1000) <= startTime.getTime()){
log.error('Too many restarts within the last '+max+' seconds. Please check the script.');
process.exit();
Expand All @@ -110,8 +146,8 @@ var monitor = function() {
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.');
if (attempts > argv.maxretries && argv.maxretries >= 0){
log.error('Too many restarts. '+argv.file+' will not be restarted because the maximum number of total restarts has been exceeded.');
process.exit();
} else {
launch('warn', 'Restarted ' + wait + ' msecs after unexpected exit; attempts = ' + attempts);
Expand All @@ -120,7 +156,7 @@ var monitor = function() {
} else {
// reset attempts and wait time
attempts = 0;
wait = argv.w * 1000;
wait = argv.wait * 1000;
}
};

Expand All @@ -138,7 +174,7 @@ var launch = function(logLevel, msg) {
return;
}

//log.info('Starting '+argv.f);
//log.info('Starting '+argv.file);
if (logLevel && msg) {
log[logLevel](msg);
}
Expand All @@ -156,18 +192,18 @@ var launch = function(logLevel, msg) {
// Fork the child process
var opts = {env:process.env};
var args = [];
if (argv.d) opts.cwd = argv.d;
if (argv.s) opts.detached = true;
if (argv.cwd) opts.cwd = argv.cwd;
if (argv.stopparentfirst) opts.detached = true;
if (argv.scriptoptions) args = argv.scriptoptions.split(' ');
child = fork(script,args,opts);

// When the child dies, attempt to restart based on configuration
child.on('exit',function(code){
log.warn(argv.f+' stopped running.');
log.warn(argv.file+' 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);
if (code !== 0 && argv.abortonerror == "yes"){
log.error(argv.file+' exited with error code '+code);
process.exit();
//server.unref();
} else if (forcekill) {
Expand All @@ -183,13 +219,13 @@ var launch = function(logLevel, msg) {
var killkid = function(){
forcekill = true;
if (child) {
if (argv.s) {
if (argv.stopparentfirst) {
child.send('shutdown');
} else {
child.kill();
}
} else {
log.warn('Attempted to kill an unrecognized process.')
log.warn('Attempted to kill an unrecognized process.');
}
}

Expand All @@ -202,4 +238,4 @@ process.on('uncaughtException', function(err) {
});

// Launch the process
launch('info', 'Starting ' + argv.f);
launch('info', 'Starting ' + argv.file);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"main": "lib/node-windows.js",
"preferGlobal": true,
"dependencies": {
"optimist": "~0.6.0",
"@author.io/arg": "^1.3.11",
"xml": "0.0.12"
},
"readmeFilename": "README.md",
Expand Down

0 comments on commit 76f8a46

Please sign in to comment.