Coverage Report

Created: 2026-02-14 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/samba/lib/util/tfork.c
Line
Count
Source
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
0
  ANNOTATE_BENIGN_RACE_SIZED(         \
71
0
    (obj), sizeof(*(obj)),          \
72
0
    "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
static void tfork_global_destructor(void *state)
144
0
{
145
0
  anonymous_shared_free(state);
146
0
}
147
#endif
148
149
static int tfork_acquire_sighandling(void)
150
0
{
151
0
  int ret = 0;
152
153
0
#ifdef HAVE_PTHREAD
154
0
  ret = pthread_mutex_lock(&signal_state.mutex);
155
0
  if (ret != 0) {
156
0
    return ret;
157
0
  }
158
159
0
  while (!signal_state.available) {
160
0
    ret = pthread_cond_wait(&signal_state.cond,
161
0
          &signal_state.mutex);
162
0
    if (ret != 0) {
163
0
      return ret;
164
0
    }
165
0
  }
166
167
0
  signal_state.available = false;
168
169
0
  ret = pthread_mutex_unlock(&signal_state.mutex);
170
0
  if (ret != 0) {
171
0
    return ret;
172
0
  }
173
0
#endif
174
175
0
  return ret;
176
0
}
177
178
static int tfork_release_sighandling(void)
179
0
{
180
0
  int ret = 0;
181
182
0
#ifdef HAVE_PTHREAD
183
0
  ret = pthread_mutex_lock(&signal_state.mutex);
184
0
  if (ret != 0) {
185
0
    return ret;
186
0
  }
187
188
0
  signal_state.available = true;
189
190
0
  ret = pthread_cond_signal(&signal_state.cond);
191
0
  if (ret != 0) {
192
0
    pthread_mutex_unlock(&signal_state.mutex);
193
0
    return ret;
194
0
  }
195
196
0
  ret = pthread_mutex_unlock(&signal_state.mutex);
197
0
  if (ret != 0) {
198
0
    return ret;
199
0
  }
200
0
#endif
201
202
0
  return ret;
203
0
}
204
205
#ifdef HAVE_PTHREAD
206
static void tfork_atfork_prepare(void)
207
0
{
208
0
  int ret;
209
210
0
  ret = pthread_mutex_lock(&signal_state.mutex);
211
0
  assert(ret == 0);
212
0
}
213
214
static void tfork_atfork_parent(void)
215
0
{
216
0
  int ret;
217
218
0
  ret = pthread_mutex_unlock(&signal_state.mutex);
219
0
  assert(ret == 0);
220
0
}
221
#endif
222
223
static void tfork_atfork_child(void)
224
0
{
225
0
  int ret;
226
227
0
#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
0
#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
0
#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
0
  }
269
270
0
  signal_state.available = true;
271
0
}
272
273
static void tfork_global_initialize(void)
274
0
{
275
0
#ifdef HAVE_PTHREAD
276
0
  int ret;
277
278
0
  pthread_atfork(tfork_atfork_prepare,
279
0
           tfork_atfork_parent,
280
0
           tfork_atfork_child);
281
282
0
  ret = pthread_key_create(&tfork_global_key, tfork_global_destructor);
283
0
  assert(ret == 0);
284
285
0
  ret = pthread_mutex_init(&signal_state.mutex, NULL);
286
0
  assert(ret == 0);
287
288
0
  ret = pthread_cond_init(&signal_state.cond, NULL);
289
0
  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
0
  TFORK_ANNOTATE_BENIGN_RACE(&signal_state.pid);
297
0
#endif
298
299
0
  signal_state.available = true;
300
0
}
301
302
static struct tfork_state *tfork_global_get(void)
303
0
{
304
0
  struct tfork_state *state = NULL;
305
0
#ifdef HAVE_PTHREAD
306
0
  int ret;
307
0
#endif
308
309
0
#ifdef HAVE_PTHREAD
310
0
  state = (struct tfork_state *)pthread_getspecific(tfork_global_key);
311
#else
312
  state = global_state;
313
#endif
314
0
  if (state != NULL) {
315
0
    return state;
316
0
  }
317
318
0
  state = (struct tfork_state *)anonymous_shared_allocate(
319
0
    sizeof(struct tfork_state));
320
0
  if (state == NULL) {
321
0
    return NULL;
322
0
  }
323
324
0
#ifdef HAVE_PTHREAD
325
0
  ret = pthread_setspecific(tfork_global_key, state);
326
0
  if (ret != 0) {
327
0
    anonymous_shared_free(state);
328
0
    return NULL;
329
0
  }
330
0
#endif
331
0
  return state;
332
0
}
333
334
static void tfork_global_free(void)
335
0
{
336
0
  struct tfork_state *state = NULL;
337
0
#ifdef HAVE_PTHREAD
338
0
  int ret;
339
0
#endif
340
341
0
#ifdef HAVE_PTHREAD
342
0
  state = (struct tfork_state *)pthread_getspecific(tfork_global_key);
343
#else
344
  state = global_state;
345
#endif
346
0
  if (state == NULL) {
347
0
    return;
348
0
  }
349
350
0
#ifdef HAVE_PTHREAD
351
0
  ret = pthread_setspecific(tfork_global_key, NULL);
352
0
  if (ret != 0) {
353
0
    return;
354
0
  }
355
0
#endif
356
0
  anonymous_shared_free(state);
357
0
}
358
359
/**
360
 * Only one thread at a time is allowed to handle SIGCHLD signals
361
 **/
