2 fork on steroids to avoid SIGCHLD and waitpid
4 Copyright (C) Stefan Metzmacher 2010
5 Copyright (C) Ralph Boehme 2017
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.
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.
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/>.
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"
40 * This is how the process hierarchy looks like:
62 * The resulting (private) state per tfork_create() call, returned as a opaque
63 * handle to the caller.
67 * This is returned to the caller with tfork_event_fd()
72 * This is used in the caller by tfork_status() to read the worker exit
73 * status and to tell the waiter to exit by closing the fd.
82 * Internal per-thread state maintained while inside tfork.
92 * A global state that synchronizes access to handling SIGCHLD and waiting for
95 struct tfork_signal_state
{
100 pthread_mutex_t mutex
;
104 * pid of the waiter child. This points at waiter_pid in either struct
105 * tfork or struct tfork_state, depending on who called
106 * tfork_install_sigchld_handler().
108 * When tfork_install_sigchld_handler() is called the waiter_pid is
109 * still -1 and only set later after fork(), that's why this is must be
110 * a pointer. The signal handler checks this.
114 struct sigaction oldact
;
118 static struct tfork_signal_state signal_state
;
121 static pthread_once_t tfork_global_is_initialized
= PTHREAD_ONCE_INIT
;
122 static pthread_key_t tfork_global_key
;
124 static struct tfork_state
*global_state
;
127 static void tfork_sigchld_handler(int signum
, siginfo_t
*si
, void *p
);
130 static void tfork_global_destructor(void *state
)
132 anonymous_shared_free(state
);
136 static int tfork_acquire_sighandling(void)
141 ret
= pthread_mutex_lock(&signal_state
.mutex
);
146 while (!signal_state
.available
) {
147 ret
= pthread_cond_wait(&signal_state
.cond
,
148 &signal_state
.mutex
);
154 signal_state
.available
= false;
156 ret
= pthread_mutex_unlock(&signal_state
.mutex
);
165 static int tfork_release_sighandling(void)
170 ret
= pthread_mutex_lock(&signal_state
.mutex
);
175 signal_state
.available
= true;
177 ret
= pthread_cond_signal(&signal_state
.cond
);
179 pthread_mutex_unlock(&signal_state
.mutex
);
183 ret
= pthread_mutex_unlock(&signal_state
.mutex
);
193 static void tfork_atfork_prepare(void)
197 ret
= pthread_mutex_lock(&signal_state
.mutex
);
201 static void tfork_atfork_parent(void)
205 ret
= pthread_mutex_unlock(&signal_state
.mutex
);
210 static void tfork_atfork_child(void)
215 ret
= pthread_mutex_unlock(&signal_state
.mutex
);
218 ret
= pthread_key_delete(tfork_global_key
);
221 ret
= pthread_key_create(&tfork_global_key
, tfork_global_destructor
);
225 * There's no way to destroy a condition variable if there are waiters,
226 * pthread_cond_destroy() will return EBUSY. Just zero out memory and
227 * then initialize again. This is not backed by POSIX but should be ok.
229 ZERO_STRUCT(signal_state
.cond
);
230 ret
= pthread_cond_init(&signal_state
.cond
, NULL
);
234 if (signal_state
.pid
!= NULL
) {
236 ret
= sigaction(SIGCHLD
, &signal_state
.oldact
, NULL
);
240 ret
= pthread_sigmask(SIG_SETMASK
, &signal_state
.oldset
, NULL
);
242 ret
= sigprocmask(SIG_SETMASK
, &signal_state
.oldset
, NULL
);
246 signal_state
.pid
= NULL
;
249 signal_state
.available
= true;
252 static void tfork_global_initialize(void)
257 pthread_atfork(tfork_atfork_prepare
,
261 ret
= pthread_key_create(&tfork_global_key
, tfork_global_destructor
);
264 ret
= pthread_mutex_init(&signal_state
.mutex
, NULL
);
267 ret
= pthread_cond_init(&signal_state
.cond
, NULL
);
271 signal_state
.available
= true;
274 static struct tfork_state
*tfork_global_get(void)
276 struct tfork_state
*state
= NULL
;
282 state
= (struct tfork_state
*)pthread_getspecific(tfork_global_key
);
284 state
= global_state
;
290 state
= (struct tfork_state
*)anonymous_shared_allocate(
291 sizeof(struct tfork_state
));
297 ret
= pthread_setspecific(tfork_global_key
, state
);
299 anonymous_shared_free(state
);
306 static void tfork_global_free(void)
308 struct tfork_state
*state
= NULL
;
314 state
= (struct tfork_state
*)pthread_getspecific(tfork_global_key
);
316 state
= global_state
;
323 ret
= pthread_setspecific(tfork_global_key
, NULL
);
328 anonymous_shared_free(state
);
332 * Only one thread at a time is allowed to handle SIGCHLD signals
334 static int tfork_install_sigchld_handler(pid_t
*pid
)
337 struct sigaction act
;
340 ret
= tfork_acquire_sighandling();
345 assert(signal_state
.pid
== NULL
);
346 signal_state
.pid
= pid
;
348 act
= (struct sigaction
) {
349 .sa_sigaction
= tfork_sigchld_handler
,
350 .sa_flags
= SA_SIGINFO
,
353 ret
= sigaction(SIGCHLD
, &act
, &signal_state
.oldact
);
359 sigaddset(&set
, SIGCHLD
);
361 ret
= pthread_sigmask(SIG_UNBLOCK
, &set
, &signal_state
.oldset
);
363 ret
= sigprocmask(SIG_UNBLOCK
, &set
, &signal_state
.oldset
);
372 static int tfork_uninstall_sigchld_handler(void)
376 signal_state
.pid
= NULL
;
378 ret
= sigaction(SIGCHLD
, &signal_state
.oldact
, NULL
);
384 ret
= pthread_sigmask(SIG_SETMASK
, &signal_state
.oldset
, NULL
);
386 ret
= sigprocmask(SIG_SETMASK
, &signal_state
.oldset
, NULL
);
392 ret
= tfork_release_sighandling();
400 static void tfork_sigchld_handler(int signum
, siginfo_t
*si
, void *p
)
402 if ((signal_state
.pid
!= NULL
) &&
403 (*signal_state
.pid
!= -1) &&
404 (si
->si_pid
== *signal_state
.pid
))
410 * Not our child, forward to old handler
412 if (signal_state
.oldact
.sa_flags
& SA_SIGINFO
) {
413 signal_state
.oldact
.sa_sigaction(signum
, si
, p
);
417 if (signal_state
.oldact
.sa_handler
== SIG_IGN
) {
420 if (signal_state
.oldact
.sa_handler
== SIG_DFL
) {
423 signal_state
.oldact
.sa_handler(signum
);
426 static pid_t
tfork_start_waiter_and_worker(struct tfork_state
*state
,
431 int status_sp_caller_fd
= -1;
432 int status_sp_waiter_fd
= -1;
433 int event_pipe_caller_fd
= -1;
434 int event_pipe_waiter_fd
= -1;
435 int ready_pipe_caller_fd
= -1;
436 int ready_pipe_worker_fd
= -1;
452 ret
= socketpair(AF_UNIX
, SOCK_STREAM
, 0, p
);
456 set_close_on_exec(p
[0]);
457 set_close_on_exec(p
[1]);
458 status_sp_caller_fd
= p
[0];
459 status_sp_waiter_fd
= p
[1];
463 close(status_sp_caller_fd
);
464 close(status_sp_waiter_fd
);
467 set_close_on_exec(p
[0]);
468 set_close_on_exec(p
[1]);
469 event_pipe_caller_fd
= p
[0];
470 event_pipe_waiter_fd
= p
[1];
475 close(status_sp_caller_fd
);
476 close(status_sp_waiter_fd
);
477 close(event_pipe_caller_fd
);
478 close(event_pipe_waiter_fd
);
481 set_close_on_exec(p
[0]);
482 set_close_on_exec(p
[1]);
483 ready_pipe_worker_fd
= p
[0];
484 ready_pipe_caller_fd
= p
[1];
488 close(status_sp_caller_fd
);
489 close(status_sp_waiter_fd
);
490 close(event_pipe_caller_fd
);
491 close(event_pipe_waiter_fd
);
492 close(ready_pipe_caller_fd
);
493 close(ready_pipe_worker_fd
);
499 state
->waiter_pid
= pid
;
501 close(status_sp_waiter_fd
);
502 close(event_pipe_waiter_fd
);
503 close(ready_pipe_worker_fd
);
505 set_blocking(event_pipe_caller_fd
, false);
508 * wait for the waiter to get ready.
510 nread
= sys_read(status_sp_caller_fd
, &c
, sizeof(char));
511 if (nread
!= sizeof(char)) {
516 * Notify the worker to start.
518 nwritten
= sys_write(ready_pipe_caller_fd
,
519 &(char){0}, sizeof(char));
520 if (nwritten
!= sizeof(char)) {
521 close(ready_pipe_caller_fd
);
524 close(ready_pipe_caller_fd
);
526 *_event_fd
= event_pipe_caller_fd
;
527 *_status_fd
= status_sp_caller_fd
;
533 /* cleanup sigchld_handler */
534 tfork_atfork_child();
538 * The "waiter" child.
540 CatchSignal(SIGCHLD
, SIG_DFL
);
542 close(status_sp_caller_fd
);
543 close(event_pipe_caller_fd
);
544 close(ready_pipe_caller_fd
);
548 state
->waiter_errno
= errno
;
556 close(status_sp_waiter_fd
);
557 close(event_pipe_waiter_fd
);
560 * Wait for the caller to give us a go!
562 nread
= sys_read(ready_pipe_worker_fd
, &c
, sizeof(char));
563 if (nread
!= sizeof(char)) {
566 close(ready_pipe_worker_fd
);
570 state
->worker_pid
= pid
;
572 close(ready_pipe_worker_fd
);
575 * We're going to stay around until child2 exits, so lets close all fds
576 * other then the pipe fd we may have inherited from the caller.
578 * Dup event_sp_waiter_fd and status_sp_waiter_fd onto fds 0 and 1 so we
579 * can then call closefrom(2).
581 if (event_pipe_waiter_fd
> 0) {
584 if (status_sp_waiter_fd
== 0) {
589 fd
= dup2(event_pipe_waiter_fd
, dup_fd
);
590 } while ((fd
== -1) && (errno
== EINTR
));
592 state
->waiter_errno
= errno
;
593 kill(state
->worker_pid
, SIGKILL
);
594 state
->worker_pid
= -1;
597 event_pipe_waiter_fd
= fd
;
600 if (status_sp_waiter_fd
> 1) {
602 fd
= dup2(status_sp_waiter_fd
, 1);
603 } while ((fd
== -1) && (errno
== EINTR
));
605 state
->waiter_errno
= errno
;
606 kill(state
->worker_pid
, SIGKILL
);
607 state
->worker_pid
= -1;
610 status_sp_waiter_fd
= fd
;
615 /* Tell the caller we're ready */
616 nwritten
= sys_write(status_sp_waiter_fd
, &(char){0}, sizeof(char));
617 if (nwritten
!= sizeof(char)) {
625 ret
= waitpid(pid
, &status
, 0);
626 } while ((ret
== -1) && (errno
== EINTR
));
633 * This writes the worker child exit status via our internal socketpair
634 * so the tfork_status() implementation can read it from its end.
636 nwritten
= sys_write(status_sp_waiter_fd
, &status
, sizeof(status
));
637 if (nwritten
== -1) {
638 if (errno
!= EPIPE
&& errno
!= ECONNRESET
) {
642 * The caller exitted and didn't call tfork_status().
646 if (nwritten
!= sizeof(status
)) {
651 * This write to the event_fd returned by tfork_event_fd() and notifies
652 * the caller that the worker child is done and he may now call
655 nwritten
= sys_write(event_pipe_waiter_fd
, &(char){0}, sizeof(char));
656 if (nwritten
!= sizeof(char)) {
661 * Wait for our parent (the process that called tfork_create()) to
662 * close() the socketpair fd in tfork_status().
664 * Again, the caller might have exitted without calling tfork_status().
666 nread
= sys_read(status_sp_waiter_fd
, &c
, 1);
668 if (errno
== EPIPE
|| errno
== ECONNRESET
) {
680 static int tfork_create_reap_waiter(pid_t waiter_pid
)
685 if (waiter_pid
== -1) {
689 kill(waiter_pid
, SIGKILL
);
692 pid
= waitpid(waiter_pid
, &waiter_status
, 0);
693 } while ((pid
== -1) && (errno
== EINTR
));
694 assert(pid
== waiter_pid
);
699 struct tfork
*tfork_create(void)
701 struct tfork_state
*state
= NULL
;
702 struct tfork
*t
= NULL
;
708 ret
= pthread_once(&tfork_global_is_initialized
,
709 tfork_global_initialize
);
714 tfork_global_initialize();
717 state
= tfork_global_get();
721 *state
= (struct tfork_state
) {
723 .waiter_errno
= ECANCELED
,
727 t
= malloc(sizeof(struct tfork
));
733 *t
= (struct tfork
) {
740 ret
= tfork_install_sigchld_handler(&state
->waiter_pid
);
745 pid
= tfork_start_waiter_and_worker(state
,
760 t
->worker_pid
= state
->worker_pid
;
767 if (t
->status_fd
!= -1) {
770 if (t
->event_fd
!= -1) {
774 ret
= tfork_create_reap_waiter(state
->waiter_pid
);
782 ret
= tfork_uninstall_sigchld_handler();
793 pid_t
tfork_child_pid(const struct tfork
*t
)
795 return t
->worker_pid
;
798 int tfork_event_fd(const struct tfork
*t
)
803 int tfork_status(struct tfork
**_t
, bool wait
)
805 struct tfork
*t
= *_t
;
817 set_blocking(t
->status_fd
, true);
819 nread
= sys_read(t
->status_fd
, &status
, sizeof(int));
821 set_blocking(t
->status_fd
, false);
823 nread
= read(t
->status_fd
, &status
, sizeof(int));
825 ((errno
== EAGAIN
) || (errno
== EWOULDBLOCK
) || errno
== EINTR
)) {
830 if (nread
!= sizeof(int)) {
834 ret
= tfork_install_sigchld_handler(&t
->waiter_pid
);
840 * This triggers process exit in the waiter.
845 pid
= waitpid(t
->waiter_pid
, &waiter_status
, 0);
846 } while ((pid
== -1) && (errno
== EINTR
));
847 assert(pid
== t
->waiter_pid
);
855 ret
= tfork_uninstall_sigchld_handler();
861 int tfork_destroy(struct tfork
**_t
)
863 struct tfork
*t
= *_t
;
871 kill(t
->worker_pid
, SIGKILL
);
873 ret
= tfork_status(_t
, true);