From: Lennart Poettering Date: Thu, 22 Jun 2023 09:51:25 +0000 (+0200) Subject: process-util: add FORK_DETACH flag for forking of detached child X-Git-Tag: v254-rc1~133^2~8 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2e7b105eb9386971f60b9876eede32dff5ed67aa;p=thirdparty%2Fsystemd.git process-util: add FORK_DETACH flag for forking of detached child A test for this is later added indirectly, via aynchronous_rm_rf() that uses this and comes with a suitable test. --- diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 483fc7b1924..437e83bc6d7 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -1195,9 +1195,12 @@ int safe_fork_full( pid_t original_pid, pid; sigset_t saved_ss, ss; _unused_ _cleanup_(restore_sigsetp) sigset_t *saved_ssp = NULL; - bool block_signals = false, block_all = false; + bool block_signals = false, block_all = false, intermediary = false; int prio, r; + assert(!FLAGS_SET(flags, FORK_DETACH) || !ret_pid); + assert(!FLAGS_SET(flags, FORK_DETACH|FORK_WAIT)); + /* A wrapper around fork(), that does a couple of important initializations in addition to mere forking. Always * returns the child's PID in *ret_pid. Returns == 0 in the child, and > 0 in the parent. */ @@ -1231,6 +1234,31 @@ int safe_fork_full( saved_ssp = &saved_ss; } + if (FLAGS_SET(flags, FORK_DETACH)) { + assert(!FLAGS_SET(flags, FORK_WAIT)); + assert(!ret_pid); + + /* Fork off intermediary child if needed */ + + r = is_reaper_process(); + if (r < 0) + return log_full_errno(prio, r, "Failed to determine if we are a reaper process: %m"); + + if (!r) { + /* Not a reaper process, hence do a double fork() so we are reparented to one */ + + pid = fork(); + if (pid < 0) + return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name)); + if (pid > 0) { + log_debug("Successfully forked off intermediary '%s' as PID " PID_FMT ".", strna(name), pid); + return 1; /* return in the parent */ + } + + intermediary = true; + } + } + if ((flags & (FORK_NEW_MOUNTNS|FORK_NEW_USERNS)) != 0) pid = raw_clone(SIGCHLD| (FLAGS_SET(flags, FORK_NEW_MOUNTNS) ? CLONE_NEWNS : 0) | @@ -1240,8 +1268,12 @@ int safe_fork_full( if (pid < 0) return log_full_errno(prio, errno, "Failed to fork off '%s': %m", strna(name)); if (pid > 0) { - /* We are in the parent process */ + /* If we are in the intermediary process, exit now */ + if (intermediary) + _exit(EXIT_SUCCESS); + + /* We are in the parent process */ log_debug("Successfully forked off '%s' as PID " PID_FMT ".", strna(name), pid); if (flags & FORK_WAIT) { diff --git a/src/basic/process-util.h b/src/basic/process-util.h index 41432a85658..1b77478cf5b 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -164,6 +164,7 @@ typedef enum ForkFlags { FORK_NEW_USERNS = 1 << 14, /* Run child in its own user namespace 💣 DO NOT USE IN THREADED PROGRAMS! 💣 */ FORK_CLOEXEC_OFF = 1 << 15, /* In the child: turn off O_CLOEXEC on all fds in except_fds[] */ FORK_KEEP_NOTIFY_SOCKET = 1 << 16, /* Unless this specified, $NOTIFY_SOCKET will be unset. */ + FORK_DETACH = 1 << 17, /* Double fork if needed to ensure PID1/subreaper is parent */ } ForkFlags; int safe_fork_full(