This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the unix category.
Last Updated: 2024-11-21
I had the following Procfile
web: rails server -p 3000
docservices: (cd ../oxnotes-docservices && APP_ENV=development bundle exec rackup config.ru)
I planted an un-rescued exception in web.1
(rails server) and the foreman output was as follows:
14:50:52 web.1 | exited with code 1
14:50:52 system | sending SIGTERM to all processes
14:50:52 docservices.1 | terminated by SIGTERM
SIGTERM
, FYI, is the default signal sent by kill
terminated by SIGTERM
for docservices
, you'd expect that process to be gone.However, running ps
shows there are remnants still running!
13360 ttys000 0:00.00 sh -c (cd ../oxnotes-docservices && APP_ENV=development bundle exec rackup config.ru)
13361 ttys000 0:00.67 /Users/jack/.rbenv/versions/2.6.4/bin/rackup config.ru
pstree
shows these two have a parent child relationship
|-+- 13360 jack sh -c (cd ../oxnotes-docservices && APP_ENV=development bundle exec rackup config.ru)
| \--- 13361 jack /Users/jack/.rbenv/versions/2.6.4/bin/rackup config.ru
Let's run it again get more info from ps
e.g. process group id pgid
# ask ps for pgid specifically
ps o pid,ppid,pgid,sess,comm,arg
# running Foreman again - unfortunately pids different to above
PID PPID PGID SESS COMM ARGS
28590 17478 28590 0 foreman: master foreman: master
28603 28590 28590 0 puma 4.1.1 (tcp: puma 4.1.1 (tcp://localhost:3000) [oxnotes4]
28604 28590 28590 0 redis-server 127 redis-server 127.0.0.1:6379
28605 28590 28590 0 /Library/Java/Ja /Library/Java/JavaVirtualMachines/jdk1.8.0_192.jdk/Contents/Home
28606 28590 28590 0 sh sh -c (cd ../oxnotes-docservices && APP_ENV=development bundle e
# It seems odd that these lines are repeated. What I believe is happened is that
# the previous one was `sh -c ""` and the subsequent one is `()`
28608 28606 28590 0 sh sh -c (cd ../oxnotes-docservices && APP_ENV=development bundle e
28609 28608 28590 0 /Users/jack/.rbe /Users/jack/.rbenv/versions/2.6.4/bin/rackup config.ru
28610 28590 28590 0 /Users/jack/.rbe /Users/jack/.rbenv/versions/2.6.4/bin/rake jobs:work
What do we see?
foreman
itself, where all children share the one PGID, 28590. A process group is a collection of related processes which can all be signalled at once.killpg $PGID
or kill -- -$PGIP
(cd ../oxnotes-docservices && y)
structure is known as "command
grouping". It starts a sub-shell. Compare to {cd ../oxnotes-docservices &&
y}
which instead runs things in the current shellkill
on the topmost process: kill 28606
, the similar next process becomes orphaned (PPID=1)So what's going on with the process staying around after being killed? Basically we have too much
indirection with sh -c
and ()
and, since child processes do not get passed along signals, the
actual server does not get shut down.
sh
: When bash is interactive, i.e. not here (i.e. not
with sh -c
) in the absence of any
traps, it ignores SIGTERM
(so that kill 0 does not kill an interactive shell),
and SIGINT is caught and handled (so that the wait builtin is interruptible)..bin/docservices
)cd ../oxnotes-docservices || echo "DocServices directory not found" || exit
# Keep the same process ID, but run what follows.
# This allows signals to be passed forwards.
exec bundle exec rackup config.ru