core: add support for pidfd_spawn
Added in glibc 2.39, allows cloning into a cgroup and to get a pid fd back instead of a pid. Removes race conditions for both changing cgroups and getting a reliable reference for the child process. Fixes https://github.com/systemd/systemd/pull/18843 Replaces https://github.com/systemd/systemd/pull/16706
This commit is contained in:
+68
-14
@@ -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;
|
||||
|
||||
#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)
|
||||
return -r;
|
||||
#endif
|
||||
|
||||
pid_t pid;
|
||||
r = posix_spawn(&pid, path, NULL, &attr, argv, envp);
|
||||
if (r != 0)
|
||||
goto fail;
|
||||
return -r;
|
||||
|
||||
posix_spawnattr_destroy(&attr);
|
||||
r = pidref_set_pid(ret_pidref, pid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return pidref_set_pid(ret_pidref, pid);
|
||||
|
||||
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) {
|
||||
|
||||
Reference in New Issue
Block a user