summaryrefslogtreecommitdiff
path: root/lib/util/tfork.h
blob: 6c59caf56c540acba0c91cc5e1b1b7aa474db35c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*
   fork on steroids to avoid SIGCHLD and waitpid

   Copyright (C) Stefan Metzmacher 2010
   Copyright (C) Ralph Boehme 2017

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef LIB_UTIL_TFORK_H
#define LIB_UTIL_TFORK_H

struct tfork;

/**
 * @brief a fork() that avoids SIGCHLD and waitpid
 *
 * This function is a solution to the problem of fork() requiring special
 * preparations in the caller to handle SIGCHLD signals and to reap the child by
 * wait()ing for it.
 *
 * The advantage over fork() is that the child process termination is signalled
 * to the caller by making a pipe fd readable returned by tfork_event_fd(), in
 * which case the exit status of the child can be fetched with tfork_status()
 * without blocking.
 *
 * The child process will start with SIGCHLD handler set to SIG_DFL.
 *
 * @return                On success, a struct tfork. NULL on failure.
 *                        Use tfork_worker_pid() to get the pid of the created
 *                        child and tfork_event_fd() to get the file descriptor
 *                        that can be used to poll for process termination and
 *                        reading the child process exit status.
 *
 * @note There's one thing this thing can't protect us against and that is if a
 * process installs a SIGCHLD handler from one thread while another thread is
 * running inside tfork_create() or tfork_status() and the signal handler
 * doesn't forward signals for exited children it didn't fork, ie our children.
 **/
struct tfork *tfork_create(void);

/**
 * @brief Return the child pid from tfork_create()
 *
 * @param[in]   t    Pointer to struct tfork returned by tfork_create()
 *
 * @return           In the caller this returns the pid of the child,
 *                   in the child this returns 0.
 **/
pid_t tfork_child_pid(const struct tfork *t);

/**
 * @brief Return an event fd that signals child termination
 *
 * @param[in]   t    Pointer to struct tfork returned by tfork_create()
 *
 * It is the callers responsibility to ensure that the event fd returned by
 * tfork_event_fd() is closed. By calling tfork_event_fd() ownership of the fd
 * is transferred to the caller, calling tfork_event_fd() again will trigger an
 * abort().
 *
 * @return           An fd that becomes readable when the child created with
 *                   tfork_create() terminates. It is guaranteed that a
 *                   subsequent call to tfork_status() will not block and return
 *                   the exit status of the child.
 **/
int tfork_event_fd(struct tfork *t);

/**
 * @brief Wait for the child to terminate and return its exit status
 *
 * @param[in]   t     Pointer-pointer to a struct tfork returned by
 *                    tfork_create(). Upon successful completion t is freed and
 *                    set to NULL.
 *
 * @param[in]   wait  Whether to wait for the child to change state. If wait is
 *                    false, and the child hasn't changed state, tfork_status()
 *                    will return -1 with errno set to EAGAIN. If wait is true,
 *                    tfork_status() will block waiting for the child to change
 *                    runstate.
 *
 * @return            The exit status of the child, -1 on error.
 *
 * @note We overload the return value a bit, but a process exit status is pretty
 * much guaranteed to be a 16-bit int and can't be -1.
 **/
int tfork_status(struct tfork **_t, bool wait);

/**
 * @brief Terminate the child discarding the exit status
 *
 * @param[in]   t     Pointer-pointer to a struct tfork returned by
 *                    tfork_create(). Upon successful completion t is freed and
 *                    set to NULL.
 *
 * @return            0 on success, -1 on error.
 **/
int tfork_destroy(struct tfork **_t);

#endif /* LIB_UTIL_TFORK_H */