summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/forkfd/forkfd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/forkfd/forkfd.c')
-rw-r--r--src/3rdparty/forkfd/forkfd.c133
1 files changed, 114 insertions, 19 deletions
diff --git a/src/3rdparty/forkfd/forkfd.c b/src/3rdparty/forkfd/forkfd.c
index a61198a954..51223aef1f 100644
--- a/src/3rdparty/forkfd/forkfd.c
+++ b/src/3rdparty/forkfd/forkfd.c
@@ -25,9 +25,8 @@
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
-# define _POSIX_C_SOURCE 200809L
-# define _XOPEN_SOURCE 700
#endif
+
#include "forkfd.h"
#include <sys/types.h>
@@ -44,6 +43,7 @@
#include <unistd.h>
#ifdef __linux__
+# define HAVE_WAIT4 1
# if defined(__BIONIC__) || (defined(__GLIBC__) && (__GLIBC__ << 8) + __GLIBC_MINOR__ >= 0x207 && \
(!defined(__UCLIBC__) || ((__UCLIBC_MAJOR__ << 16) + (__UCLIBC_MINOR__ << 8) + __UCLIBC_SUBLEVEL__ > 0x921)))
# include <sys/eventfd.h>
@@ -54,6 +54,9 @@
# define HAVE_PIPE2 1
# endif
#endif
+#if defined(__FreeBSD__) && __FreeBSD__ >= 9
+# include <sys/procdesc.h>
+#endif
#if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500
# define HAVE_WAITID 1
@@ -62,6 +65,10 @@
#if defined(__FreeBSD__)
# define HAVE_PIPE2 1
#endif
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) || \
+ defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
+# define HAVE_WAIT4 1
+#endif
#if defined(__APPLE__)
/* Up until OS X 10.7, waitid(P_ALL, ...) will return success, but will not
@@ -194,12 +201,33 @@ static int isChildReady(pid_t pid, siginfo_t *info)
}
#endif
+static void convertStatusToForkfdInfo(int status, struct forkfd_info *info)
+{
+ if (WIFEXITED(status)) {
+ info->code = CLD_EXITED;
+ info->status = WEXITSTATUS(status);
+ } else if (WIFSIGNALED(status)) {
+ info->code = CLD_KILLED;
+# ifdef WCOREDUMP
+ if (WCOREDUMP(status))
+ info->code = CLD_DUMPED;
+# endif
+ info->status = WTERMSIG(status);
+ }
+}
+
static int tryReaping(pid_t pid, struct pipe_payload *payload)
{
/* reap the child */
-#ifdef HAVE_WAITID
+#if defined(HAVE_WAIT4)
+ int status;
+ if (wait4(pid, &status, WNOHANG, &payload->rusage) <= 0)
+ return 0;
+ convertStatusToForkfdInfo(status, &payload->info);
+#else
+# if defined(HAVE_WAITID)
if (waitid_works) {
- // we have waitid(2), which fills in siginfo_t for us
+ /* we have waitid(2), which gets us some payload values on some systems */
siginfo_t info;
info.si_pid = 0;
int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG) == 0 && info.si_pid == pid;
@@ -208,30 +236,20 @@ static int tryReaping(pid_t pid, struct pipe_payload *payload)
payload->info.code = info.si_code;
payload->info.status = info.si_status;
-# ifdef __linux__
+# ifdef __linux__
payload->rusage.ru_utime.tv_sec = info.si_utime / CLOCKS_PER_SEC;
payload->rusage.ru_utime.tv_usec = info.si_utime % CLOCKS_PER_SEC;
payload->rusage.ru_stime.tv_sec = info.si_stime / CLOCKS_PER_SEC;
payload->rusage.ru_stime.tv_usec = info.si_stime % CLOCKS_PER_SEC;
-# endif
+# endif
return 1;
}
-#endif
+# endif // HAVE_WAITID
int status;
if (waitpid(pid, &status, WNOHANG) <= 0)
return 0; // child did not change state
-
- if (WIFEXITED(status)) {
- payload->info.code = CLD_EXITED;
- payload->info.status = WEXITSTATUS(status);
- } else if (WIFSIGNALED(status)) {
- payload->info.code = CLD_KILLED;
-# ifdef WCOREDUMP
- if (WCOREDUMP(status))
- payload->info.code = CLD_DUMPED;
-# endif
- payload->info.status = WTERMSIG(status);
- }
+ convertStatusToForkfdInfo(status, &payload->info);
+#endif // !HAVE_WAIT4
return 1;
}
@@ -491,6 +509,55 @@ static int create_pipe(int filedes[], int flags)
return ret;
}
+#if defined(FORKFD_NO_SPAWNFD) && defined(__FreeBSD__) && __FreeBSD__ >= 9
+# if __FreeBSD__ == 9
+/* PROCDESC is an optional feature in the kernel and wasn't enabled
+ * by default on FreeBSD 9. So we need to check for it at runtime. */
+static ffd_atomic_int system_has_forkfd = FFD_ATOMIC_INIT(1);
+# else
+/* On FreeBSD 10, PROCDESC was enabled by default. On v11, it's not an option
+ * anymore and can't be disabled. */
+static const int system_has_forkfd = 1;
+# endif
+
+static int system_forkfd(int flags, pid_t *ppid)
+{
+ int ret;
+ pid_t pid;
+ pid = pdfork(&ret, PD_DAEMON);
+ if (__builtin_expect(pid == -1, 0)) {
+# if __FreeBSD__ == 9
+ if (errno == ENOSYS) {
+ /* PROCDESC wasn't compiled into the kernel: don't try it again. */
+ ffd_atomic_store(&system_has_forkfd, 0, FFD_ATOMIC_RELAXED);
+ }
+# endif
+ return -1;
+ }
+ if (pid == 0) {
+ /* child process */
+ return FFD_CHILD_PROCESS;
+ }
+
+ /* parent process */
+ if (flags & FFD_CLOEXEC)
+ fcntl(ret, F_SETFD, FD_CLOEXEC);
+ if (flags & FFD_NONBLOCK)
+ fcntl(ret, F_SETFL, fcntl(ret, F_GETFL) | O_NONBLOCK);
+ if (ppid)
+ *ppid = pid;
+ return ret;
+}
+#else
+static const int system_has_forkfd = 0;
+static int system_forkfd(int flags, pid_t *ppid)
+{
+ (void)flags;
+ (void)ppid;
+ return -1;
+}
+#endif
+
#ifndef FORKFD_NO_FORKFD
/**
* @brief forkfd returns a file descriptor representing a child process
@@ -538,6 +605,12 @@ int forkfd(int flags, pid_t *ppid)
int efd;
#endif
+ if (system_has_forkfd) {
+ ret = system_forkfd(flags, ppid);
+ if (system_has_forkfd)
+ return ret;
+ }
+
(void) pthread_once(&forkfd_initialization, forkfd_initialize);
info = allocateInfo(&header);
@@ -661,6 +734,8 @@ int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_act
/* we can only do work if we have a way to start the child in stopped mode;
* otherwise, we have a major race condition. */
+ assert(!system_has_forkfd);
+
(void) pthread_once(&forkfd_initialization, forkfd_initialize);
info = allocateInfo(&header);
@@ -717,6 +792,26 @@ int forkfd_wait(int ffd, forkfd_info *info, struct rusage *rusage)
struct pipe_payload payload;
int ret;
+ if (system_has_forkfd) {
+#if defined(__FreeBSD__) && __FreeBSD__ >= 9
+ pid_t pid;
+ int status;
+ int options = WEXITED;
+
+ ret = pdgetpid(ffd, &pid);
+ if (ret == -1)
+ return ret;
+ ret = fcntl(ffd, F_GETFL);
+ if (ret == -1)
+ return ret;
+ options |= (ret & O_NONBLOCK) ? WNOHANG : 0;
+ ret = wait4(pid, &status, options, rusage);
+ if (ret != -1 && info)
+ convertStatusToForkfdInfo(status, info);
+ return ret == -1 ? -1 : 0;
+#endif
+ }
+
ret = read(ffd, &payload, sizeof(payload));
if (ret == -1)
return ret; /* pass errno, probably EINTR, EBADF or EWOULDBLOCK */