summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-05-10 13:12:53 +0200
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2021-07-20 18:15:27 +0200
commit1657a0483f655ff1e01c2acc112ac433a88924ea (patch)
treed0541d71f4a877f91bc24c1f1b71153fd6228cf8
parentb7d39dcbabd27b6c3970e580edcb7d2656f9236c (diff)
downloadsystemd-1657a0483f655ff1e01c2acc112ac433a88924ea.tar.gz
core/service: rework management of exec_fd event source
The code in service_spawn() was written as if exec_fd_event_source was always unset. (We would either fail the assertion that is moved in the patch, or leak the event source object if it was set.) To make this work, let's always assert that exec_fd_event_source is unset, and actually unset it service_sigchld_event(). I think this is the most elegant approach. The problem is that we don't have the same information about execution flags as in service_spawn(), so we need to conditionalize on pid==main_pid to know if we should disable exec_fd_event_source. I think this matches all cases where we may set exec_fd_event_source: service_enter_start() and service_run_next_main(). service_enter_stop_post() calls service_set_state(), which will also destroy the source. But that happens too late, because from service_enter_stop_post() we call service_spawn() first, and then service_set_state() second. (An alternative approach would be to deallocate the existing exec_fd_event_source in service_spawn(). But this would mean that we would temporarily have an event source attached to a process that we already know is dead, which seems less than ideal.) Original report from Dimitri John Ledkov <dimitri.ledkov@canonical.com>: > Ubuntu private bug reference for this issue at the moment is > https://bugs.launchpad.net/ubuntu/+source/systemd/+bug/1921145 > Michael's and Ian's team run into an issue when using systemd in the > initrd, without dbus daemon running, and launching a unit in a > particular way that appears to lock up systemd (pid 1) it self. > michael vogt: "The attached script works for me to reproduce this on > classic. I tested 20.04 (245) and 21.04 (247) in a qemu VM. Sometimes > I need to run it multiple times but usually it crashes after at most 2 > runs. Use "journalctl | tail" to see the messages, it's the same that > Ian reported. There is also a /var/crash/_usr_lib_systemd_systemd > crash file created." > I understand that the particular way to run a unit is very odd, > however, it is currently possible to invoke, and it would be expected > for pid1 to not lock up and crash. > The Assertion that systemd hits is along the lines of: > [ 10.182627] systemd[1]: Assertion 's' failed at > src/core/service.c:3204, function service_dispatch_exec_io(). > Aborting. > [ 10.195458] systemd[1]: Caught <ABRT>, dumped core as pid 449. > [ 10.204446] systemd[1]: Freezing execution. (cherry picked from commit bc989831e634123c2ff43bcbbeae19097ccc9ff9) (cherry picked from commit 493c5c7bab9713afcd647dada885bed68b9d3cf3) (cherry picked from commit 68fcea49fb630fe2475d6fb0220c9330c58e7c36)
-rw-r--r--src/core/service.c11
1 files changed, 8 insertions, 3 deletions
diff --git a/src/core/service.c b/src/core/service.c
index 33be3d93f8..ed6acc2a6b 100644
--- a/src/core/service.c
+++ b/src/core/service.c
@@ -1380,7 +1380,7 @@ static int service_allocate_exec_fd_event_source(
if (r < 0)
return log_unit_error_errno(UNIT(s), r, "Failed to adjust priority of exec_fd event source: %m");
- (void) sd_event_source_set_description(source, "service event_fd");
+ (void) sd_event_source_set_description(source, "service exec_fd");
r = sd_event_source_set_io_fd_own(source, true);
if (r < 0)
@@ -1462,6 +1462,8 @@ static int service_spawn(
if (r < 0)
return r;
+ assert(!s->exec_fd_event_source);
+
if (flags & EXEC_IS_CONTROL) {
/* If this is a control process, mask the permissions/chroot application if this is requested. */
if (s->permissions_start_only)
@@ -1487,8 +1489,6 @@ static int service_spawn(
}
if (!FLAGS_SET(flags, EXEC_IS_CONTROL) && s->type == SERVICE_EXEC) {
- assert(!s->exec_fd_event_source);
-
r = service_allocate_exec_fd(s, &exec_fd_source, &exec_params.exec_fd);
if (r < 0)
return r;
@@ -3405,6 +3405,11 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) {
else
clean_mode = EXIT_CLEAN_DAEMON;
+ if (s->main_pid == pid)
+ /* Clean up the exec_fd event source. The source owns its end of the pipe, so this will close
+ * that too. */
+ s->exec_fd_event_source = sd_event_source_disable_unref(s->exec_fd_event_source);
+
if (is_clean_exit(code, status, clean_mode, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)