Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js: Killing subprocesses of ChildProcess#spawn

Consider the following code:

import {spawn, exec} from 'child_process';

var child = spawn('su',
    [process.env.USER, '-c', 'while (true); do sleep 0.3; echo "tick"; done'], 
    {stdio: ['ignore', 'pipe', 'pipe']}
);

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

setTimeout(() => {
    child.kill();
}, 1000);

Here I'm trying to run a particular script which runs some other child process (in that example su will spawn a bash process) and closes it all. However, I can't make it work as I expect.

Calling child.kill() kills just the parent process of su and not its child bash.

What can be done to make it work — calling exec(`pkill -TERM -P ${child.pid}`) instead of child.kill()? As far as I understand, this will kill the whole process tree with parent child.pid.

Yet, it has some oddity when combining two methods:

setTimeout(() => {
    child.kill();
    exec(`pkill -TERM -P ${child.pid}`);
}, 1000);`

This code continues writing tick into the console even after the process has been killed.

Why is this happening? Can somebody explain, please?

like image 584
vtambourine Avatar asked Jan 21 '15 15:01

vtambourine


1 Answers

I was facing the exact problem. I found the solution from How to kill child processes that spawn their own child processes in Node.js.

Here is the working form of your code:

const {spawn, exec} = require('child_process');

var child = spawn('./test.sh',
    [], 
    {stdio: ['ignore', 'pipe', 'pipe'], detached: true} // <---- this
);

child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);

setTimeout(() => {
    process.kill(-child.pid); // <---- and this
    // child.kill();
}, 1000);

When I ran your original code, the terminal prevented me to run su from script, so I modified the testing code to ./test.sh, which does the same thing:

(while (true); do sleep 0.3; echo "tick"; done)

So the lines that do the magic are detached:true and process.kill(-child.pid).

Quoted from the original site:

We can start child processes with {detached: true} option so those processes will not be attached to main process but they will go to a new group of processes. Then using process.kill(-pid) method on main process we can kill all processes that are in the same group of a child process with the same pid group.

like image 96
Richard Wong Avatar answered Sep 24 '22 23:09

Richard Wong