Wait for process.kill to terminate the process - node.js

My application terminates a process (exe file) then attempts to replace it with an updated version, I'm using process.kill with the pid of the process, I keep getting an error when trying to replace it with a newer version because the exe file is still in use and cannot be deleted, I have "resolved" this by waiting for 500ms but I wouldn't call that a good solution, I was expecting the method to be synchronous or at least have a sync counterpart just like the rest of fs methods.
Are there any other ways to do it in node.js?

The docs says the following :
Even though the name of this function is process.kill(), it is really just a signal sender, like the kill system call. The signal sent may do something other than kill the target process.
To me, it implies that process.kill will not indicate that the process is killed, but just that the signal has been sent (when it returns).
But there's another interesting line :
This method will throw an error if the target pid does not exist. As a special case, a signal of 0 can be used to test for the existence of a process. Windows platforms will throw an error if the pid is used to kill a process group.
So you can come up with something like that :
const killProcess = ({pid, signal = 'SIGTERM', timeout} = {}) => new Promise((resolve, reject) => {
process.kill(pid, signal);
let count = 0;
setInterval(() => {
try {
process.kill(pid, 0);
} catch (e) {
// the process does not exists anymore
resolve();
}
if ((count += 100) > timeout) {
reject(new Error("Timeout process kill"))
}
}, 100)
})

Related

Execute script from Node in a separate process

What I want to do is when an endpoint in my Express app is hit, I want to run a command line script - without waiting for the result - in a separate process.
Right now I am using the child_process’s spawn function and it is working, but if the Node server were to quit, the child script would quit as well. I need to have the child script run to completion even if the server quits.
I don’t need access to stdout or anything from the child script. I just need a way to basically “fire and forget”
Is there any way to do this with spawn that I may be missing? Or is there another way I should be going about this?
Thanks in advance for any guidance!
What you want here is options.detached of spawn. Setting this option will allow the sub-process to continue even after the main process calling spawn has terminated.
Quoting the documentation:
On Windows, setting options.detached to true makes it possible for the child process to continue running after the parent exits. The child will have its own console window. Once enabled for a child process, it cannot be disabled.
On non-Windows platforms, if options.detached is set to true, the child process will be made the leader of a new process group and session. Note that child processes may continue running after the parent exits regardless of whether they are detached or not. See setsid(2) for more information.
Basically this means what you "launch" keeps running until it actually terminates itself. As 'detached', there is nothing that "ties" the sub-process to the execution of the parent from which it was spawned.
Example:
listing of sub.js:
(async function() {
try {
await new Promise((resolve,reject) => {
let i = 0;
let ival = setInterval(() => {
i++;
console.log('Run ',i);
if (i === 5) {
clearInterval(ival);
resolve();
}
}, 2000);
});
} catch(e) {
console.error(e);
} finally {
process.exit();
}
})();
listing of main.js
const fs = require('fs');
const { spawn } = require('child_process');
(async function() {
try {
const out = fs.openSync('./out.log', 'a');
const err = fs.openSync('./out.log', 'a');
console.log('spawn sub');
const sub = spawn(process.argv[0], ['sub.js'], {
detached: true, // this removes ties to the parent
stdio: [ 'ignore', out, err ]
});
sub.unref();
console.log('waiting..');
await new Promise((resolve,reject) =>
setTimeout(() => resolve(), 3000)
);
console.log('exiting main..');
} catch(e) {
console.error();
} finally {
process.exit();
}
})();
The basics there are that the sub.js listing is going to output every 2 seconds for 5 iterations. The main.js is going to "spawn" this process as detached, then wait for 3 seconds and terminate itself.
Though it's not really needed, for demonstration purposes we are setting up the spawned sub-process to redirect its output ( both stdout and stderr ) to a file named out.log in the same directory.
What you see here is that the main listing does it's job and spawns the new process then terminates after 3 seconds. At this time the sub-process will only have output 1 line, but it will continue to run and produce output to the redirected file for another 7 seconds, despite the main process being terminated.

Is ChildProcess running or dead? - node.js

