mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Add optional timeout to exec node
(both exec and spawn modes) and add test for it (both exec and spawn) also extra test for trigger node.
This commit is contained in:
@@ -31,7 +31,11 @@
|
||||
<div class="form-row">
|
||||
<label> </label>
|
||||
<input type="checkbox" id="node-input-useSpawn" placeholder="spawn" style="display: inline-block; width: auto; vertical-align: top;">
|
||||
<label for="node-input-useSpawn" style="width: 70%;"><span data-i18n="exec.spawn"></span></label>
|
||||
<label for="node-input-useSpawn" style="width:70%;"><span data-i18n="exec.spawn"></span></label>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-timer"><i class="fa fa-clock-o"></i> <span data-i18n="exec.label.timeout"></span></label>
|
||||
<input type="text" id="node-input-timer" style="width:50px; text-align:end;" data-i18n="[placeholder]exec.label.timeoutplace"> seconds
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||
@@ -59,6 +63,7 @@
|
||||
addpay: {value:true},
|
||||
append: {value:""},
|
||||
useSpawn: {value:""},
|
||||
timer: {value:""},
|
||||
name: {value:""}
|
||||
},
|
||||
inputs:1,
|
||||
|
@@ -27,55 +27,64 @@ module.exports = function(RED) {
|
||||
this.addpay = n.addpay;
|
||||
this.append = (n.append || "").trim();
|
||||
this.useSpawn = n.useSpawn;
|
||||
this.timer = Number(n.timer || 0)*1000;
|
||||
this.activeProcesses = {};
|
||||
|
||||
var cleanup = function(p) {
|
||||
//console.log("CLEANUP!!!",p);
|
||||
node.activeProcesses[p].kill();
|
||||
node.status({fill:"red",shape:"dot",text:"timeout"});
|
||||
node.error("Exec node timeout");
|
||||
}
|
||||
|
||||
var node = this;
|
||||
this.on("input", function(msg) {
|
||||
var child;
|
||||
node.status({fill:"blue",shape:"dot",text:" "});
|
||||
if (this.useSpawn === true) {
|
||||
// make the extra args into an array
|
||||
// then prepend with the msg.payload
|
||||
|
||||
var arg = node.cmd;
|
||||
if (node.addpay) {
|
||||
arg += " "+msg.payload;
|
||||
}
|
||||
if (node.addpay) { arg += " "+msg.payload; }
|
||||
arg += " "+node.append;
|
||||
// slice whole line by spaces (trying to honour quotes);
|
||||
arg = arg.match(/(?:[^\s"]+|"[^"]*")+/g);
|
||||
var cmd = arg.shift();
|
||||
/* istanbul ignore else */
|
||||
if (RED.settings.verbose) { node.log(cmd+" ["+arg+"]"); }
|
||||
if (cmd.indexOf(" ") == -1) {
|
||||
var ex = spawn(cmd,arg);
|
||||
node.activeProcesses[ex.pid] = ex;
|
||||
ex.stdout.on('data', function (data) {
|
||||
//console.log('[exec] stdout: ' + data);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = data; }
|
||||
node.send([msg,null,null]);
|
||||
});
|
||||
ex.stderr.on('data', function (data) {
|
||||
//console.log('[exec] stderr: ' + data);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = new Buffer(data); }
|
||||
node.send([null,msg,null]);
|
||||
});
|
||||
ex.on('close', function (code) {
|
||||
//console.log('[exec] result: ' + code);
|
||||
delete node.activeProcesses[ex.pid];
|
||||
msg.payload = code;
|
||||
if (code === 0) { node.status({}); }
|
||||
else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc: "+code}); }
|
||||
else { node.status({fill:"yellow",shape:"dot",text:"rc: "+code}); }
|
||||
node.send([null,null,msg]);
|
||||
});
|
||||
ex.on('error', function (code) {
|
||||
delete node.activeProcesses[ex.pid];
|
||||
node.error(code,msg);
|
||||
});
|
||||
child = spawn(cmd,arg);
|
||||
if (node.timer !== 0) {
|
||||
child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
|
||||
}
|
||||
else { node.error(RED._("exec.spawnerr")); }
|
||||
node.activeProcesses[child.pid] = child;
|
||||
child.stdout.on('data', function (data) {
|
||||
//console.log('[exec] stdout: ' + data);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = data; }
|
||||
node.send([msg,null,null]);
|
||||
});
|
||||
child.stderr.on('data', function (data) {
|
||||
//console.log('[exec] stderr: ' + data);
|
||||
if (isUtf8(data)) { msg.payload = data.toString(); }
|
||||
else { msg.payload = new Buffer(data); }
|
||||
node.send([null,msg,null]);
|
||||
});
|
||||
child.on('close', function (code) {
|
||||
//console.log('[exec] result: ' + code);
|
||||
delete node.activeProcesses[child.pid];
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
msg.payload = code;
|
||||
if (code === 0) { node.status({}); }
|
||||
if (code === null) { node.status({fill:"red",shape:"dot",text:"timeout"}); }
|
||||
else if (code < 0) { node.status({fill:"red",shape:"dot",text:"rc: "+code}); }
|
||||
else { node.status({fill:"yellow",shape:"dot",text:"rc: "+code}); }
|
||||
node.send([null,null,msg]);
|
||||
});
|
||||
child.on('error', function (code) {
|
||||
delete node.activeProcesses[child.pid];
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
node.error(code,msg);
|
||||
});
|
||||
}
|
||||
else {
|
||||
var cl = node.cmd;
|
||||
@@ -83,13 +92,9 @@ module.exports = function(RED) {
|
||||
if (node.append.trim() !== "") { cl += " "+node.append; }
|
||||
/* istanbul ignore else */
|
||||
if (RED.settings.verbose) { node.log(cl); }
|
||||
var child = exec(cl, {encoding: 'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
|
||||
child = exec(cl, {encoding: 'binary', maxBuffer:10000000}, function (error, stdout, stderr) {
|
||||
msg.payload = new Buffer(stdout,"binary");
|
||||
try {
|
||||
if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
|
||||
} catch(e) {
|
||||
node.log(RED._("exec.badstdout"));
|
||||
}
|
||||
if (isUtf8(msg.payload)) { msg.payload = msg.payload.toString(); }
|
||||
var msg2 = {payload:stderr};
|
||||
var msg3 = null;
|
||||
//console.log('[exec] stdout: ' + stdout);
|
||||
@@ -100,9 +105,13 @@ module.exports = function(RED) {
|
||||
}
|
||||
node.status({});
|
||||
node.send([msg,msg2,msg3]);
|
||||
if (child.tout) { clearTimeout(child.tout); }
|
||||
delete node.activeProcesses[child.pid];
|
||||
});
|
||||
child.on('error',function() {});
|
||||
if (node.timer !== 0) {
|
||||
child.tout = setTimeout(function() { cleanup(child.pid); }, node.timer);
|
||||
}
|
||||
node.activeProcesses[child.pid] = child;
|
||||
}
|
||||
});
|
||||
@@ -110,10 +119,12 @@ module.exports = function(RED) {
|
||||
for (var pid in node.activeProcesses) {
|
||||
/* istanbul ignore else */
|
||||
if (node.activeProcesses.hasOwnProperty(pid)) {
|
||||
if (node.activeProcesses[pid].tout) { clearTimeout(node.activeProcesses[pid].tout); }
|
||||
node.activeProcesses[pid].kill();
|
||||
}
|
||||
}
|
||||
node.activeProcesses = {};
|
||||
node.status({});
|
||||
});
|
||||
}
|
||||
RED.nodes.registerType("exec",ExecNode);
|
||||
|
@@ -124,11 +124,11 @@
|
||||
}
|
||||
},
|
||||
"exec": {
|
||||
"spawnerr": "Spawn command must be just the command - no spaces or extra parameters",
|
||||
"badstdout": "Bad STDOUT",
|
||||
"label": {
|
||||
"command": "Command",
|
||||
"append": "Append"
|
||||
"append": "Append",
|
||||
"timeout": "Timeout",
|
||||
"timeoutplace": "optional"
|
||||
},
|
||||
"placeholder": {
|
||||
"extraparams": "extra input parameters"
|
||||
|
Reference in New Issue
Block a user