362
static int tfork_install_sigchld_handler(pid_t *pid)
363
0
{
364
0
  int ret;
365
0
  struct sigaction act;
366
0
  sigset_t set;
367
368
0
  ret = tfork_acquire_sighandling();
369
0
  if (ret != 0) {
370
0
    return -1;
371
0
  }
372
373
0
  assert(signal_state.pid == NULL);
374
0
  signal_state.pid = pid;
375
376
0
  act = (struct sigaction) {
377
0
    .sa_sigaction = tfork_sigchld_handler,
378
0
    .sa_flags = SA_SIGINFO,
379
0
  };
380
381
0
  ret = sigaction(SIGCHLD, &act, &signal_state.oldact);
382
0
  if (ret != 0) {
383
0
    return -1;
384
0
  }
385
386
0
  sigemptyset(&set);
387
0
  sigaddset(&set, SIGCHLD);
388
0
#ifdef HAVE_PTHREAD
389
0
  ret = pthread_sigmask(SIG_UNBLOCK, &set, &signal_state.oldset);
390
#else
391
  ret = sigprocmask(SIG_UNBLOCK, &set, &signal_state.oldset);
392
#endif
393
0
  if (ret != 0) {
394
0
    return -1;
395
0
  }
396
397
0
  return 0;
398
0
}
399
400
static int tfork_uninstall_sigchld_handler(void)
401
0
{
402
0
  int ret;
403
404
0
  signal_state.pid = NULL;
405
406
0
  ret = sigaction(SIGCHLD, &signal_state.oldact, NULL);
407
0
  if (ret != 0) {
408
0
    return -1;
409
0
  }
410
411
0
#ifdef HAVE_PTHREAD
412
0
  ret = pthread_sigmask(SIG_SETMASK, &signal_state.oldset, NULL);
413
#else
414
  ret = sigprocmask(SIG_SETMASK, &signal_state.oldset, NULL);
415
#endif
416
0
  if (ret != 0) {
417
0
    return -1;
418
0
  }
419
420
0
  ret = tfork_release_sighandling();
421
0
  if (ret != 0) {
422
0
    return -1;
423
0
  }
424
425
0
  return 0;
426
0
}
427
428
static void tfork_sigchld_handler(int signum, siginfo_t *si, void *p)
429
0
{
430
0
  if ((signal_state.pid != NULL) &&
431
0
      (*signal_state.pid != -1) &&
432
0
      (si->si_pid == *signal_state.pid))
433
0
  {
434
0
    return;
435
0
  }
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
0
  }
444
445
0
  if (signal_state.oldact.sa_handler == SIG_IGN) {
446
0
    return;
447
0
  }
448
0
  if (signal_state.oldact.sa_handler == SIG_DFL) {
449
0
    return;
450
0
  }
451
0
  signal_state.oldact.sa_handler(signum);
452
0
}
453
454
static pid_t tfork_start_waiter_and_worker(struct tfork_state *state,
455
             int *_event_fd,
456
             int *_status_fd)
457
0
{
458
0
  int p[2];
459
0
  int status_sp_caller_fd = -1;
460
0
  int status_sp_waiter_fd = -1;
461
0
  int event_pipe_caller_fd = -1;
462
0
  int event_pipe_waiter_fd = -1;
463
0
  int ready_pipe_caller_fd = -1;
464
0
  int ready_pipe_worker_fd = -1;
465
0
  ssize_t nwritten;
466
0
  ssize_t nread;
467
0
  pid_t pid;
468
0
  int status;
469
0
  int fd;
470
0
  char c;
471
0
  int ret;
472
473
0
  *_event_fd = -1;
474
0
  *_status_fd = -1;
475
476
0
  if (state == NULL) {
477
0
    return -1;
478
0
  }
479
480
0
  ret = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
481
0
  if (ret != 0) {
482
0
    return -1;
483
0
  }
484
0
  set_close_on_exec(p[0]);
485
0
  set_close_on_exec(p[1]);
486
0
  status_sp_caller_fd = p[0];
487
0
  status_sp_waiter_fd = p[1];
488
489
0
  ret = pipe(p);
490
0
  if (ret != 0) {
491
0
    close(status_sp_caller_fd);
492
0
    close(status_sp_waiter_fd);
493
0
    return -1;
494
0
  }
495
0
  set_close_on_exec(p[0]);
496
0
  set_close_on_exec(p[1]);
497
0
  event_pipe_caller_fd = p[0];
498
0
  event_pipe_waiter_fd = p[1];
499
500
501
0
  ret = pipe(p);
502
0
  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
0
  }
