Comparing version 1.0.9 to 1.1.0
338
lib/index.js
var fs = require('fs'), | ||
net = require('net'), | ||
spawn = require('child_process').spawn, | ||
exec = require('child_process').exec, | ||
EventEmitter = require('events').EventEmitter, | ||
config, | ||
daemon = require('daemon'), | ||
keepAliveInterval, | ||
configFilePath, | ||
self = this, | ||
DEFAULT_SERVER_SOCK = '/tmp/restarter.sock', | ||
globalLogger; | ||
@@ -27,12 +32,14 @@ | ||
if (processChecker.stdout) { | ||
var pidMatches = {}; | ||
processChecker.on('exit', function() { | ||
callback(pidMatches); | ||
}); | ||
processChecker.stdout.on('data', function(data) { | ||
if (data) { | ||
var pidMatches = data.toString().split('\n').reduce(function(array, line) { | ||
var matches = line.match(/^(\d+)/); | ||
data.toString().split('\n').forEach(function(line) { | ||
var matches = line.match(/^\s*(\d+)/); | ||
if (matches && matches.length > 0) { | ||
array.push(matches[1]); | ||
pidMatches[matches[1]] = true; | ||
} | ||
return array; | ||
}, []); | ||
callback(pidMatches.join(' ')); | ||
}); | ||
} | ||
@@ -44,3 +51,15 @@ }); | ||
function restart(item) { | ||
function readConfig(cb) { | ||
if (fs.statSync(configFilePath).isFile()) { | ||
fs.readFile(configFilePath, function(err, data) { | ||
if (cb) { | ||
cb(JSON.parse(data.toString())); | ||
} | ||
}); | ||
} else { | ||
console.log("config file is not a file!"); | ||
} | ||
} | ||
function stop(item, cb) { | ||
var command = item.command, | ||
@@ -63,22 +82,42 @@ pidFile = item.pid_file, | ||
} | ||
var cmds = command.split(' '); | ||
var child = spawn(cmds.shift(), cmds, { cwd: item.working_directory, env: process.env}); | ||
globalLogger(Date().toLocaleString() + " : spawn pid: "+child.pid+" from command: "+command); | ||
var fd = fs.openSync(pidFile, 'w+'); | ||
var pidString = child.pid.toString(); | ||
item.pid = child.pid; | ||
fs.writeSync(fd, new Buffer(pidString, 'ascii'), 0, pidString.length, 0); | ||
fs.closeSync(fd); | ||
var appender = logAppender(logFile); | ||
child.stdout.on('data', appender); | ||
child.stderr.on('data', appender); | ||
child.on('exit', function() { | ||
globalLogger(Date().toLocaleString() + " : exited: "+child.pid); | ||
}); | ||
child.stdout.end(); | ||
child.stderr.end(); | ||
if (cb) {cb(item)} | ||
}); | ||
} | ||
function main() { | ||
function start(item, cb) { | ||
var command = item.command, | ||
pidFile = item.pid_file, | ||
logFile = item.log_file, | ||
signal = item.signal; | ||
var cmds = command.split(' '); | ||
var child = spawn(cmds.shift(), cmds, { cwd: item.working_directory, env: process.env}); | ||
globalLogger(Date().toLocaleString() + " : spawn pid: "+child.pid+" from command: "+command); | ||
var fd = fs.openSync(pidFile, 'w+'); | ||
var pidString = child.pid.toString(); | ||
item.pid = child.pid; | ||
fs.writeSync(fd, new Buffer(pidString, 'ascii'), 0, pidString.length, 0); | ||
fs.closeSync(fd); | ||
var appender = logAppender(logFile); | ||
child.stdout.on('data', appender); | ||
child.stderr.on('data', appender); | ||
child.on('exit', function() { | ||
globalLogger(Date().toLocaleString() + " : exited: "+child.pid); | ||
}); | ||
child.stdout.end(); | ||
child.stderr.end(); | ||
if (cb) {cb(item)} | ||
} | ||
function restart(item) { | ||
stop(item, start) | ||
} | ||
function startServer(config) { | ||
var watchedFiles = []; | ||
// configure global logger | ||
if (config.global_log_file) { | ||
@@ -91,2 +130,159 @@ var appender = logAppender(config.global_log_file); | ||
function getChildPids() { | ||
return config.restartables.reduce(function(array, item) { | ||
if (item.pid) { | ||
array.push(item.pid); | ||
} | ||
return array | ||
}, []); | ||
} | ||
function checkRunning(cb, lastcb) { | ||
checkProcess(getChildPids(), function(runningPids) { | ||
config.restartables.forEach(function(item) { | ||
try { | ||
var isRunning = !!runningPids[item.pid.toString()]; | ||
cb(item, isRunning); | ||
} catch (e) { | ||
cb(item, false); | ||
} | ||
}); | ||
if (lastcb) { | ||
lastcb(); | ||
} | ||
}); | ||
} | ||
function reloadServer() { | ||
// clear process checker | ||
clearInterval(keepAliveInterval); | ||
// stop monitor server | ||
if (server) { | ||
server.close(); | ||
} | ||
// stop everything | ||
globalLogger(Date().toLocaleString() + " : stopping all processes because of global watch file!"); | ||
config.restartables.forEach(function(item) { | ||
stop(item); | ||
}); | ||
// unwatch all watched files | ||
watchedFiles.forEach(function(file) { | ||
fs.unwatchFile(file); | ||
}); | ||
// reread configuration and start restarter | ||
readConfig(function(configJson) { | ||
startServer(configJson); | ||
}); | ||
} | ||
// start server | ||
var server = net.createServer(function (sock) { | ||
function send(obj) { | ||
sock.write(JSON.stringify({success: obj})); | ||
} | ||
sock.on('data', function(data) { | ||
try { | ||
var incomingPacket = JSON.parse(data.toString()); | ||
// process commands from client | ||
var args = incomingPacket.command; | ||
if (args) { | ||
var command = args.shift(); | ||
switch (command) { | ||
case 'status': | ||
var status = '\nCurrently running:\n\n'; | ||
checkRunning(function(item, isRunning) { | ||
var pid = item.pid; | ||
if (pid) { | ||
if (isRunning) { | ||
status += "\t[ " + item.command + " ] is alive at: "+pid+"\n"; | ||
} else { | ||
status += "\t[ " + item.command + " ] is dead!\n"; | ||
} | ||
} | ||
}, function() { | ||
send(status); | ||
}); | ||
break; | ||
case 'stop': | ||
if (args.length == 0) { | ||
sock.write(JSON.stringify({error: 'specify a matching regex on the command you want to stop'})) | ||
} else { | ||
var regex = new RegExp(args[0]); | ||
var status = '\nStopping the following:\n\n'; | ||
checkRunning(function(item, isRunning) { | ||
var pid = item.pid; | ||
if (pid) { | ||
if (isRunning && item.command.match(regex)) { | ||
status += "\t[ " + item.command + " ] at: "+pid+" will be stopped!\n"; | ||
item.ignore = true; | ||
stop(item); | ||
} | ||
} | ||
}, function() { | ||
send(status); | ||
}); | ||
} | ||
break; | ||
case 'start': | ||
if (args.length == 0) { | ||
sock.write(JSON.stringify({error: 'specify a matching regex on the command you want to start'})) | ||
} else { | ||
var regex = new RegExp(args[0]); | ||
var status = '\nStopping the following:\n\n'; | ||
checkRunning(function(item, isRunning) { | ||
var pid = item.pid; | ||
if (pid) { | ||
if (!isRunning && item.command.match(regex)) { | ||
status += "\t[ " + item.command + " ] will be started.\n"; | ||
item.ignore = false; | ||
start(item); | ||
} | ||
} | ||
}, function() { | ||
send(status); | ||
}); | ||
} | ||
break; | ||
case 'exit': | ||
// stop everything | ||
globalLogger(Date().toLocaleString() + " : stopping all processes because client issued a remote command"); | ||
var status = '\nStopping the following:\n\n'; | ||
checkRunning(function(item, isRunning) { | ||
var pid = item.pid; | ||
if (pid) { | ||
if (isRunning) { | ||
status += "\t[ " + item.command + " ] at: "+pid+" will be stopped!\n"; | ||
item.ignore = true; | ||
stop(item); | ||
} | ||
} | ||
}, function() { | ||
status += "\nrestarter is exiting!!\n\n"; | ||
send(status); | ||
if (server) { | ||
server.close(); | ||
} | ||
setTimeout(function() { | ||
process.exit(0); | ||
}, 1000); | ||
}); | ||
break; | ||
case 'reload': | ||
send('stopping everything, re-reading configuration, and restarting restarter'); | ||
reloadServer(); | ||
break; | ||
default: | ||
sock.write(JSON.stringify({error: 'unknown command'})) | ||
} | ||
} else { | ||
globalLogger('unknown commands sent from remote client: '+data.toString()); | ||
} | ||
} catch (e) { | ||
sock.write(JSON.stringify({error: 'bad command'})) | ||
} | ||
}) | ||
}); | ||
server.listen(config.server_sock || DEFAULT_SERVER_SOCK); | ||
// loop through restarter items and start them | ||
if (config.restartables && config.restartables.constructor === Array) { | ||
@@ -97,18 +293,10 @@ if (config.global_working_directory) { | ||
if (config.keep_alive) { | ||
setInterval(function() { | ||
var pids = config.restartables.reduce(function(array, item) { | ||
if (item.pid) { | ||
array.push(item.pid); | ||
keepAliveInterval = setInterval(function() { | ||
checkRunning(function(item, isRunning) { | ||
var pid = item.pid; | ||
if (pid && !isRunning && !item.ignore) { | ||
globalLogger(Date().toLocaleString() + " : keep alive command died, pid: "+pid+" for command: "+item.command); | ||
globalLogger(Date().toLocaleString() + " : rerunning command: "+item.command); | ||
restart(item); | ||
} | ||
return array | ||
}, []); | ||
checkProcess(pids, function(runningPids) { | ||
config.restartables.forEach(function(item) { | ||
var pid = item.pid; | ||
if (pid && (!runningPids || !runningPids.match(pid.toString()))) { | ||
globalLogger(Date().toLocaleString() + " : keep alive command died, pid: "+pid+" for command: "+item.command); | ||
globalLogger(Date().toLocaleString() + " : rerunning command: "+item.command); | ||
restart(item); | ||
} | ||
}); | ||
}); | ||
@@ -118,7 +306,6 @@ }, 2000); | ||
if (config.global_watch_file) { | ||
// if global_watch_file is touched, we will stop everything, re-read config, and reload server | ||
watchedFiles.push(config.global_watch_file); | ||
fs.watchFile(config.global_watch_file, function () { | ||
globalLogger(Date().toLocaleString() + " : restarting all processes!"); | ||
config.restartables.forEach(function(item) { | ||
restart(item); | ||
}); | ||
reloadServer(); | ||
}); | ||
@@ -139,2 +326,3 @@ } | ||
restart(item); | ||
watchedFiles.push(watchFile); | ||
fs.watchFile(watchFile, function () { | ||
@@ -149,8 +337,62 @@ globalLogger(Date().toLocaleString() + " : watch file updated for command: "+item.command); | ||
exports.setConfig = function(configJson) { | ||
config = configJson; | ||
function startClient(command, config) { | ||
var serverSock = DEFAULT_SERVER_SOCK; | ||
if (config) { | ||
serverSock = config.server_sock || DEFAULT_SERVER_SOCK; | ||
} | ||
if (serverSock) { | ||
fs.stat(serverSock, function(err) { | ||
if (err) { | ||
console.log('\nERROR: restarter is probably not running, you need to start it before you can run client commands!\n'); | ||
} else { | ||
var sock = new net.Socket(); | ||
sock.connect(serverSock, function(c) { | ||
sock.write(JSON.stringify({command: command})); | ||
sock.on('data', function(data) { | ||
try { | ||
var incomingPacket = JSON.parse(data.toString()); | ||
// receive command output from server | ||
if (incomingPacket.success) { | ||
console.log(incomingPacket.success); | ||
} else if (incomingPacket.error) { | ||
console.log(incomingPacket.error); | ||
} else { | ||
console.log('unknown response from server'); | ||
} | ||
} catch (e) { | ||
console.log(e); | ||
} | ||
sock.end() | ||
}); | ||
}); | ||
} | ||
}); | ||
} else { | ||
console.log('no socket file specified!'); | ||
} | ||
} | ||
exports.server = function(filePath) { | ||
configFilePath = filePath; | ||
readConfig(function(configJson) { | ||
daemon.daemonize(configJson.global_log_file, configJson.global_pid_file, function (err, pid) { | ||
if (err) { | ||
console.log('error daemonizing'); | ||
process.exit(0); | ||
} | ||
startServer(configJson); | ||
process.pid = pid; | ||
}); | ||
}); | ||
}; | ||
exports.startWatching = function() { | ||
main(); | ||
exports.client = function(command, filePath) { | ||
if (filePath) { | ||
configFilePath = filePath; | ||
readConfig(function(configJson) { | ||
startClient(command, configJson); | ||
}); | ||
} else { | ||
startClient(command); | ||
} | ||
} |
@@ -5,7 +5,12 @@ { | ||
"description": "Start/stop daemon", | ||
"version": "1.0.9", | ||
"version": "1.1.0", | ||
"author": "Dean Mao <dean@luxdelux.com>", | ||
"directories": { "lib": "./lib" }, | ||
"bin": { "restarter": "./bin/restarter" }, | ||
"engines": { "node": ">= 0.2.5" } | ||
"engines": { "node": ">= 0.4.0" }, | ||
"dependencies": { | ||
"daemon": "= 0.3.0", | ||
"emailjs": "= 0.1.6", | ||
"optimist": "= 0.1.6" | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17308
368
3
4
+ Addeddaemon@= 0.3.0
+ Addedemailjs@= 0.1.6
+ Addedoptimist@= 0.1.6