Skip to content

openrc-init: keep a dummy writer on the control FIFO - #1055

Merged
navi-desu merged 1 commit into
OpenRC:masterfrom
pva:fix-init-busy-loop
Jul 30, 2026
Merged

openrc-init: keep a dummy writer on the control FIFO#1055
navi-desu merged 1 commit into
OpenRC:masterfrom
pva:fix-init-busy-loop

Conversation

@pva

@pva pva commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

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")

@pva

pva commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Note: this can be fixed with a much shorter patch, by just changing O_RDONLY to O_RDWR:

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?

@navi-desu

navi-desu commented Jul 29, 2026

Copy link
Copy Markdown
Member

Note: this can be fixed with a much shorter patch, by just changing O_RDONLY to O_RDWR:

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

Comment thread src/openrc-init/openrc-init.c Outdated
@pva

pva commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

openrc-init is also used on gentoo's hurd port (though with patches that are not upstream yet)

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")
@pva
pva force-pushed the fix-init-busy-loop branch from bd05b18 to 6516ab9 Compare July 29, 2026 21:24
@navi-desu
navi-desu merged commit 5db6a6a into OpenRC:master Jul 30, 2026
5 checks passed
@pva
pva deleted the fix-init-busy-loop branch July 31, 2026 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants