Search code examples
node.jsbashprocesschild-processsigkill

Killing a bash script does not kill child processes


I have written a test script which runs another script to start the server to test. When the tests have completed a SIGKILL message is sent to the server process, however when running the test script again the server throws a EADDRINUSE error (I‘m in a node.js environment) which means the port the server is trying to mount to is currently in use. The process we tried to kill with a SIGKILL is still running. I don‘t believe this is a node specific issue, but rather a lack of education on my end for how bash processes work.

Here are some specifics, this is my start script called scripts/start-node.sh:

#!/bin/bash

node_modules/.bin/babel-node --stage 0 index.js

This is my node server called index.js (I haven‘t authored any process event listeners):

Http.createServer(…).listen(PORT, () => console.log(`Server listening on ${PORT}`))

And the start script is controlled with the node child_process module:

var child = child_process.spawn('scripts/start-node.sh')
// Later…
child.kill('SIGKILL')

Solution

  • To kill a child process and all it's children you may use process.kill with a negative pid (to kill a process group)

    var child = child_process.spawn('scripts/start-node.sh', {detached: true})
    // Later…
    process.kill(-child.pid, 'SIGKILL');
    

    See details on child_process documentation for options.detached

    On non-Windows, if the detached option is set, the child process will be made the leader of a new process group and session.

    Referencing here a portion of man 2 kill for some details:

    If pid is less than -1, then sig is sent to every process in the process group whose ID is -pid.


    Another option may be using trap in your shell script to intercept a signal and kill all the children and using child.kill('SIGTERM') from node (as SIGKILL will not be intercepted by trap)

    #!/bin/bash
    
    trap 'kill $(jobs -p)' EXIT
    node_modules/.bin/babel-node --stage 0 index.js