the-shepherd
Version:
Control a herd of wild processes.
419 lines (394 loc) • 11.7 kB
JavaScript
// Generated by CoffeeScript 1.8.0
(function() {
var $, Process, Shell, attach_ports, log, lsof_cmd, port, psCache, ps_cmd, ps_parse, signals;
$ = require('bling');
Shell = require('shelljs');
Process = module.exports;
log = $.logger("[Process]");
Process.exec = function(cmd, verbose) {
var append_output, child, err, p, ret, _ref;
try {
return p = $.Promise();
} finally {
try {
if (verbose) {
log("shell >", cmd);
}
ret = {
output: ""
};
child = Shell.exec(cmd, {
silent: true,
async: true
}, function(exitCode) {
var err, _ref;
try {
if (exitCode !== 0) {
return p.reject(ret.output);
} else {
return p.resolve(ret.output);
}
} catch (_error) {
err = _error;
return log("exec: error handling process exit:", (_ref = err.stack) != null ? _ref : err);
}
});
child.stdout.on("data", append_output = function(data) {
return ret.output += String(data);
});
child.stderr.on("data", append_output);
} catch (_error) {
err = _error;
log("exec: error in running process:", (_ref = err.stack) != null ? _ref : err);
}
}
};
psCache = new $.Cache(2, 100);
ps_cmd = "ps -eo uid,pid,ppid,pcpu,rss,command";
ps_parse = function(output) {
var err, keys, _ref;
try {
output = output.split('\n').map(function(line) {
return line.split(/[ ]+/).slice(1);
}).slice(0, -1);
keys = output[0].map($.slugize);
return output.slice(1).map(function(row) {
var e, i, key, ret, val, _i, _len;
try {
return ret = Object.create(null);
} finally {
for (i = _i = 0, _len = keys.length; _i < _len; i = ++_i) {
key = keys[i];
if (i === keys.length - 1) {
ret[key] = row.slice(i).join(' ');
} else {
val = row[i];
try {
val = parseInt(val, 10);
if (!isFinite(val)) {
val = row[i];
}
} catch (_error) {
e = _error;
val = row[i];
} finally {
ret[key] = val;
}
}
if (ret[key] == null) {
log("ps_parse failed to parse line:", key, i, row[i], ret[key]);
}
}
}
});
} catch (_error) {
err = _error;
return log("ps_parse error:", (_ref = err.stack) != null ? _ref : err);
}
};
lsof_cmd = "lsof -Pni | grep LISTEN";
attach_ports = function(procs) {
var attached, err, index, proc, _i, _len, _ref;
try {
return attached = $.Promise();
} finally {
try {
index = Object.create(null);
for (_i = 0, _len = procs.length; _i < _len; _i++) {
proc = procs[_i];
index[proc.pid] = proc;
proc.ports = [];
}
Process.exec(lsof_cmd).then(function(output) {
var err, line, pid, port, _j, _len1, _ref, _ref1;
try {
_ref = output.split(/\n/g);
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
line = _ref[_j];
line = line.split(/\s+/g);
if (line.length < 8) {
continue;
}
pid = parseInt(line[1], 10);
port = parseInt(line[8].split(/:/)[1], 10);
if (!(pid in index)) {
index[pid] = {
pid: pid,
ports: []
};
}
try {
index[pid].ports.push(port);
} catch (_error) {
err = _error;
log(err, pid, index[pid]);
}
}
return attached.resolve(procs);
} catch (_error) {
err = _error;
return log("attach_ports error while parsing output:", (_ref1 = err.stack) != null ? _ref1 : err);
}
});
} catch (_error) {
err = _error;
log("attach_ports error:", (_ref = err.stack) != null ? _ref : err);
}
}
};
Process.clearCache = function() {
psCache.del(ps_cmd);
return Process;
};
Process.find = function(query) {
var err, p, _ref;
try {
return p = $.Promise();
} finally {
try {
query = (function() {
switch ($.type(query)) {
case "string":
return {
cmd: new RegExp(query)
};
case "number":
return {
pid: query
};
default:
return query;
}
})();
if (psCache.has(ps_cmd)) {
p.resolve(psCache.get(ps_cmd).filter(function(item) {
return $.matches(query, item);
}));
} else {
Process.exec(ps_cmd).then((function(output) {
return attach_ports(ps_parse(output)).then((function(procs) {
var err, _ref;
try {
return p.resolve(psCache.set(ps_cmd, procs).filter(function(item) {
return $.matches(query, item);
}));
} catch (_error) {
err = _error;
return log("find error in results:", (_ref = err.stack) != null ? _ref : err);
}
}), p.reject);
}), p.reject);
}
} catch (_error) {
err = _error;
log("find error:", (_ref = err.stack) != null ? _ref : err);
}
}
};
Process.findOne = function(query) {
var p;
try {
return p = $.Promise();
} finally {
Process.find(query).then((function(out) {
var err, _ref;
try {
return p.resolve(out[0]);
} catch (_error) {
err = _error;
return log("findOne error:", (_ref = err.stack) != null ? _ref : err);
}
}), p.reject);
}
};
Process.findTree = function(query) {
var p;
try {
return p = $.Promise();
} finally {
Process.findOne(query).then(function(proc) {
return Process.tree(proc).then(p.resolve, p.reject);
});
}
};
Process.signals = signals = {
SIGHUP: 1,
SIGINT: 2,
SIGKILL: 9,
SIGTERM: 15,
HUP: 1,
INT: 2,
KILL: 9,
TERM: 15
};
Process.getSignalNumber = function(signal) {
var _ref;
return (_ref = signals[signal]) != null ? _ref : ($.is('number', signal) ? signal : 15);
};
Process.kill = function(pid, signal) {
var err, _ref;
try {
return Process.exec("kill -" + (Process.getSignalNumber(signal)) + " " + pid);
} catch (_error) {
err = _error;
return log("kill error:", (_ref = err.stack) != null ? _ref : err);
}
};
Process.tree = function(proc) {
var p, q;
try {
return q = $.Promise();
} finally {
p = $.Progress(1);
if (proc) {
Process.find({
ppid: proc.pid
}).then((function(children) {
var child, err, _i, _len, _ref;
try {
proc.children = children;
for (_i = 0, _len = children.length; _i < _len; _i++) {
child = children[_i];
p.include(Process.tree(child));
}
return p.resolve(1, proc);
} catch (_error) {
err = _error;
return log("tree error:", (_ref = err.stack) != null ? _ref : err);
}
}), q.reject);
}
p.then((function() {
return q.resolve(proc);
}), q.reject);
}
};
Process.walk = function(node, visit, depth) {
var child, err, p, _i, _len, _ref, _ref1;
if (depth == null) {
depth = 0;
}
try {
return p = $.Progress(1);
} finally {
try {
p.include(visit(node, depth));
} catch (_error) {
err = _error;
log("walk error (in visit):", (_ref = err.stack) != null ? _ref : err);
p.reject(err);
}
_ref1 = node.children;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
child = _ref1[_i];
p.include(Process.walk(child, visit, depth + 1));
}
p.finish(1);
}
};
Process.killTree = function(proc, signal) {
var err, fail, p, tokill;
try {
return p = $.Promise();
} finally {
try {
signal = Process.getSignalNumber(signal);
proc = (function() {
switch ($.type(proc)) {
case 'string':
case 'number':
return {
pid: proc
};
default:
return proc;
}
})();
tokill = [];
fail = function(msg, err) {
var _ref;
log(msg, (_ref = err != null ? err.stack : void 0) != null ? _ref : err);
return p.reject(err);
};
Process.tree(proc).then((function(tree) {
var err;
try {
Process.walk(tree, function(node) {
if (node.pid) {
return tokill.push(node.pid);
} else {
return fail("killTree invalid node (no pid):", node);
}
});
if (tokill.length) {
return Process.exec("kill -" + signal + " " + (tokill.join(' ')) + " &> /dev/null").then(p.resolve, function(err) {
return fail("killTree error while killing", err);
});
}
} catch (_error) {
err = _error;
return fail("killTree error while walking:", err);
}
}), p.reject);
} catch (_error) {
err = _error;
fail("killTree error:", err);
}
}
};
Process.summarize = function(proc) {
var p;
proc.rss = proc.cpu = 0;
try {
return p = $.Promise();
} finally {
Process.tree(proc).then(function(tree) {
Process.walk(tree, function(node, depth) {
proc.rss += node.rss;
return proc.cpu += node.cpu;
});
return p.resolve(tree);
});
}
};
Process.printTree = function(proc, indent, spacer) {
var child, err, ret, _i, _len, _ref, _ref1, _ref2;
try {
spacer || (spacer = " \\_");
indent || (indent = "* ");
ret = indent + proc.pid + " " + proc.command;
if ((_ref = proc.ports) != null ? _ref.length : void 0) {
ret += " [:" + proc.ports.join(", :") + "]";
}
ret += " {mem: " + ($.commaize(proc.rss)) + "kb cpu: " + proc.cpu + "%}\n";
indent = spacer + indent;
_ref1 = proc.children;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
child = _ref1[_i];
ret += Process.printTree(child, indent, " ");
}
indent.replace(/^ /, '');
return ret;
} catch (_error) {
err = _error;
return log("printTree error:", (_ref2 = err.stack) != null ? _ref2 : err);
}
};
if (require.main === module) {
port = parseInt(process.argv[2], 10) || 8000;
log("Tree for owner of:", port);
Process.find({
ports: port
}).then(function(procs) {
var proc, _i, _len, _results;
_results = [];
for (_i = 0, _len = procs.length; _i < _len; _i++) {
proc = procs[_i];
_results.push(Process.tree(proc).then(function(tree) {
return console.log(Process.printTree(tree));
}));
}
return _results;
});
}
}).call(this);