Line | Branch | Exec | Source |
---|---|---|---|
1 | #include "feature.h" | ||
2 | #include <errno.h> | ||
3 | #include <signal.h> | ||
4 | #include <stdlib.h> | ||
5 | #include <sys/wait.h> | ||
6 | #include <unistd.h> | ||
7 | #include "fork-exec.h" | ||
8 | #include "debug.h" | ||
9 | #include "fd.h" | ||
10 | #include "log.h" | ||
11 | #include "numtostr.h" | ||
12 | #include "terminal/ioctl.h" | ||
13 | #include "xreadwrite.h" | ||
14 | |||
15 | // Reset ignored signal dispositions (i.e. as originally set up by | ||
16 | // set_basic_signal_dispositions()) to SIG_DFL | ||
17 | 21 | static bool reset_ignored_signals(void) | |
18 | { | ||
19 | // Note that handled signals don't need to be restored here, since | ||
20 | // they're necessarily and automatically reset after exec(3p) | ||
21 | 21 | static const int ignored_signals[] = { | |
22 | SIGINT, SIGQUIT, SIGTSTP, | ||
23 | SIGTTIN, SIGTTOU, SIGXFSZ, | ||
24 | SIGPIPE, SIGUSR1, SIGUSR2, | ||
25 | }; | ||
26 | |||
27 | 21 | struct sigaction action = {.sa_handler = SIG_DFL}; | |
28 |
1/2✓ Branch 0 (3→7) taken 21 times.
✗ Branch 1 (3→8) not taken.
|
21 | if (unlikely(sigemptyset(&action.sa_mask) != 0)) { |
29 | return false; | ||
30 | } | ||
31 | |||
32 |
2/2✓ Branch 0 (7→4) taken 189 times.
✓ Branch 1 (7→8) taken 21 times.
|
210 | for (size_t i = 0; i < ARRAYLEN(ignored_signals); i++) { |
33 | 189 | int r = sigaction(ignored_signals[i], &action, NULL); | |
34 |
1/2✓ Branch 0 (5→6) taken 189 times.
✗ Branch 1 (5→8) not taken.
|
189 | if (unlikely(r != 0)) { |
35 | return false; | ||
36 | } | ||
37 | } | ||
38 | |||
39 | return true; | ||
40 | } | ||
41 | |||
42 | // NOLINTNEXTLINE(readability-function-size) | ||
43 | 21 | static noreturn void child_process_exec ( | |
44 | const char **argv, | ||
45 | const int fd[3], | ||
46 | int error_fd, // Pipe to parent, for communicating pre-exec errors | ||
47 | unsigned int lines, | ||
48 | unsigned int columns, | ||
49 | bool drop_ctty | ||
50 | ) { | ||
51 |
1/2✓ Branch 0 (2→3) taken 21 times.
✗ Branch 1 (2→11) not taken.
|
21 | if (drop_ctty) { |
52 | 21 | term_drop_controlling_tty(STDIN_FILENO); | |
53 | } | ||
54 | |||
55 |
2/2✓ Branch 0 (12→4) taken 63 times.
✓ Branch 1 (12→13) taken 21 times.
|
84 | for (int i = STDIN_FILENO; i <= STDERR_FILENO; i++) { |
56 | 63 | int f = fd[i]; | |
57 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 63 times.
|
63 | bool ok = (i == f) ? fd_set_cloexec(f, false) : xdup3(f, i, 0) >= 0; |
58 |
1/2✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→10) taken 63 times.
|
63 | if (unlikely(!ok)) { |
59 | ✗ | goto error; | |
60 | } | ||
61 | } | ||
62 | |||
63 |
2/2✓ Branch 0 (13→14) taken 7 times.
✓ Branch 1 (13→16) taken 14 times.
|
21 | if (lines) { |
64 | 7 | setenv("LINES", uint_to_str(lines), 1); | |
65 | } | ||
66 |
2/2✓ Branch 0 (16→17) taken 7 times.
✓ Branch 1 (16→19) taken 14 times.
|
21 | if (columns) { |
67 | 7 | setenv("COLUMNS", uint_to_str(columns), 1); | |
68 | } | ||
69 | |||
70 |
1/2✗ Branch 0 (20→21) not taken.
✓ Branch 1 (20→22) taken 1 times.
|
21 | if (unlikely(!reset_ignored_signals())) { |
71 | ✗ | goto error; | |
72 | } | ||
73 | |||
74 | 1 | execvp(argv[0], (char**)argv); | |
75 | |||
76 | 1 | error:; | |
77 | 1 | int error = errno; | |
78 | 1 | error = xwrite(error_fd, &error, sizeof(error)); | |
79 | 1 | exit(42); | |
80 | } | ||
81 | |||
82 | 21 | static pid_t xwaitpid(pid_t pid, int *status, int options) | |
83 | { | ||
84 | 21 | pid_t ret; | |
85 | 42 | do { | |
86 | 21 | ret = waitpid(pid, status, options); | |
87 |
1/4✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 21 times.
✗ Branch 2 (5→3) not taken.
✗ Branch 3 (5→6) not taken.
|
21 | } while (unlikely(ret < 0 && errno == EINTR)); |
88 | 21 | return ret; | |
89 | } | ||
90 | |||
91 | 21 | pid_t fork_exec ( | |
92 | const char **argv, | ||
93 | int fd[3], | ||
94 | unsigned int lines, | ||
95 | unsigned int columns, | ||
96 | bool drop_ctty | ||
97 | ) { | ||
98 | // Create an "error pipe" before forking, so that child_process_exec() | ||
99 | // can signal pre-exec errors and allow the parent differentiate them | ||
100 | // from a successful exec(3) with a non-zero exit status | ||
101 | 21 | int ep[2]; | |
102 |
1/2✓ Branch 0 (3→4) taken 21 times.
✗ Branch 1 (3→33) not taken.
|
21 | if (xpipe2(ep, O_CLOEXEC) != 0) { |
103 | return -1; | ||
104 | } | ||
105 | |||
106 | 21 | BUG_ON(ep[0] <= STDERR_FILENO); | |
107 | 21 | BUG_ON(ep[1] <= STDERR_FILENO); | |
108 | 21 | BUG_ON(fd[0] <= STDERR_FILENO && fd[0] != 0); | |
109 | 21 | BUG_ON(fd[1] <= STDERR_FILENO && fd[1] != 1); | |
110 | 21 | BUG_ON(fd[2] <= STDERR_FILENO && fd[2] != 2); | |
111 | |||
112 | 21 | const pid_t pid = fork(); | |
113 |
1/2✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→19) taken 42 times.
|
42 | if (unlikely(pid == -1)) { |
114 | ✗ | xclose(ep[0]); | |
115 | ✗ | xclose(ep[1]); | |
116 | ✗ | return -1; | |
117 | } | ||
118 | |||
119 |
2/2✓ Branch 0 (19→20) taken 21 times.
✓ Branch 1 (19→21) taken 21 times.
|
42 | if (pid == 0) { |
120 | // Child | ||
121 | 21 | child_process_exec(argv, fd, ep[1], lines, columns, drop_ctty); | |
122 | BUG("child_process_exec() should never return"); | ||
123 | return -1; | ||
124 | } | ||
125 | |||
126 | // Parent | ||
127 | 21 | xclose(ep[1]); | |
128 | 21 | int error = 0; | |
129 | 21 | ssize_t rc = xread(ep[0], &error, sizeof(error)); | |
130 | 21 | int xread_errno = errno; | |
131 | 21 | xclose(ep[0]); | |
132 | 21 | BUG_ON(rc > sizeof(error)); | |
133 | |||
134 |
2/2✓ Branch 0 (26→27) taken 1 times.
✓ Branch 1 (26→33) taken 20 times.
|
21 | if (rc == 0) { |
135 | // Child exec was successful | ||
136 | return pid; | ||
137 | } | ||
138 | |||
139 |
1/2✗ Branch 0 (27→28) not taken.
✓ Branch 1 (27→31) taken 1 times.
|
1 | if (unlikely(rc != sizeof(error))) { |
140 | ✗ | error = (rc < 0) ? xread_errno : EPIPE; | |
141 | } | ||
142 | |||
143 | 1 | int status; | |
144 | 1 | xwaitpid(pid, &status, 0); | |
145 | 1 | errno = error; | |
146 | 1 | return -1; | |
147 | } | ||
148 | |||
149 | 20 | int wait_child(pid_t pid) | |
150 | { | ||
151 | 20 | int status; | |
152 |
1/2✗ Branch 0 (3→4) not taken.
✓ Branch 1 (3→5) taken 20 times.
|
20 | if (unlikely(xwaitpid(pid, &status, 0) < 0)) { |
153 | ✗ | return -errno; | |
154 | } | ||
155 | |||
156 |
2/2✓ Branch 0 (5→6) taken 18 times.
✓ Branch 1 (5→7) taken 2 times.
|
20 | if (likely(WIFEXITED(status))) { |
157 | 18 | return WEXITSTATUS(status) & 0xFF; | |
158 | } | ||
159 | |||
160 |
1/2✓ Branch 0 (7→8) taken 2 times.
✗ Branch 1 (7→9) not taken.
|
2 | if (likely(WIFSIGNALED(status))) { |
161 | 2 | return WTERMSIG(status) << 8; | |
162 | } | ||
163 | |||
164 | ✗ | LOG_ERROR("unhandled waitpid() status: %d", status); | |
165 | ✗ | return -EINVAL; | |
166 | } | ||
167 |