diff options
Diffstat (limited to 'src/3rdparty/forkfd/forkfd.c')
-rw-r--r-- | src/3rdparty/forkfd/forkfd.c | 133 |
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 */ |