@@ -42,6 +42,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:
* In the Linux kernel for the hppa/parisc architecture some of the
@@ -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,35 @@ 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}, 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{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
@@ -84,6 +84,7 @@ routines := \
fexecve \
fnmatch \
fork \
+ fork-internal \
fpathconf \
gai_strerror \
get_child_max \
@@ -579,7 +580,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;
@@ -489,14 +489,17 @@ sysdep_headers += \
sysdep_routines += \
getcpu \
oldglob \
+ pidfd_fork \
pidfd_spawn \
pidfd_spawnp \
sched_getcpu \
+ sched_getcpu \
# sysdep_routines
tests += \
tst-affinity \
tst-affinity-pid \
+ tst-pidfd_fork \
tst-posix_spawn-setsid-pidfd \
tst-spawn-chdir-pidfd \
tst-spawn-pidfd \
@@ -322,6 +322,7 @@ libc {
%endif
}
GLIBC_2.38 {
+ pidfd_fork;
pidfd_spawn;
pidfd_spawnp;
}
@@ -2665,5 +2665,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2774,6 +2774,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2426,5 +2426,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp 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
@@ -546,6 +546,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _Exit F
@@ -543,6 +543,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _Exit F
@@ -2702,5 +2702,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2835,6 +2835,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2600,6 +2600,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2186,5 +2186,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -547,6 +547,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _Exit F
@@ -2778,6 +2778,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2751,5 +2751,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2748,5 +2748,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2743,6 +2743,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2741,6 +2741,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2749,6 +2749,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2651,6 +2651,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2790,5 +2790,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2172,5 +2172,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
new file mode 100644
@@ -0,0 +1,76 @@
+/* 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 <arch-fork.h>
+#include <clone_internal.h>
+#include <fork-internal.h>
+#include <sys/pidfd.h>
+
+static pid_t
+forkfd (int *pidfd)
+{
+ int flags = pidfd == NULL ? 0 : CLONE_PIDFD;
+ pid_t pid = arch_fork (flags, pidfd, &THREAD_SELF->tid);
+ 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, 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);
+ /* 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);
+
+ return pid;
+}
@@ -2817,6 +2817,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2850,6 +2850,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2571,6 +2571,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2885,5 +2885,6 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2428,5 +2428,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2628,5 +2628,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
@@ -2815,6 +2815,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2608,6 +2608,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2658,6 +2658,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2655,6 +2655,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2810,6 +2810,7 @@ GLIBC_2.38 __nldbl___isoc23_vsscanf F
GLIBC_2.38 __nldbl___isoc23_vswscanf F
GLIBC_2.38 __nldbl___isoc23_vwscanf F
GLIBC_2.38 __nldbl___isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 _IO_fprintf F
@@ -2623,6 +2623,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -46,4 +46,18 @@ 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 __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, unsigned int __flags) __THROW;
+
#endif /* _PIDFD_H */
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, 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, 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>
@@ -2574,6 +2574,7 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F
GLIBC_2.4 __confstr_chk F
@@ -2680,5 +2680,6 @@ GLIBC_2.38 __isoc23_wcstoull F
GLIBC_2.38 __isoc23_wcstoull_l F
GLIBC_2.38 __isoc23_wcstoumax F
GLIBC_2.38 __isoc23_wscanf F
+GLIBC_2.38 pidfd_fork F
GLIBC_2.38 pidfd_spawn F
GLIBC_2.38 pidfd_spawnp F