File: | 3rdparty/vboot/host/lib/subprocess.c |
Warning: | line 328, column 3 Null pointer passed to 1st parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* Copyright 2019 The ChromiumOS Authors | |||
2 | * Use of this source code is governed by a BSD-style license that can be | |||
3 | * found in the LICENSE file. | |||
4 | */ | |||
5 | ||||
6 | #include <errno(*__errno_location ()).h> | |||
7 | #include <fcntl.h> | |||
8 | #include <stdbool.h> | |||
9 | #include <stdlib.h> | |||
10 | #include <string.h> | |||
11 | #include <sys/wait.h> | |||
12 | #include <unistd.h> | |||
13 | ||||
14 | #include "2common.h" | |||
15 | #include "subprocess.h" | |||
16 | ||||
17 | #define MAX_CB_BUF_SIZE2048 2048 | |||
18 | ||||
19 | static int init_target_private(struct subprocess_target *target) | |||
20 | { | |||
21 | switch (target->type) { | |||
22 | case TARGET_BUFFER: | |||
23 | case TARGET_BUFFER_NULL_TERMINATED: | |||
24 | case TARGET_CALLBACK: | |||
25 | return pipe(target->priv.pipefd); | |||
26 | default: | |||
27 | return 0; | |||
28 | } | |||
29 | } | |||
30 | ||||
31 | static int flags_for_fd(int fd) | |||
32 | { | |||
33 | switch (fd) { | |||
34 | case STDIN_FILENO0: | |||
35 | return O_RDONLY00; | |||
36 | case STDOUT_FILENO1: | |||
37 | case STDERR_FILENO2: | |||
38 | return O_WRONLY01; | |||
39 | default: | |||
40 | return -1; | |||
41 | } | |||
42 | } | |||
43 | ||||
44 | static int connect_process_target(struct subprocess_target *target, int fd) | |||
45 | { | |||
46 | int target_fd; | |||
47 | ||||
48 | switch (target->type) { | |||
49 | case TARGET_NULL: | |||
50 | target_fd = open("/dev/null", flags_for_fd(fd)); | |||
51 | break; | |||
52 | case TARGET_FD: | |||
53 | target_fd = target->fd; | |||
54 | break; | |||
55 | case TARGET_FILE: | |||
56 | target_fd = fileno(target->file); | |||
57 | break; | |||
58 | case TARGET_BUFFER: | |||
59 | case TARGET_BUFFER_NULL_TERMINATED: | |||
60 | case TARGET_CALLBACK: | |||
61 | switch (fd) { | |||
62 | case STDIN_FILENO0: | |||
63 | target_fd = target->priv.pipefd[0]; | |||
64 | close(target->priv.pipefd[1]); | |||
65 | break; | |||
66 | case STDOUT_FILENO1: | |||
67 | case STDERR_FILENO2: | |||
68 | target_fd = target->priv.pipefd[1]; | |||
69 | close(target->priv.pipefd[0]); | |||
70 | break; | |||
71 | default: | |||
72 | return -1; | |||
73 | } | |||
74 | break; | |||
75 | default: | |||
76 | return -1; | |||
77 | } | |||
78 | ||||
79 | return dup2(target_fd, fd); | |||
80 | } | |||
81 | ||||
82 | static int process_target_input_buffer(struct subprocess_target *target) | |||
83 | { | |||
84 | ssize_t write_rv; | |||
85 | size_t bytes_to_write; | |||
86 | char *buf; | |||
87 | ||||
88 | switch (target->type) { | |||
89 | case TARGET_BUFFER: | |||
90 | bytes_to_write = target->buffer.size; | |||
91 | break; | |||
92 | case TARGET_BUFFER_NULL_TERMINATED: | |||
93 | bytes_to_write = strlen(target->buffer.buf); | |||
94 | break; | |||
95 | default: | |||
96 | return -1; | |||
97 | } | |||
98 | ||||
99 | buf = target->buffer.buf; | |||
100 | while (bytes_to_write) { | |||
101 | write_rv = write(target->priv.pipefd[1], buf, bytes_to_write); | |||
102 | if (write_rv <= 0) | |||
103 | return -1; | |||
104 | buf += write_rv; | |||
105 | bytes_to_write -= write_rv; | |||
106 | } | |||
107 | ||||
108 | return 0; | |||
109 | } | |||
110 | ||||
111 | static int process_target_input_cb(struct subprocess_target *target) | |||
112 | { | |||
113 | ssize_t write_rv, bytes_to_write; | |||
114 | char buf[MAX_CB_BUF_SIZE2048]; | |||
115 | char *bufptr; | |||
116 | ||||
117 | for (;;) { | |||
118 | bytes_to_write = target->callback.cb(buf, MAX_CB_BUF_SIZE2048, | |||
119 | target->callback.data); | |||
120 | if (bytes_to_write < 0 || bytes_to_write > MAX_CB_BUF_SIZE2048) | |||
121 | return -1; | |||
122 | if (bytes_to_write == 0) | |||
123 | return 0; | |||
124 | ||||
125 | bufptr = buf; | |||
126 | while (bytes_to_write) { | |||
127 | write_rv = write(target->priv.pipefd[1], bufptr, | |||
128 | bytes_to_write); | |||
129 | if (write_rv <= 0) | |||
130 | return -1; | |||
131 | bufptr += write_rv; | |||
132 | bytes_to_write -= write_rv; | |||
133 | } | |||
134 | } | |||
135 | } | |||
136 | ||||
137 | static int process_target_input(struct subprocess_target *target) | |||
138 | { | |||
139 | int rv; | |||
140 | ||||
141 | switch (target->type) { | |||
142 | case TARGET_BUFFER: | |||
143 | case TARGET_BUFFER_NULL_TERMINATED: | |||
144 | case TARGET_CALLBACK: | |||
145 | break; | |||
146 | default: | |||
147 | return 0; | |||
148 | } | |||
149 | ||||
150 | close(target->priv.pipefd[0]); | |||
151 | switch (target->type) { | |||
152 | case TARGET_BUFFER: | |||
153 | case TARGET_BUFFER_NULL_TERMINATED: | |||
154 | rv = process_target_input_buffer(target); | |||
155 | break; | |||
156 | case TARGET_CALLBACK: | |||
157 | rv = process_target_input_cb(target); | |||
158 | break; | |||
159 | default: | |||
160 | return -1; | |||
161 | } | |||
162 | ||||
163 | close(target->priv.pipefd[1]); | |||
164 | return rv; | |||
165 | } | |||
166 | ||||
167 | static int process_target_output_buffer(struct subprocess_target *target) | |||
168 | { | |||
169 | ssize_t read_rv; | |||
170 | size_t bytes_remaining; | |||
171 | ||||
172 | switch (target->type) { | |||
173 | case TARGET_BUFFER: | |||
174 | bytes_remaining = target->buffer.size; | |||
175 | break; | |||
176 | case TARGET_BUFFER_NULL_TERMINATED: | |||
177 | if (target->buffer.size == 0) | |||
178 | return -1; | |||
179 | bytes_remaining = target->buffer.size - 1; | |||
180 | break; | |||
181 | default: | |||
182 | return 0; | |||
183 | } | |||
184 | ||||
185 | target->buffer.bytes_consumed = 0; | |||
186 | while (bytes_remaining) { | |||
187 | read_rv = read( | |||
188 | target->priv.pipefd[0], | |||
189 | target->buffer.buf + target->buffer.bytes_consumed, | |||
190 | bytes_remaining); | |||
191 | if (read_rv < 0) | |||
192 | return -1; | |||
193 | if (read_rv == 0) | |||
194 | break; | |||
195 | target->buffer.bytes_consumed += read_rv; | |||
196 | bytes_remaining -= read_rv; | |||
197 | } | |||
198 | ||||
199 | if (target->type == TARGET_BUFFER_NULL_TERMINATED) | |||
200 | target->buffer.buf[target->buffer.bytes_consumed] = '\0'; | |||
201 | return 0; | |||
202 | } | |||
203 | ||||
204 | static int process_target_output_cb(struct subprocess_target *target) | |||
205 | { | |||
206 | char buf[MAX_CB_BUF_SIZE2048]; | |||
207 | ssize_t rv; | |||
208 | ||||
209 | for (;;) { | |||
210 | rv = read(target->priv.pipefd[0], buf, MAX_CB_BUF_SIZE2048); | |||
211 | if (rv < 0) | |||
212 | return -1; | |||
213 | if (rv == 0) | |||
214 | break; | |||
215 | if (target->callback.cb(buf, rv, target->callback.data) < 0) | |||
216 | return -1; | |||
217 | } | |||
218 | ||||
219 | return 0; | |||
220 | } | |||
221 | ||||
222 | static int process_target_output(struct subprocess_target *target) | |||
223 | { | |||
224 | int rv; | |||
225 | ||||
226 | switch (target->type) { | |||
227 | case TARGET_BUFFER: | |||
228 | case TARGET_BUFFER_NULL_TERMINATED: | |||
229 | case TARGET_CALLBACK: | |||
230 | break; | |||
231 | default: | |||
232 | return 0; | |||
233 | } | |||
234 | ||||
235 | close(target->priv.pipefd[1]); | |||
236 | switch (target->type) { | |||
237 | case TARGET_BUFFER: | |||
238 | case TARGET_BUFFER_NULL_TERMINATED: | |||
239 | rv = process_target_output_buffer(target); | |||
240 | break; | |||
241 | case TARGET_CALLBACK: | |||
242 | rv = process_target_output_cb(target); | |||
243 | break; | |||
244 | default: | |||
245 | return -1; | |||
246 | } | |||
247 | ||||
248 | close(target->priv.pipefd[0]); | |||
249 | return rv; | |||
250 | } | |||
251 | ||||
252 | static bool_Bool contains_spaces(const char *s) | |||
253 | { | |||
254 | for (size_t i = 0; s[i]; i++) { | |||
255 | if (isspace(s[i])((*__ctype_b_loc ())[(int) ((s[i]))] & (unsigned short int ) _ISspace)) | |||
256 | return true1; | |||
257 | } | |||
258 | return false0; | |||
259 | } | |||
260 | ||||
261 | static void subprocess_log_call(const char *const argv[]) | |||
262 | { | |||
263 | VB2_DEBUG("Run:")vb2ex_printf(__func__, "Run:"); | |||
264 | ||||
265 | for (size_t i = 0; argv[i]; i++) { | |||
266 | if (contains_spaces(argv[i])) | |||
267 | VB2_DEBUG_RAW(" '%s'", argv[i])vb2ex_printf(((void*)0), " '%s'", argv[i]); | |||
268 | else | |||
269 | VB2_DEBUG_RAW(" %s", argv[i])vb2ex_printf(((void*)0), " %s", argv[i]); | |||
270 | } | |||
271 | VB2_DEBUG_RAW("\n")vb2ex_printf(((void*)0), "\n"); | |||
272 | } | |||
273 | ||||
274 | struct subprocess_target subprocess_null = { | |||
275 | .type = TARGET_NULL, | |||
276 | }; | |||
277 | ||||
278 | struct subprocess_target subprocess_stdin = { | |||
279 | .type = TARGET_FD, | |||
280 | .fd = STDIN_FILENO0, | |||
281 | }; | |||
282 | ||||
283 | struct subprocess_target subprocess_stdout = { | |||
284 | .type = TARGET_FD, | |||
285 | .fd = STDOUT_FILENO1, | |||
286 | }; | |||
287 | ||||
288 | struct subprocess_target subprocess_stderr = { | |||
289 | .type = TARGET_FD, | |||
290 | .fd = STDERR_FILENO2, | |||
291 | }; | |||
292 | ||||
293 | test_mockable__attribute__((weak)) | |||
294 | int subprocess_run(const char *const argv[], | |||
295 | struct subprocess_target *input, | |||
296 | struct subprocess_target *output, | |||
297 | struct subprocess_target *error) | |||
298 | { | |||
299 | int status; | |||
300 | pid_t pid = -1; | |||
301 | ||||
302 | subprocess_log_call(argv); | |||
| ||||
303 | ||||
304 | if (!input) | |||
305 | input = &subprocess_stdin; | |||
306 | if (!output) | |||
307 | output = &subprocess_stdout; | |||
308 | if (!error) | |||
309 | error = &subprocess_stderr; | |||
310 | ||||
311 | if (init_target_private(input) < 0) | |||
312 | goto fail; | |||
313 | if (init_target_private(output) < 0) | |||
314 | goto fail; | |||
315 | if (init_target_private(error) < 0) | |||
316 | goto fail; | |||
317 | ||||
318 | if ((pid = fork()) < 0) | |||
319 | goto fail; | |||
320 | if (pid == 0) { | |||
321 | /* Child process */ | |||
322 | if (connect_process_target(input, STDIN_FILENO0) < 0) | |||
323 | goto fail; | |||
324 | if (connect_process_target(output, STDOUT_FILENO1) < 0) | |||
325 | goto fail; | |||
326 | if (connect_process_target(error, STDERR_FILENO2) < 0) | |||
327 | goto fail; | |||
328 | execvp(*argv, (char *const *)argv); | |||
| ||||
329 | goto fail; | |||
330 | } | |||
331 | ||||
332 | /* Parent process */ | |||
333 | if (process_target_input(input) < 0) | |||
334 | goto fail; | |||
335 | if (process_target_output(output) < 0) | |||
336 | goto fail; | |||
337 | if (process_target_output(error) < 0) | |||
338 | goto fail; | |||
339 | ||||
340 | if (waitpid(pid, &status, 0) < 0) | |||
341 | goto fail; | |||
342 | ||||
343 | if (WIFEXITED(status)(((status) & 0x7f) == 0)) | |||
344 | return WEXITSTATUS(status)(((status) & 0xff00) >> 8); | |||
345 | ||||
346 | fail: | |||
347 | VB2_DEBUG("Failed to execute external command: %s\n", strerror(errno))vb2ex_printf(__func__, "Failed to execute external command: %s\n" , strerror((*__errno_location ()))); | |||
348 | if (pid == 0) | |||
349 | exit(127); | |||
350 | return -1; | |||
351 | } |