Is there any official (documented) way, given ChildProcess instance how to find out whether the process is still alive or dead?
I don't want to listen to exit event, I only want to synchronously obtain info whether the process has already terminated.
So far I found undocumented:
const isAlive = process.exitCode === null;
Assuming that by "dead" you mean that the process is no longer running, the "correct" method is to listen for the ChildProcess' exit event.
Other than than, it depends on the architecture on which you're running the process.
On Linux
On Linuxes that support procfs - the process pseudo-filesystem, you can check the state of a process by checking for the existence of its id under the /proc directory.
Assuming your child process id is 1234, reading /proc/1234/status, you'll find quite a lot of information about the process including:
State: R (running)
Example Code:
var fs=require('fs'),
pid = 1234
procInfo;
// possible values for State value in /proc/pid/status
// R running,
// S is sleeping,
// D is sleeping in an uninterruptible wait,
// Z is zombie (not running but held by process owner)
// T is traced or stopped
try {
procInfo=fs.readFileSync('/proc/'+pid+'/status').toString();
}
catch(e){
console.error('process does not exist');
}
if(!procInfo.match(/State:\s+[RSDT]/)){
console.error('process is not running');
}
On OSX (or other Unix-like operating systems)
The only generic way to check process status would be to shell out to the ps command to view the status of a single process or of the list of currently known processes.
However, this is not (and cannot be made) a synchronous process using Node versions less than and including v0.10.44 (which relies on events to handle all communications with external processes).
For versions of Node greater than v0.10.44, there are synchronous versions of the standard child_process functions that can be used.
Example Code
'use strict';
var util=require('util'),
nodeVersion=parseFloat(process.version.replace(/^v|\.\d+$/g,'')), // version as Major.MinorPatch like: 0.1044 for v0.10.44
pid=1234,
txt='';
// expected output from ps:
// PID TT STAT TIME COMMAND
// 1224 s000 S 0:00.08 child_process
// meaning of first letter in STAT column
// I IDLE
// R RUNNING
// S SLEEPING
// T STOPPED
// U WAIT
// Z DEAD
/**
* Returns true if process id is currently running.
*
* #param {Number|String} pid - id of process
* #param {String} str - output from `ps` command
* #return {boolean}
*/
var isProcessRunning=function(pid,str){
if(!str.match(new RegExp('(^'+pid.toString()+'.+?$)','m'))){
//throw new Error('process does not exist');
return false;
}
var parts=RegExp.$1.split(/\s+/);
if(parts.length < 5){
throw new Error('unexpected result from ps');
}
if(!parts[2].match(/^[IRSTU]/)){
//throw new Error('process is not running: %s',parts[2]);
return false;
}
return true;
};
if(nodeVersion > 0.1044){ // node versions greater than v0.10.44
var result=require('child_process').spawnSync('ps',[pid]);
console.log(util.format('process %s %s running', pid, isProcessRunning(pid,result.stdout.toString()) ? 'is' : 'IS NOT'));
}
else { // node versions less than or equal to v0.10.44
var spawn = require('child_process').spawn,
ps = spawn('ps', [pid]);
ps.stdout.on('data', function(data){
txt+=data.toString();
});
ps.stderr.on('data',function(data){
console.error('ps error: '+data);
});
ps.on('close', function() {
console.log(util.format('process %s %s running', pid, isProcessRunning(pid,txt) ? 'is' : 'IS NOT'));
});
}
The isProcessRunning function takes a pid to check and str which is the output from running the ps command to retrieve the run state of the process, extracts the useful state from the string (using Regular Expressions to find the correct line and field on that line), and returns true or false, depending on the process' reported run state.
It's very important to note that for versions of Node that are greater than v0.10.44, the call to child_process.spawnSync is synchronous and so, will block the event loop from doing anything else until the child process has completed.
This code was tested using Node v4.0.0.
On Windows
The same approach (shelling out to execute a command to check process status) can be used but instead of ps, you'd need to use the Windows-specific tasklist command to retrieve information about a specific process.

Nodejs child process exit before stdio streams close

