let exec node take msg.kill SIG... param and pid param

and redo test
This commit is contained in:
Dave Conway-Jones 2017-03-06 15:27:29 +00:00
parent 4a8a5ed8d4
commit c6436f47eb
3 changed files with 70 additions and 20 deletions

View File

@ -46,22 +46,23 @@
<script type="text/x-red" data-help-name="exec">
<p>Calls out to a system command.<br/></p>
<p>Provides 3 outputs: stdout, stderr, and return code.</p>
<p>By default uses the <code>exec</code> system call which calls the command, then gets a callback
on completion, returning stdout as the payload to the first, the error code as the third
and, if available, stderr to the second output. If no error occurred, a zero is returned on the third output.</p>
<p>By default uses the <code>exec</code> system call which calls the command, waits for it to complete, and
returns the normal output to the first port, any error text to the second port, and a return code object to the
third port. For example a succesful command should return <code>{ code: 0 }</code>.</p>
<p>Optionally can use <code>spawn</code> instead, which returns the output from stdout and stderr
as the command runs (usually one line at a time). On completion it then returns a return code
(on the 3rd output).</p>
as the command runs, usually one line at a time. On completion it then returns a numeric return code
on the 3rd port. For example a successful command should return <code>0</code>.</p>
<p>The <code>exec</code> method spawns a subshell and therefore can be used for more complicated
commands involving pipes. However, it waits for completion of the whole command before returing anything.</p>
<p>The optional append gets added to the command after <code>msg.payload</code> - so you can do
commands involving pipes. However, it waits for completion of the whole command before returing anything.</p>
<p>The optional <b>append</b> gets added to the command after <code>msg.payload</code> - so you can do
things like pipe the result to another command.</p>
<p>Commands or parameters with spaces should be enclosed in quotes - <i>"This is a single parameter"</i></p>
<p>If stdout is binary a <i>buffer</i> is returned - otherwise returns a <i>string</i>.</p>
<p>The blue status icon and PID will be visible while the node is active.</p>
<p>Sending <code>msg.kill</code> will kill a single active process. If there is more than one process running then
<code>msg.kill</code> must be set with the value of the PID to be killed.</p>
<p>Tip: If running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p>
<p>The <code>payload</code> is usually a <i>string</i>, unless binary is detected, in which case it contains a <i>buffer</i>.</p>
<p>The blue status icon and PID will be visible while the node is active. This can be read by a <code>status</code> node.</p>
<p>Sending <code>msg.kill</code> will kill a single active process. <code>msg.kill</code> should be a string containing
the type of signal to be sent, e.g. "SIGINT", "SIGQUIT", "SIGHUP", etc. Defaults to "SIGTERM" if blank ("").
If there is more than one process running then <code>msg.pid</code> must also be set with the value of the PID to be killed.</p>
<p><b>Tip: </b>If running a Python app you may need to use the <code>-u</code> parameter to stop the output being buffered.</p>
</script>
<script type="text/javascript">
@ -78,7 +79,7 @@
},
inputs:1,
outputs:3,
outputLabels: ["stdout","stderr","rc"],
outputLabels: ["stdout","stderr","return code"],
icon: "arrow-in.png",
align: "right",
label: function() {

View File

@ -39,13 +39,16 @@ module.exports = function(RED) {
this.on("input", function(msg) {
if (msg.hasOwnProperty("kill")) {
if (node.activeProcesses.hasOwnProperty(msg.kill) ) {
node.activeProcesses[msg.kill].kill();
node.status({fill:"red",shape:"dot",text:"killed"});
if (typeof msg.kill !== "string" || msg.kill.length === 0 || !msg.kill.toUpperCase().startsWith("SIG") ) { msg.kill = ""; }
if (msg.hasOwnProperty("pid")) {
if (node.activeProcesses.hasOwnProperty(msg.pid) ) {
node.activeProcesses[msg.pid].kill(msg.kill.toUpperCase());
node.status({fill:"red",shape:"dot",text:"killed"});
}
}
else {
if (Object.keys(node.activeProcesses).length === 1) {
node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill();
node.activeProcesses[Object.keys(node.activeProcesses)[0]].kill(msg.kill.toUpperCase());
node.status({fill:"red",shape:"dot",text:"killed"});
}
}
@ -149,7 +152,7 @@ module.exports = function(RED) {
/* istanbul ignore else */
if (node.activeProcesses.hasOwnProperty(pid)) {
if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); }
// console.log("KILLLING",pid);
// console.log("KILLING",pid);
var process = node.activeProcesses[pid];
node.activeProcesses[pid] = null;
process.kill();

View File

@ -239,7 +239,32 @@ describe('exec node', function() {
}
});
setTimeout(function() {
n1.receive({kill:true});
n1.receive({kill:""});
},150);
n1.receive({});
});
});
it('should be able to kill a long running command - SIGINT', function(done) {
var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
try {
msg.should.have.property("payload");
msg.payload.should.have.property("killed",true);
msg.payload.should.have.property("signal","SIGINT");
done();
} catch(err) {
done(err);
}
});
setTimeout(function() {
n1.receive({kill:"sigint"});
},150);
n1.receive({});
});
@ -509,7 +534,28 @@ describe('exec node', function() {
done();
});
setTimeout(function() {
n1.receive({kill:true});
n1.receive({kill:""});
},150);
n1.receive({});
});
});
it('should be able to kill a long running command - SIGQUIT', function(done) {
var flow = [{id:"n1",type:"exec",wires:[["n2"],["n3"],["n4"]],command:"sleep", addpay:false, append:"1", timer:"2"},
{id:"n2", type:"helper"},{id:"n3", type:"helper"},{id:"n4", type:"helper"}];
helper.load(execNode, flow, function() {
var n1 = helper.getNode("n1");
var n2 = helper.getNode("n2");
var n3 = helper.getNode("n3");
var n4 = helper.getNode("n4");
n4.on("input", function(msg) {
msg.should.have.property("payload");
msg.payload.should.have.property("killed",true);
msg.payload.should.have.property("signal","SIGQUIT");
done();
});
setTimeout(function() {
n1.receive({kill:"sigquit"});
},150);
n1.receive({});
});