@@ -386,6 +386,12 @@ following flags:
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).
+
+@item PIDFDFORK_NOSIGCHLD
+Do not send a @code{SIGCHLD} termination signal when child terminates.
+@strong{NB:} When using this flag, the parent process must specify the
+@code{__WALL} or @code{__WCLONE} options when waiting for the child with
+@code{wait} or @code{waitid}.
@end table
@end deftypefun
@@ -22,7 +22,7 @@
pid_t
_Fork (void)
{
- pid_t pid = arch_fork (0, NULL, &THREAD_SELF->tid);
+ pid_t pid = arch_fork (SIGCHLD, NULL, &THREAD_SELF->tid);
if (pid == 0)
{
struct pthread *self = THREAD_SELF;
@@ -35,7 +35,7 @@ static inline pid_t
arch_fork (int flags, void *ptid, void *ctid)
{
long int ret;
- flags |= CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD;
+ flags |= CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID;
#ifdef __ASSUME_CLONE_BACKWARDS
# ifdef INLINE_CLONE_SYSCALL
ret = INLINE_CLONE_SYSCALL (flags, 0, ptid, 0, ctid);
@@ -23,9 +23,10 @@
#include <sys/pidfd.h>
static pid_t
-forkfd (int *pidfd)
+forkfd (int *pidfd, bool nosigchld)
{
- int flags = pidfd == NULL ? 0 : CLONE_PIDFD;
+ int flags = (pidfd == NULL ? 0 : CLONE_PIDFD)
+ | (nosigchld ? 0 : SIGCHLD);
pid_t pid = arch_fork (flags, pidfd, &THREAD_SELF->tid);
if (pid == 0)
{
@@ -49,9 +50,11 @@ pidfd_fork (int *pidfd, unsigned int flags)
if (!__clone_pidfd_supported ())
return INLINE_SYSCALL_ERROR_RETURN_VALUE (ENOSYS);
- if (flags & ~(PIDFDFORK_ASYNCSAFE))
+ if (flags & ~(PIDFDFORK_ASYNCSAFE | PIDFDFORK_NOSIGCHLD))
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
+ bool nosigchld = flags & PIDFDFORK_NOSIGCHLD;
+
pid_t pid;
if (!(flags & PIDFDFORK_ASYNCSAFE))
{
@@ -62,7 +65,7 @@ pidfd_fork (int *pidfd, unsigned int flags)
struct nss_database_data nss_database_data;
state.lastrun = __fork_pre (multiple_threads, &nss_database_data);
- state.pid = forkfd (pidfd);
+ state.pid = forkfd (pidfd, nosigchld);
/* 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);
@@ -70,7 +73,7 @@ pidfd_fork (int *pidfd, unsigned int flags)
pid = state.pid;
}
else
- pid = forkfd (pidfd);
+ pid = forkfd (pidfd, nosigchld);
return pid;
}
@@ -49,6 +49,8 @@ extern int pidfd_send_signal (int __pidfd, int __sig, siginfo_t *__info,
/* Do not issue the pthread_atfork on pidfd_fork. */
#define PIDFDFORK_ASYNCSAFE (1U << 1)
+/* Do not send a SIGCHLD termination signal. */
+#define PIDFDFORK_NOSIGCHLD (1U << 2)
/* Clone the calling process, creating an exact copy and return a file
descriptor that can be used along other pidfd functions.
@@ -24,6 +24,7 @@
#include <support/check.h>
#include <support/temp_file.h>
#include <support/xunistd.h>
+#include <support/xsignal.h>
#include <sys/pidfd.h>
#include <sys/wait.h>
@@ -33,6 +34,14 @@ static bool atfork_prepare_var;
static bool atfork_parent_var;
static bool atfork_child_var;
+static sig_atomic_t sigchld_called;
+
+static void
+sigchld_handler (int sig)
+{
+ sigchld_called = 1;
+}
+
static void
atfork_prepare (void)
{
@@ -69,6 +78,9 @@ singlethread_test (unsigned int flags, bool wait_with_pid)
off_t off = xlseek (tempfd, 0, SEEK_CUR);
TEST_COMPARE (off, testdatalen1);
+ bool check_nosigchld = flags & PIDFDFORK_NOSIGCHLD;
+ sigchld_called = 0;
+
int pidfd;
pid_t pid = pidfd_fork (&pidfd, flags);
TEST_VERIFY_EXIT (pid != -1);
@@ -100,13 +112,18 @@ singlethread_test (unsigned int flags, bool wait_with_pid)
{
siginfo_t sinfo;
+ int options = WEXITED | (check_nosigchld ? __WCLONE : 0);
if (wait_with_pid)
- TEST_COMPARE (waitid (P_PID, pid, &sinfo, WEXITED), 0);
+ TEST_COMPARE (waitid (P_PID, pid, &sinfo, options), 0);
else
- TEST_COMPARE (waitid (P_PIDFD, pidfd, &sinfo, WEXITED), 0);
+ TEST_COMPARE (waitid (P_PIDFD, pidfd, &sinfo, options), 0);
TEST_COMPARE (sinfo.si_signo, SIGCHLD);
TEST_COMPARE (sinfo.si_code, CLD_EXITED);
TEST_COMPARE (sinfo.si_status, 0);
+
+ /* If PIDFDFORK_NOSIGCHLD is specified no SIGCHLD should be sent by the
+ kernel. */
+ TEST_COMPARE (sigchld_called, check_nosigchld ? 0 : 1);
}
TEST_COMPARE (xlseek (tempfd, 0, SEEK_CUR), testdatalen2);
@@ -140,6 +157,14 @@ do_test (void)
TEST_COMPARE (sinfo.si_status, 0);
}
+ {
+ struct sigaction sa;
+ sa.sa_handler = sigchld_handler;
+ sa.sa_flags = 0;
+ sigemptyset (&sa.sa_mask);
+ xsigaction (SIGCHLD, &sa, NULL);
+ }
+
pthread_atfork (atfork_prepare, atfork_parent, atfork_child);
/* With default flags, pidfd_fork acts as fork and run the pthread_atfork
@@ -161,6 +186,14 @@ do_test (void)
TEST_VERIFY (!atfork_child_var);
}
+ {
+ atfork_prepare_var = atfork_parent_var = atfork_child_var = false;
+ singlethread_test (PIDFDFORK_NOSIGCHLD, false);
+ 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;
@@ -180,6 +213,14 @@ do_test (void)
TEST_VERIFY (!atfork_child_var);
}
+ {
+ atfork_prepare_var = atfork_parent_var = atfork_child_var = false;
+ singlethread_test (PIDFDFORK_NOSIGCHLD | PIDFDFORK_ASYNCSAFE, true);
+ TEST_VERIFY (!atfork_prepare_var);
+ TEST_VERIFY (!atfork_parent_var);
+ TEST_VERIFY (!atfork_child_var);
+ }
+
return 0;
}