openrc-init: keep a dummy writer on the control FIFO - #1055
Conversation
|
Note: this can be fixed with a much shorter patch, by just changing static int open_fifo(const char *path)
{
if (mkfifo(path, 0600) == -1 && errno != EEXIST)
return -1;
return open(path, O_RDWR | O_NONBLOCK | O_CLOEXEC);
}POSIX leaves opening a FIFO with O_RDWR undefined, so this relies on platform-specific behaviour. On Linux it is documented in fifo(7), and sysvinit has kept /run/initctl open this way for decades. Since openrc-init is Linux-only anyway, maybe we should go with the shorter change. What do you think? |
openrc-init is also used on gentoo's hurd port (though with patches that are not upstream yet) if the same about O_RDWR is true for hurd, then we can go ahead with the simpler patch |
For GNU/Hurd, I found this open issue concerning open(FIFO, O_RDWR). Looking at the current FIFO translator implementation, the issue does not appear to have been fixed. Since openrc-init is expected to run on GNU/Hurd, we should keep the longer version of the patch. |
The init FIFO is opened read-only and monitored with poll(). After a
client writes a command and closes the FIFO, Linux continues reporting
POLLHUP even after the buffered data has been consumed. Since the main
loop only handles POLLIN, poll() then returns immediately on every
iteration, causing PID 1 to consume all available CPU time.
This can be reproduced on a system using openrc-init as PID 1 with a
harmless unknown command:
printf invalid > /run/openrc/init.ctl
top -p 1
Keep a dummy write descriptor open for the lifetime of openrc-init so
that disconnecting a client does not leave the read end hung up. Mark
the descriptor close-on-exec so it is replaced normally during reexec.
A single O_RDWR descriptor would achieve the same, but POSIX leaves
O_RDWR on a FIFO undefined, so use a separate write descriptor.
Regression after: afe3eca ("openrc-init: use poll for waiting on the fifo")
The init FIFO is opened read-only and monitored with poll(). After a client writes a command and closes the FIFO, Linux continues reporting POLLHUP even after the buffered data has been consumed. Since the main loop only handles POLLIN, poll() then returns immediately on every iteration, causing PID 1 to consume all available CPU time.
This can be reproduced on a system using openrc-init as PID 1 with a harmless unknown command:
Keep a dummy write descriptor open for the lifetime of openrc-init so that disconnecting a client does not leave the read end hung up. Mark the descriptor close-on-exec so it is replaced normally during reexec. A single O_RDWR descriptor would achieve the same, but POSIX leaves O_RDWR on a FIFO undefined, so use a separate write descriptor.
Regression after: afe3eca ("openrc-init: use poll for waiting on the fifo")