Open ratijas opened 4 years ago
Reproduced locally, also got to the reason it works that way.
Within Docker container, websocat is running as init process (pid=1 when looked from within the container)
Init processes don't typically terminate on signals
If websocat is using stdin/stdout the way it typically uses it on Unix, it sets up explicit signal handler to restore blocking status of stdin/stdout's file descriptor before existing.
Before first client is connected to Websocat, it does not yet open stdin/stdout and does not yet install the signal handler.
When the first client connects, BroadcastReuser gets activated, then Stdio gets activated and before beginning routing data to/from console it sets up the signal handler.
$ websocat --dump-spec -s 1234
Listening on ws://127.0.0.1:1234/
Message2Line(WsServer(TcpListen(127.0.0.1:1234)))
BroadcastReuser(Line2Message(Stdio))
With the signal handler active, Websocat actively responds to Ctrl+C despite of being pid=1.
Workaround: start websocat as non-init process:
$ docker run -it --rm -p 1234:1234 --entrypoint=/bin/bash solsson/websocat -c "(websocat -s 1234)"
That sounds logical from the implementation point of view, but practically most tools in docker used to terminate on Ctrl-C regardless of being init or not.
Maybe I can make this signal handler default, not dependent on necessity of running pre-termination procedures.
most tools in docker used to terminate on Ctrl-C regardless of being init or not
I tried the simplest one (/bin/cat
) and it seems to also fail to terminate on Ctrl+C:
# docker run -it --rm -p 1234:1234 --entrypoint=/bin/cat solsson/websocat
sdf
sdf
sdf
eee
eee
^C
sdfsf
sdfsf
rrrr
rrrr
^C
^C
Can you list some of those other tools? Maybe there is some trick to make it straighforward.
Hmm... I don't remember ever running into that. Maybe I was just lucky until today.
But what about --entrypoint=/bin/bash
? I guess it works only because bash handles SIGCHLD when child dies?
Init processes don't typically terminate on signals
And what exactly means that line of your? Is there some code like if pid != 1 { handle_sigint() }
somewhere in... runtime's default handler? operating system kernel?
But what about --entrypoint=/bin/bash?
It makes websocat
stopping being init process (instead bash becomes one), so signals work as usual.
With proper ENTRYPOINT
script it can be made appear as if you are starting Websocat directly (without those -c "(websocat ... )"
).
I guess it works only because bash handles SIGCHLD when child dies?
I don't know how it is related to SIGCHLD
s
And what exactly means that line of your? Is there some code like if pid != 1 { handle_sigint() } somewhere in... runtime's default handler?
No
operating system kernel?
Yes.
Maybe it's not a special code, just alternative default dispositions for unhandled signals.
How interesting. Never thought docker would introduce that kind of problems.
I guess it works only because bash handles SIGCHLD when child dies?
I don't know how it is related to
SIGCHLD
s
I mean, child handles Ctrl-C and thus exits. When that happens, parent process (bash) receives a signal SIGCHLD, and since that was its only child process, it also exits while propagating exit status. At least, that's my understanding of things.
When that happens, parent process (bash) receives a signal SIGCHLD, and since that was its only child process, it also exits while propagating exit status.
Something like that. Bash would just act as a wrapper.
Oh wait oh fck I think I know why.
There are many ways to specify both ENTRYPOINT and CMD in Dockerfile, docker-compose and command line. Some of them (namely, without formal JSON array notation) involve automatic wrapping the whole script in sh -c
, known as shell form. And that might be the reason some tools in docker handle Ctrl-C out-of-the-box as they should.
https://docs.docker.com/engine/reference/builder/#entrypoint
Another interesting and related stuff found: STOPSIGNAL
instruction in Dockerfile. Used by nginx, for example.
https://docs.docker.com/engine/reference/builder/#stopsignal
Summary
Websocat does not respond to Ctrl-C in docker. It can't be stopped — only killing its container helps.
~It claims to start listening on port, but the not even telnet is able to connect to that port, nor it is listed in
lsof -i
output. (yep, I run docker with-p 1234:1234
alright).~ (See partial solution below)Solution (partial)
Part of my problem is that I blindly copied
websocat -s 1234
example from the README. Indeed, websocat is up and running, but listening on container-local interface 127.0.0.1 which is not visible on the host machine. But Ctrl-C is not working yet.-- no reaction whatsoever.
Once I run
docker exec
inside a running websocat container, I am able to connect to a server from an instance of websocat client:-- and it works as expected: server prints the same message on its terminal.
After handling even a single message from a client, server starts responding to Ctrl-C.