Line data Source code
1 : /*
2 : fork on steroids to avoid SIGCHLD and waitpid
3 :
4 : Copyright (C) Stefan Metzmacher 2010
5 : Copyright (C) Ralph Boehme 2017
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "replace.h"
22 : #include "system/wait.h"
23 : #include "system/filesys.h"
24 : #include "system/network.h"
25 : #include "lib/util/samba_util.h"
26 : #include "lib/util/sys_rw.h"
27 : #include "lib/util/tfork.h"
28 : #include "lib/util/debug.h"
29 : #include "lib/util/util_process.h"
30 :
31 : #ifdef HAVE_PTHREAD
32 : #include <pthread.h>
33 : #endif
34 :
35 : #ifdef NDEBUG
36 : #undef NDEBUG
37 : #endif
38 : #include <assert.h>
39 :
40 : /*
41 : * This is how the process hierarchy looks like:
42 : *
43 : * +----------+
44 : * | caller |
45 : * +----------+
46 : * |
47 : * fork
48 : * |
49 : * v
50 : * +----------+
51 : * | waiter |
52 : * +----------+
53 : * |
54 : * fork
55 : * |
56 : * v
57 : * +----------+
58 : * | worker |
59 : * +----------+
60 : */
61 :
62 : #ifdef HAVE_VALGRIND_HELGRIND_H
63 : #include <valgrind/helgrind.h>
64 : #endif
65 : #ifndef ANNOTATE_BENIGN_RACE_SIZED
66 : #define ANNOTATE_BENIGN_RACE_SIZED(obj, size, description)
67 : #endif
68 :
69 : #define TFORK_ANNOTATE_BENIGN_RACE(obj) \
70 : ANNOTATE_BENIGN_RACE_SIZED( \
71 : (obj), sizeof(*(obj)), \
72 : "no race, serialized by tfork_[un]install_sigchld_handler");
73 :
74 : /*
75 : * The resulting (private) state per tfork_create() call, returned as a opaque
76 : * handle to the caller.
77 : */
78 : struct tfork {
79 : /*
80 : * This is returned to the caller with tfork_event_fd()
81 : */
82 : int event_fd;
83 :
84 : /*
85 : * This is used in the caller by tfork_status() to read the worker exit
86 : * status and to tell the waiter to exit by closing the fd.
87 : */
88 : int status_fd;
89 :
90 : pid_t waiter_pid;
91 : pid_t worker_pid;
92 : };
93 :
94 : /*
95 : * Internal per-thread state maintained while inside tfork.
96 : */
97 : struct tfork_state {
98 : pid_t waiter_pid;
99 : int waiter_errno;
100 :
101 : pid_t worker_pid;
102 : };
103 :
104 : /*
105 : * A global state that synchronizes access to handling SIGCHLD and waiting for
106 : * childs.
107 : */
108 : struct tfork_signal_state {
109 : bool available;
110 :
111 : #ifdef HAVE_PTHREAD
112 : pthread_cond_t cond;
113 : pthread_mutex_t mutex;
114 : #endif
115 :
116 : /*
117 : * pid of the waiter child. This points at waiter_pid in either struct
118 : * tfork or struct tfork_state, depending on who called
119 : * tfork_install_sigchld_handler().
120 : *
121 : * When tfork_install_sigchld_handler() is called the waiter_pid is
122 : * still -1 and only set later after fork(), that's why this is must be
123 : * a pointer. The signal handler checks this.
124 : */
125 : pid_t *pid;
126 :
127 : struct sigaction oldact;
128 : sigset_t oldset;
129 : };
130 :
131 : static struct tfork_signal_state signal_state;
132 :
133 : #ifdef HAVE_PTHREAD
134 : static pthread_once_t tfork_global_is_initialized = PTHREAD_ONCE_INIT;
135 : static pthread_key_t tfork_global_key;
136 : #else
137 : static struct tfork_state *global_state;
138 : #endif
139 :
140 : static void tfork_sigchld_handler(int signum, siginfo_t *si, void *p);
141 :
142 : #ifdef HAVE_PTHREAD
143 0 : static void tfork_global_destructor(void *state)
144 : {
145 0 : anonymous_shared_free(state);
146 0 : }
147 : #endif
148 :
149 1546 : static int tfork_acquire_sighandling(void)
150 : {
151 1546 : int ret = 0;
152 :
153 : #ifdef HAVE_PTHREAD
154 1546 : ret = pthread_mutex_lock(&signal_state.mutex);
155 1546 : if (ret != 0) {
156 0 : return ret;
157 : }
158 :
159 2784 : while (!signal_state.available) {
160 0 : ret = pthread_cond_wait(&signal_state.cond,
161 : &signal_state.mutex);
162 0 : if (ret != 0) {
163 0 : return ret;
164 : }
165 : }
166 :
167 1546 : signal_state.available = false;
168 :
169 1546 : ret = pthread_mutex_unlock(&signal_state.mutex);
170 1546 : if (ret != 0) {
171 0 : return ret;
172 : }
173 : #endif
174 :
175 1546 : return ret;
176 : }
177 :
178 1546 : static int tfork_release_sighandling(void)
179 : {
180 1546 : int ret = 0;
181 :
182 : #ifdef HAVE_PTHREAD
183 1546 : ret = pthread_mutex_lock(&signal_state.mutex);
184 1546 : if (ret != 0) {
185 0 : return ret;
186 : }
187 :
188 1546 : signal_state.available = true;
189 :
190 1546 : ret = pthread_cond_signal(&signal_state.cond);
191 1546 : if (ret != 0) {
192 0 : pthread_mutex_unlock(&signal_state.mutex);
193 0 : return ret;
194 : }
195 :
196 1546 : ret = pthread_mutex_unlock(&signal_state.mutex);
197 1546 : if (ret != 0) {
198 0 : return ret;
199 : }
200 : #endif
201 :
202 1546 : return ret;
203 : }
204 :
205 : #ifdef HAVE_PTHREAD
206 16494 : static void tfork_atfork_prepare(void)
207 : {
208 : int ret;
209 :
210 16494 : ret = pthread_mutex_lock(&signal_state.mutex);
211 16494 : assert(ret == 0);
212 16494 : }
213 :
214 14785 : static void tfork_atfork_parent(void)
215 : {
216 : int ret;
217 :
218 14785 : ret = pthread_mutex_unlock(&signal_state.mutex);
219 14785 : assert(ret == 0);
220 14785 : }
221 : #endif
222 :
223 1709 : static void tfork_atfork_child(void)
224 : {
225 : int ret;
226 :
227 : #ifdef HAVE_PTHREAD
228 1709 : ret = pthread_mutex_unlock(&signal_state.mutex);
229 1709 : assert(ret == 0);
230 :
231 1709 : ret = pthread_key_delete(tfork_global_key);
232 1709 : assert(ret == 0);
233 :
234 1709 : ret = pthread_key_create(&tfork_global_key, tfork_global_destructor);
235 1709 : assert(ret == 0);
236 :
237 : /*
238 : * There's no data race on the cond variable from the signal state, we
239 : * are writing here, but there are no readers yet. Some data race
240 : * detection tools report a race, but the readers are in the parent
241 : * process.
242 : */
243 : TFORK_ANNOTATE_BENIGN_RACE(&signal_state.cond);
244 :
245 : /*
246 : * There's no way to destroy a condition variable if there are waiters,
247 : * pthread_cond_destroy() will return EBUSY. Just zero out memory and
248 : * then initialize again. This is not backed by POSIX but should be ok.
249 : */
250 1709 : ZERO_STRUCT(signal_state.cond);
251 1709 : ret = pthread_cond_init(&signal_state.cond, NULL);
252 1709 : assert(ret == 0);
253 : #endif
254 :
255 1709 : if (signal_state.pid != NULL) {
256 :
257 847 : ret = sigaction(SIGCHLD, &signal_state.oldact, NULL);
258 847 : assert(ret == 0);
259 :
260 : #ifdef HAVE_PTHREAD
261 847 : ret = pthread_sigmask(SIG_SETMASK, &signal_state.oldset, NULL);
262 : #else
263 : ret = sigprocmask(SIG_SETMASK, &signal_state.oldset, NULL);
264 : #endif
265 847 : assert(ret == 0);
266 :
267 847 : signal_state.pid = NULL;
268 : }
269 :
270 1709 : signal_state.available = true;
271 1709 : }
272 :
273 142 : static void tfork_global_initialize(void)
274 : {
275 : #ifdef HAVE_PTHREAD
276 : int ret;
277 :
278 142 : pthread_atfork(tfork_atfork_prepare,
279 : tfork_atfork_parent,
280 : tfork_atfork_child);
281 :
282 142 : ret = pthread_key_create(&tfork_global_key, tfork_global_destructor);
283 142 : assert(ret == 0);
284 :
285 142 : ret = pthread_mutex_init(&signal_state.mutex, NULL);
286 142 : assert(ret == 0);
287 :
288 142 : ret = pthread_cond_init(&signal_state.cond, NULL);
289 142 : assert(ret == 0);
290 :
291 : /*
292 : * In a threaded process there's no data race on t->waiter_pid as
293 : * we're serializing globally via tfork_acquire_sighandling() and
294 : * tfork_release_sighandling().
295 : */
296 : TFORK_ANNOTATE_BENIGN_RACE(&signal_state.pid);
297 : #endif
298 :
299 142 : signal_state.available = true;
300 142 : }
301 :
302 1053 : static struct tfork_state *tfork_global_get(void)
303 : {
304 1053 : struct tfork_state *state = NULL;
305 : #ifdef HAVE_PTHREAD
306 : int ret;
307 : #endif
308 :
309 : #ifdef HAVE_PTHREAD
310 1053 : state = (struct tfork_state *)pthread_getspecific(tfork_global_key);
311 : #else
312 : state = global_state;
313 : #endif
314 1053 : if (state != NULL) {
315 0 : return state;
316 : }
317 :
318 1053 : state = (struct tfork_state *)anonymous_shared_allocate(
319 : sizeof(struct tfork_state));
320 1053 : if (state == NULL) {
321 0 : return NULL;
322 : }
323 :
324 : #ifdef HAVE_PTHREAD
325 1053 : ret = pthread_setspecific(tfork_global_key, state);
326 1053 : if (ret != 0) {
327 0 : anonymous_shared_free(state);
328 0 : return NULL;
329 : }
330 : #endif
331 1053 : return state;
332 : }
333 :
334 2105 : static void tfork_global_free(void)
335 : {
336 2105 : struct tfork_state *state = NULL;
337 : #ifdef HAVE_PTHREAD
338 : int ret;
339 : #endif
340 :
341 : #ifdef HAVE_PTHREAD
342 2105 : state = (struct tfork_state *)pthread_getspecific(tfork_global_key);
343 : #else
344 : state = global_state;
345 : #endif
346 2105 : if (state == NULL) {
347 1052 : return;
348 : }
349 :
350 : #ifdef HAVE_PTHREAD
351 1053 : ret = pthread_setspecific(tfork_global_key, NULL);
352 1053 : if (ret != 0) {
353 0 : return;
354 : }
355 : #endif
356 1053 : anonymous_shared_free(state);
357 : }
358 :
359 : /**
360 : * Only one thread at a time is allowed to handle SIGCHLD signals
361 : **/
362 1546 : static int tfork_install_sigchld_handler(pid_t *pid)
363 : {
364 : int ret;
365 : struct sigaction act;
366 : sigset_t set;
367 :
368 1546 : ret = tfork_acquire_sighandling();
369 1546 : if (ret != 0) {
370 0 : return -1;
371 : }
372 :
373 1546 : assert(signal_state.pid == NULL);
374 1546 : signal_state.pid = pid;
375 :
376 1546 : act = (struct sigaction) {
377 : .sa_sigaction = tfork_sigchld_handler,
378 : .sa_flags = SA_SIGINFO,
379 : };
380 :
381 1546 : ret = sigaction(SIGCHLD, &act, &signal_state.oldact);
382 1546 : if (ret != 0) {
383 0 : return -1;
384 : }
385 :
386 1546 : sigemptyset(&set);
387 1546 : sigaddset(&set, SIGCHLD);
388 : #ifdef HAVE_PTHREAD
389 1546 : ret = pthread_sigmask(SIG_UNBLOCK, &set, &signal_state.oldset);
390 : #else
391 : ret = sigprocmask(SIG_UNBLOCK, &set, &signal_state.oldset);
392 : #endif
393 1546 : if (ret != 0) {
394 0 : return -1;
395 : }
396 :
397 1546 : return 0;
398 : }
399 :
400 1546 : static int tfork_uninstall_sigchld_handler(void)
401 : {
402 : int ret;
403 :
404 1546 : signal_state.pid = NULL;
405 :
406 1546 : ret = sigaction(SIGCHLD, &signal_state.oldact, NULL);
407 1546 : if (ret != 0) {
408 0 : return -1;
409 : }
410 :
411 : #ifdef HAVE_PTHREAD
412 1546 : ret = pthread_sigmask(SIG_SETMASK, &signal_state.oldset, NULL);
413 : #else
414 : ret = sigprocmask(SIG_SETMASK, &signal_state.oldset, NULL);
415 : #endif
416 1546 : if (ret != 0) {
417 0 : return -1;
418 : }
419 :
420 1546 : ret = tfork_release_sighandling();
421 1546 : if (ret != 0) {
422 0 : return -1;
423 : }
424 :
425 1546 : return 0;
426 : }
427 :
428 494 : static void tfork_sigchld_handler(int signum, siginfo_t *si, void *p)
429 : {
430 886 : if ((signal_state.pid != NULL) &&
431 885 : (*signal_state.pid != -1) &&
432 493 : (si->si_pid == *signal_state.pid))
433 : {
434 493 : return;
435 : }
436 :
437 : /*
438 : * Not our child, forward to old handler
439 : */
440 1 : if (signal_state.oldact.sa_flags & SA_SIGINFO) {
441 0 : signal_state.oldact.sa_sigaction(signum, si, p);
442 0 : return;
443 : }
444 :
445 1 : if (signal_state.oldact.sa_handler == SIG_IGN) {
446 0 : return;
447 : }
448 1 : if (signal_state.oldact.sa_handler == SIG_DFL) {
449 1 : return;
450 : }
451 0 : signal_state.oldact.sa_handler(signum);
452 : }
453 :
454 1053 : static pid_t tfork_start_waiter_and_worker(struct tfork_state *state,
455 : int *_event_fd,
456 : int *_status_fd)
457 : {
458 : int p[2];
459 1053 : int status_sp_caller_fd = -1;
460 1053 : int status_sp_waiter_fd = -1;
461 1053 : int event_pipe_caller_fd = -1;
462 1053 : int event_pipe_waiter_fd = -1;
463 1053 : int ready_pipe_caller_fd = -1;
464 1053 : int ready_pipe_worker_fd = -1;
465 : ssize_t nwritten;
466 : ssize_t nread;
467 : pid_t pid;
468 : int status;
469 : int fd;
470 : char c;
471 : int ret;
472 :
473 1053 : *_event_fd = -1;
474 1053 : *_status_fd = -1;
475 :
476 1053 : if (state == NULL) {
477 0 : return -1;
478 : }
479 :
480 1053 : ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
481 1053 : if (ret != 0) {
482 0 : return -1;
483 : }
484 1053 : set_close_on_exec(p[0]);
485 1053 : set_close_on_exec(p[1]);
486 1053 : status_sp_caller_fd = p[0];
487 1053 : status_sp_waiter_fd = p[1];
488 :
489 1053 : ret = pipe(p);
490 1053 : if (ret != 0) {
491 0 : close(status_sp_caller_fd);
492 0 : close(status_sp_waiter_fd);
493 0 : return -1;
494 : }
495 1053 : set_close_on_exec(p[0]);
496 1053 : set_close_on_exec(p[1]);
497 1053 : event_pipe_caller_fd = p[0];
498 1053 : event_pipe_waiter_fd = p[1];
499 :
500 :
501 1053 : ret = pipe(p);
502 1053 : if (ret != 0) {
503 0 : close(status_sp_caller_fd);
504 0 : close(status_sp_waiter_fd);
505 0 : close(event_pipe_caller_fd);
506 0 : close(event_pipe_waiter_fd);
507 0 : return -1;
508 : }
509 1053 : set_close_on_exec(p[0]);
510 1053 : set_close_on_exec(p[1]);
511 1053 : ready_pipe_worker_fd = p[0];
512 1053 : ready_pipe_caller_fd = p[1];
513 :
514 1053 : pid = fork();
515 1900 : if (pid == -1) {
516 0 : close(status_sp_caller_fd);
517 0 : close(status_sp_waiter_fd);
518 0 : close(event_pipe_caller_fd);
519 0 : close(event_pipe_waiter_fd);
520 0 : close(ready_pipe_caller_fd);
521 0 : close(ready_pipe_worker_fd);
522 0 : return -1;
523 : }
524 1900 : if (pid != 0) {
525 : /* The caller */
526 :
527 : /*
528 : * In a threaded process there's no data race on
529 : * state->waiter_pid as we're serializing globally via
530 : * tfork_acquire_sighandling() and tfork_release_sighandling().
531 : */
532 : TFORK_ANNOTATE_BENIGN_RACE(&state->waiter_pid);
533 :
534 1053 : state->waiter_pid = pid;
535 :
536 1053 : close(status_sp_waiter_fd);
537 1053 : close(event_pipe_waiter_fd);
538 1053 : close(ready_pipe_worker_fd);
539 :
540 1053 : set_blocking(event_pipe_caller_fd, false);
541 :
542 : /*
543 : * wait for the waiter to get ready.
544 : */
545 1053 : nread = sys_read(status_sp_caller_fd, &c, sizeof(char));
546 1053 : if (nread != sizeof(char)) {
547 0 : return -1;
548 : }
549 :
550 : /*
551 : * Notify the worker to start.
552 : */
553 1053 : nwritten = sys_write(ready_pipe_caller_fd,
554 1053 : &(char){0}, sizeof(char));
555 1053 : if (nwritten != sizeof(char)) {
556 0 : close(ready_pipe_caller_fd);
557 0 : return -1;
558 : }
559 1053 : close(ready_pipe_caller_fd);
560 :
561 1053 : *_event_fd = event_pipe_caller_fd;
562 1053 : *_status_fd = status_sp_caller_fd;
563 :
564 1053 : return pid;
565 : }
566 :
567 : #ifndef HAVE_PTHREAD
568 : /* cleanup sigchld_handler */
569 : tfork_atfork_child();
570 : #endif
571 :
572 : /*
573 : * The "waiter" child.
574 : */
575 847 : setproctitle("tfork waiter process");
576 847 : prctl_set_comment("tfork waiter");
577 847 : CatchSignal(SIGCHLD, SIG_DFL);
578 :
579 847 : close(status_sp_caller_fd);
580 847 : close(event_pipe_caller_fd);
581 847 : close(ready_pipe_caller_fd);
582 :
583 847 : pid = fork();
584 1052 : if (pid == -1) {
585 0 : state->waiter_errno = errno;
586 0 : _exit(0);
587 : }
588 1052 : if (pid == 0) {
589 : /*
590 : * The worker child.
591 : */
592 :
593 1052 : close(status_sp_waiter_fd);
594 1052 : close(event_pipe_waiter_fd);
595 :
596 : /*
597 : * Wait for the caller to give us a go!
598 : */
599 1052 : nread = sys_read(ready_pipe_worker_fd, &c, sizeof(char));
600 1052 : if (nread != sizeof(char)) {
601 0 : _exit(1);
602 : }
603 1052 : close(ready_pipe_worker_fd);
604 :
605 1052 : return 0;
606 : }
607 0 : state->worker_pid = pid;
608 0 : setproctitle("tfork waiter process(%d)", pid);
609 0 : prctl_set_comment("tfork(%d)", pid);
610 :
611 0 : close(ready_pipe_worker_fd);
612 :
613 : /*
614 : * We're going to stay around until child2 exits, so lets close all fds
615 : * other than the pipe fd we may have inherited from the caller.
616 : *
617 : * Dup event_sp_waiter_fd and status_sp_waiter_fd onto fds 0 and 1 so we
618 : * can then call closefrom(2).
619 : */
620 0 : if (event_pipe_waiter_fd > 0) {
621 0 : int dup_fd = 0;
622 :
623 0 : if (status_sp_waiter_fd == 0) {
624 0 : dup_fd = 1;
625 : }
626 :
627 : do {
628 0 : fd = dup2(event_pipe_waiter_fd, dup_fd);
629 0 : } while ((fd == -1) && (errno == EINTR));
630 0 : if (fd == -1) {
631 0 : state->waiter_errno = errno;
632 0 : kill(state->worker_pid, SIGKILL);
633 0 : state->worker_pid = -1;
634 0 : _exit(1);
635 : }
636 0 : event_pipe_waiter_fd = fd;
637 : }
638 :
639 0 : if (status_sp_waiter_fd > 1) {
640 : do {
641 0 : fd = dup2(status_sp_waiter_fd, 1);
642 0 : } while ((fd == -1) && (errno == EINTR));
643 0 : if (fd == -1) {
644 0 : state->waiter_errno = errno;
645 0 : kill(state->worker_pid, SIGKILL);
646 0 : state->worker_pid = -1;
647 0 : _exit(1);
648 : }
649 0 : status_sp_waiter_fd = fd;
650 : }
651 :
652 0 : closefrom(2);
653 :
654 : /* Tell the caller we're ready */
655 0 : nwritten = sys_write(status_sp_waiter_fd, &(char){0}, sizeof(char));
656 0 : if (nwritten != sizeof(char)) {
657 0 : _exit(1);
658 : }
659 :
660 0 : tfork_global_free();
661 0 : state = NULL;
662 :
663 : do {
664 0 : ret = waitpid(pid, &status, 0);
665 0 : } while ((ret == -1) && (errno == EINTR));
666 0 : if (ret == -1) {
667 0 : status = errno;
668 0 : kill(pid, SIGKILL);
669 : }
670 :
671 : /*
672 : * This writes the worker child exit status via our internal socketpair
673 : * so the tfork_status() implementation can read it from its end.
674 : */
675 0 : nwritten = sys_write(status_sp_waiter_fd, &status, sizeof(status));
676 0 : if (nwritten == -1) {
677 0 : if (errno != EPIPE && errno != ECONNRESET) {
678 0 : _exit(errno);
679 : }
680 : /*
681 : * The caller exitted and didn't call tfork_status().
682 : */
683 0 : _exit(0);
684 : }
685 0 : if (nwritten != sizeof(status)) {
686 0 : _exit(1);
687 : }
688 :
689 : /*
690 : * This write to the event_fd returned by tfork_event_fd() and notifies
691 : * the caller that the worker child is done and he may now call
692 : * tfork_status().
693 : */
694 0 : nwritten = sys_write(event_pipe_waiter_fd, &(char){0}, sizeof(char));
695 0 : if (nwritten != sizeof(char)) {
696 0 : _exit(1);
697 : }
698 :
699 : /*
700 : * Wait for our parent (the process that called tfork_create()) to
701 : * close() the socketpair fd in tfork_status().
702 : *
703 : * Again, the caller might have exitted without calling tfork_status().
704 : */
705 0 : nread = sys_read(status_sp_waiter_fd, &c, 1);
706 0 : if (nread == -1) {
707 0 : if (errno == EPIPE || errno == ECONNRESET) {
708 0 : _exit(0);
709 : }
710 0 : _exit(errno);
711 : }
712 0 : if (nread != 1) {
713 0 : _exit(255);
714 : }
715 :
716 0 : _exit(0);
717 : }
718 :
719 0 : static int tfork_create_reap_waiter(pid_t waiter_pid)
720 : {
721 : pid_t pid;
722 : int waiter_status;
723 :
724 0 : if (waiter_pid == -1) {
725 0 : return 0;
726 : }
727 :
728 0 : kill(waiter_pid, SIGKILL);
729 :
730 : do {
731 0 : pid = waitpid(waiter_pid, &waiter_status, 0);
732 0 : } while ((pid == -1) && (errno == EINTR));
733 0 : assert(pid == waiter_pid);
734 :
735 0 : return 0;
736 : }
737 :
738 1053 : struct tfork *tfork_create(void)
739 : {
740 1053 : struct tfork_state *state = NULL;
741 1053 : struct tfork *t = NULL;
742 : pid_t pid;
743 : int saved_errno;
744 1053 : int ret = 0;
745 :
746 : #ifdef HAVE_PTHREAD
747 1053 : ret = pthread_once(&tfork_global_is_initialized,
748 : tfork_global_initialize);
749 1053 : if (ret != 0) {
750 0 : return NULL;
751 : }
752 : #else
753 : tfork_global_initialize();
754 : #endif
755 :
756 1053 : state = tfork_global_get();
757 1053 : if (state == NULL) {
758 0 : return NULL;
759 : }
760 1053 : *state = (struct tfork_state) {
761 : .waiter_pid = -1,
762 : .waiter_errno = ECANCELED,
763 : .worker_pid = -1,
764 : };
765 :
766 1053 : t = malloc(sizeof(struct tfork));
767 1053 : if (t == NULL) {
768 0 : ret = -1;
769 0 : goto cleanup;
770 : }
771 :
772 1053 : *t = (struct tfork) {
773 : .event_fd = -1,
774 : .status_fd = -1,
775 : .waiter_pid = -1,
776 : .worker_pid = -1,
777 : };
778 :
779 1053 : ret = tfork_install_sigchld_handler(&state->waiter_pid);
780 1053 : if (ret != 0) {
781 0 : goto cleanup;
782 : }
783 :
784 1053 : pid = tfork_start_waiter_and_worker(state,
785 : &t->event_fd,
786 : &t->status_fd);
787 2105 : if (pid == -1) {
788 0 : ret = -1;
789 0 : goto cleanup;
790 : }
791 2105 : if (pid == 0) {
792 : /* In the worker */
793 1052 : tfork_global_free();
794 1052 : t->worker_pid = 0;
795 1052 : return t;
796 : }
797 :
798 : /*
799 : * In a threaded process there's no data race on t->waiter_pid as
800 : * we're serializing globally via tfork_acquire_sighandling() and
801 : * tfork_release_sighandling().
802 : */
803 : TFORK_ANNOTATE_BENIGN_RACE(&t->waiter_pid);
804 :
805 1053 : t->waiter_pid = pid;
806 1053 : t->worker_pid = state->worker_pid;
807 :
808 1053 : cleanup:
809 1053 : if (ret == -1) {
810 0 : saved_errno = errno;
811 :
812 0 : if (t != NULL) {
813 0 : if (t->status_fd != -1) {
814 0 : close(t->status_fd);
815 : }
816 0 : if (t->event_fd != -1) {
817 0 : close(t->event_fd);
818 : }
819 :
820 0 : ret = tfork_create_reap_waiter(state->waiter_pid);
821 0 : assert(ret == 0);
822 :
823 0 : free(t);
824 0 : t = NULL;
825 : }
826 : }
827 :
828 1053 : ret = tfork_uninstall_sigchld_handler();
829 1053 : assert(ret == 0);
830 :
831 1053 : tfork_global_free();
832 :
833 1053 : if (ret == -1) {
834 0 : errno = saved_errno;
835 : }
836 1053 : return t;
837 : }
838 :
839 2126 : pid_t tfork_child_pid(const struct tfork *t)
840 : {
841 2126 : return t->worker_pid;
842 : }
843 :
844 1053 : int tfork_event_fd(struct tfork *t)
845 : {
846 1053 : int fd = t->event_fd;
847 :
848 1053 : assert(t->event_fd != -1);
849 1053 : t->event_fd = -1;
850 :
851 1053 : return fd;
852 : }
853 :
854 493 : int tfork_status(struct tfork **_t, bool wait)
855 : {
856 493 : struct tfork *t = *_t;
857 : int status;
858 : ssize_t nread;
859 : int waiter_status;
860 : pid_t pid;
861 : int ret;
862 :
863 493 : if (t == NULL) {
864 0 : return -1;
865 : }
866 :
867 493 : if (wait) {
868 103 : set_blocking(t->status_fd, true);
869 :
870 103 : nread = sys_read(t->status_fd, &status, sizeof(int));
871 : } else {
872 390 : set_blocking(t->status_fd, false);
873 :
874 390 : nread = read(t->status_fd, &status, sizeof(int));
875 390 : if ((nread == -1) &&
876 0 : ((errno == EAGAIN) || (errno == EWOULDBLOCK) || errno == EINTR)) {
877 0 : errno = EAGAIN;
878 0 : return -1;
879 : }
880 : }
881 493 : if (nread != sizeof(int)) {
882 0 : return -1;
883 : }
884 :
885 493 : ret = tfork_install_sigchld_handler(&t->waiter_pid);
886 493 : if (ret != 0) {
887 0 : return -1;
888 : }
889 :
890 : /*
891 : * This triggers process exit in the waiter.
892 : * We write to the fd as well as closing it, as any tforked sibling
893 : * processes will also have the writable end of this socket open.
894 : *
895 : */
896 : {
897 : size_t nwritten;
898 493 : nwritten = sys_write(t->status_fd, &(char){0}, sizeof(char));
899 493 : if (nwritten != sizeof(char)) {
900 0 : close(t->status_fd);
901 0 : return -1;
902 : }
903 : }
904 493 : close(t->status_fd);
905 :
906 : do {
907 493 : pid = waitpid(t->waiter_pid, &waiter_status, 0);
908 493 : } while ((pid == -1) && (errno == EINTR));
909 493 : assert(pid == t->waiter_pid);
910 :
911 493 : if (t->event_fd != -1) {
912 0 : close(t->event_fd);
913 0 : t->event_fd = -1;
914 : }
915 :
916 493 : free(t);
917 493 : t = NULL;
918 493 : *_t = NULL;
919 :
920 493 : ret = tfork_uninstall_sigchld_handler();
921 493 : assert(ret == 0);
922 :
923 493 : return status;
924 : }
925 :
926 124 : int tfork_destroy(struct tfork **_t)
927 : {
928 124 : struct tfork *t = *_t;
929 : int ret;
930 :
931 124 : if (t == NULL) {
932 21 : errno = EINVAL;
933 21 : return -1;
934 : }
935 :
936 103 : kill(t->worker_pid, SIGKILL);
937 :
938 103 : ret = tfork_status(_t, true);
939 103 : if (ret == -1) {
940 0 : return -1;
941 : }
942 :
943 103 : return 0;
944 : }
|