509
0
  set_close_on_exec(p[0]);
510
0
  set_close_on_exec(p[1]);
511
0
  ready_pipe_worker_fd = p[0];
512
0
  ready_pipe_caller_fd = p[1];
513
514
0
  pid = fork();
515
0
  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
0
  }
524
0
  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
0
    TFORK_ANNOTATE_BENIGN_RACE(&state->waiter_pid);
533
534
0
    state->waiter_pid = pid;
535
536
0
    close(status_sp_waiter_fd);
537
0
    close(event_pipe_waiter_fd);
538
0
    close(ready_pipe_worker_fd);
539
540
0
    set_blocking(event_pipe_caller_fd, false);
541
542
    /*
543
     * wait for the waiter to get ready.
544
     */
545
0
    nread = sys_read(status_sp_caller_fd, &c, sizeof(char));
546
0
    if (nread != sizeof(char)) {
547
0
      return -1;
548
0
    }
549
550
    /*
551
     * Notify the worker to start.
552
     */
553
0
    nwritten = sys_write(ready_pipe_caller_fd,
554
0
             &(char){0}, sizeof(char));
555
0
    if (nwritten != sizeof(char)) {
556
0
      close(ready_pipe_caller_fd);
557
0
      return -1;
558
0
    }
559
0
    close(ready_pipe_caller_fd);
560
561
0
    *_event_fd = event_pipe_caller_fd;
562
0
    *_status_fd = status_sp_caller_fd;
563
564
0
    return pid;
565
0
  }
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
0
  if (pid == -1) {
585
0
    state->waiter_errno = errno;
586
0
    _exit(0);
587
0
  }
588
0
  if (pid == 0) {
589
    /*
590
     * The worker child.
591
     */
592
593
0
    close(status_sp_waiter_fd);
594
0
    close(event_pipe_waiter_fd);
595
596
    /*
597
     * Wait for the caller to give us a go!
598
     */
599
0
    nread = sys_read(ready_pipe_worker_fd, &c, sizeof(char));
600
0
    if (nread != sizeof(char)) {
601
0
      _exit(1);
602
0
    }
603
0
    close(ready_pipe_worker_fd);
604
605
0
    return 0;
606
0
  }
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
0
    }
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
0
    }
635
0
    event_pipe_waiter_fd = fd;
636
0
  }
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
0
    }
648
0
    status_sp_waiter_fd = fd;
649
0
  }
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
0
  }
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
0
  }
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
0
    }
679
    /*
680
     * The caller exited and didn't call tfork_status().
681
     */
682
0
    _exit(0);
683
0
  }
684
0
  if (nwritten != sizeof(status)) {
685
0
    _exit(1);
686
0
  }
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
0
  }
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
0
    }
709
0
    _exit(errno);
710
0
  }
711
0
  if (nread != 1) {
712
0
    _exit(255);
713
0
  }
714
715
0
  _exit(0);
716
0
}
717
718
static int tfork_create_reap_waiter(pid_t waiter_pid)
719
0
{
720
0
  pid_t pid;
721
0
  int waiter_status;
722
723
0
  if (waiter_pid == -1) {
724
0
    return 0;
725
0
  }
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
0
}
736
737
struct tfork *tfork_create(void)
738
0
{
739
0
  struct tfork_state *state = NULL;
740
0
  struct tfork *t = NULL;
741
0
  pid_t pid;
742
0
  int saved_errno = 0;
743
0
  int ret = 0;
744
0
  int ret2;
745
746
0
#ifdef HAVE_PTHREAD
747
0
  ret = pthread_once(&tfork_global_is_initialized,
748
0
         tfork_global_initialize);
749
0
  if (ret != 0) {
750
0
    return NULL;
751
0
  }
752
#else
753
  tfork_global_initialize();
754
#endif
755
756
0
  state = tfork_global_get();
757
0
  if (state == NULL) {
758
0
    return NULL;
759
0
  }
760
0
  *state = (struct tfork_state) {
761
0
    .waiter_pid = -1,
762
0
    .waiter_errno = ECANCELED,
763
0
    .worker_pid = -1,
764
0
  };
765
766
0
  t = malloc(sizeof(struct tfork));
767
0
  if (t == NULL) {
768
0
    ret = -1;
769
0
    goto cleanup;
770
0
  }
771
772
0
  *t = (struct tfork) {
773
0
    .event_fd = -1,
774
0
    .status_fd = -1,
775
0
    .waiter_pid = -1,
776
0
    .worker_pid = -1,
777
0
  };
778
779
0
  ret = tfork_install_sigchld_handler(&state->waiter_pid);
780
0
  if (ret != 0) {
781
0
    goto cleanup;
782
0
  }
783
784
0
  pid = tfork_start_waiter_and_worker(state,
785
0
              &t->event_fd,
786
0
              &t->status_fd);
787
0
  if (pid == -1) {
788
0
    ret = -1;
789
0
    goto cleanup;
790
0
  }
791
0
  if (pid == 0) {
792
    /* In the worker */
793
0
    tfork_global_free();
794
0
    t->worker_pid = 0;
795
0
    return t;
796
0
  }
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
0
  TFORK_ANNOTATE_BENIGN_RACE(&t->waiter_pid);
804
805
0
  t->waiter_pid = pid;
806
0
  t->worker_pid = state->worker_pid;
807
808
0
cleanup:
809
0
  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
0
      }
