Files
meteor/tools/mongo_runner.js

197 lines
5.5 KiB
JavaScript

var fs = require("fs");
var path = require("path");
var files = require('./files.js');
var _ = require('underscore');
/** Internal.
*
* If passed, app_dir and port act as filters on the list of running mongos.
*
* callback is called with (err, [{pid, port, app_dir}])
*/
var find_mongo_pids = function (app_dir, port, callback) {
// 'ps ax' should be standard across all MacOS and Linux.
var child_process = require('child_process');
child_process.exec('ps ax',
function (error, stdout, stderr) {
if (error) {
callback({reason: error});
} else {
var pids = [];
_.each(stdout.split('\n'), function (ps_line) {
// matches mongos we start.
var m = ps_line.match(/^\s*(\d+).+mongod .+--port (\d+) --dbpath (.+)(?:\/|\\)\.meteor(?:\/|\\)local(?:\/|\\)db\s*$/);
if (m && m.length === 4) {
var found_pid = parseInt(m[1]);
var found_port = parseInt(m[2]);
var found_path = m[3];
if ( (!port || port === found_port) &&
(!app_dir || app_dir === found_path)) {
pids.push({
pid: found_pid, port: found_port, app_dir: found_path});
}
}
});
callback(null, pids);
}
});
};
// See if mongo is running already. Callback takes a single argument,
// 'port', which is the port mongo is running on or null if mongo is not
// running.
exports.find_mongo_port = function (app_dir, callback) {
find_mongo_pids(app_dir, null, function (err, pids) {
if (err || pids.length !== 1) {
callback(null);
return;
}
var pid = pids[0].pid;
try {
process.kill(pid, 0); // make sure it is still alive
} catch (e) {
callback(null);
return;
}
callback(pids[0].port);
});
}
// Try to kill any other mongos running on our port. Calls callback
// once they are all gone. Callback takes one arg: err (falsy means all
// good).
//
// This is a big hammer for dealing with still running mongos, but
// smaller hammers have failed before and it is getting tiresome.
var find_mongo_and_kill_it_dead = function (port, callback) {
find_mongo_pids(null, port, function (err, pids) {
if (err) {
callback(err);
return;
}
if (pids.length) {
// Send kill attempts and wait. First a SIGINT, then if it isn't
// dead within 2 sec, SIGKILL. This goes through the list
// serially, but thats OK because there really should only ever be
// one.
var attempts = 0;
var dead_yet = function () {
attempts = attempts + 1;
var pid = pids[0].pid;
var signal = 0;
if (attempts === 1)
signal = 'SIGINT';
else if (attempts === 20 || attempts === 30)
signal = 'SIGKILL';
try {
process.kill(pid, signal);
} catch (e) {
// it's dead. remove this pid from the list.
pids.shift();
// if no more in the list, we're done!
if (!pids.length) {
callback();
return;
}
}
if (attempts === 40) {
// give up after 4 seconds.
callback({
reason: "Can't kill running mongo (pid " + pid + ")."});
return;
}
// recurse
setTimeout(dead_yet, 100);
};
dead_yet();
} else {
// nothing to kill, fire OK callback
callback();
}
});
};
exports.launch_mongo = function (app_dir, port, launch_callback, on_exit_callback) {
var handle = {stop: function (callback) { callback(); } };
launch_callback = launch_callback || function () {};
on_exit_callback = on_exit_callback || function () {};
// If we are passed an external mongo, assume it is launched and never
// exits. Matches code in run.js:exports.run.
// Since it is externally managed, asking it to actually stop would be
// impolite, so our stoppable handle is a noop
if (process.env.MONGO_URL) {
launch_callback();
return handle;
}
var mongod_path = path.join(files.get_dev_bundle(),
'mongodb',
'bin',
'mongod');
// store data in app_dir
var data_path = path.join(app_dir, '.meteor', 'local', 'db');
files.mkdir_p(data_path, 0755);
// add .gitignore if needed.
files.add_to_gitignore(path.join(app_dir, '.meteor'), 'local');
find_mongo_and_kill_it_dead(port, function (err) {
if (err) {
launch_callback({reason: "Can't kill running mongo: " + err.reason});
return;
}
var child_process = require('child_process');
var proc = child_process.spawn(mongod_path, [
'--bind_ip', '127.0.0.1',
'--smallfiles',
'--nohttpinterface',
'--port', port,
'--dbpath', data_path
]);
var callOnExit = function (code, signal) {
on_exit_callback(code, signal, stderrOutput);
};
handle.stop = function (callback) {
var tries = 0;
var exited = false;
proc.removeListener('exit', callOnExit);
proc.kill('SIGINT');
callback && callback(err);
};
var stderrOutput = '';
proc.stderr.setEncoding('utf8');
proc.stderr.on('data', function (data) {
stderrOutput += data;
});
proc.on('exit', callOnExit);
proc.stdout.setEncoding('utf8');
proc.stdout.on('data', function (data) {
// process.stdout.write(data);
if (/ \[initandlisten\] waiting for connections on port/.test(data))
launch_callback();
});
});
return handle;
};