Graceful shutdown on Docker container

The ocserv running inside a Docker container does not gracefully shut down when receiving a SIGTERM signal from Docker.

The ocserv process running as PID 1 in the container would receive the SIGTERM signal from either docker stop ocserv or docker kill --signal SIGTERM ocserv commands. However, the ocserv's main process does not propagate the received signal to the worker processes and causes the container to be terminated after the container graceful timeout. (or if the timeout is long, ocserv kills the workers itself)

For example, if the container is running and the graceful timeout value is 10 seconds (default value), when docker stop ocserv is executed, the container will terminate ungracefully in ~8 seconds:

ocserv-1     | note: vhost:default: setting 'plain' as primary authentication method
ocserv-1     | note: setting 'file' as supplemental config option
ocserv-1     | listening (TCP) on 0.0.0.0:443...
ocserv-1     | listening (TCP) on [::]:443...
ocserv-1     | listening (UDP) on 0.0.0.0:8443...
ocserv-1     | listening (UDP) on [::]:8443...
ocserv-1     | ocserv[1]: main: Starting 1 instances of ocserv-sm
ocserv-1     | ocserv[1]: main: created sec-mod socket file (/tmp/ocserv/ocserv.sock.c36c5d67.0)
ocserv-1     | ocserv[1]: main: initializing control unix socket: /tmp/ocserv/occtl.sock
ocserv-1     | ocserv[1]: main: initialized ocserv 1.2.2
ocserv-1     | ocserv[86]: sec-mod: reading supplemental config from files
ocserv-1     | ocserv[86]: sec-mod: loaded 1 keys
ocserv-1     | ocserv[86]: sec-mod: sec-mod initialized (socket: /tmp/ocserv/ocserv.sock.c36c5d67.0)
ocserv-1     | ocserv[1]: main: termination request received; waiting for sessions to die
ocserv-1     | ocserv[1]: main: not all sessions died; forcing kill
ocserv-1 exited with code 0

However, if the signal send to the ocserv's process group by docker exec ocserv kill -TERM -1 command, the container gracefully terminates immediately:

ocserv-1     | note: vhost:default: setting 'plain' as primary authentication method
ocserv-1     | note: setting 'file' as supplemental config option
ocserv-1     | listening (TCP) on 0.0.0.0:443...
ocserv-1     | listening (TCP) on [::]:443...
ocserv-1     | listening (UDP) on 0.0.0.0:8443...
ocserv-1     | listening (UDP) on [::]:8443...
ocserv-1     | ocserv[1]: main: Starting 1 instances of ocserv-sm
ocserv-1     | ocserv[1]: main: created sec-mod socket file (/tmp/ocserv/ocserv.sock.d0e7d091.0)
ocserv-1     | ocserv[1]: main: initializing control unix socket: /tmp/ocserv/occtl.sock
ocserv-1     | ocserv[1]: main: initialized ocserv 1.2.2
ocserv-1     | ocserv[85]: sec-mod: reading supplemental config from files
ocserv-1     | ocserv[85]: sec-mod: loaded 1 keys
ocserv-1     | ocserv[85]: sec-mod: sec-mod initialized (socket: /tmp/ocserv/ocserv.sock.d0e7d091.0)
ocserv-1     | ocserv[1]: main: main-sec-mod-cmd.c:107: command socket for sec-mod closed
ocserv-1     | ocserv[1]: main: main.c:1228: error in command from sec-mod
ocserv-1     | ocserv[1]: main: termination request received; waiting for sessions to die
ocserv-1 exited with code 0

It's possible to manually handle the signal propagation to the worker processes with the CMD instruction in shell form in the DOCKERFILE:

CMD trap 'kill -TERM -$(< /tmp/ocserv/ocserv.pid)' TERM; \
    ocserv --foreground --config /tmp/ocserv/ocserv.conf & \

    pid=$!; \
    while kill -0 $pid >/dev/null 2>&1; do \
        wait $pid; \
    done

But, there is no way to gracefully shut down the process with exec form:

CMD ["ocserv", "--foreground", "--config", "/tmp/ocserv/ocserv.conf"]