Never run plugins on exit()#1007
Open
funderscore1 wants to merge 2 commits into
Open
Conversation
added 2 commits
May 13, 2026 21:00
On exit, openrc's cleanup function, which is ran on exit(), checks if there are remaining hooks to run (by checking hook_out) and runs plugins. Running plugins means fork()ing and afterwards exit(). Calling exit() more than once like OpenRC does is undefined behavior. glibc seems to not care about this, but musl keeps track of if exit() is getting called >1 times (exit() being called in forks do count), and if it is, it will helpfully hang, as this should not happen. In practise this means, if a plugin (such as plymouth-openrc-plugin) is installed in a musl-based system, a failing `service <...> start` will cause a hang of that command. Remove all the handling of plugin hooks on exit, and instead explicitely run the OUT hooks on most failures, but *before* exit(). Given this plugin system will be replaced in the future anyway, and there aren't many users of it, this slight change of behavior should be fine. Signed-off-by: Ferass El Hafidi <funderscore@postmarketos.org>
Just like openrc-run, the cleanup function in rc.c checks if there are remaining hooks to run (by checking hook_out) and runs plugins. Calling exit() more than once like OpenRC does is undefined behavior. For more details, see the previous commit message. In practise this means, if a plugin (such as plymouth-openrc-plugin) is installed in a musl-based system, 2 scenarios may occur: * A service fails on boot: OpenRC is sort of stuck. If a GUI is running already, it may not look like much has happened, but things like shutting down won't work (would need to use `reboot -f` without going through openrc) * A service fails on shutdown: shutdown just hangs, have to forcibly poweroff. Remove all the handling of plugin hooks on exit, and instead explicitely run the OUT hooks on most failures, but *before* exit(). Signed-off-by: Ferass El Hafidi <funderscore@postmarketos.org>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The commits should be self-explanatory, but I'll recap anyway:
OpenRC has been running plugins in
cleanup()on premature exit, to make sure that all plugin hooks are ran.cleanup()on openrc/openrc-run is anatexitfunction, as in, it will run onexit(). Running plugins means afork(), then letting the plugins do their job, and finallyexit(). At this point this is the secondexit()that has been ran.The problem is that running
exit()more than once is undefined behavior. While glibc did not complain about it for more than 20 years (this bug was around since 2008 in rc: 4ae5aeb), musl gets angry at it. This will make the child process (the plugin) helpfully hang as soon as it's runningexit(), to indicate that this should not happen. A side effect of this is OpenRC(-run) itself also hanging, as it is waiting for the plugin to exit.However the hang only happens if a service fails. If a service succeeds, then no amount of running plugin hooks will be done in
cleanup()(as this kind of thing is only done on failure).This can make a system unbootable or not able to shutdown anymore if a service fails on boot/shutdown.
I've been told that this plugin system will be eventually replaced by a more robust messaging system, but for now, we can stop doing that, and instead just manually call OUT hooks before exit(). This is a slight change in behavior but it shouldn't really matter given the small number of OpenRC plugins.
This issue was found in Alpine edge, initially running
plymouth-openrc-plugin, but any plugin will cause this bug to happen.