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 : * children.
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 2204 : static int tfork_acquire_sighandling(void)
150 : {
151 2204 : int ret = 0;
152 :
153 : #ifdef HAVE_PTHREAD
154 2204 : ret = pthread_mutex_lock(&signal_state.mutex);
155 2204 : if (ret != 0) {
156 0 : return ret;
157 : }
158 :
159 2331 : while (!signal_state.available) {
160 127 : ret = pthread_cond_wait(&signal_state.cond,
161 : &signal_state.mutex);
162 127 : if (ret != 0) {
163 0 : return ret;
164 : }
165 : }
166 :
167 2204 : signal_state.available = false;
168 :
169 2204 : ret = pthread_mutex_unlock(&signal_state.mutex);
170 2204 : if (ret != 0) {
171 0 : return ret;
172 : }
173 : #endif
174 :
175 1974 : return ret;
176 : }
177 :
178 2204 : static int tfork_release_sighandling(void)
179 : {
180 2204 : int ret = 0;
181 :
182 : #ifdef HAVE_PTHREAD
183 2204 : ret = pthread_mutex_lock(&signal_state.mutex);
184 2204 : if (ret != 0) {
185 0 : return ret;
186 : }
187 :
188 2204 : signal_state.available = true;
189 :
190 2204 : ret = pthread_cond_signal(&signal_state.cond);
191 2204 : if (ret != 0) {
192 0 : pthread_mutex_unlock(&signal_state.mutex);
193 0 : return ret;
194 : }
195 :
196 2204 : ret = pthread_mutex_unlock(&signal_state.mutex);
197 2204 : if (ret != 0) {
198 0 : return ret;
199 : }
200 : #endif
201 :
202 1974 : return ret;
203 : }
204 :
205 : #ifdef HAVE_PTHREAD
206 30294 : static void tfork_atfork_prepare(void)
207 : {
208 464 : int ret;
209 :
210 30294 : ret = pthread_mutex_lock(&signal_state.mutex);
211 30294 : assert(ret == 0);
212 30294 : }
213 :
214 30294 : static void tfork_atfork_parent(void)
215 : {
216 464 : int ret;
217 :
218 30294 : ret = pthread_mutex_unlock(&signal_state.mutex);
219 30294 : assert(ret == 0);
220 30294 : }
221 : #endif
222 :
223 0 : static void tfork_atfork_child(void)
224 : {
225 0 : int ret;
226 :
227 : #ifdef HAVE_PTHREAD
228 0 : ret = pthread_mutex_unlock(&signal_state.mutex);
229 0 : assert(ret == 0);
230 :
231 0 : ret = pthread_key_delete(tfork_global_key);
232 0 : assert(ret == 0);
233 :
234 0 : ret = pthread_key_create(&tfork_global_key, tfork_global_destructor);
235 0 : 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 0 : 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 0 : ZERO_STRUCT(signal_state.cond);
251 0 : ret = pthread_cond_init(&signal_state.cond, NULL);
252 0 : assert(ret == 0);
253 : #endif
254 :
255 0 : if (signal_state.pid != NULL) {
256 :
257 0 : ret = sigaction(SIGCHLD, &signal_state.oldact, NULL);
258 0 : assert(ret == 0);
259 :
260 : #ifdef HAVE_PTHREAD
261 0 : ret = pthread_sigmask(SIG_SETMASK, &signal_state.oldset, NULL);
262 : #else
263 : ret = sigprocmask(SIG_SETMASK, &signal_state.oldset, NULL);
264 : #endif
265 0 : assert(ret == 0);
266 :
267 0 : signal_state.pid = NULL;
268 : }
269 :
270 0 : signal_state.available = true;
271 0 : }
272 :
273 158 : static void tfork_global_initialize(void)
274 : {
275 : #ifdef HAVE_PTHREAD
276 3 : int ret;
277 :
278 158 : pthread_atfork(tfork_atfork_prepare,
279 : tfork_atfork_parent,
280 : tfork_atfork_child);
281 :
282 158 : ret = pthread_key_create(&tfork_global_key, tfork_global_destructor);
283 158 : assert(ret == 0);
284 :
285 158 : ret = pthread_mutex_init(&signal_state.mutex, NULL);
286 158 : assert(ret == 0);
287 :
288 158 : ret = pthread_cond_init(&signal_state.cond, NULL);
289 158 : 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 3 : TFORK_ANNOTATE_BENIGN_RACE(&signal_state.pid);
297 : #endif
298 :
299 158 : signal_state.available = true;
300 158 : }
301 :
302 1497 : static struct tfork_state *tfork_global_get(void)
303 : {
304 1497 : struct tfork_state *state = NULL;
305 : #ifdef HAVE_PTHREAD
306 146 : int ret;
307 : #endif
308 :
309 : #ifdef HAVE_PTHREAD
310 1497 : state = (struct tfork_state *)pthread_getspecific(tfork_global_key);
311 : #else
312 : state = global_state;
313 : #endif
314 1497 : if (state != NULL) {
315 0 : return state;
316 : }
317 :
318 1497 : state = (struct tfork_state *)anonymous_shared_allocate(
319 : sizeof(struct tfork_state));
320 1496 : if (state == NULL) {
321 0 : return NULL;
322 : }
323 :
324 : #ifdef HAVE_PTHREAD
325 1496 : ret = pthread_setspecific(tfork_global_key, state);
326 1496 : if (ret != 0) {
327 0 : anonymous_shared_free(state);
328 0 : return NULL;
329 : }
330 : #endif
331 1351 : return state;
332 : }
333 :
334 2918 : static void tfork_global_free(void)
335 : {
336 2918 : struct tfork_state *state = NULL;
337 : #ifdef HAVE_PTHREAD
338 217 : int ret;
339 : #endif
340 :
341 : #ifdef HAVE_PTHREAD
342 2918 : state = (struct tfork_state *)pthread_getspecific(tfork_global_key);
343 : #else
344 : state = global_state;
345 : #endif
346 2918 : if (state == NULL) {
347 1350 : return;
348 : }
349 :
350 : #ifdef HAVE_PTHREAD
351 1497 : ret = pthread_setspecific(tfork_global_key, NULL);
352 1497 : if (ret != 0) {
353 0 : return;
354 : }
355 : #endif
356 1497 : anonymous_shared_free(state);
357 : }
358 :
359 : /**
360 : * Only one thread at a time is allowed to handle SIGCHLD signals
361 : **/
362 2204 : static int tfork_install_sigchld_handler(pid_t *pid)
363 : {
364 230 : int ret;
365 230 : struct sigaction act;
366 230 : sigset_t set;
367 :
368 2204 : ret = tfork_acquire_sighandling();
369 2204 : if (ret != 0) {
370 0 : return -1;
371 : }
372 :
373 2204 : assert(signal_state.pid == NULL);
374 2204 : signal_state.pid = pid;
375 :
376 2204 : act = (struct sigaction) {
377 : .sa_sigaction = tfork_sigchld_handler,
378 : .sa_flags = SA_SIGINFO,
379 : };
380 :
381 2204 : ret = sigaction(SIGCHLD, &act, &signal_state.oldact);
382 2204 : if (ret != 0) {
383 0 : return -1;
384 : }
385 :
386 2204 : sigemptyset(&set);
387 2204 : sigaddset(&set, SIGCHLD);
388 : #ifdef HAVE_PTHREAD
389 2204 : ret = pthread_sigmask(SIG_UNBLOCK, &set, &signal_state.oldset);
390 : #else
391 : ret = sigprocmask(SIG_UNBLOCK, &set, &signal_state.oldset);
392 : #endif
393 2204 : if (ret != 0) {
394 0 : return -1;
395 : }
396 :
397 1974 : return 0;
398 : }
399 :
400 2204 : static int tfork_uninstall_sigchld_handler(void)
401 : {
402 230 : int ret;
403 :
404 2204 : signal_state.pid = NULL;
405 :
406 2204 : ret = sigaction(SIGCHLD, &signal_state.oldact, NULL);
407 2204 : if (ret != 0) {
408 0 : return -1;
409 : }
410 :
411 : #ifdef HAVE_PTHREAD
412 2204 : ret = pthread_sigmask(SIG_SETMASK, &signal_state.oldset, NULL);
413 : #else
414 : ret = sigprocmask(SIG_SETMASK, &signal_state.oldset, NULL);
415 : #endif
416 2204 : if (ret != 0) {
417 0 : return -1;
418 : }
419 :
420 2204 : ret = tfork_release_sighandling();
421 2204 : if (ret != 0) {
422 0 : return -1;
423 : }
424 :
425 1974 : return 0;
426 : }
427 :
428 707 : static void tfork_sigchld_handler(int signum, siginfo_t *si, void *p)
429 : {
430 707 : if ((signal_state.pid != NULL) &&
431 707 : (*signal_state.pid != -1) &&
432 707 : (si->si_pid == *signal_state.pid))
433 : {
434 623 : return;
435 : }
436 :
437 : /*
438 : * Not our child, forward to old handler
439 : */
440 0 : if (signal_state.oldact.sa_flags & SA_SIGINFO) {
441 0 : signal_state.oldact.sa_sigaction(signum, si, p);
442 0 : return;
443 : }
444 :
445 0 : if (signal_state.oldact.sa_handler == SIG_IGN) {
446 0 : return;
447 : }
448 0 : if (signal_state.oldact.sa_handler == SIG_DFL) {
449 0 : return;
450 : }
451 0 : signal_state.oldact.sa_handler(signum);
452 : }
453 :
454 1497 : static pid_t tfork_start_waiter_and_worker(struct tfork_state *state,
455 : int *_event_fd,
456 : int *_status_fd)
457 : {
458 146 : int p[2];
459 1497 : int status_sp_caller_fd = -1;
460 1497 : int status_sp_waiter_fd = -1;
461 1497 : int event_pipe_caller_fd = -1;
462 1497 : int event_pipe_waiter_fd = -1;
463 1497 : int ready_pipe_caller_fd = -1;
464 1497 : int ready_pipe_worker_fd = -1;
465 146 : ssize_t nwritten;
466 146 : ssize_t nread;
467 146 : pid_t pid;
468 146 : int status;
469 146 : int fd;
470 146 : char c;
471 146 : int ret;
472 :
473 1497 : *_event_fd = -1;
474 1497 : *_status_fd = -1;
475 :
476 1497 : if (state == NULL) {
477 0 : return -1;
478 : }
479 :
480 1497 : ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
481 1497 : if (ret != 0) {
482 0 : return -1;
483 : }
484 1497 : set_close_on_exec(p[0]);
485 1497 : set_close_on_exec(p[1]);
486 1497 : status_sp_caller_fd = p[0];
487 1497 : status_sp_waiter_fd = p[1];
488 :
489 1497 : ret = pipe(p);
490 1497 : if (ret != 0) {
491 0 : close(status_sp_caller_fd);
492 0 : close(status_sp_waiter_fd);
493 0 : return -1;
494 : }
495 1497 : set_close_on_exec(p[0]);
496 1497 : set_close_on_exec(p[1]);
497 1497 : event_pipe_caller_fd = p[0];
498 1497 : event_pipe_waiter_fd = p[1];
499 :
500 :
501 1497 : ret = pipe(p);
502 1497 : 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 1497 : set_close_on_exec(p[0]);
510 1497 : set_close_on_exec(p[1]);
511 1497 : ready_pipe_worker_fd = p[0];
512 1497 : ready_pipe_caller_fd = p[1];
513 :
514 1497 : pid = fork();
515 1497 : 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 1497 : 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 146 : TFORK_ANNOTATE_BENIGN_RACE(&state->waiter_pid);
533 :
534 1497 : state->waiter_pid = pid;
535 :
536 1497 : close(status_sp_waiter_fd);
537 1497 : close(event_pipe_waiter_fd);
538 1497 : close(ready_pipe_worker_fd);
539 :
540 1497 : set_blocking(event_pipe_caller_fd, false);
541 :
542 : /*
543 : * wait for the waiter to get ready.
544 : */
545 1497 : nread = sys_read(status_sp_caller_fd, &c, sizeof(char));
546 1497 : if (nread != sizeof(char)) {
547 0 : return -1;
548 : }
549 :
550 : /*
551 : * Notify the worker to start.
552 : */
553 1643 : nwritten = sys_write(ready_pipe_caller_fd,
554 1497 : &(char){0}, sizeof(char));
555 1497 : if (nwritten != sizeof(char)) {
556 0 : close(ready_pipe_caller_fd);
557 0 : return -1;
558 : }
559 1497 : close(ready_pipe_caller_fd);
560 :
561 1497 : *_event_fd = event_pipe_caller_fd;
562 1497 : *_status_fd = status_sp_caller_fd;
563 :
564 1497 : 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 0 : process_set_title("tfork waiter", "tfork waiter process");
576 :
577 0 : CatchSignal(SIGCHLD, SIG_DFL);
578 :
579 0 : close(status_sp_caller_fd);
580 0 : close(event_pipe_caller_fd);
581 0 : close(ready_pipe_caller_fd);
582 :
583 0 : pid = fork();
584 1421 : if (pid == -1) {
585 0 : state->waiter_errno = errno;
586 0 : _exit(0);
587 : }
588 1421 : if (pid == 0) {
589 : /*
590 : * The worker child.
591 : */
592 :
593 1421 : close(status_sp_waiter_fd);
594 1421 : close(event_pipe_waiter_fd);
595 :
596 : /*
597 : * Wait for the caller to give us a go!
598 : */
599 1421 : nread = sys_read(ready_pipe_worker_fd, &c, sizeof(char));
600 1421 : if (nread != sizeof(char)) {
601 0 : _exit(1);
602 : }
603 1421 : close(ready_pipe_worker_fd);
604 :
605 1421 : return 0;
606 : }
607 0 : state->worker_pid = pid;
608 0 : process_set_title("tfork(%d)", "tfork waiter process(%d)", pid);
609 :
610 0 : close(ready_pipe_worker_fd);
611 :
612 : /*
613 : * We're going to stay around until child2 exits, so lets close all fds
614 : * other than the pipe fd we may have inherited from the caller.
615 : *
616 : * Dup event_sp_waiter_fd and status_sp_waiter_fd onto fds 0 and 1 so we
617 : * can then call closefrom(2).
618 : */
619 0 : if (event_pipe_waiter_fd > 0) {
620 0 : int dup_fd = 0;
621 :
622 0 : if (status_sp_waiter_fd == 0) {
623 0 : dup_fd = 1;
624 : }
625 :
626 0 : do {
627 0 : fd = dup2(event_pipe_waiter_fd, dup_fd);
628 0 : } while ((fd == -1) && (errno == EINTR));
629 0 : if (fd == -1) {
630 0 : state->waiter_errno = errno;
631 0 : kill(state->worker_pid, SIGKILL);
632 0 : state->worker_pid = -1;
633 0 : _exit(1);
634 : }
635 0 : event_pipe_waiter_fd = fd;
636 : }
637 :
638 0 : if (status_sp_waiter_fd > 1) {
639 0 : do {
640 0 : fd = dup2(status_sp_waiter_fd, 1);
641 0 : } while ((fd == -1) && (errno == EINTR));
642 0 : if (fd == -1) {
643 0 : state->waiter_errno = errno;
644 0 : kill(state->worker_pid, SIGKILL);
645 0 : state->worker_pid = -1;
646 0 : _exit(1);
647 : }
648 0 : status_sp_waiter_fd = fd;
649 : }
650 :
651 0 : closefrom(2);
652 :
653 : /* Tell the caller we're ready */
654 0 : nwritten = sys_write(status_sp_waiter_fd, &(char){0}, sizeof(char));
655 0 : if (nwritten != sizeof(char)) {
656 0 : _exit(1);
657 : }
658 :
659 0 : tfork_global_free();
660 0 : state = NULL;
661 :
662 0 : do {
663 0 : ret = waitpid(pid, &status, 0);
664 0 : } while ((ret == -1) && (errno == EINTR));
665 0 : if (ret == -1) {
666 0 : status = errno;
667 0 : kill(pid, SIGKILL);
668 : }
669 :
670 : /*
671 : * This writes the worker child exit status via our internal socketpair
672 : * so the tfork_status() implementation can read it from its end.
673 : */
674 0 : nwritten = sys_write(status_sp_waiter_fd, &status, sizeof(status));
675 0 : if (nwritten == -1) {
676 0 : if (errno != EPIPE && errno != ECONNRESET) {
677 0 : _exit(errno);
678 : }
679 : /*
680 : * The caller exited and didn't call tfork_status().
681 : */
682 0 : _exit(0);
683 : }
684 0 : if (nwritten != sizeof(status)) {
685 0 : _exit(1);
686 : }
687 :
688 : /*
689 : * This write to the event_fd returned by tfork_event_fd() and notifies
690 : * the caller that the worker child is done and he may now call
691 : * tfork_status().
692 : */
693 0 : nwritten = sys_write(event_pipe_waiter_fd, &(char){0}, sizeof(char));
694 0 : if (nwritten != sizeof(char)) {
695 0 : _exit(1);
696 : }
697 :
698 : /*
699 : * Wait for our parent (the process that called tfork_create()) to
700 : * close() the socketpair fd in tfork_status().
701 : *
702 : * Again, the caller might have exited without calling tfork_status().
703 : */
704 0 : nread = sys_read(status_sp_waiter_fd, &c, 1);
705 0 : if (nread == -1) {
706 0 : if (errno == EPIPE || errno == ECONNRESET) {
707 0 : _exit(0);
708 : }
709 0 : _exit(errno);
710 : }
711 0 : if (nread != 1) {
712 0 : _exit(255);
713 : }
714 :
715 0 : _exit(0);
716 : }
717 :
718 0 : static int tfork_create_reap_waiter(pid_t waiter_pid)
719 : {
720 0 : pid_t pid;
721 0 : int waiter_status;
722 :
723 0 : if (waiter_pid == -1) {
724 0 : return 0;
725 : }
726 :
727 0 : kill(waiter_pid, SIGKILL);
728 :
729 0 : do {
730 0 : pid = waitpid(waiter_pid, &waiter_status, 0);
731 0 : } while ((pid == -1) && (errno == EINTR));
732 0 : assert(pid == waiter_pid);
733 :
734 0 : return 0;
735 : }
736 :
737 1497 : struct tfork *tfork_create(void)
738 : {
739 1497 : struct tfork_state *state = NULL;
740 1497 : struct tfork *t = NULL;
741 146 : pid_t pid;
742 1497 : int saved_errno = 0;
743 1497 : int ret = 0;
744 146 : int ret2;
745 :
746 : #ifdef HAVE_PTHREAD
747 1497 : ret = pthread_once(&tfork_global_is_initialized,
748 : tfork_global_initialize);
749 1497 : if (ret != 0) {
750 0 : return NULL;
751 : }
752 : #else
753 : tfork_global_initialize();
754 : #endif
755 :
756 1497 : state = tfork_global_get();
757 1497 : if (state == NULL) {
758 0 : return NULL;
759 : }
760 1497 : *state = (struct tfork_state) {
761 : .waiter_pid = -1,
762 : .waiter_errno = ECANCELED,
763 : .worker_pid = -1,
764 : };
765 :
766 1497 : t = malloc(sizeof(struct tfork));
767 1497 : if (t == NULL) {
768 0 : ret = -1;
769 0 : goto cleanup;
770 : }
771 :
772 1497 : *t = (struct tfork) {
773 : .event_fd = -1,
774 : .status_fd = -1,
775 : .waiter_pid = -1,
776 : .worker_pid = -1,
777 : };
778 :
779 1497 : ret = tfork_install_sigchld_handler(&state->waiter_pid);
780 1497 : if (ret != 0) {
781 0 : goto cleanup;
782 : }
783 :
784 1497 : pid = tfork_start_waiter_and_worker(state,
785 : &t->event_fd,
786 : &t->status_fd);
787 2918 : if (pid == -1) {
788 0 : ret = -1;
789 0 : goto cleanup;
790 : }
791 2918 : if (pid == 0) {
792 : /* In the worker */
793 1421 : tfork_global_free();
794 1421 : t->worker_pid = 0;
795 1421 : 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 146 : TFORK_ANNOTATE_BENIGN_RACE(&t->waiter_pid);
804 :
805 1497 : t->waiter_pid = pid;
806 1497 : t->worker_pid = state->worker_pid;
807 :
808 1497 : cleanup:
809 1497 : 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 : ret2 = tfork_create_reap_waiter(state->waiter_pid);
821 0 : assert(ret2 == 0);
822 :
823 0 : free(t);
824 0 : t = NULL;
825 : }
826 : }
827 :
828 1497 : ret2 = tfork_uninstall_sigchld_handler();
829 1497 : assert(ret2 == 0);
830 :
831 1497 : tfork_global_free();
832 :
833 1497 : if (ret == -1) {
834 0 : errno = saved_errno;
835 : }
836 1351 : return t;
837 : }
838 :
839 2939 : pid_t tfork_child_pid(const struct tfork *t)
840 : {
841 2939 : return t->worker_pid;
842 : }
843 :
844 1426 : int tfork_event_fd(struct tfork *t)
845 : {
846 1426 : int fd = t->event_fd;
847 :
848 1426 : assert(t->event_fd != -1);
849 1426 : t->event_fd = -1;
850 :
851 1426 : return fd;
852 : }
853 :
854 710 : int tfork_status(struct tfork **_t, bool wait)
855 : {
856 710 : struct tfork *t = *_t;
857 87 : int status;
858 87 : ssize_t nread;
859 87 : int waiter_status;
860 87 : pid_t pid;
861 87 : int ret;
862 :
863 710 : if (t == NULL) {
864 0 : return -1;
865 : }
866 :
867 710 : if (wait) {
868 203 : set_blocking(t->status_fd, true);
869 :
870 203 : nread = sys_read(t->status_fd, &status, sizeof(int));
871 : } else {
872 507 : set_blocking(t->status_fd, false);
873 :
874 507 : nread = read(t->status_fd, &status, sizeof(int));
875 507 : if ((nread == -1) &&
876 3 : ((errno == EAGAIN) || (errno == EWOULDBLOCK) || errno == EINTR)) {
877 3 : errno = EAGAIN;
878 3 : return -1;
879 : }
880 : }
881 707 : if (nread != sizeof(int)) {
882 0 : return -1;
883 : }
884 :
885 707 : ret = tfork_install_sigchld_handler(&t->waiter_pid);
886 707 : 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 84 : size_t nwritten;
898 707 : nwritten = sys_write(t->status_fd, &(char){0}, sizeof(char));
899 707 : if (nwritten != sizeof(char)) {
900 0 : close(t->status_fd);
901 0 : return -1;
902 : }
903 : }
904 707 : close(t->status_fd);
905 :
906 84 : do {
907 707 : pid = waitpid(t->waiter_pid, &waiter_status, 0);
908 707 : } while ((pid == -1) && (errno == EINTR));
909 707 : assert(pid == t->waiter_pid);
910 :
911 707 : if (t->event_fd != -1) {
912 71 : close(t->event_fd);
913 71 : t->event_fd = -1;
914 : }
915 :
916 707 : free(t);
917 707 : t = NULL;
918 707 : *_t = NULL;
919 :
920 707 : ret = tfork_uninstall_sigchld_handler();
921 707 : assert(ret == 0);
922 :
923 707 : return status;
924 : }
925 :
926 154 : int tfork_destroy(struct tfork **_t)
927 : {
928 154 : struct tfork *t = *_t;
929 5 : int ret;
930 :
931 154 : if (t == NULL) {
932 21 : errno = EINVAL;
933 21 : return -1;
934 : }
935 :
936 133 : kill(t->worker_pid, SIGKILL);
937 :
938 133 : ret = tfork_status(_t, true);
939 133 : if (ret == -1) {
940 0 : return -1;
941 : }
942 :
943 128 : return 0;
944 : }
|