Is ChildProcess running or dead? - node.js - 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.

Related

Wait for process.kill to terminate the process

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)
})

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.

how detect if node.js spawn need input?

detect state of spawn process if is sleeping in an uninterruptible wait. by node.js windows.
// 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
var spawn = require("child_process").spawn;
var sh = spawn("java", ["Test"]);
sh.stdout.setEncoding("utf8");
sh.stderr.setEncoding("utf8");
sh.stdin.pipe(process.stdin);
sh.stdout.on('data', function(data) {
console.log(data);
if () {//check if sh need input
sh.stdin.write("ddssd");
sh.stdin.end();
}
});
sh.on('exit', function(ecode) {
console.log(ecode);
});
sh.stderr.on("data", (data) => {
console.log(data);
});
You don't need to detect that. If it needs input, you can just write to the stdin and it will read it when it needs it -- if it is able to read from stdin at all like that. If that doesn't work, the Java program may think it doesn't have an interactive terminal. In that case, you can try pty.js.

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 })));
});

Node.JS Parent Process ID

Is it possible to get the parent process-id using Node.JS? I would like to detect if the parent is killed or fails in such a way that it cannot notify the child. If this happens, the parent process id of the child should become 1.
This would be preferable to requiring the parent to periodically send a keep-alive signal and also preferable to running the ps command.
You can use pid-file. Something like that
var util = require('util'),
fs = require('fs'),
pidfile = '/var/run/nodemaster.pid';
try {
var pid = fs.readFileSync(pidfile);
//REPLACE with your signal or use another method to check process existence :)
process.kill(pid, 'SIGUSR2');
util.puts('Master already running');
process.exit(1);
} catch (e) {
fs.writeFileSync(pidfile, process.pid.toString(), 'ascii');
}
//run your childs here
Also you can send pid as argument in spawn() call
I start Node.JS from within a native OSX application as a background worker. To make node.js exit when the parent process which consumes node.js stdout dies/exits, I do the following:
// Watch parent exit when it dies
process.stdout.resume();
process.stdout.on('end', function() {
 process.exit();
});
Easy like that, but I'm not exactly sure if it's what you've been asking for ;-)

Resources