@@ -22,6 +22,11 @@ Major new features:
The pidfd functionality avoid the issue of PID reuse with traditional
posix_spawn interface.
+* On Linux, the pidfd_fork has been added. It has a similar semantic
+ as fork or _Fork, where it clones the calling process. However instead
+ of return a process ID, it returns a file descriptor that can be used
+ along other pidfd functions.
+
Deprecated and removed features, and other changes affecting compatibility:
[Add deprecations, removals and changes affecting compatibility here]
@@ -2,6 +2,8 @@
#define _CLONE_INTERNAL_H
#include <clone3.h>
+#include <stdbool.h>
+#include <stdint.h>
/* The clone3 syscall provides a superset of the functionality of the clone
interface. The kernel might extend __CL_ARGS struct in the future, with
@@ -35,6 +37,20 @@ extern int __clone_internal_fallback (struct clone_args *__cl_args,
void *__arg)
attribute_hidden;
+/* Call the clone3/clone syscall with fork semantic (i.e. no stack setting
+ required). The EXTRA_FLAGS define any additional flag to be used besides
+ CLONE_CHILD_SETTID and CLONE_CHILD_CLEARTID, the PIDFD indicates where
+ the process file descriptor (set with CLONE_PIDFD) should be returned,
+ and the CGROUP specifies the cgroupsv2 (set with CLONE_INTO_CGROUP).
+
+ Similar to __clone3_internal, it uses the stick check to avoid re-issue
+ the clone3 syscall if kernel does not support it.
+
+ It does not provide CLONE_INTO_CGROUP/CGROUP fallback if clone3 is not
+ supported, in this case the function returns -1/ENOTSUP. */
+extern int __clone_fork (uint64_t __extra_flags, void *__pidfd, int __cgroup)
+ attribute_hidden;
+
/* Return whether the kernel supports pid file descriptor, including clone
with CLONE_PIDFD and waitid with P_PIDFD. */
extern bool __clone_pidfd_supported (void) attribute_hidden;
@@ -137,12 +137,12 @@ creating a process and making it run another program.
@cindex subprocess
A new processes is created when one of the functions
@code{posix_spawn}, @code{fork}, @code{_Fork}, @code{vfork}, or
-@code{pidfd_spawn} is called. (The @code{system} and @code{popen} also
-create new processes internally.) Due to the name of the @code{fork}
-function, the act of creating a new process is sometimes called
-@dfn{forking} a process. Each new process (the @dfn{child process} or
-@dfn{subprocess}) is allocated a process ID, distinct from the process
-ID of the parent process. @xref{Process Identification}.
+@code{pidfd_spawn}, or @code{pidfd_fork} is called. (The @code{system}
+and @code{popen} also create new processes internally.) Due to the name
+of the @code{fork} function, the act of creating a new process is
+sometimes called @dfn{forking} a process. Each new process (the
+@dfn{child process} or @dfn{subprocess}) is allocated a process ID,
+distinct from the process ID of the parent process. @xref{Process Identification}.
After forking a child process, both the parent and child processes
continue to execute normally. If you want your program to wait for a
@@ -153,10 +153,10 @@ limited information about why the child terminated---for example, its
exit status code.
A newly forked child process continues to execute the same program as
-its parent process, at the point where the @code{fork} or @code{_Fork}
-call returns. You can use the return value from @code{fork} or
-@code{_Fork} to tell whether the program is running in the parent process
-or the child.
+its parent process, at the point where the @code{fork}, @code{_Fork},
+or @code{pidfd_fork} call returns. You can use the return value from
+@code{fork}, @code{_Fork}, or @code{pidfd_fork} to tell whether the
+program is running in the parent process or the child.
@cindex process image
Having several processes run the same program is only occasionally
@@ -362,6 +362,39 @@ the proper precautions for using @code{vfork}, your program will still
work even if the system uses @code{fork} instead.
@end deftypefun
+@deftypefun pid_t pidfd_fork (int *@var{pidfd}, int @var{cgroup}, unsigned int @var{flags})
+@standards{GNU, sys/pidfd.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
+The @code{fork} function is similar to @code{fork} but return a file
+descriptor instead of process ID.
+
+If the operation is sucessful, there are both parent and child processes
+and both see @code{pidfd_fork} return, but with different values: it return
+a value of @code{0} in the child process and returns the child's process ID
+in the parent process.
+
+Also, if the process is correctly created and @code{pidfd} is non @code{NULL}
+the input argument will contain a file descriptor that can be used along other
+pidfd functions (like @code{pidfd_send_signal} or with @code{waitid} along with
+@code{P_PIDFD}.
+
+The @var{cgroup} argument should either -1 or a file descriptor to a cgroup v2
+directory used on process creation. There is no fallback implementation, meaning
+If the kernel does not provide the required support an error is returned.
+
+The @var{flags} argument should be either zero, or the bitwise OR of some of the
+following flags:
+
+@table @code
+@item PIDFDFORK_ASYNCSAFE
+Acts as @code{_Fork}, where it does not invoke any callbacks registered with
+@code{pthread_atfork}, nor does it reset internal state or locks (such as the
+@code{malloc} locks).
+@end table
+@end deftypefun
+
+This function is specific to Linux.
+
@node Executing a File
@section Executing a File
@cindex executing a file
@@ -85,6 +85,7 @@ routines := \
fexecve \
fnmatch \
fork \
+ fork-internal \
fpathconf \
gai_strerror \
get_child_max \
@@ -589,7 +590,7 @@ CFLAGS-execl.os = -fomit-frame-pointer
CFLAGS-execvp.os = -fomit-frame-pointer
CFLAGS-execlp.os = -fomit-frame-pointer
CFLAGS-nanosleep.c += -fexceptions -fasynchronous-unwind-tables
-CFLAGS-fork.c = $(libio-mtsafe) $(config-cflags-wno-ignored-attributes)
+CFLAGS-fork-internal.c = $(libio-mtsafe) $(config-cflags-wno-ignored-attributes)
tstgetopt-ARGS = -a -b -cfoobar --required foobar --optional=bazbug \
--none random --col --color --colour
new file mode 100644
@@ -0,0 +1,127 @@
+/* Internal fork definitions.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <fork.h>
+#include <fork-internal.h>
+#include <ldsodefs.h>
+#include <libio/libioP.h>
+#include <malloc/malloc-internal.h>
+#include <register-atfork.h>
+#include <stdio-lock.h>
+#include <unwind-link.h>
+
+static void
+fresetlockfiles (void)
+{
+ _IO_ITER i;
+
+ for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
+ if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0)
+ _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock));
+}
+
+uint64_t
+__fork_pre (bool multiple_threads, struct nss_database_data *nss_database_data)
+{
+ uint64_t lastrun = __run_prefork_handlers (multiple_threads);
+
+ /* If we are not running multiple threads, we do not have to
+ preserve lock state. If fork runs from a signal handler, only
+ async-signal-safe functions can be used in the child. These data
+ structures are only used by unsafe functions, so their state does
+ not matter if fork was called from a signal handler. */
+ if (multiple_threads)
+ {
+ call_function_static_weak (__nss_database_fork_prepare_parent,
+ nss_database_data);
+
+ _IO_list_lock ();
+
+ /* Acquire malloc locks. This needs to come last because fork
+ handlers may use malloc, and the libio list lock has an
+ indirect malloc dependency as well (via the getdelim
+ function). */
+ call_function_static_weak (__malloc_fork_lock_parent);
+ }
+
+ return lastrun;
+}
+
+void
+__fork_post (struct fork_post_state_t *state,
+ struct nss_database_data *nss_database_data)
+{
+ if (state->pid == 0)
+ {
+ fork_system_setup ();
+
+ /* Reset the lock state in the multi-threaded case. */
+ if (state->multiple_threads)
+ {
+ __libc_unwind_link_after_fork ();
+
+ fork_system_setup_after_fork ();
+
+ /* Release malloc locks. */
+ call_function_static_weak (__malloc_fork_unlock_child);
+
+ /* Reset the file list. These are recursive mutexes. */
+ fresetlockfiles ();
+
+ /* Reset locks in the I/O code. */
+ _IO_list_resetlock ();
+
+ call_function_static_weak (__nss_database_fork_subprocess,
+ nss_database_data);
+ }
+
+ /* Reset the lock the dynamic loader uses to protect its data. */
+ __rtld_lock_initialize (GL(dl_load_lock));
+
+ /* Reset the lock protecting dynamic TLS related data. */
+ __rtld_lock_initialize (GL(dl_load_tls_lock));
+
+ reclaim_stacks ();
+
+ /* Run the handlers registered for the child. */
+ __run_postfork_handlers (atfork_run_child, state->multiple_threads,
+ state->lastrun);
+ }
+ else
+ {
+ /* If _Fork failed, preserve its errno value. */
+ int save_errno = errno;
+
+ /* Release acquired locks in the multi-threaded case. */
+ if (state->multiple_threads)
+ {
+ /* Release malloc locks, parent process variant. */
+ call_function_static_weak (__malloc_fork_unlock_parent);
+
+ /* We execute this even if the 'fork' call failed. */
+ _IO_list_unlock ();
+ }
+
+ /* Run the handlers registered for the parent. */
+ __run_postfork_handlers (atfork_run_parent, state->multiple_threads,
+ state->lastrun);
+
+ if (state->pid < 0)
+ __set_errno (save_errno);
+ }
+}
new file mode 100644
@@ -0,0 +1,36 @@
+/* Internal fork definitions.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _FORK_INTERNAL_H
+#define _FORK_INTERNAL_H
+
+#include <stdint.h>
+#include <nss/nss_database.h>
+
+struct fork_post_state_t
+{
+ bool multiple_threads;
+ pid_t pid;
+ uint64_t lastrun;
+};
+
+uint64_t __fork_pre (bool, struct nss_database_data *) attribute_hidden;
+void __fork_post (struct fork_post_state_t *, struct nss_database_data *)
+ attribute_hidden;
+
+#endif
@@ -16,25 +16,10 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
-#include <fork.h>
-#include <libio/libioP.h>
-#include <ldsodefs.h>
-#include <malloc/malloc-internal.h>
-#include <nss/nss_database.h>
-#include <register-atfork.h>
-#include <stdio-lock.h>
+#include <fork-internal.h>
#include <sys/single_threaded.h>
#include <unwind-link.h>
-
-static void
-fresetlockfiles (void)
-{
- _IO_ITER i;
-
- for (i = _IO_iter_begin(); i != _IO_iter_end(); i = _IO_iter_next(i))
- if ((_IO_iter_file (i)->_flags & _IO_USER_LOCK) == 0)
- _IO_lock_init (*((_IO_lock_t *) _IO_iter_file(i)->_lock));
-}
+#include <unistd.h>
pid_t
__libc_fork (void)
@@ -45,92 +30,18 @@ __libc_fork (void)
requirement for fork (Austin Group tracker issue #62) this is
best effort to make is async-signal-safe at least for single-thread
case. */
- bool multiple_threads = !SINGLE_THREAD_P;
- uint64_t lastrun;
-
- lastrun = __run_prefork_handlers (multiple_threads);
-
+ struct fork_post_state_t state = {
+ .multiple_threads = !SINGLE_THREAD_P
+ };
struct nss_database_data nss_database_data;
- /* If we are not running multiple threads, we do not have to
- preserve lock state. If fork runs from a signal handler, only
- async-signal-safe functions can be used in the child. These data
- structures are only used by unsafe functions, so their state does
- not matter if fork was called from a signal handler. */
- if (multiple_threads)
- {
- call_function_static_weak (__nss_database_fork_prepare_parent,
- &nss_database_data);
-
- _IO_list_lock ();
-
- /* Acquire malloc locks. This needs to come last because fork
- handlers may use malloc, and the libio list lock has an
- indirect malloc dependency as well (via the getdelim
- function). */
- call_function_static_weak (__malloc_fork_lock_parent);
- }
-
- pid_t pid = _Fork ();
-
- if (pid == 0)
- {
- fork_system_setup ();
-
- /* Reset the lock state in the multi-threaded case. */
- if (multiple_threads)
- {
- __libc_unwind_link_after_fork ();
-
- fork_system_setup_after_fork ();
-
- /* Release malloc locks. */
- call_function_static_weak (__malloc_fork_unlock_child);
-
- /* Reset the file list. These are recursive mutexes. */
- fresetlockfiles ();
-
- /* Reset locks in the I/O code. */
- _IO_list_resetlock ();
-
- call_function_static_weak (__nss_database_fork_subprocess,
- &nss_database_data);
- }
-
- /* Reset the lock the dynamic loader uses to protect its data. */
- __rtld_lock_initialize (GL(dl_load_lock));
-
- /* Reset the lock protecting dynamic TLS related data. */
- __rtld_lock_initialize (GL(dl_load_tls_lock));
-
- reclaim_stacks ();
-
- /* Run the handlers registered for the child. */
- __run_postfork_handlers (atfork_run_child, multiple_threads, lastrun);
- }
- else
- {
- /* If _Fork failed, preserve its errno value. */
- int save_errno = errno;
-
- /* Release acquired locks in the multi-threaded case. */
- if (multiple_threads)
- {
- /* Release malloc locks, parent process variant. */
- call_function_static_weak (__malloc_fork_unlock_parent);
-
- /* We execute this even if the 'fork' call failed. */
- _IO_list_unlock ();
- }
+ state.lastrun = __fork_pre (state.multiple_threads, &nss_database_data);
- /* Run the handlers registered for the parent. */
- __run_postfork_handlers (atfork_run_parent, multiple_threads, lastrun);
+ state.pid = _Fork ();
- if (pid < 0)
- __set_errno (save_errno);
- }
+ __fork_post (&state, &nss_database_data);
- return pid;
+ return state.pid;
}
weak_alias (__libc_fork, __fork)
libc_hidden_def (__fork)
@@ -22,7 +22,7 @@
pid_t
_Fork (void)
{
- pid_t pid = arch_fork (&THREAD_SELF->tid);
+ pid_t pid = arch_fork (0, NULL, &THREAD_SELF->tid);
if (pid == 0)
{
struct pthread *self = THREAD_SELF;
@@ -493,6 +493,7 @@ sysdep_headers += \
sysdep_routines += \
getcpu \
oldglob \
+ pidfd_fork \
pidfd_spawn \
pidfd_spawnp \
sched_getcpu \
@@ -503,6 +504,8 @@ sysdep_routines += \
tests += \
tst-affinity \
tst-affinity-pid \
+ tst-pidfd_fork \
+ tst-pidfd_fork-cgroup \
tst-posix_spawn-setsid-pidfd \
tst-spawn-cgroup \
tst-spawn-chdir-pidfd \
@@ -324,6 +324,7 @@ libc {
GLIBC_2.39 {
posix_spawnattr_getcgroup_np;
posix_spawnattr_setcgroup_np;
+ pidfd_fork;
pidfd_spawn;
pidfd_spawnp;
}
@@ -2673,6 +2673,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2782,6 +2782,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2434,6 +2434,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -32,24 +32,24 @@
override it with one of the supported calling convention (check generic
kernel-features.h for the clone abi variants). */
static inline pid_t
-arch_fork (void *ctid)
+arch_fork (int flags, void *ptid, void *ctid)
{
- const int flags = CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD;
long int ret;
+ flags |= CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD;
#ifdef __ASSUME_CLONE_BACKWARDS
# ifdef INLINE_CLONE_SYSCALL
- ret = INLINE_CLONE_SYSCALL (flags, 0, NULL, 0, ctid);
+ ret = INLINE_CLONE_SYSCALL (flags, 0, ptid, 0, ctid);
# else
- ret = INLINE_SYSCALL_CALL (clone, flags, 0, NULL, 0, ctid);
+ ret = INLINE_SYSCALL_CALL (clone, flags, 0, ptid, 0, ctid);
# endif
#elif defined(__ASSUME_CLONE_BACKWARDS2)
- ret = INLINE_SYSCALL_CALL (clone, 0, flags, NULL, ctid, 0);
+ ret = INLINE_SYSCALL_CALL (clone, 0, flags, ptid, ctid, 0);
#elif defined(__ASSUME_CLONE_BACKWARDS3)
- ret = INLINE_SYSCALL_CALL (clone, flags, 0, 0, NULL, ctid, 0);
+ ret = INLINE_SYSCALL_CALL (clone, flags, 0, 0, ptid, ctid, 0);
#elif defined(__ASSUME_CLONE2)
- ret = INLINE_SYSCALL_CALL (clone2, flags, 0, 0, NULL, ctid, 0);
+ ret = INLINE_SYSCALL_CALL (clone2, flags, 0, 0, ptid, ctid, 0);
#elif defined(__ASSUME_CLONE_DEFAULT)
- ret = INLINE_SYSCALL_CALL (clone, flags, 0, NULL, ctid, 0);
+ ret = INLINE_SYSCALL_CALL (clone, flags, 0, ptid, ctid, 0);
#else
# error "Undefined clone variant"
#endif
@@ -554,6 +554,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -551,6 +551,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -16,6 +16,7 @@
License along with the GNU C Library. If not, see
<https://www.gnu.org/licenses/>. */
+#include <arch-fork.h>
#include <sysdep.h>
#include <stddef.h>
#include <errno.h>
@@ -43,6 +44,11 @@ _Static_assert (offsetofend (struct clone_args, cgroup) == CLONE_ARGS_SIZE_VER2,
_Static_assert (sizeof (struct clone_args) == CLONE_ARGS_SIZE_VER2,
"sizeof (struct clone_args) != CLONE_ARGS_SIZE_VER2");
+#if !defined __ASSUME_CLONE3 && defined __NR_clone3
+/* Set to 0 if kernel does not support clone3 syscall. */
+static int clone3_supported = 1;
+#endif
+
int
__clone_internal_fallback (struct clone_args *cl_args,
int (*func) (void *arg), void *arg)
@@ -81,10 +87,9 @@ __clone3_internal (struct clone_args *cl_args, int (*func) (void *args),
void *arg)
{
#ifdef HAVE_CLONE3_WRAPPER
-# if __ASSUME_CLONE3
+# ifdef __ASSUME_CLONE3
return __clone3 (cl_args, sizeof (*cl_args), func, arg);
# else
- static int clone3_supported = 1;
if (atomic_load_relaxed (&clone3_supported) == 1)
{
int ret = __clone3 (cl_args, sizeof (*cl_args), func, arg);
@@ -118,3 +123,52 @@ __clone_internal (struct clone_args *cl_args,
}
libc_hidden_def (__clone_internal)
+
+int
+__clone_fork (uint64_t extra_flags, void *pidfd, int cgroup)
+{
+#ifdef __NR_clone3
+ struct clone_args clone_args =
+ {
+ .flags = extra_flags
+ | CLONE_CHILD_SETTID
+ | CLONE_CHILD_CLEARTID,
+ .exit_signal = SIGCHLD,
+ .cgroup = cgroup,
+ .child_tid = (uintptr_t) &THREAD_SELF->tid,
+ .pidfd = (uintptr_t) pidfd,
+ .parent_tid = (uintptr_t) pidfd
+ };
+#endif
+
+#ifdef __ASSUME_CLONE3
+ return INLINE_SYSCALL_CALL (clone3, &clone_args, sizeof (clone_args));
+#else
+ /* Some architecture still does not export clone3. */
+ pid_t pid;
+# ifdef __NR_clone3
+ if (atomic_load_relaxed (&clone3_supported) == 1)
+ {
+ pid = INLINE_SYSCALL_CALL (clone3, &clone_args, sizeof (clone_args));
+ if (pid != -1 || errno != ENOSYS)
+ return pid;
+
+ atomic_store_relaxed (&clone3_supported, 0);
+ }
+# endif
+
+ bool set_cgroup = cgroup != -1;
+ bool use_pidfd = pidfd != NULL;
+
+ if (!set_cgroup)
+ pid = arch_fork (use_pidfd ? CLONE_PIDFD : 0, pidfd, &THREAD_SELF->tid);
+ else
+ {
+ /* No fallback for POSIX_SPAWN_SETCGROUP if clone3 is not supported. */
+ pid = -1;
+ if (errno == ENOSYS)
+ errno = ENOTSUP;
+ }
+ return pid;
+#endif
+}
@@ -2710,6 +2710,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2659,6 +2659,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2843,6 +2843,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2608,6 +2608,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2194,6 +2194,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -555,6 +555,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2786,6 +2786,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2759,6 +2759,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2756,6 +2756,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2751,6 +2751,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2749,6 +2749,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2757,6 +2757,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2659,6 +2659,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2798,6 +2798,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2180,6 +2180,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
new file mode 100644
@@ -0,0 +1,81 @@
+/* pidfd_fork - Duplicated calling process and return a process file
+ descriptor.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <clone_internal.h>
+#include <fork-internal.h>
+#include <sys/pidfd.h>
+
+static pid_t
+forkfd (int *pidfd, int cgroup)
+{
+ bool use_pidfd = pidfd != NULL;
+ bool set_cgroup = cgroup != -1;
+
+ uint64_t extra_flags = (use_pidfd ? CLONE_PIDFD : 0)
+ | (set_cgroup ? CLONE_INTO_CGROUP : 0);
+ pid_t pid = __clone_fork (extra_flags, use_pidfd ? pidfd : NULL,
+ set_cgroup ? cgroup: 0);
+
+ if (pid == 0)
+ {
+ struct pthread *self = THREAD_SELF;
+
+ /* Initialize the robust mutex, check _Fork implementation for a full
+ description why this is required. */
+#if __PTHREAD_MUTEX_HAVE_PREV
+ self->robust_prev = &self->robust_head;
+#endif
+ self->robust_head.list = &self->robust_head;
+ INTERNAL_SYSCALL_CALL (set_robust_list, &self->robust_head,
+ sizeof (struct robust_list_head));
+ }
+ return pid;
+}
+
+pid_t
+pidfd_fork (int *pidfd, int cgroup, unsigned int flags)
+{
+ if (!__clone_pidfd_supported ())
+ return INLINE_SYSCALL_ERROR_RETURN_VALUE (ENOSYS);
+
+ if (flags & ~(PIDFDFORK_ASYNCSAFE))
+ return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
+
+ pid_t pid;
+ if (!(flags & PIDFDFORK_ASYNCSAFE))
+ {
+ bool multiple_threads = !SINGLE_THREAD_P;
+ struct fork_post_state_t state = {
+ .multiple_threads = !SINGLE_THREAD_P
+ };
+ struct nss_database_data nss_database_data;
+
+ state.lastrun = __fork_pre (multiple_threads, &nss_database_data);
+ state.pid = forkfd (pidfd, cgroup);
+ /* It follow the usual fork semantic, where a positive or negative
+ value is returned to parent, and 0 for the child. */
+ __fork_post (&state, &nss_database_data);
+
+ pid = state.pid;
+ }
+ else
+ pid = forkfd (pidfd, cgroup);
+
+ return pid;
+}
@@ -2825,6 +2825,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2858,6 +2858,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2579,6 +2579,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2893,6 +2893,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2436,6 +2436,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2636,6 +2636,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2823,6 +2823,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2616,6 +2616,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2666,6 +2666,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2663,6 +2663,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2818,6 +2818,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2631,6 +2631,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -46,4 +46,23 @@ extern int pidfd_getfd (int __pidfd, int __targetfd,
extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
unsigned int __flags) __THROW;
+
+/* Do not issue the pthread_atfork on pidfd_fork. */
+#define PIDFDFORK_ASYNCSAFE (1U << 1)
+
+/* Clone the calling process, creating an exact copy and return a file
+ descriptor that can be used along other pidfd functions.
+
+ THE __CGROUP can be used to specify a different cgroup2 than the default
+ one. This is done with the CLONE_INTO_CGROUP clone3 flag, and passing an
+ value -1 disables it. If clone3 is not supported the call will fail.
+
+ The __FLAGS can be used to specify whether to run pthread_atfork handlers
+ and reset internal states. The default is to run it, similar to fork.
+
+ Return -1 for errors, 0 to the new process, and the process ID of the new
+ process to the parent process. */
+extern pid_t pidfd_fork (int *__pidfd, int __cgroup, unsigned int __flags)
+ __THROW;
+
#endif /* _PIDFD_H */
new file mode 100644
@@ -0,0 +1,162 @@
+/* pidfd_fork test using cgroupsv2.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+#include <sched.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+#include <support/temp_file.h>
+#include <sys/pidfd.h>
+#include <sys/vfs.h>
+#include <sys/wait.h>
+
+#include <dirent.h>
+
+#define CGROUPFS "/sys/fs/cgroup/"
+#ifndef CGROUP2_SUPER_MAGIC
+# define CGROUP2_SUPER_MAGIC 0x63677270
+#endif
+
+#define F_TYPE_EQUAL(a, b) (a == (typeof(a)) b)
+
+static inline char *
+startswith(const char *s, const char *prefix)
+{
+ size_t l = strlen (prefix);
+ if (strncmp (s, prefix, l) == 0)
+ return (char*) s + l;
+ return NULL;
+}
+
+static char *
+get_cgroup (void)
+{
+ FILE *f = xfopen ("/proc/self/cgroup", "re");
+
+ char *cgroup = NULL;
+
+ char *line = NULL;
+ size_t linesiz = 0;
+ while (xgetline (&line, &linesiz, f) > 0)
+ {
+ char *entry = startswith (line, "0:");
+ if (entry == NULL)
+ continue;
+
+ entry = strchr (entry, ':');
+ if (entry == NULL)
+ continue;
+
+ cgroup = entry + 1;
+ size_t l = strlen (cgroup);
+ if (cgroup[l - 1] == '\n')
+ cgroup[l - 1] = '\0';
+
+ cgroup = xstrdup (entry + 1);
+ break;
+ }
+
+ xfclose (f);
+ free (line);
+
+ return cgroup;
+}
+
+static int
+do_test (void)
+{
+ struct statfs fs;
+ if (statfs (CGROUPFS, &fs) < 0)
+ {
+ if (errno == ENOENT)
+ FAIL_UNSUPPORTED ("not cgroupv2 mount found");
+ FAIL_EXIT1 ("statfs (%s): %m\n", CGROUPFS);
+ }
+
+ if (!F_TYPE_EQUAL (fs.f_type, CGROUP2_SUPER_MAGIC))
+ FAIL_UNSUPPORTED ("%s is not a cgroupv2", CGROUPFS);
+
+ char *cgroup = get_cgroup ();
+ TEST_VERIFY_EXIT (cgroup != NULL);
+ char *newcgroup = xasprintf ("%s/%s", cgroup, "test-pidfd_fork-cgroup");
+ char *cgpath = xasprintf ("%s%s/test-pidfd_fork-cgroup", CGROUPFS, cgroup);
+ free (cgroup);
+
+ if (mkdir (cgpath, 0755) == -1 && errno != EEXIST)
+ {
+ if (errno == EACCES || errno == EPERM)
+ FAIL_UNSUPPORTED ("can not create a new cgroupv2 group");
+ FAIL_EXIT1 ("mkdir (%s): %m", cgpath);
+ }
+ add_temp_file (cgpath);
+
+ int dfd = xopen (cgpath, O_DIRECTORY | O_RDONLY | O_CLOEXEC, 0666);
+
+ /* Check if the cgroup used at creation is the same returned by the kernel
+ and not as the parent. */
+ {
+ pid_t pid = pidfd_fork (NULL, dfd, 0);
+ if (pid == -1 && errno == ENOSYS)
+ FAIL_UNSUPPORTED ("kernel does not support CLONE_PIDFD clone flag");
+ TEST_VERIFY_EXIT (pid != -1);
+ if (pid == 0)
+ {
+ char *child_cgroup = get_cgroup ();
+ TEST_VERIFY_EXIT (child_cgroup != NULL);
+ TEST_COMPARE_STRING (newcgroup, child_cgroup);
+ _exit (EXIT_SUCCESS);
+ }
+
+ siginfo_t sinfo;
+ TEST_COMPARE (waitid (P_PID, pid, &sinfo, WEXITED), 0);
+ TEST_COMPARE (sinfo.si_signo, SIGCHLD);
+ TEST_COMPARE (sinfo.si_code, CLD_EXITED);
+ TEST_COMPARE (sinfo.si_status, 0);
+ }
+
+ /* Same as before, but also check along with process file descriptor. */
+ {
+ int pidfd;
+ pid_t pid = pidfd_fork (&pidfd, dfd, 0);
+ TEST_VERIFY_EXIT (pid != -1);
+ if (pid == 0)
+ {
+ char *child_cgroup = get_cgroup ();
+ TEST_VERIFY_EXIT (child_cgroup != NULL);
+ TEST_COMPARE_STRING (newcgroup, child_cgroup);
+ _exit (EXIT_SUCCESS);
+ }
+
+ siginfo_t sinfo;
+ TEST_COMPARE (waitid (P_PIDFD, pidfd, &sinfo, WEXITED), 0);
+ TEST_COMPARE (sinfo.si_signo, SIGCHLD);
+ TEST_COMPARE (sinfo.si_code, CLD_EXITED);
+ TEST_COMPARE (sinfo.si_status, 0);
+ }
+
+ free (cgpath);
+ free (newcgroup);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,186 @@
+/* Basic tests for pidfd_fork.
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <array_length.h>
+#include <errno.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xunistd.h>
+#include <sys/pidfd.h>
+#include <sys/wait.h>
+
+#define SIG_PID_EXIT_CODE 20
+
+static bool atfork_prepare_var;
+static bool atfork_parent_var;
+static bool atfork_child_var;
+
+static void
+atfork_prepare (void)
+{
+ atfork_prepare_var = true;
+}
+
+static void
+atfork_parent (void)
+{
+ atfork_parent_var = true;
+}
+
+static void
+atfork_child (void)
+{
+ atfork_child_var = true;
+}
+
+static int
+singlethread_test (unsigned int flags, bool wait_with_pid)
+{
+ const char testdata1[] = "abcdefghijklmnopqrtuvwxz";
+ enum { testdatalen1 = array_length (testdata1) };
+ const char testdata2[] = "01234567890";
+ enum { testdatalen2 = array_length (testdata2) };
+
+ pid_t ppid = getpid ();
+
+ int tempfd = create_temp_file ("tst-pidfd_fork", NULL);
+
+ /* Check if the opened file is shared between process by read and write
+ some data on parent and child processes. */
+ xwrite (tempfd, testdata1, testdatalen1);
+ off_t off = xlseek (tempfd, 0, SEEK_CUR);
+ TEST_COMPARE (off, testdatalen1);
+
+ int pidfd;
+ pid_t pid = pidfd_fork (&pidfd, -1, flags);
+ TEST_VERIFY_EXIT (pid != -1);
+
+ if (pid == 0)
+ {
+ if (flags & PIDFDFORK_ASYNCSAFE)
+ TEST_VERIFY (!atfork_child_var);
+ else
+ TEST_VERIFY (atfork_child_var);
+
+ TEST_VERIFY_EXIT (getpid () != ppid);
+ TEST_COMPARE (getppid(), ppid);
+
+ TEST_COMPARE (xlseek (tempfd, 0, SEEK_CUR), testdatalen1);
+
+ xlseek (tempfd, 0, SEEK_SET);
+ char buf[testdatalen1];
+ TEST_COMPARE (read (tempfd, buf, sizeof (buf)), testdatalen1);
+ TEST_COMPARE_BLOB (buf, testdatalen1, testdata1, testdatalen1);
+
+ xlseek (tempfd, 0, SEEK_SET);
+ xwrite (tempfd, testdata2, testdatalen2);
+
+ xclose (tempfd);
+
+ _exit (EXIT_SUCCESS);
+ }
+
+ {
+ siginfo_t sinfo;
+ if (wait_with_pid)
+ TEST_COMPARE (waitid (P_PID, pid, &sinfo, WEXITED), 0);
+ else
+ TEST_COMPARE (waitid (P_PIDFD, pidfd, &sinfo, WEXITED), 0);
+ TEST_COMPARE (sinfo.si_signo, SIGCHLD);
+ TEST_COMPARE (sinfo.si_code, CLD_EXITED);
+ TEST_COMPARE (sinfo.si_status, 0);
+ }
+
+ TEST_COMPARE (xlseek (tempfd, 0, SEEK_CUR), testdatalen2);
+
+ xlseek (tempfd, 0, SEEK_SET);
+ char buf[testdatalen2];
+ TEST_COMPARE (read (tempfd, buf, sizeof (buf)), testdatalen2);
+
+ TEST_COMPARE_BLOB (buf, testdatalen2, testdata2, testdatalen2);
+
+ return 0;
+}
+
+static int
+do_test (void)
+{
+ /* Sanity check for pidfd support and check if passing NULL as the argument
+ make pidfd_fork acts as fork. */
+ {
+ pid_t pid = pidfd_fork (NULL, -1, 0);
+ if (pid == -1 && errno == ENOSYS)
+ FAIL_UNSUPPORTED ("kernel does not support CLONE_PIDFD clone flag");
+ TEST_VERIFY_EXIT (pid != -1);
+ if (pid == 0)
+ _exit (EXIT_SUCCESS);
+
+ siginfo_t sinfo;
+ TEST_COMPARE (waitid (P_PID, pid, &sinfo, WEXITED), 0);
+ TEST_COMPARE (sinfo.si_signo, SIGCHLD);
+ TEST_COMPARE (sinfo.si_code, CLD_EXITED);
+ TEST_COMPARE (sinfo.si_status, 0);
+ }
+
+ pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
+
+ /* With default flags, pidfd_fork acts as fork and run the pthread_atfork
+ handlers. */
+ {
+ atfork_prepare_var = atfork_parent_var = atfork_child_var = false;
+ singlethread_test (0, false);
+ TEST_VERIFY (atfork_prepare_var);
+ TEST_VERIFY (atfork_parent_var);
+ TEST_VERIFY (!atfork_child_var);
+ }
+
+ /* Same as before, but also wait using the PID instead of pidfd. */
+ {
+ atfork_prepare_var = atfork_parent_var = atfork_child_var = false;
+ singlethread_test (0, true);
+ TEST_VERIFY (atfork_prepare_var);
+ TEST_VERIFY (atfork_parent_var);
+ TEST_VERIFY (!atfork_child_var);
+ }
+
+ /* With PIDFDFORK_ASYNCSAFE, pidfd_fork acts as _Fork. */
+ {
+ atfork_prepare_var = atfork_parent_var = atfork_child_var = false;
+ pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
+ singlethread_test (PIDFDFORK_ASYNCSAFE, false);
+ TEST_VERIFY (!atfork_prepare_var);
+ TEST_VERIFY (!atfork_parent_var);
+ TEST_VERIFY (!atfork_child_var);
+ }
+
+ {
+ atfork_prepare_var = atfork_parent_var = atfork_child_var = false;
+ pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
+ singlethread_test (PIDFDFORK_ASYNCSAFE, true);
+ TEST_VERIFY (!atfork_prepare_var);
+ TEST_VERIFY (!atfork_parent_var);
+ TEST_VERIFY (!atfork_child_var);
+ }
+
+ return 0;
+}
+
+#include <support/test-driver.c>
@@ -2582,6 +2582,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F
@@ -2688,6 +2688,7 @@ GLIBC_2.38 strlcat F
GLIBC_2.38 strlcpy F
GLIBC_2.38 wcslcat F
GLIBC_2.38 wcslcpy F
+GLIBC_2.39 pidfd_fork F
GLIBC_2.39 pidfd_spawn F
GLIBC_2.39 pidfd_spawnp F
GLIBC_2.39 posix_spawnattr_getcgroup_np F