When I start nodemon within a tmux session on macOS, the nodemon process lives on after the tmux session has been killed. I can't get nodemon to die unless I specifically kill it.
I'd instead like to persuade nodemon to die like other processes do when the tmux window they started from is killed.
I'm using nodemon v1.71.1, node v9.5.0, tmux 2.6, and macOS 10.13.3.
Here's an example:
Start a tmux session (no config file presumed, just using the out-of-box settings):
tmux
Create a sample project, and install nodemon locally:
$ mkdir test; cd test; npm init --yes
$ echo "console.log('hello world')" > index.js
$ npm install -D nodemon
Start nodemon and note the parent PID:
$ ./node_modules/.bin/nodemon --verbose
In a separate terminal, verify the parent PID shows up in ps:
$ ps x | grep nodemon
In my case I see:
8643 s002 S+ 0:00.44 node ./node_modules/.bin/nodemon --verbose
Kill the tmux window running nodemon. Since there is only one window, the tmux session will also end:
C-b : kill-window
Finally, re-run the ps command. When I do that, the nodemon process is still there, now in limbo since its controlling terminal is gone:
8643 ?? S 0:00.45 node ./node_modules/.bin/nodemon --verbose
I'm not sure whether tmux is doing something nodemon doesn't expect, or the other way around. I can't replicate the issue under Linux.
I'm not entirely familiar with the inner workings of tmux, but do you know what kill signal tmux uses to end the child processes when it closes?
I think it's SIGHUP, based on a casual looking-around, but I claim no particular authority on that.
I did come across a similar discussion which suggests this behavior isn't unique to nodemon.
I'd like to subtract tmux from the equation, however, because I can replicate the behavior without it. If I launch nodemon in a plain macOS terminal and then close the window (which is more-or-less what tmux would do), the nodemon process lives on in the detached state described previously.
Would it be possible for nodemon to distinguish SIGHUPs meant to trigger restart from SIGHUPs meant to terminate for good by checking for a controlling terminal?
A SIGHUP (as far as I understand) is simply a single int signal to the process, so no, I don't think there's a way to distinguish.
Nodemon does actually trap SIGHUP to tell the child process to restart, so I suspect that's why nodemon is still running after tmux ends.
If you run a daemon process in tmux (that would restart with a SIGHUP) how would the process know to end when the tmux session starts? (real question, wondering if it's possible to get some ideas from prior art).
Oh wait, I misread your part about running without tmux.
I'm pretty sure, if the process is still attached to the session, if you close s terminal session, it'll send a SIGTERM - I'd like to double check this on my own machine tomorrow but I'm 99% sure that's the case which might mean there's something else at play here.
I took a closer look at the signal that is sent when the terminal session closes, and it turned out to be SIGHUP rather than SIGTERM.
I used the kill.d utility, which uses DTrace and available by default on macOS as far as I can tell. With that running in one session and nodemon running in a second session, closing the terminal window registers as a HUP and not a TERM. If I send a TERM from a third window via kill -TERM [nodemon parent pid] then nodemon dies as intended.
No answers, unfortunately, just a reframing of the issue. As you mentioned earlier, the trapping of SIGHUP is the trouble. If it is used to tell the child process to restart, in a daemon-ish sort of way, there is an implied expectation that it won't also be used to tell the parent process to shut down. But it is.
Can you try running nodemon with the following:
nodemon --signal SIGHUP
This tells nodemon to use SIGHUP to restart your child process, but in addition, it listens for SIGHUP2 to trigger restarts - which, _might_ circumvent the issue you're seeing.
Listening for SIGHUP2 has resolved the problem. Use of the --signal argument has allowed the nodemon parent process to fully exit when intended, both for the with-tmux and without-tmux scenarios.
Thanks for the help.
Most helpful comment
Can you try running nodemon with the following:
This tells nodemon to use
SIGHUPto restart your child process, but in addition, it listens forSIGHUP2to trigger restarts - which, _might_ circumvent the issue you're seeing.