I've just been experimenting with child processes and noticed that the exit event fires before the close event - the following code throws an error because this._instance.stdin no longer exists (this._instance is already null).
'use strict';
const spawn = require('child_process').spawn;
class Foo {
constructor() {
this._instance = null;
}
bar() {
this._instance = spawn('ls', ['-l']);
this._instance.on('close', (code, signal) => {
this._instance.stdin.end();
});
this._instance.on('exit', (code, signal) => {
this._instance = null;
});
return this._instance;
}
}
var foo = new Foo();
console.log(foo.bar());
The documentation states:
"Note that when the 'exit' event is triggered, child process stdio streams might still be open."
I wondered how this happens, why do the streams still exists after the process has exited? And how do they get 'closed', is this part handled by the OS or does node do the closing of the left over stdio streams?
In practice I wouldn't necessarily set this._instance to null on exit as it doesn't seem like a nice thing to do and is obviously a bit premature.
The documentation of the close event sheds some light on why this might happen.
In short, the stdio can be used by other processes which did not exit yet.
I haven't looked into the code itself, but it would make sense that the OS handles the part of closing the stdio streams. Think about piping stdio into multiple processes (piping with tee might be a good example).
In the presented case, I would suspect that you don't even need to end() stdin, as the close event suggests that the stdin stream has already been closed.

Electron kill child_process.exec

I have an electron app that uses child_process.exec to run long running tasks.
I am struggling to manage when the user exits the app during those tasks.
If they exit my app or hit close the child processes continue to run until they finish however the electron app window has already closed and exited.
Is there a way to notify the user that there are process still running and when they have finished then close the app window?
All I have in my main.js is the standard code:
// Quit when all windows are closed.
app.on('window-all-closed', function() {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform != 'darwin') {
app.quit();
}
});
Should I be adding a check somewhere?
Thanks for your help
EDITED
I cannot seem to get the PID of the child_process until it has finished. This is my child_process code
var loader = child_process.exec(cmd, function(error, stdout, stderr) {
console.log(loader.pid)
if (error) {
console.log(error.message);
}
console.log('Loaded: ', value);
});
Should I be trying to get it in a different way?
So after everyones great comments I was able to update my code with a number of additions to get it to work, so am posting my updates for everyone else.
1) Change from child_process.exec to child_process.spawn
var loader = child_process.spawn('program', options, { detached: true })
2) Use the Electron ipcRenderer to communicate from my module to the main.js script. This allows me to send the PIDs to main.js
ipcRenderer.send('pid-message', loader.pid);
ipcMain.on('pid-message', function(event, arg) {
console.log('Main:', arg);
pids.push(arg);
});
3) Add those PIDs to array
4) In my main.js I added the following code to kill any PIDs that exist in the array before exiting the app.
// App close handler
app.on('before-quit', function() {
pids.forEach(function(pid) {
// A simple pid lookup
ps.kill( pid, function( err ) {
if (err) {
throw new Error( err );
}
else {
console.log( 'Process %s has been killed!', pid );
}
});
});
});
Thanks for everyones help.
ChildProcess emits an exit event when the process has finished - if you keep track of the current processes in an array, and have them remove themselves after the exit event fires, you should be able to just foreach over the remaining ones running ChildProcess.kill() when you exit your app.
This may not be 100% working code/not the best way of doing things, as I'm not in a position to test it right now, but it should be enough to set you down the right path.
var processes = [];
// Adding a process
var newProcess = child_process.exec("mycommand");
processes.push(newProcess);
newProcess.on("exit", function () {
processes.splice(processes.indexOf(newProcess), 1);
});
// App close handler
app.on('window-all-closed', function() {
if (process.platform != 'darwin') {
processes.forEach(function(proc) {
proc.kill();
});
app.quit();
}
});
EDIT: As shreik mentioned in a comment, you could also just store the PIDs in the array instead of the ChildProcess objects, then use process.kill(pid) to kill them. Might be a little more efficient!
Another solution. If you want to keep using exec()
In order to kill the child process running by exec() take a look to the module ps-tree. They exaplain what is happening.
in UNIX, a process may terminate by using the exit call, and it's
parent process may wait for that event by using the wait system call.
the wait system call returns the process identifier of a terminated
child, so that the parent tell which of the possibly many children has
terminated. If the parent terminates, however, all it's children have
assigned as their new parent the init process. Thus, the children
still have a parent to collect their status and execution statistics.
(from "operating system concepts")
SOLUTION: use ps-tree to get all processes that a child_process may have started, so that they
exec() actually works like this:
function exec (cmd, cb) {
spawn('sh', ['-c', cmd]);
...
}
So check the example and adapt it to your needs
var cp = require('child_process'),
psTree = require('ps-tree');
var child = cp.exec("node -e 'while (true);'", function () { /*...*/ });
psTree(child.pid, function (err, children) {
cp.spawn('kill', ['-9'].concat(children.map(function (p) { return p.PID })));
});

