Node.js: Killing sub processes of ChildProcess#spawn
Asked Answered
O

1

7

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?

Omeromero answered 21/1, 2015 at 15:25 Comment(0)
T
7

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.

Tidewaiter answered 15/4, 2018 at 13:39 Comment(1)
For future readers that may be confused, process is a built-in node module, i.e. const process = require('node:process');. It is not a spawned process instance. But processis a special module that is available globally and does not need to be imported.Soap

© 2022 - 2024 — McMap. All rights reserved.