Skip to content
Advertisement

Why does SIGHUP not work on busybox sh in an Alpine Docker container?

Sending SIGHUP with

kill -HUP <pid>

to a busybox sh process on my native system works as expected and the shell hangs up. However, if I use docker kill to send the signal to a container with

docker kill -s HUP <container>

it doesn’t do anything. The Alpine container is still running:

$ CONTAINER=$(docker run -dt alpine:latest)
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 1 second
$ docker kill -s HUP $CONTAINER
4fea4f2dabe0f8a717b0e1272528af1a97050bcec51babbe0ed801e75fb15f1b
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 7 seconds

By the way, with an Ubuntu container (which runs bash) it does work as expected:

$ CONTAINER=$(docker run -dt debian:latest)
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 1 second
$ docker kill -s HUP $CONTAINER
9a4aff456716397527cd87492066230e5088fbbb2a1bb6fc80f04f01b3368986
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Exited (129) 1 second ago

Sending SIGKILL does work, but I’d rather find out why SIGHUP does not.


Update: I’ll add another example. Here you can see that busybox sh generally does hang up on SIGHUP successfully:

$ busybox sh -c 'while true; do sleep 10; done' &
[1] 28276
$ PID=$!
$ ps -e | grep busybox
28276 pts/5    00:00:00 busybox
$ kill -HUP $PID
$ 
[1]+  Hangup                  busybox sh -c 'while true; do sleep 10; done'
$ ps -e | grep busybox
$

However, running the same infinite sleep loop inside the docker container doesn’t quit. As you can see, the container is still running after SIGHUP and only exits after SIGKILL:

$ CONTAINER=$(docker run -dt alpine:latest busybox sh -c 'while true; do sleep 10; done')
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}" 
Up 14 seconds
$ docker kill -s HUP $CONTAINER
31574ba7c0eb0505b776c459b55ffc8137042e1ce0562a3cf9aac80bfe8f65a0
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Up 28 seconds
$ docker kill -s KILL $CONTAINER
31574ba7c0eb0505b776c459b55ffc8137042e1ce0562a3cf9aac80bfe8f65a0
$ docker ps -a --filter "id=$CONTAINER" --format "{{.Status}}"
Exited (137) 2 seconds ago
$

Advertisement

Answer

(I don’t have Docker env at hand for a try. Just guessing.)

For your case, docker run must be running busybox/sh or bash as PID 1.

According to Docker doc:

Note: A process running as PID 1 inside a container is treated specially by Linux: it ignores any signal with the default action. So, the process will not terminate on SIGINT or SIGTERM unless it is coded to do so.

For the differece between busybox/sh and bash regarding SIGHUP

On my system (Debian 9.6, x86_64), the signal masks for busybox/sh and bash are as follows:

busybox/sh:

USER     PID %CPU %MEM    VSZ   RSS TTY    STAT START   TIME COMMAND
root   82817  0.0  0.0   6952  1904 pts/2  S+   10:23   0:00 busybox sh

PENDING (0000000000000000):
BLOCKED (0000000000000000):
IGNORED (0000000000284004):
   3 QUIT
  15 TERM
  20 TSTP
  22 TTOU
CAUGHT (0000000008000002):
   2 INT
  28 WINCH

bash:

USER    PID %CPU %MEM    VSZ   RSS TTY     STAT START   TIME COMMAND
root   4871  0.0  0.1  21752  6176 pts/16  Ss    2019   0:00 /usr/local/bin/bash

PENDING (0000000000000000):
BLOCKED (0000000000000000):
IGNORED (0000000000380004):
   3 QUIT
  20 TSTP
  21 TTIN
  22 TTOU
CAUGHT (000000004b817efb):
   1 HUP
   2 INT
   4 ILL
   5 TRAP
   6 ABRT
   7 BUS
   8 FPE
  10 USR1
  11 SEGV
  12 USR2
  13 PIPE
  14 ALRM
  15 TERM
  17 CHLD
  24 XCPU
  25 XFSZ
  26 VTALRM
  28 WINCH
  31 SYS

As we can see busybox/sh does not handle SIGHUP so the signal is ignored. Bash catches SIGHUP so docker kill can deliver the signal to Bash and then Bash will be terminated because, according to its manual, “the shell exits by default upon receipt of a SIGHUP.


UPDATE 2020-03-07 #1:

Did a quick test and my previous analysis is basically correct. You can verify like this:

[STEP 104] # docker run -dt debian busybox sh -c 
             'trap exit HUP; while true; do sleep 1; done'
331380090c59018dae4dbc17dd5af9d355260057fdbd2f2ce9fc6548a39df1db
[STEP 105] # docker ps 
CONTAINER ID        IMAGE            COMMAND                  CREATED             
331380090c59        debian           "busybox sh -c 'trap…"   11 seconds ago      
[STEP 106] # docker kill -s HUP 331380090c59    
331380090c59
[STEP 107] # docker ps 
CONTAINER ID        IMAGE               COMMAND             CREATED             
[STEP 108] #

As I showed earlier, by default busybox/sh does not catch SIGHUP so the signal will be ignored. But after busybox/sh explicitly trap SIGHUP, the signal will be delivered to it.

I also tried SIGKILL and yes it’ll always terminate the running container. This is reasonable since SIGKILL cannot be caught by any process so the signal will always be delivered to the container and kill it.


UPDATE 2020-03-07 #2:

You can also verify it this way (much simpler):

[STEP 110] # docker run -ti alpine
/ # ps
PID   USER     TIME  COMMAND
    1 root      0:00 /bin/sh
    7 root      0:00 ps
/ # kill -HUP 1    <-- this does not kill it because linux ignored the signal
/ # 
/ # trap 'echo received SIGHUP' HUP
/ # kill -HUP 1
received SIGHUP    <-- this indicates it can receive SIGHUP now
/ # 
/ # trap exit HUP
/ # kill -HUP 1    <-- this terminates it because the action changed to `exit`
[STEP 111] #
User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement