]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/process-util.c
core: add support for pidfd_spawn
[thirdparty/systemd.git] / src / basic / process-util.c
index 701eea673c5aa38b4bc3ff2e0e917471b58a529e..ebf3344bf1b8b31c8f3165fa6bf73980ededc9d3 100644 (file)
@@ -2034,10 +2034,12 @@ int make_reaper_process(bool b) {
         return 0;
 }
 
-int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp, PidRef *ret_pidref) {
+DEFINE_TRIVIAL_CLEANUP_FUNC_FULL(posix_spawnattr_t*, posix_spawnattr_destroy, NULL);
+
+int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp, const char *cgroup, PidRef *ret_pidref) {
+        short flags = POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF;
         posix_spawnattr_t attr;
         sigset_t mask;
-        pid_t pid;
         int r;
 
         /* Forks and invokes 'path' with 'argv' and 'envp' using CLONE_VM and CLONE_VFORK, which means the
@@ -2054,26 +2056,78 @@ int posix_spawn_wrapper(const char *path, char *const *argv, char *const *envp,
         r = posix_spawnattr_init(&attr);
         if (r != 0)
                 return -r; /* These functions return a positive errno on failure */
-        /* Set all signals to SIG_DFL */
-        r = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK|POSIX_SPAWN_SETSIGDEF);
+
+        /* Initialization needs to succeed before we can set up a destructor. */
+        _unused_ _cleanup_(posix_spawnattr_destroyp) posix_spawnattr_t *attr_destructor = &attr;
+
+#if HAVE_PIDFD_SPAWN
+        _cleanup_close_ int cgroup_fd = -EBADF;
+
+        if (cgroup) {
+                _cleanup_free_ char *resolved_cgroup = NULL;
+
+                r = cg_get_path_and_check(
+                                SYSTEMD_CGROUP_CONTROLLER,
+                                cgroup,
+                                /* suffix= */ NULL,
+                                &resolved_cgroup);
+                if (r < 0)
+                        return r;
+
+                cgroup_fd = open(resolved_cgroup, O_PATH|O_DIRECTORY|O_CLOEXEC);
+                if (cgroup_fd < 0)
+                        return -errno;
+
+                r = posix_spawnattr_setcgroup_np(&attr, cgroup_fd);
+                if (r != 0)
+                        return -r;
+
+                flags |= POSIX_SPAWN_SETCGROUP;
+        }
+#endif
+
+        r = posix_spawnattr_setflags(&attr, flags);
         if (r != 0)
-                goto fail;
+                return -r;
         r = posix_spawnattr_setsigmask(&attr, &mask);
         if (r != 0)
-                goto fail;
+                return -r;
 
-        r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+#if HAVE_PIDFD_SPAWN
+        _cleanup_close_ int pidfd = -EBADF;
+
+        r = pidfd_spawn(&pidfd, path, NULL, &attr, argv, envp);
+        if (r == 0) {
+                r = pidref_set_pidfd_consume(ret_pidref, TAKE_FD(pidfd));
+                if (r < 0)
+                        return r;
+
+                if (cgroup_fd == -EBADF)
+                        return 0; /* We managed to use the new API but we are running under cgroupv1 */
+
+                return 1; /* We managed to use the new API and we are already in the new cgroup */
+        }
+        if (!(ERRNO_IS_NOT_SUPPORTED(r) || ERRNO_IS_PRIVILEGE(r)))
+                return -r;
+
+        /* Compiled on a newer host, or seccomp&friends blocking clone3()? Fallback, but need to change the
+         * flags to remove the cgroup one, which is what redirects to clone3() */
+        flags &= ~POSIX_SPAWN_SETCGROUP;
+        r = posix_spawnattr_setflags(&attr, flags);
         if (r != 0)
-                goto fail;
+                return -r;
+#endif
 
-        posix_spawnattr_destroy(&attr);
+        pid_t pid;
+        r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
+        if (r != 0)
+                return -r;
 
-        return pidref_set_pid(ret_pidref, pid);
+        r = pidref_set_pid(ret_pidref, pid);
+        if (r < 0)
+                return r;
 
-fail:
-        assert(r > 0);
-        posix_spawnattr_destroy(&attr);
-        return -r;
+        return 0; /* We did not use CLONE_INTO_CGROUP so return 0, the caller will have to move the child */
 }
 
 int proc_dir_open(DIR **ret) {