Comparing version
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
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
17308
83.74%368
164.75%3
Infinity%4
100%+ Added
+ Added
+ Added