1/****************************************************************************
2**
3** Copyright (C) 2020 Intel Corporation.
4** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
5**
6** Permission is hereby granted, free of charge, to any person obtaining a copy
7** of this software and associated documentation files (the "Software"), to deal
8** in the Software without restriction, including without limitation the rights
9** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10** copies of the Software, and to permit persons to whom the Software is
11** furnished to do so, subject to the following conditions:
12**
13** The above copyright notice and this permission notice shall be included in
14** all copies or substantial portions of the Software.
15**
16** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22** THE SOFTWARE.
23**
24****************************************************************************/
25
26#ifndef _GNU_SOURCE
27# define _GNU_SOURCE
28#endif
29
30#include "forkfd.h"
31
32/* Macros fine-tuning the build: */
33//#define FORKFD_NO_FORKFD 1 /* disable the forkfd() function */
34//#define FORKFD_NO_SPAWNFD 1 /* disable the spawnfd() function */
35//#define FORKFD_DISABLE_FORK_FALLBACK 1 /* disable falling back to fork() from system_forkfd() */
36
37#include <sys/types.h>
38#if defined(__OpenBSD__) || defined(__NetBSD__)
39# include <sys/param.h>
40#endif
41#include <sys/time.h>
42#include <sys/resource.h>
43#include <sys/wait.h>
44#include <assert.h>
45#include <errno.h>
46#include <pthread.h>
47#include <signal.h>
48#include <stdlib.h>
49#include <string.h>
50#include <time.h>
51#include <unistd.h>
52
53#ifdef __linux__
54# define HAVE_WAIT4 1
55# if defined(__BIONIC__) || (defined(__GLIBC__) && (__GLIBC__ << 8) + __GLIBC_MINOR__ >= 0x208 && \
56 (!defined(__UCLIBC__) || ((__UCLIBC_MAJOR__ << 16) + (__UCLIBC_MINOR__ << 8) + __UCLIBC_SUBLEVEL__ > 0x90201)))
57# include <sys/eventfd.h>
58# ifdef EFD_CLOEXEC
59# define HAVE_EVENTFD 1
60# endif
61# endif
62# if defined(__BIONIC__) || (defined(__GLIBC__) && (__GLIBC__ << 8) + __GLIBC_MINOR__ >= 0x209 && \
63 (!defined(__UCLIBC__) || ((__UCLIBC_MAJOR__ << 16) + (__UCLIBC_MINOR__ << 8) + __UCLIBC_SUBLEVEL__ > 0x90201)))
64# define HAVE_PIPE2 1
65# endif
66#endif
67
68#if _POSIX_VERSION-0 >= 200809L || _XOPEN_VERSION-0 >= 500
69# define HAVE_WAITID 1
70#endif
71#if !defined(WEXITED) || !defined(WNOWAIT)
72# undef HAVE_WAITID
73#endif
74
75#if (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1300000)
76# include <sys/eventfd.h>
77# define HAVE_EVENTFD 1
78#endif
79#if (defined(__FreeBSD__) && defined(__FreeBSD_version) && __FreeBSD_version >= 1000032) || \
80 (defined(__OpenBSD__) && OpenBSD >= 201505) || \
81 (defined(__NetBSD__) && __NetBSD_Version__ >= 600000000)
82# define HAVE_PIPE2 1
83#endif
84#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) || \
85 defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__)
86# define HAVE_WAIT4 1
87#endif
88
89#if defined(__APPLE__)
90/* Up until OS X 10.7, waitid(P_ALL, ...) will return success, but will not
91 * fill in the details of the dead child. That means waitid is not useful to us.
92 * Therefore, we only enable waitid() support if we're targetting OS X 10.8 or
93 * later.
94 */
95# include <Availability.h>
96# include <AvailabilityMacros.h>
97# if MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
98# define HAVE_BROKEN_WAITID 1
99# endif
100#endif
101
102#include "forkfd_atomic.h"
103
104static int system_has_forkfd(void);
105static int system_forkfd(int flags, pid_t *ppid, int *system);
106static int system_vforkfd(int flags, pid_t *ppid, int (*)(void *), void *, int *system);
107static int system_forkfd_wait(int ffd, struct forkfd_info *info, int ffdwoptions, struct rusage *rusage);
108
109static int disable_fork_fallback(void)
110{
111#ifdef FORKFD_DISABLE_FORK_FALLBACK
112 /* if there's no system forkfd, we have to use the fallback */
113 return system_has_forkfd();
114#else
115 return 0;
116#endif
117}
118
119#define CHILDREN_IN_SMALL_ARRAY 16
120#define CHILDREN_IN_BIG_ARRAY 256
121#define sizeofarray(array) (sizeof(array)/sizeof(array[0]))
122#define EINTR_LOOP(ret, call) \
123 do { \
124 ret = call; \
125 } while (ret == -1 && errno == EINTR)
126
127struct pipe_payload
128{
129 struct forkfd_info info;
130 struct rusage rusage;
131};
132
133typedef struct process_info
134{
135 ffd_atomic_int pid;
136 int deathPipe;
137} ProcessInfo;
138
139struct BigArray;
140typedef struct Header
141{
142 ffd_atomic_pointer(struct BigArray) nextArray;
143 ffd_atomic_int busyCount;
144} Header;
145
146typedef struct BigArray
147{
148 Header header;
149 ProcessInfo entries[CHILDREN_IN_BIG_ARRAY];
150} BigArray;
151
152typedef struct SmallArray
153{
154 Header header;
155 ProcessInfo entries[CHILDREN_IN_SMALL_ARRAY];
156} SmallArray;
157static SmallArray children;
158
159static struct sigaction old_sigaction;
160static pthread_once_t forkfd_initialization = PTHREAD_ONCE_INIT;
161static ffd_atomic_int forkfd_status = FFD_ATOMIC_INIT(0);
162
163#ifdef HAVE_BROKEN_WAITID
164static int waitid_works = 0;
165#else
166static const int waitid_works = 1;
167#endif
168
169static ProcessInfo *tryAllocateInSection(Header *header, ProcessInfo entries[], int maxCount)
170{
171 /* we use ACQUIRE here because the signal handler might have released the PID */
172 int busyCount = ffd_atomic_add_fetch(&header->busyCount, 1, FFD_ATOMIC_ACQUIRE);
173 if (busyCount <= maxCount) {
174 /* there's an available entry in this section, find it and take it */
175 int i;
176 for (i = 0; i < maxCount; ++i) {
177 /* if the PID is 0, it's free; mark it as used by swapping it with -1 */
178 int expected_pid = 0;
179 if (ffd_atomic_compare_exchange(&entries[i].pid, &expected_pid,
180 -1, FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
181 return &entries[i];
182 }
183 }
184
185 /* there isn't an available entry, undo our increment */
186 (void)ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELAXED);
187 return NULL;
188}
189
190static ProcessInfo *allocateInfo(Header **header)
191{
192 Header *currentHeader = &children.header;
193
194 /* try to find an available entry in the small array first */
195 ProcessInfo *info =
196 tryAllocateInSection(header: currentHeader, entries: children.entries, sizeofarray(children.entries));
197
198 /* go on to the next arrays */
199 while (info == NULL) {
200 BigArray *array = ffd_atomic_load(&currentHeader->nextArray, FFD_ATOMIC_ACQUIRE);
201 if (array == NULL) {
202 /* allocate an array and try to use it */
203 BigArray *allocatedArray = (BigArray *)calloc(nmemb: 1, size: sizeof(BigArray));
204 if (allocatedArray == NULL)
205 return NULL;
206
207 if (ffd_atomic_compare_exchange(&currentHeader->nextArray, &array, allocatedArray,
208 FFD_ATOMIC_RELEASE, FFD_ATOMIC_ACQUIRE)) {
209 /* success */
210 array = allocatedArray;
211 } else {
212 /* failed, the atomic updated 'array' */
213 free(ptr: allocatedArray);
214 }
215 }
216
217 currentHeader = &array->header;
218 info = tryAllocateInSection(header: currentHeader, entries: array->entries, sizeofarray(array->entries));
219 }
220
221 *header = currentHeader;
222 return info;
223}
224
225#ifdef HAVE_WAITID
226static int isChildReady(pid_t pid, siginfo_t *info)
227{
228 info->si_pid = 0;
229 return waitid(idtype: P_PID, id: pid, infop: info, WEXITED | WNOHANG | WNOWAIT) == 0 && info->si_pid == pid;
230}
231#endif
232
233static void convertStatusToForkfdInfo(int status, struct forkfd_info *info)
234{
235 if (WIFEXITED(status)) {
236 info->code = CLD_EXITED;
237 info->status = WEXITSTATUS(status);
238 } else if (WIFSIGNALED(status)) {
239 info->code = CLD_KILLED;
240# ifdef WCOREDUMP
241 if (WCOREDUMP(status))
242 info->code = CLD_DUMPED;
243# endif
244 info->status = WTERMSIG(status);
245 }
246}
247
248#ifdef __GNUC__
249__attribute__((unused))
250#endif
251static int convertForkfdWaitFlagsToWaitFlags(int ffdoptions)
252{
253 int woptions = WEXITED;
254 if (ffdoptions & FFDW_NOWAIT)
255 woptions |= WNOWAIT;
256 if (ffdoptions & FFDW_NOHANG)
257 woptions |= WNOHANG;
258 return woptions;
259}
260
261static int tryReaping(pid_t pid, struct pipe_payload *payload)
262{
263 /* reap the child */
264#if defined(HAVE_WAIT4)
265 int status;
266 if (wait4(pid: pid, stat_loc: &status, WNOHANG, usage: &payload->rusage) <= 0)
267 return 0;
268 convertStatusToForkfdInfo(status, info: &payload->info);
269#else
270# if defined(HAVE_WAITID)
271 if (waitid_works) {
272 /* we have waitid(2), which gets us some payload values on some systems */
273 siginfo_t info;
274 info.si_pid = 0;
275 int ret = waitid(P_PID, pid, &info, WEXITED | WNOHANG) == 0 && info.si_pid == pid;
276 if (!ret)
277 return ret;
278
279 payload->info.code = info.si_code;
280 payload->info.status = info.si_status;
281# ifdef __linux__
282 payload->rusage.ru_utime.tv_sec = info.si_utime / CLOCKS_PER_SEC;
283 payload->rusage.ru_utime.tv_usec = info.si_utime % CLOCKS_PER_SEC;
284 payload->rusage.ru_stime.tv_sec = info.si_stime / CLOCKS_PER_SEC;
285 payload->rusage.ru_stime.tv_usec = info.si_stime % CLOCKS_PER_SEC;
286# endif
287 return 1;
288 }
289# endif // HAVE_WAITID
290 int status;
291 if (waitpid(pid, &status, WNOHANG) <= 0)
292 return 0; // child did not change state
293 convertStatusToForkfdInfo(status, &payload->info);
294#endif // !HAVE_WAIT4
295
296 return 1;
297}
298
299static void freeInfo(Header *header, ProcessInfo *entry)
300{
301 entry->deathPipe = -1;
302 ffd_atomic_store(&entry->pid, 0, FFD_ATOMIC_RELEASE);
303
304 (void)ffd_atomic_add_fetch(&header->busyCount, -1, FFD_ATOMIC_RELEASE);
305 assert(header->busyCount >= 0);
306}
307
308static void notifyAndFreeInfo(Header *header, ProcessInfo *entry,
309 const struct pipe_payload *payload)
310{
311 ssize_t ret;
312 EINTR_LOOP(ret, write(entry->deathPipe, payload, sizeof(*payload)));
313 EINTR_LOOP(ret, close(entry->deathPipe));
314
315 freeInfo(header, entry);
316}
317
318static void reapChildProcesses();
319static void sigchld_handler(int signum, siginfo_t *handler_info, void *handler_context)
320{
321 /*
322 * This is a signal handler, so we need to be careful about which functions
323 * we can call. See the full, official listing in the POSIX.1-2008
324 * specification at:
325 * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
326 *
327 * The handler_info and handler_context parameters may not be valid, if
328 * we're a chained handler from another handler that did not use
329 * SA_SIGINFO. Therefore, we must obtain the siginfo ourselves directly by
330 * calling waitid.
331 *
332 * But we pass them anyway. Let's call the chained handler first, while
333 * those two arguments have a chance of being correct.
334 */
335 if (old_sigaction.sa_handler != SIG_IGN && old_sigaction.sa_handler != SIG_DFL) {
336 if (old_sigaction.sa_flags & SA_SIGINFO)
337 old_sigaction.sa_sigaction(signum, handler_info, handler_context);
338 else
339 old_sigaction.sa_handler(signum);
340 }
341
342 if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 1) {
343 int saved_errno = errno;
344 reapChildProcesses();
345 errno = saved_errno;
346 }
347}
348
349static inline void reapChildProcesses()
350{
351 /* is this one of our children? */
352 BigArray *array;
353 siginfo_t info;
354 struct pipe_payload payload;
355 int i;
356
357 memset(s: &info, c: 0, n: sizeof info);
358 memset(s: &payload, c: 0, n: sizeof payload);
359
360#ifdef HAVE_WAITID
361 if (waitid_works) {
362 /* be optimistic: try to see if we can get the child that exited */
363search_next_child:
364 /* waitid returns -1 ECHILD if there are no further children at all;
365 * it returns 0 and sets si_pid to 0 if there are children but they are not ready
366 * to be waited (we're passing WNOHANG). We should not get EINTR because
367 * we're passing WNOHANG and we should definitely not get EINVAL or anything else.
368 * That means we can actually ignore the return code and only inspect si_pid.
369 */
370 info.si_pid = 0;
371 waitid(idtype: P_ALL, id: 0, infop: &info, WNOHANG | WNOWAIT | WEXITED);
372 if (info.si_pid == 0) {
373 /* there are no further un-waited-for children, so we can just exit.
374 */
375 return;
376 }
377
378 for (i = 0; i < (int)sizeofarray(children.entries); ++i) {
379 /* acquire the child first: swap the PID with -1 to indicate it's busy */
380 int pid = info.si_pid;
381 if (ffd_atomic_compare_exchange(&children.entries[i].pid, &pid, -1,
382 FFD_ATOMIC_ACQUIRE, FFD_ATOMIC_RELAXED)) {
383 /* this is our child, send notification and free up this entry */
384 /* ### FIXME: what if tryReaping returns false? */
385 if (tryReaping(pid, payload: &payload))
386 notifyAndFreeInfo(header: &children.header, entry: &children.entries[i], payload: &payload);
387 goto search_next_child;
388 }
389 }
390
391 /* try the arrays */
392 array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
393 while (array != NULL) {
394 for (i = 0; i < (int)sizeofarray(array->entries); ++i) {
395 int pid = info.si_pid;
396 if (ffd_atomic_compare_exchange(&array->entries[i].pid, &pid, -1,
397 FFD_ATOMIC_ACQUIRE, FFD_ATOMIC_RELAXED)) {
398 /* this is our child, send notification and free up this entry */
399 /* ### FIXME: what if tryReaping returns false? */
400 if (tryReaping(pid, payload: &payload))
401 notifyAndFreeInfo(header: &array->header, entry: &array->entries[i], payload: &payload);
402 goto search_next_child;
403 }
404 }
405
406 array = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
407 }
408
409 /* if we got here, we couldn't find this child in our list. That means this child
410 * belongs to one of the chained SIGCHLD handlers. However, there might be another
411 * child that exited and does belong to us, so we need to check each one individually.
412 */
413 }
414#endif
415
416 for (i = 0; i < (int)sizeofarray(children.entries); ++i) {
417 int pid = ffd_atomic_load(&children.entries[i].pid, FFD_ATOMIC_ACQUIRE);
418 if (pid <= 0)
419 continue;
420#ifdef HAVE_WAITID
421 if (waitid_works) {
422 /* The child might have been reaped by the block above in another thread,
423 * so first check if it's ready and, if it is, lock it */
424 if (!isChildReady(pid, info: &info) ||
425 !ffd_atomic_compare_exchange(&children.entries[i].pid, &pid, -1,
426 FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
427 continue;
428 }
429#endif
430 if (tryReaping(pid, payload: &payload)) {
431 /* this is our child, send notification and free up this entry */
432 notifyAndFreeInfo(header: &children.header, entry: &children.entries[i], payload: &payload);
433 }
434 }
435
436 /* try the arrays */
437 array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
438 while (array != NULL) {
439 for (i = 0; i < (int)sizeofarray(array->entries); ++i) {
440 int pid = ffd_atomic_load(&array->entries[i].pid, FFD_ATOMIC_ACQUIRE);
441 if (pid <= 0)
442 continue;
443#ifdef HAVE_WAITID
444 if (waitid_works) {
445 /* The child might have been reaped by the block above in another thread,
446 * so first check if it's ready and, if it is, lock it */
447 if (!isChildReady(pid, info: &info) ||
448 !ffd_atomic_compare_exchange(&array->entries[i].pid, &pid, -1,
449 FFD_ATOMIC_RELAXED, FFD_ATOMIC_RELAXED))
450 continue;
451 }
452#endif
453 if (tryReaping(pid, payload: &payload)) {
454 /* this is our child, send notification and free up this entry */
455 notifyAndFreeInfo(header: &array->header, entry: &array->entries[i], payload: &payload);
456 }
457 }
458
459 array = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
460 }
461}
462
463static void ignore_sigpipe()
464{
465#ifdef O_NOSIGPIPE
466 static ffd_atomic_int done = FFD_ATOMIC_INIT(0);
467 if (ffd_atomic_load(&done, FFD_ATOMIC_RELAXED))
468 return;
469#endif
470
471 struct sigaction action;
472 memset(s: &action, c: 0, n: sizeof action);
473 sigemptyset(set: &action.sa_mask);
474 action.sa_handler = SIG_IGN;
475 action.sa_flags = 0;
476 sigaction(SIGPIPE, act: &action, NULL);
477
478#ifdef O_NOSIGPIPE
479 ffd_atomic_store(&done, 1, FFD_ATOMIC_RELAXED);
480#endif
481}
482
483#if defined(__GNUC__) && (!defined(__FreeBSD__) || __FreeBSD__ < 10)
484__attribute((destructor, unused)) static void cleanup();
485#endif
486
487static void cleanup()
488{
489 BigArray *array;
490 /* This function is not thread-safe!
491 * It must only be called when the process is shutting down.
492 * At shutdown, we expect no one to be calling forkfd(), so we don't
493 * need to be thread-safe with what is done there.
494 *
495 * But SIGCHLD might be delivered to any thread, including this one.
496 * There's no way to prevent that. The correct solution would be to
497 * cooperatively delete. We don't do that.
498 */
499 if (ffd_atomic_load(&forkfd_status, FFD_ATOMIC_RELAXED) == 0)
500 return;
501
502 /* notify the handler that we're no longer in operation */
503 ffd_atomic_store(&forkfd_status, 0, FFD_ATOMIC_RELAXED);
504
505 /* free any arrays we might have */
506 array = ffd_atomic_load(&children.header.nextArray, FFD_ATOMIC_ACQUIRE);
507 while (array != NULL) {
508 BigArray *next = ffd_atomic_load(&array->header.nextArray, FFD_ATOMIC_ACQUIRE);
509 free(ptr: array);
510 array = next;
511 }
512}
513
514static void forkfd_initialize()
515{
516#if defined(HAVE_BROKEN_WAITID)
517 pid_t pid = fork();
518 if (pid == 0) {
519 _exit(0);
520 } else if (pid > 0) {
521 siginfo_t info;
522 waitid(P_ALL, 0, &info, WNOWAIT | WEXITED);
523 waitid_works = (info.si_pid != 0);
524 info.si_pid = 0;
525
526 // now really reap the child
527 waitid(P_PID, pid, &info, WEXITED);
528 waitid_works = waitid_works && (info.si_pid != 0);
529 }
530#endif
531
532 /* install our signal handler */
533 struct sigaction action;
534 memset(s: &action, c: 0, n: sizeof action);
535 sigemptyset(set: &action.sa_mask);
536 action.sa_flags = SA_NOCLDSTOP | SA_SIGINFO;
537 action.sa_sigaction = sigchld_handler;
538
539 /* ### RACE CONDITION
540 * The sigaction function does a memcpy from an internal buffer
541 * to old_sigaction, which we use in the SIGCHLD handler. If a
542 * SIGCHLD is delivered before or during that memcpy, the handler will
543 * see an inconsistent state.
544 *
545 * There is no solution. pthread_sigmask doesn't work here because the
546 * signal could be delivered to another thread.
547 */
548 sigaction(SIGCHLD, act: &action, oact: &old_sigaction);
549
550#ifndef O_NOSIGPIPE
551 /* disable SIGPIPE too */
552 ignore_sigpipe();
553#endif
554
555#ifdef __GNUC__
556 (void) cleanup; /* suppress unused static function warning */
557#else
558 atexit(cleanup);
559#endif
560
561 ffd_atomic_store(&forkfd_status, 1, FFD_ATOMIC_RELAXED);
562}
563
564static int create_pipe(int filedes[], int flags)
565{
566 int ret = -1;
567#ifdef HAVE_PIPE2
568 /* use pipe2(2) whenever possible, since it can thread-safely create a
569 * cloexec pair of pipes. Without it, we have a race condition setting
570 * FD_CLOEXEC
571 */
572
573# ifdef O_NOSIGPIPE
574 /* try first with O_NOSIGPIPE */
575 ret = pipe2(filedes, O_CLOEXEC | O_NOSIGPIPE);
576 if (ret == -1) {
577 /* O_NOSIGPIPE not supported, ignore SIGPIPE */
578 ignore_sigpipe();
579 }
580# endif
581 if (ret == -1)
582 ret = pipe2(pipedes: filedes, O_CLOEXEC);
583 if (ret == -1)
584 return ret;
585
586 if ((flags & FFD_CLOEXEC) == 0)
587 fcntl(fd: filedes[0], F_SETFD, 0);
588#else
589 ret = pipe(filedes);
590 if (ret == -1)
591 return ret;
592
593 fcntl(filedes[1], F_SETFD, FD_CLOEXEC);
594 if (flags & FFD_CLOEXEC)
595 fcntl(filedes[0], F_SETFD, FD_CLOEXEC);
596#endif
597 if (flags & FFD_NONBLOCK)
598 fcntl(fd: filedes[0], F_SETFL, fcntl(fd: filedes[0], F_GETFL) | O_NONBLOCK);
599 return ret;
600}
601
602#ifndef FORKFD_NO_FORKFD
603static int forkfd_fork_fallback(int flags, pid_t *ppid)
604{
605 Header *header;
606 ProcessInfo *info;
607 pid_t pid;
608 int fd = -1;
609 int death_pipe[2];
610 int sync_pipe[2];
611 int ret;
612 int efd = -1;
613
614 (void) pthread_once(once_control: &forkfd_initialization, init_routine: forkfd_initialize);
615
616 info = allocateInfo(header: &header);
617 if (info == NULL) {
618 errno = ENOMEM;
619 return -1;
620 }
621
622 /* create the pipes before we fork */
623 if (create_pipe(filedes: death_pipe, flags) == -1)
624 goto err_free; /* failed to create the pipes, pass errno */
625
626#ifdef HAVE_EVENTFD
627 /* try using an eventfd, which consumes less resources */
628 efd = eventfd(count: 0, EFD_CLOEXEC);
629#endif
630 if (efd == -1) {
631 /* try a pipe */
632 if (create_pipe(filedes: sync_pipe, FFD_CLOEXEC) == -1) {
633 /* failed both at eventfd and pipe; fail and pass errno */
634 goto err_close;
635 }
636 }
637
638 /* now fork */
639 pid = fork();
640 if (pid == -1)
641 goto err_close2; /* failed to fork, pass errno */
642 if (ppid)
643 *ppid = pid;
644
645 /*
646 * We need to store the child's PID in the info structure, so
647 * the SIGCHLD handler knows that this child is present and it
648 * knows the writing end of the pipe to pass information on.
649 * However, the child process could exit before we stored the
650 * information (or the handler could run for other children exiting).
651 * We prevent that from happening by blocking the child process in
652 * a read(2) until we're finished storing the information.
653 */
654 if (pid == 0) {
655 /* this is the child process */
656 /* first, wait for the all clear */
657 if (efd != -1) {
658#ifdef HAVE_EVENTFD
659 eventfd_t val64;
660 EINTR_LOOP(ret, eventfd_read(efd, &val64));
661 EINTR_LOOP(ret, close(efd));
662#endif
663 } else {
664 char c;
665 EINTR_LOOP(ret, close(sync_pipe[1]));
666 EINTR_LOOP(ret, read(sync_pipe[0], &c, sizeof c));
667 EINTR_LOOP(ret, close(sync_pipe[0]));
668 }
669
670 /* now close the pipes and return to the caller */
671 EINTR_LOOP(ret, close(death_pipe[0]));
672 EINTR_LOOP(ret, close(death_pipe[1]));
673 fd = FFD_CHILD_PROCESS;
674 } else {
675 /* parent process */
676 info->deathPipe = death_pipe[1];
677 fd = death_pipe[0];
678 ffd_atomic_store(&info->pid, pid, FFD_ATOMIC_RELEASE);
679
680 /* release the child */
681#ifdef HAVE_EVENTFD
682 if (efd != -1) {
683 eventfd_t val64 = 42;
684 EINTR_LOOP(ret, eventfd_write(efd, val64));
685 EINTR_LOOP(ret, close(efd));
686 } else
687#endif
688 {
689 /*
690 * Usually, closing would be enough to make read(2) return and the child process
691 * continue. We need to write here: another thread could be calling forkfd at the
692 * same time, which means auxpipe[1] might be open in another child process.
693 */
694 EINTR_LOOP(ret, close(sync_pipe[0]));
695 EINTR_LOOP(ret, write(sync_pipe[1], "", 1));
696 EINTR_LOOP(ret, close(sync_pipe[1]));
697 }
698 }
699
700 return fd;
701
702err_close2:
703#ifdef HAVE_EVENTFD
704 if (efd != -1) {
705 EINTR_LOOP(ret, close(efd));
706 } else
707#endif
708 {
709 EINTR_LOOP(ret, close(sync_pipe[0]));
710 EINTR_LOOP(ret, close(sync_pipe[1]));
711 }
712err_close:
713 EINTR_LOOP(ret, close(death_pipe[0]));
714 EINTR_LOOP(ret, close(death_pipe[1]));
715err_free:
716 /* free the info pointer */
717 freeInfo(header, entry: info);
718 return -1;
719}
720
721/**
722 * @brief forkfd returns a file descriptor representing a child process
723 * @return a file descriptor, or -1 in case of failure
724 *
725 * forkfd() creates a file descriptor that can be used to be notified of when a
726 * child process exits. This file descriptor can be monitored using select(2),
727 * poll(2) or similar mechanisms.
728 *
729 * The @a flags parameter can contain the following values ORed to change the
730 * behaviour of forkfd():
731 *
732 * @li @c FFD_NONBLOCK Set the O_NONBLOCK file status flag on the new open file
733 * descriptor. Using this flag saves extra calls to fnctl(2) to achieve the same
734 * result.
735 *
736 * @li @c FFD_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file
737 * descriptor. You probably want to set this flag, since forkfd() does not work
738 * if the original parent process dies.
739 *
740 * @li @c FFD_USE_FORK Tell forkfd() to actually call fork() instead of a
741 * different system implementation that may be available. On systems where a
742 * different implementation is available, its behavior may differ from that of
743 * fork(), such as not calling the functions registered with pthread_atfork().
744 * If that's necessary, pass this flag.
745 *
746 * The file descriptor returned by forkfd() supports the following operations:
747 *
748 * @li read(2) When the child process exits, then the buffer supplied to
749 * read(2) is used to return information about the status of the child in the
750 * form of one @c siginfo_t structure. The buffer must be at least
751 * sizeof(siginfo_t) bytes. The return value of read(2) is the total number of
752 * bytes read.
753 *
754 * @li poll(2), select(2) (and similar) The file descriptor is readable (the
755 * select(2) readfds argument; the poll(2) POLLIN flag) if the child has exited
756 * or signalled via SIGCHLD.
757 *
758 * @li close(2) When the file descriptor is no longer required it should be closed.
759 */
760int forkfd(int flags, pid_t *ppid)
761{
762 int fd;
763 if (disable_fork_fallback())
764 flags &= ~FFD_USE_FORK;
765
766 if ((flags & FFD_USE_FORK) == 0) {
767 int system_forkfd_works;
768 fd = system_forkfd(flags, ppid, system: &system_forkfd_works);
769 if (system_forkfd_works || disable_fork_fallback())
770 return fd;
771 }
772
773 return forkfd_fork_fallback(flags, ppid);
774}
775
776/**
777 * @brief vforkfd returns a file descriptor representing a child process
778 * @return a file descriptor, or -1 in case of failure
779 *
780 * vforkfd() operates in the same way as forkfd() and the @a flags and @a ppid
781 * arguments are the same. See the forkfd() documentation for details on the
782 * possible values and information on the returned file descriptor.
783 *
784 * This function does not return @c FFD_CHILD_PROCESS. Instead, the function @a
785 * childFn is called in the child process with the @a token parameter as
786 * argument. If that function returns, its return value will be passed to
787 * _exit(2).
788 *
789 * This function differs from forkfd() the same way that vfork() differs from
790 * fork(): the parent process may be suspended while the child is has not yet
791 * called _exit(2) or execve(2). Additionally, on some systems, the child
792 * process may share memory with the parent process the same way an auxiliary
793 * thread would, so extreme care should be employed on what functions the child
794 * process uses before termination.
795 *
796 * The @c FFD_USE_FORK flag retains its behavior as described in the forkfd()
797 * documentation, including that of actually using fork(2) and no other
798 * implementation.
799 *
800 * Currently, only on Linux will this function have any behavior different from
801 * forkfd(). In all other systems, it is equivalent to the following code:
802 *
803 * @code
804 * int ffd = forkfd(flags, &pid);
805 * if (ffd == FFD_CHILD_PROCESS)
806 * _exit(childFn(token));
807 * @endcode
808 */
809int vforkfd(int flags, pid_t *ppid, int (*childFn)(void *), void *token)
810{
811 int fd;
812 if ((flags & FFD_USE_FORK) == 0) {
813 int system_forkfd_works;
814 fd = system_vforkfd(flags, ppid, childFn, token, system: &system_forkfd_works);
815 if (system_forkfd_works || disable_fork_fallback())
816 return fd;
817 }
818
819 fd = forkfd_fork_fallback(flags, ppid);
820 if (fd == FFD_CHILD_PROCESS) {
821 /* child process */
822 _exit(status: childFn(token));
823 }
824 return fd;
825}
826#endif // FORKFD_NO_FORKFD
827
828#if _POSIX_SPAWN > 0 && !defined(FORKFD_NO_SPAWNFD)
829int spawnfd(int flags, pid_t *ppid, const char *path, const posix_spawn_file_actions_t *file_actions,
830 posix_spawnattr_t *attrp, char *const argv[], char *const envp[])
831{
832 Header *header;
833 ProcessInfo *info;
834 struct pipe_payload payload;
835 pid_t pid;
836 int death_pipe[2];
837 int ret = -1;
838 /* we can only do work if we have a way to start the child in stopped mode;
839 * otherwise, we have a major race condition. */
840
841 assert(!system_has_forkfd());
842
843 (void) pthread_once(&forkfd_initialization, forkfd_initialize);
844
845 info = allocateInfo(&header);
846 if (info == NULL) {
847 errno = ENOMEM;
848 goto out;
849 }
850
851 /* create the pipe before we spawn */
852 if (create_pipe(death_pipe, flags) == -1)
853 goto err_free; /* failed to create the pipes, pass errno */
854
855 /* start the process */
856 if (flags & FFD_SPAWN_SEARCH_PATH) {
857 /* use posix_spawnp */
858 if (posix_spawnp(&pid, path, file_actions, attrp, argv, envp) != 0)
859 goto err_close;
860 } else {
861 if (posix_spawn(&pid, path, file_actions, attrp, argv, envp) != 0)
862 goto err_close;
863 }
864
865 if (ppid)
866 *ppid = pid;
867
868 /* Store the child's PID in the info structure.
869 */
870 info->deathPipe = death_pipe[1];
871 ffd_atomic_store(&info->pid, pid, FFD_ATOMIC_RELEASE);
872
873 /* check if the child has already exited */
874 if (tryReaping(pid, &payload))
875 notifyAndFreeInfo(header, info, &payload);
876
877 ret = death_pipe[0];
878 return ret;
879
880err_close:
881 EINTR_LOOP(ret, close(death_pipe[0]));
882 EINTR_LOOP(ret, close(death_pipe[1]));
883
884err_free:
885 /* free the info pointer */
886 freeInfo(header, info);
887
888out:
889 return -1;
890}
891#endif // _POSIX_SPAWN && !FORKFD_NO_SPAWNFD
892
893int forkfd_wait4(int ffd, struct forkfd_info *info, int options, struct rusage *rusage)
894{
895 struct pipe_payload payload;
896 int ret;
897
898 if (system_has_forkfd()) {
899 /* if this is one of our pipes, not a procdesc/pidfd, we'll get an EBADF */
900 ret = system_forkfd_wait(ffd, info, ffdwoptions: options, rusage);
901 if (disable_fork_fallback() || ret != -1 || errno != EBADF)
902 return ret;
903 }
904
905 ret = read(fd: ffd, buf: &payload, nbytes: sizeof(payload));
906 if (ret == -1)
907 return ret; /* pass errno, probably EINTR, EBADF or EWOULDBLOCK */
908
909 assert(ret == sizeof(payload));
910 if (info)
911 *info = payload.info;
912 if (rusage)
913 *rusage = payload.rusage;
914
915 return 0; /* success */
916}
917
918
919int forkfd_close(int ffd)
920{
921 return close(fd: ffd);
922}
923
924#if defined(__FreeBSD__) && __FreeBSD__ >= 9
925# include "forkfd_freebsd.c"
926#elif defined(__linux__)
927# include "forkfd_linux.c"
928#else
929int system_has_forkfd()
930{
931 return 0;
932}
933
934int system_forkfd(int flags, pid_t *ppid, int *system)
935{
936 (void)flags;
937 (void)ppid;
938 *system = 0;
939 return -1;
940}
941
942int system_forkfd_wait(int ffd, struct forkfd_info *info, int options, struct rusage *rusage)
943{
944 (void)ffd;
945 (void)info;
946 (void)options;
947 (void)rusage;
948 return -1;
949}
950#endif
951#ifndef SYSTEM_FORKFD_CAN_VFORK
952int system_vforkfd(int flags, pid_t *ppid, int (*childFn)(void *), void *token, int *system)
953{
954 /* we don't have a way to vfork(), so fake it */
955 int ret = system_forkfd(flags, ppid, system);
956 if (ret == FFD_CHILD_PROCESS) {
957 /* child process */
958 _exit(childFn(token));
959 }
960 return ret;
961}
962#endif
963#undef SYSTEM_FORKFD_CAN_VFORK
964

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/3rdparty/forkfd/forkfd.c