12

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')
Calebmer
  • 2,972
  • 6
  • 29
  • 36
  • Have you tried `child.kill('SIGINT')` maybe node doesn't support the `SIGKILL` signal, and will then *assume* default to: `SIGTERM `: https://nodejs.org/api/child_process.html#child_process_child_kill_signal – Andreas Louv Oct 25 '15 at 02:00
  • I‘ve tried `SIGINT`, `SIGKILL`, `SIGHUP`, `SIGQUIT`, and `SIGTERM`. From what I‘ve read, `SIGKILL` seems like the most semantic way to do it. – Calebmer Oct 25 '15 at 02:05
  • wouldn't it be better to close the server regularly with `.close()` instead of killing the process? – Holger Will Oct 27 '15 at 11:15
  • I don't see `.close()` documented [here](https://nodejs.org/api/child_process.html) – Calebmer Oct 27 '15 at 11:22
  • no, i mean close the server from inside your server code https://nodejs.org/api/net.html#net_server_close_callback – Holger Will Oct 27 '15 at 11:39
  • So where is that method called? – Calebmer Oct 27 '15 at 11:55

3 Answers3

15

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
Juicy Scripter
  • 25,778
  • 6
  • 72
  • 93
  • Ok, that worked, thank you. Although I would prefer a bash level solution as the same error occurs when using external software like [pm2](https://github.com/Unitech/pm2/tree/master). Should software like pm2 be using the negative pid? If so I can see if I can get a pull request in. – Calebmer Oct 27 '15 at 22:01
  • @Calebmer, I've added alternative that you may use on a shell script level, you may also want to read first answer of [How to propagate a signal through an arborescence of scripts ? Bash](http://stackoverflow.com/questions/2525855/how-to-propagate-a-signal-through-an-arborescence-of-scripts-bash) – Juicy Scripter Oct 28 '15 at 11:50
1

Simply:

#!/bin/bash
if pgrep -x "node" > /dev/null
then
mv -f  /usr/local/bin/node /usr/local/bin/node.1
killall  node
mv -f /usr/local/bin/node.1 /usr/local/bin/node
which node
else
echo "process node not exists"
fi

node is creating child process every-time we kill it. So it's not possible to kill the process from kill,pkill or killall commands. So we are removing node command to make forking process fail and then we kill the process.Finally we restore the node command.

Akhil
  • 912
  • 12
  • 23
0

have a handler for process signals in your server

server.js

 var http = require('http');

function handleRequest(request, response){
    response.end("Hello");
}
var server = http.createServer(handleRequest);

server.listen(3000, function(){console.log("listening...")});
process.title="testserver"

process.on('SIGQUIT', function() {
  server.close()
});

startup.sh

 #!/bin/bash
 node server.js & echo "started"

and then have a "shutdown.sh".

shutdown.sh

#!/bin/bash
pkill -QUIT testserver & echo "stoped"

It will kill all processes with that name, in case you are spawning multible processes in your "start-node.sh". This way you can have some clean-up code when shutting down e.g. closing all connections etc.

and in your test runner you can do

var exec = require('child_process').exec;
exec("./start.sh")
// later...
exec("./stop.sh")
Holger Will
  • 7,228
  • 1
  • 31
  • 39
  • The problem with this approach is that I want to use a bash script so that it’s interoperable. It's harder in development to continually call start/stop scripts, and I use a [process manager](http://npmjs.com/pm2) which I don't believe has a shutdown script option. – Calebmer Oct 27 '15 at 16:11
  • Then why don't you just `require('pm2')` and start and stop the server from there? – Holger Will Oct 27 '15 at 17:44
  • and btw you cant kill the server when it's managed by pm2, because pm2 will allways restart the server for you when its killed. in the end thats what pm2 is for ;-) – Holger Will Oct 27 '15 at 18:54
  • Because `require('pm2')` means I lose some of the interoperability which I desire from the bash script. Also, to restart the process with pm2, it first has to stop the process then start it again. – Calebmer Oct 27 '15 at 21:04
  • its unclear to me if you use pm2 in your test runner or not. if so, you can not kill the process from outside of pm2! If not, where is the problem with the solution i gave above? " It's harder in development to continually call start/stop scripts" you don't have to call the start an stop scripts, this is only done by your testrunner – Holger Will Oct 27 '15 at 21:46
  • I don't use pm2 in my test runner, only in production. But I would like to use the same bash script for both pm2 and my tests. So if I were to use a pair of start/stop scripts I'd like to use them everywhere. Also, I just wanted to say the `process.title` is super cool :) – Calebmer Oct 27 '15 at 22:14
  • any reason not to use pm2 in your test runner? – Holger Will Oct 28 '15 at 10:02
  • It over complicates things. It's a chainsaw tool when I only need a screwdriver. Also it's currently experiencing the same problem. – Calebmer Oct 28 '15 at 10:39