Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
0.1.0 master process and ipc
- Loading branch information
1 parent
6b0ed5e
commit be9ec49
Showing
11 changed files
with
449 additions
and
81 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,33 @@ | ||
// Node God | ||
// keeps node apps running | ||
// master.js | ||
// master process for nodegod | ||
// © Harald Rudell 2012 | ||
|
||
var defaults = require('haraldops').init({appName: 'Node God', | ||
appFolder: __dirname, | ||
sessionSecret: 'veryGreat', | ||
PORT: 1111 }) | ||
var establish = require('./lib/master/establish') | ||
var uimanager = require('./lib/master/uimanager') | ||
// http://nodejs.org/api/path.html | ||
var path = require('path') | ||
|
||
// https://github.com/visionmedia/express | ||
var express = require('express') | ||
var apprunner = require('apprunner') | ||
//apprunner.enableAnomalyMail(false) | ||
var cbc = apprunner.getCbCounter(/*{callback: initAppResult}*/) | ||
var theSignal = 'SIGUSR2' | ||
var marker = path.basename(__filename, path.extname(__filename)) | ||
var fileId = marker + ':' + process.pid | ||
|
||
// get app and start error listener | ||
var app = module.exports = express.createServer() | ||
apprunner.initApp(defaults, app, cbc.add(initAppResult)) | ||
var godcontrol = require('./lib/godcontrol') | ||
var godview = require('./routes/godview') | ||
// determine if this process should launch ui | ||
console.log(fileId, 'starting') | ||
process.on(theSignal, uimanager.signalHandler) | ||
process.on('uncaughtException', processException) | ||
establish.establish(marker, theSignal, masterResult) | ||
|
||
// Configuration | ||
godview.setTitle(defaults.init.appName) | ||
godcontrol.init(app, defaults) | ||
app.configure(function(){ | ||
app.set('views', __dirname + '/views') | ||
app.set('view engine', 'ejs') | ||
//3 app.use(express.favicon()) | ||
app.use(express.bodyParser()) | ||
//3 app.use(express.methodOverride()) | ||
//3 app.use(express.cookieParser(defaults.sessionSecret)) | ||
app.use(express.cookieParser()) | ||
//3 app.use(express.session()) | ||
app.use(express.session({ secret: defaults.sessionSecret })) | ||
app.use(express.methodOverride()) | ||
app.use(app.router) | ||
app.use(express.static(__dirname + '/public')) | ||
}) | ||
/* | ||
require('ejsinbrowser').writeScript({ | ||
folder: app.settings.views, | ||
ext: app.settings['view engine'], | ||
jsGlobalVariable: 'NODEGOD', | ||
templates: 'partials', | ||
filename: __dirname + '/public/javascripts/templates.js'}) | ||
*/ | ||
app.configure('development', function(){ | ||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true })) | ||
}) | ||
app.configure('production', function(){ | ||
app.use(express.errorHandler()) | ||
}) | ||
|
||
// Routes | ||
app.get('/', godview.index) | ||
|
||
/*3 | ||
app.listen(defaults.PORT, function(){ | ||
console.log("Express server listening on port %d in %s mode", | ||
defaults.PORT, | ||
app.settings.env) | ||
}) | ||
*/ | ||
|
||
function initAppResult(err) { | ||
if (err) throw err | ||
function masterResult(isMaster) { | ||
if (isMaster) { | ||
console.log(fileId, 'launching ui process') | ||
uimanager.launchUi(fileId, __dirname) | ||
} else console.log(fileId, 'notified the master process') | ||
} | ||
app.listen(defaults.PORT, defaults.appInterface) | ||
//app.listen(defaults.worldPort) | ||
console.log('Application %s on node %s available on port %s in %s mode', | ||
defaults.init.appName, | ||
process.version, | ||
defaults.PORT, | ||
app.settings.env) | ||
|
||
function processException() { | ||
console.log(marker, 'uncaughtException') | ||
Array.prototype.slice.call(arguments).forEach(function (value, index) { | ||
console.log(index + ': ', value) | ||
if (value instanceof Error && value.stack) console.log(value.stack) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// establish.js | ||
// establish a nodegod parent process without any dependencies | ||
// © Harald Rudell 2012 | ||
|
||
exports.establish = establish | ||
|
||
// http://nodejs.org/api/path.html | ||
var path = require('path') | ||
// http://nodejs.org/api/fs.html | ||
var fs = require('fs') | ||
|
||
var pidFile | ||
/* | ||
Establish if this is the master process | ||
marker: string: the id for pid file | ||
cb(boolean): function: | ||
- true: this is the master process | ||
- false: the master process has been notified to restart the user interface | ||
*/ | ||
function establish(marker, theSignal, cb) { | ||
if (!pidFile) pidFile = getFileName(marker) | ||
fs.readFile(pidFile, 'utf-8', pidFromFile) | ||
|
||
function pidFromFile(err, data) { | ||
var pid | ||
if (!err && data) { | ||
var num = parseInt(data) | ||
if (num > 0) { // there was a pid | ||
try { // signal to see if that process still exists | ||
process.kill(num, theSignal) | ||
pid = num | ||
} catch (e) { | ||
// if process is missing, it's ok | ||
if (e.errno != 'ESRCH') throw e | ||
} | ||
} | ||
} | ||
|
||
if (!pid) { // we are taking over | ||
fs.writeFile(pidFile, String(process.pid), writeResult) | ||
} else cb(false) // we're not master exit | ||
} | ||
|
||
function writeResult(err) { | ||
if (err) console.log('writing pid:', err.toString) | ||
cb(true) // we're master exit | ||
} | ||
} | ||
|
||
// helper functions | ||
|
||
function getFileName(marker) { | ||
var f = path.basename(__filename, path.extname(__filename)) | ||
return path.join(getTmpFolder(), f + '.pid') | ||
} | ||
|
||
function getTmpFolder() { | ||
folder = path.join(getHomeFolder(), 'tmp') | ||
if (getType(folder) !== 1) { | ||
folder = process.env.TEMP | ||
if (!folder || getType(folder) !== 1) { | ||
folder = '/tmp' | ||
if (getType(folder) !== 1) throw Error('no tmp folder found') | ||
} | ||
} | ||
|
||
return folder | ||
} | ||
|
||
function getHomeFolder() { | ||
return process.env[ | ||
process.platform == 'win32' ? | ||
'USERPROFILE' : | ||
'HOME'] | ||
} | ||
|
||
function getType(path1) { | ||
var result | ||
var stats | ||
try { | ||
stats = fs.statSync(path1) | ||
} catch (e) { | ||
var bad = true | ||
if (e instanceof Error && e.code == 'ENOENT') bad = false | ||
if (bad) { | ||
console.error('Exception for:', typeof path1, path1, path1 != null && path1.length) | ||
throw e | ||
} | ||
} | ||
if (stats) { | ||
if (stats.isFile()) result = true | ||
if (stats.isDirectory()) result = 1 | ||
} | ||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
// manager.js | ||
// manage a child process for the ui | ||
// © Harald Rudell 2012 | ||
|
||
exports.receiver = receiver | ||
exports.setSend = setSend | ||
|
||
var send | ||
var marker = 'master' | ||
setSend() | ||
|
||
var childMap = {} | ||
|
||
// http://nodejs.org/api/child_process.html | ||
var child_process = require('child_process') | ||
|
||
function launch(proc, start, debug) { | ||
var args = Array.isArray(start) ? start : start.split(' ') | ||
if (debug) args.unshift('--debug-brk') | ||
proc.didKill = false | ||
proc.child = child_process.spawn('node', args) | ||
var thisPid = proc.child.pid | ||
send({app: proc.app, pid: proc.child.pid, debug: debug}) | ||
proc.child.stdout.on('data', log) | ||
proc.child.stderr.on('data', log) | ||
proc.child.on('exit', function (code) { | ||
var isCurrent = !proc.child || proc.child.pid == thisPid | ||
if (isCurrent) { | ||
proc.child = null | ||
proc.pendingKill = false | ||
// code is number | ||
send({app: proc.app, exit: code, state: (proc.didKill ? 'stop' : 'crash')}) | ||
} | ||
}) | ||
proc.child.stderr.setEncoding('utf-8') | ||
proc.child.stdout.setEncoding('utf-8') | ||
send({app: proc.app, state: (debug ? 'debug' : 'run')}) | ||
|
||
function log(string) { | ||
console.log(proc.app, string.slice(0, -1)) | ||
} | ||
} | ||
|
||
function kill(proc) { | ||
var t | ||
if (proc.child && proc.child.pid && !proc.didKill) { | ||
proc.didKill = true | ||
proc.pendingKill = true | ||
send({app: proc.app, state: 'stopping'}) | ||
t = Date.now() | ||
setTimeout(sigResult, 3000) | ||
// try the nice sigint first (ctrl-break) | ||
process.kill(proc.child.pid, 'SIGINT') | ||
} | ||
|
||
function sigResult() { | ||
proc.killTimer = null | ||
if (proc.pendingKill) { | ||
console.log(marker, proc.app, 'Force kill at: ' + (Date.now() - t) / 1e3) | ||
// now the evil sigterm | ||
process.kill(proc.child.pid) | ||
} | ||
} | ||
} | ||
|
||
function sendMap() { | ||
var data = {} | ||
for (var i in childMap) { | ||
var pid | ||
var child = childMap[i].child | ||
if (child && child.pid) pid = child.pid | ||
data[i] = pid | ||
} | ||
send({map: data}) | ||
} | ||
|
||
function receiver(message) { | ||
var ok | ||
if (message.app) { | ||
var proc = childMap[message.app] | ||
if (!proc) { | ||
childMap[message.app] = proc = {app: message.app} | ||
} | ||
if (message.launch) { | ||
launch(proc, message.launch, message.debug) | ||
ok = true | ||
} else if (message.kill) { | ||
kill(proc) | ||
ok = true | ||
} else if (message.getMap) { | ||
sendMap() | ||
ok = true | ||
} else if (message.del) { | ||
delete childMap[message.app] | ||
} | ||
} | ||
if (!ok) console.log(marker, 'Bad message', message) | ||
} | ||
|
||
function setSend(f, marker0) { | ||
send = typeof f == 'function' ? f : badSend | ||
if (marker0) marker = marker0 | ||
|
||
function badSend() { | ||
console.log(marker, 'No ipc available:', Array.prototype.slice.call(arguments)) | ||
} | ||
} |
Oops, something went wrong.