]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
process-util: add FORK_DETACH flag for forking of detached child
authorLennart Poettering <lennart@poettering.net>
Thu, 22 Jun 2023 09:51:25 +0000 (11:51 +0200)
committerLennart Poettering <lennart@poettering.net>
Fri, 23 Jun 2023 08:02:15 +0000 (10:02 +0200)
A test for this is later added indirectly, via aynchronous_rm_rf() that
uses this and comes with a suitable test.

src/basic/process-util.c
src/basic/process-util.h

index 483fc7b19241b5c54731454dec6e6a770e314f7c..437e83bc6d7f2d3f07eca2eee568db454e9345e0 100644 (file)
@@ -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) {
index 41432a8565847e733734e7f95cdc525090ec8f9a..1b77478cf5b24bf0aefd216de64d5fcb09a8cbf1 100644 (file)
@@ -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(