816
0
      if (t->event_fd != -1) {
817
0
        close(t->event_fd);
818
0
      }
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
0
    }
826
0
  }
827
828
0
  ret2 = tfork_uninstall_sigchld_handler();
829
0
  assert(ret2 == 0);
830
831
0
  tfork_global_free();
832
833
0
  if (ret == -1) {
834
0
    errno = saved_errno;
835
0
  }
836
0
  return t;
837
0
}
838
839
pid_t tfork_child_pid(const struct tfork *t)
840
0
{
841
0
  return t->worker_pid;
842
0
}
843
844
int tfork_event_fd(struct tfork *t)
845
0
{
846
0
  int fd = t->event_fd;
847
848
0
  assert(t->event_fd != -1);
849
0
  t->event_fd = -1;
850
851
0
  return fd;
852
0
}
853
854
int tfork_status(struct tfork **_t, bool wait)
855
0
{
856
0
  struct tfork *t = *_t;
857
0
  int status;
858
0
  ssize_t nread;
859
0
  int waiter_status;
860
0
  pid_t pid;
861
0
  int ret;
862
863
0
  if (t == NULL) {
864
0
    return -1;
865
0
  }
866
867
0
  if (wait) {
868
0
    set_blocking(t->status_fd, true);
869
870
0
    nread = sys_read(t->status_fd, &status, sizeof(int));
871
0
  } else {
872
0
    set_blocking(t->status_fd, false);
873
874
0
    nread = read(t->status_fd, &status, sizeof(int));
875
0
    if ((nread == -1) &&
876
0
        ((errno == EAGAIN) || (errno == EWOULDBLOCK) || errno == EINTR)) {
877
0
      errno = EAGAIN;
878
0
      return -1;
879
0
    }
880
0
  }
881
0
  if (nread != sizeof(int)) {
882
0
    return -1;
883
0
  }
884
885
0
  ret = tfork_install_sigchld_handler(&t->waiter_pid);
886
0
  if (ret != 0) {
887
0
    return -1;
888
0
  }
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
0
  {
897
0
    size_t nwritten;
898
0
    nwritten = sys_write(t->status_fd, &(char){0}, sizeof(char));
899
0
    if (nwritten != sizeof(char)) {
900
0
      close(t->status_fd);
901
0
      return -1;
902
0
    }
903
0
  }
904
0
  close(t->status_fd);
905
906
0
  do {
907
0
    pid = waitpid(t->waiter_pid, &waiter_status, 0);
908
0
  } while ((pid == -1) && (errno == EINTR));
909
0
  assert(pid == t->waiter_pid);
910
911
0
  if (t->event_fd != -1) {
912
0
    close(t->event_fd);
913
0
    t->event_fd = -1;
914
0
  }
915
916
0
  free(t);
917
0
  t = NULL;
918
0
  *_t = NULL;
919
920
0
  ret = tfork_uninstall_sigchld_handler();
921
0
  assert(ret == 0);
922
923
0
  return status;
924
0
}
925
926
int tfork_destroy(struct tfork **_t)
927
0
{
928
0
        struct tfork *t = *_t;
929
0
        int ret;
930
931
0
        if (t == NULL) {
932
0
                errno = EINVAL;
933
0
                return -1;
934
0
        }
935
936
0
        kill(t->worker_pid, SIGKILL);
937
938
0
        ret = tfork_status(_t, true);
939
0
        if (ret == -1) {
940
0
                return -1;
941
0
        }
942
943
0
        return 0;
944
0
}