child_process spawn Race condition possibility in nodejs

I'm starting to learn and use node and I like it but I'm not really sure how certain features work. Maybe you can help me resolve one such issue:
I want to spawn local scripts and programs from my node server upon rest commands. looking at the fs library I saw the example below of how to spawn a child process and add some pipes/event handlers on it.
var spawn = require('child_process').spawn,
ps = spawn('ps', ['ax']),
grep = spawn('grep', ['ssh']);
ps.stdout.on('data', function (data) {
grep.stdin.write(data);
});
ps.stderr.on('data', function (data) {
console.log('ps stderr: ' + data);
});
ps.on('close', function (code) {
if (code !== 0) {
console.log('ps process exited with code ' + code);
}
grep.stdin.end();
});
grep.stdout.on('data', function (data) {
console.log('' + data);
});
grep.stderr.on('data', function (data) {
console.log('grep stderr: ' + data);
});
grep.on('close', function (code) {
if (code !== 0) {
console.log('grep process exited with code ' + code);
}
});
What's weird to me is that I don't understand how I can be guaranteed that the event handler code will be registered before the program starts to run. It's not like there's a 'resume' function that you run to start up the child. Isn't this a race condition? Granted the condition would be minisculy small and would almost never hit because its such a short snipping of code afterward but still, if it is I'd rather not code it this way out of good habits.
So:
1) if it's not a race condition why?
2) if it is a race condition how could I write it the right way?
Thanks for your time!
Given the slight conflict and ambiguity in the accepted answer's comments, the sample and output below tells me two things:
The child process (referring to the node object returned by spawn) emits no events even though the real underlying process is live / executing.
The pipes for the IPC are setup before the child process is executed.
Both are obvious. The conflict is w.r.t. interpretation of the OP's question:-
Actually 'yes', this is the epitome of a data race condition if one needs to consider the real child process's side effects. But 'no', there's no data race as far as IPC pipe plumbing is concerned. The data is written to a buffer and retrieved as a (bigger) blob as and when (as already well described) the context completes allowing the event loop to continue.
The first data event seen below pushes not 1 but 5 chunks written to stdout by the child process whilst we were blocking.. thus nothing is lost.
sample:
let t = () => (new Date()).toTimeString().split(' ')[0]
let p = new Promise(function (resolve, reject) {
console.log(`[${t()}|info] spawning`);
let cp = spawn('bash', ['-c', 'for x in `seq 1 1 10`; do printf "$x\n"; sleep 1; done']);
let resolved = false;
if (cp === undefined)
reject();
cp.on('error', (err) => {
console.log(`error: ${err}`);
reject(err);
});
cp.stdout.on('data', (data) => {
if (!resolved) {
console.log(`[${t()}|info] spawn succeeded`);
resolved = true;
resolve();
}
process.stdout.write(`[${t()}|data] ${data}`);
});
let ts = parseInt(Date.now() / 1000);
while (parseInt(Date.now() / 1000) - ts < 5) {
// waste some cycles in the current context
ts--; ts++;
}
console.log(`[${t()}|info] synchronous time wasted`);
});
Promise.resolve(p);
output:
[18:54:18|info] spawning
[18:54:23|info] synchronous time wasted
[18:54:23|info] spawn succeeded
[18:54:23|data] 1
2
3
4
5
[18:54:23|data] 6
[18:54:24|data] 7
[18:54:25|data] 8
[18:54:26|data] 9
[18:54:27|data] 10
It is not a race condition. Node.js is single threaded and handles events on a first come first serve basis. New events are put at the end of the event loop. Node will execute your code in a synchronous manner, part of which will involve setting up event emitters. When these event emitters emit events, they will be put to the end of the queue, and will not be handled until Node finishes executing whatever piece of code its currently working on, which happens to be the same code that registers the listener. Therefore, the listener will always be registered before the event is handled.

Resources