#!/usr/bin/env node "use strict"; var installer = require('../lib/installer'); var platforms = require('../lib/platforms'); var path = require('path'); var shell = require('shelljs'); platforms.get(function(err, platform){ console.log('forever-service version '+require('../package.json').version+'\n'); if(err || !platform){ console.error('This platform is not yet supported by forever-service'); console.error('To help us add this platform, please contibute to https://github.com/zapty/forever-service\n'); return; } var program = require('commander'); program .version(require('../package.json').version) program .command('install [service]') .option('-s, --script [script]','Script to run as service e.g. app.js, defaults to app.js\n') .option('-e --envVars [vars]','Environment Variables for the script\n e.g. -e "PORT=80 ENV=prod FOO=bar\n') .option('-o --scriptOptions [options]','Command line options for the script\n') .option('--minUptime [value]','Minimum uptime (millis) for a script to not be considered "spinning", default 5000\n') .option('--spinSleepTime [value]','Time to wait (millis) between launches of a spinning script., default 2000\n') .option('--noGracefulShutdown','Disable graceful shutdown\n') .option('-t --forceKillWaitTime [waittime]','Time to wait in milliseconds before force killing; after failed graceful stop\n defaults to 5000 ms, after which entire process tree is forcibly terminated\n') .option('-f --foreverOptions " [options]"','Extra command line options for forever\n e.g. -f " --watchDirectory /your/watch/directory -w -c /custom/cli" etc..\n NOTE: a mandatory space is required after double quotes, if begining with -\n') .option('--start','Start service after provisioning\n') .option('--nologrotate','Do not generate logrotate script\n') .option('--logrotateFrequency [frequency]','Frequency of logrotation\n valid values are daily, weekly, monthly, "size 100k" etc, default daily\n') .option('--logrotateMax [value]','Maximum logrotated files to retain, default 10 (logrotate parameter)\n') .option('--logrotateDateExt', 'Archive old versions of log files adding a daily extension like YYYYMMDD instead of simply adding a number\n') .option('--logrotateCompress', 'Enable compression for logrotate\n') .option('-p --foreverPath [value]','Path for forever cli e.g. /usr/local/bin,\n by default forever cli is searched in system Path variable\n') .option('-u --applyUlimits','Apply increased ulimits in supported environment\n') .option('-r --runAsUser [user]','*Experimental* Run service as a specific user, defaults to root (No ubuntu support yet)\n') .description( 'Install node script (defaults to app.js in current directory) as service via forever\n\n' ) .action(function(service, options){ if(!service){ console.error('Service name missing'); return 3; } console.log('Platform - '+platform.os); var ctx = Object.create(platform); ctx.service = service; if(options.script) ctx.script = options.script; else ctx.script = 'app.js'; if(options.envVars) ctx.envVars = options.envVars; if(ctx.envVars){ //Split at space, but ignore space inside quotes.. ctx.envVarsNameValueArray = installer.splitEnvVariables(ctx.envVars); } if(options.scriptOptions) ctx.scriptOptions = options.scriptOptions; if(options.minUptime) ctx.minUptime = options.minUptime; if(options.spinSleepTime) ctx.spinSleepTime = options.spinSleepTime; if(options.noGracefulShutdown) ctx.killSignal = 'SIGKILL'; //Default is SIGTERM so non graceful shutdown makes it SIGKILL if(options.forceKillWaitTime) ctx.forceKillWaitTime = options.forceKillWaitTime; if(options.foreverOptions) ctx.foreverOptions = options.foreverOptions; if(options.nologrotate) ctx.nologrotate = true; if(options.logrotateFrequency) ctx.logrotateFrequency = options.logrotateFrequency; if(options.logrotateMax) ctx.logrotateMax = options.logrotateMax; if(options.logrotateDateExt) ctx.logrotateDateExt = 'dateext'; if(options.logrotateCompress) ctx.logrotateCompress = 'compress'; if(options.applyUlimits) ctx.applyUlimits = true; if(options.foreverPath){ var fp = options.foreverPath.trim(); if(!fp.match(/\/$/)) fp = fp+'/'; ctx.foreverPath = fp; } else { var foreverCmd = getCmdPath('forever'); if(foreverCmd){ ctx.foreverPath = path.dirname(foreverCmd) + "/"; } else { console.error("forever not found, forever must be installed using 'npm install -g forever'"); process.exit(1); } } if(options.runAsUser){ ctx.runAsUser = options.runAsUser; } else ctx.runAsUser = null; if(getCmdPath('runuser')){ ctx.suprog="runuser"; } else ctx.suprog="su"; ctx.foreverRoot = getForeverRoot(ctx.runAsUser); if( !installer.validateScriptName(ctx.script) ){ console.error(ctx.script+' not found'); return; } installer.install(ctx, function(err, result){ if(err){ console.error('Could not provision service'); console.error(err); return; } console.log(ctx.service+' provisioned successfully\n'); if(result.help){ console.log(result.help); } if(options.start && ctx.installer.startService){ ctx.installer.startService(ctx, function(err){ if(err){ console.error('Service could not be started'); } else { console.log('Service started successfully'); } }); } }); }); program .command('delete [service]') .description('Delete all provisioned files for the service, will stop service if running before delete') .action(function(service){ if(!service){ console.error('Service name missing'); return 3; } console.log('Platform - '+platform.os); var ctx = Object.create(platform); ctx.service = service; installer.delete(ctx, function(err, data){ if(err){ console.error('Could not delete service'); console.error(err); return; } console.log('Service '+ctx.service+' deleted successfully'); }); }); var result = program.parse(process.argv); if(!(result.args && result.args.length > 1 && result.args[1] && typeof result.args[1]==='object')){ //If command line arguments are invalid display help console.error("Invalid arguments!"); program.outputHelp(); console.log(' Command help:\n') console.log(' forever-service install --help'); console.log(' forever-service delete --help\n'); } }); function getForeverRoot(user){ if(process.getuid() != 0 ){ console.log("forever-service must be run as root"); process.exit(1); } var r; if(user){ r = shell.exec('su - '+user+' -c '+__dirname+'"/get-forever-config"',{silent:true}); } else { return process.env.FOREVER_ROOT || path.join(process.env.HOME || process.env.USERPROFILE || '/root', '.forever'); //r= shell.exec(__dirname+'"/get-forever-config"',{silent:true}); } if(r.code != 0){ console.log("Unable to get forever root ["+r.code+"]"); console.error(r.output); process.exit(1); } else return r.output.trim(); } function getCmdPath(cmd){ var r=shell.exec('whereis '+cmd, {silent: true}); var rs = r.output && r.output.split(" "); if(rs && rs.length && rs.length > 1) { return rs[1]; } else { console.log(cmd + 'path not found'); } }