dte test coverage


Directory: ./
File: src/util/fork-exec.c
Date: 2024-12-21 16:03:22
Exec Total Coverage
Lines: 65 91 71.4%
Functions: 5 5 100.0%
Branches: 33 66 50.0%

Line Branch Exec Source
1 #include <errno.h>
2 #include <signal.h>
3 #include <stdlib.h>
4 #include <sys/wait.h>
5 #include <unistd.h>
6 #include "fork-exec.h"
7 #include "debug.h"
8 #include "fd.h"
9 #include "log.h"
10 #include "terminal/ioctl.h"
11 #include "xreadwrite.h"
12
13 // Reset ignored signal dispositions (i.e. as originally set up by
14 // set_basic_signal_dispositions()) to SIG_DFL
15 18 static bool reset_ignored_signals(void)
16 {
17 // Note that handled signals don't need to be restored here, since
18 // they're necessarily and automatically reset after exec(3p)
19 18 static const int ignored_signals[] = {
20 SIGINT, SIGQUIT, SIGTSTP,
21 SIGTTIN, SIGTTOU, SIGXFSZ,
22 SIGPIPE, SIGUSR1, SIGUSR2,
23 };
24
25 18 struct sigaction action = {.sa_handler = SIG_DFL};
26
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (unlikely(sigemptyset(&action.sa_mask) != 0)) {
27 return false;
28 }
29
30
2/2
✓ Branch 0 taken 162 times.
✓ Branch 1 taken 18 times.
180 for (size_t i = 0; i < ARRAYLEN(ignored_signals); i++) {
31 162 int r = sigaction(ignored_signals[i], &action, NULL);
32
1/2
✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
162 if (unlikely(r != 0)) {
33 return false;
34 }
35 }
36
37 return true;
38 }
39
40 18 static noreturn void child_process_exec (
41 const char **argv,
42 const char **env,
43 int fd[3],
44 int error_fd,
45 bool drop_ctty
46 ) {
47 18 int error;
48 18 int nr_fds = 3;
49 18 bool move = error_fd < nr_fds;
50 18 int max = error_fd;
51
52
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (drop_ctty) {
53 18 term_drop_controlling_tty(STDIN_FILENO);
54 }
55
56 // Find if we must move fds out of the way
57
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 18 times.
72 for (int i = 0; i < nr_fds; i++) {
58
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (fd[i] > max) {
59 max = fd[i];
60 }
61
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (fd[i] < i) {
62 move = true;
63 }
64 }
65
66
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
18 if (move) {
67 int next_free = max + 1;
68 if (error_fd < nr_fds) {
69 error_fd = xdup3(error_fd, next_free++, O_CLOEXEC);
70 if (error_fd < 0) {
71 goto error;
72 }
73 }
74 for (int i = 0; i < nr_fds; i++) {
75 if (fd[i] < i) {
76 fd[i] = xdup3(fd[i], next_free++, O_CLOEXEC);
77 if (fd[i] < 0) {
78 goto error;
79 }
80 }
81 }
82 }
83
84 // Now it is safe to duplicate fds in this order
85
2/2
✓ Branch 0 taken 54 times.
✓ Branch 1 taken 18 times.
72 for (int i = 0; i < nr_fds; i++) {
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (i == fd[i]) {
87 // Clear FD_CLOEXEC flag
88 if (!fd_set_cloexec(fd[i], false)) {
89 goto error;
90 }
91 } else {
92
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 54 times.
54 if (xdup3(fd[i], i, 0) < 0) {
93 goto error;
94 }
95 }
96 }
97
98
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 16 times.
18 if (env) {
99
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (size_t i = 0; env[i]; i += 2) {
100 4 const char *name = env[i];
101 4 const char *value = env[i + 1];
102
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 int r = value ? setenv(name, value, 1) : unsetenv(name);
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
4 if (unlikely(r != 0)) {
104 goto error;
105 }
106 }
107 }
108
109
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
18 if (!reset_ignored_signals()) {
110 goto error;
111 }
112
113 1 execvp(argv[0], (char**)argv);
114
115 1 error:
116 1 error = errno;
117 1 error = xwrite(error_fd, &error, sizeof(error));
118 1 exit(42);
119 }
120
121 18 static pid_t xwaitpid(pid_t pid, int *status, int options)
122 {
123 18 pid_t ret;
124 36 do {
125 18 ret = waitpid(pid, status, options);
126
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
18 } while (unlikely(ret < 0 && errno == EINTR));
127 18 return ret;
128 }
129
130 18 pid_t fork_exec(const char **argv, const char **env, int fd[3], bool drop_ctty)
131 {
132 18 int ep[2];
133
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (xpipe2(ep, O_CLOEXEC) != 0) {
134 return -1;
135 }
136
137 18 const pid_t pid = fork();
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36 times.
36 if (unlikely(pid == -1)) {
139 int saved_errno = errno;
140 xclose(ep[0]);
141 xclose(ep[1]);
142 errno = saved_errno;
143 return -1;
144 }
145
146
2/2
✓ Branch 0 taken 18 times.
✓ Branch 1 taken 18 times.
36 if (pid == 0) {
147 // Child
148 18 child_process_exec(argv, env, fd, ep[1], drop_ctty);
149 BUG("child_process_exec() should never return");
150 return -1;
151 }
152
153 // Parent
154 18 xclose(ep[1]);
155 18 int error = 0;
156 18 ssize_t rc = xread(ep[0], &error, sizeof(error));
157 18 int xread_errno = errno;
158 18 xclose(ep[0]);
159 18 BUG_ON(rc > sizeof(error));
160
161
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 17 times.
18 if (rc == 0) {
162 // Child exec was successful
163 return pid;
164 }
165
166
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (unlikely(rc != sizeof(error))) {
167 error = (rc < 0) ? xread_errno : EPIPE;
168 }
169
170 1 int status;
171 1 xwaitpid(pid, &status, 0);
172 1 errno = error;
173 1 return -1;
174 }
175
176 17 int wait_child(pid_t pid)
177 {
178 17 int status;
179
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
17 if (unlikely(xwaitpid(pid, &status, 0) < 0)) {
180 return -errno;
181 }
182
183
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 2 times.
17 if (likely(WIFEXITED(status))) {
184 15 return WEXITSTATUS(status) & 0xFF;
185 }
186
187
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (likely(WIFSIGNALED(status))) {
188 2 return WTERMSIG(status) << 8;
189 }
190
191 LOG_ERROR("unhandled waitpid() status: %d", status);
192 return -EINVAL;
193 }
194