blob: d00d1e77aba79eecfea46a7f617d88e04b6181db [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var path = require('path');
var spawn = require('child_process').spawn;
var inherits = require('util').inherits;
var EE = require('events').EventEmitter;
var extend = require('xtend');
var which = require('which');
var PassThrough = require('stream').PassThrough;
var fs = require('fs');
var JIRI_ROOT = process.env.JIRI_ROOT;
var VANADIUM_BINS = [
path.join(JIRI_ROOT, 'release/javascript/core/go/bin'),
path.join(JIRI_ROOT, 'release/javascript/core/nacl/scripts')
];
var DEFAULT_FLAGS = {
v: 3,
log_dir: path.resolve('tmp/log') // jshint ignore:line
};
process.env.PATH += ':' + VANADIUM_BINS.join(':');
module.exports = Service;
function Service(name, env) {
if (!(this instanceof Service)) {
return new Service(name, env);
}
var service = this;
EE.call(service);
try {
service.config = require('./config-' + name);
} catch (e) {
service.config = {};
}
service.name = name;
service.args = flags(extend(DEFAULT_FLAGS, service.config.flags));
service.env = env || {};
}
inherits(Service, EE);
Service.prototype.spawn = function(args) {
var service = this;
args = args || service.args;
if (!JIRI_ROOT) {
var err = new Error('Please export $JIRI_ROOT to proceed');
return service.emit('error', err);
}
which(service.name, function(err, bin) {
if (err) {
return service.emit('error', notfound(service.name));
}
var errlog = '';
service.process = spawn(bin, args, {env: service.env});
if (service.name !== 'servicerunner') {
service.emit('ready');
}
if (service.process.stdout) {
service.process.stdout.pipe(fs.createWriteStream(
path.join('tmp', service.name + '.stdout.log')));
if (service.name === 'servicerunner') {
service.process.stdout.on('data', function(data) {
service.emit('vars', JSON.parse(data.toString()));
service.emit('ready');
});
}
}
// Buffer stderr until close so that a meaningful error can be emitted
// when a non-zero exit code is encountered.
//
// NOTE: All vanadium bins log to stderr so this buffer will just grow
// until that is resolved...
if (service.process.stderr) {
var stderr = new PassThrough();
service.process.stderr.pipe(stderr);
stderr.pipe(fs.createWriteStream(
path.join('tmp', service.name + '.stderr.log')));
stderr.on('data', function(data) {
errlog += data;
});
}
service.process.on('close', function(code) {
if (code === 0) {
return;
}
var message = [
'non-zero exit code: ' + code,
'while running: ' + service.name + ' ' + args.join(' '),
'\n',
errlog
].join('\n');
service.emit('error', new Error(message));
});
[ 'exit', 'close' ].forEach(function(name) {
service.process.on(name, service.emit.bind(service, name));
});
service.emit('spawn', service.process.stdout, service.process.stderr);
});
};
// A quick wrapper for short run execs, useful for non-deamons like the
// principal command.
Service.prototype.exec = function(command, cb) {
var service = this;
service
.on('error', cb.bind(service))
.on('spawn', cb.bind(service, null))
.spawn(command.split(' '));
};
Service.prototype.kill = function() {
var service = this;
if (service.process) {
service.process.kill.apply(service.process, arguments);
}
};
function notfound(name) {
var message = 'Vanadium binary not found: ' + name + '\n' +
'Please run "make clean" and try again.' +
'If problem persists, "make go/bin -B" can force building of binaries.';
return new Error(message);
}
function flags(obj) {
var args = Object.keys(obj).map(function(key) {
return ['-', key, '=', obj[key]].join('');
});
return args;
}