LCOV - code coverage report
Current view: top level - lib/util - tfork.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 242 411 58.9 %
Date: 2024-04-21 15:09:00 Functions: 16 19 84.2 %

          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             : }

Generated by: LCOV version 1.14