/src/freeradius-server/src/lib/server/exec.c
Line | Count | Source |
1 | | /* |
2 | | * This program is free software; you can redistribute it and/or modify |
3 | | * it under the terms of the GNU General Public License as published by |
4 | | * the Free Software Foundation; either version 2 of the License, or |
5 | | * (at your option) any later version. |
6 | | * |
7 | | * This program is distributed in the hope that it will be useful, |
8 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
9 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
10 | | * GNU General Public License for more details. |
11 | | * |
12 | | * You should have received a copy of the GNU General Public License |
13 | | * along with this program; if not, write to the Free Software |
14 | | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
15 | | */ |
16 | | |
17 | | /** |
18 | | * $Id: 14bb1a11c213ce1e2dc1226f354533ddbee6ee1d $ |
19 | | * |
20 | | * @file src/lib/server/exec.c |
21 | | * @brief Execute external programs. |
22 | | * |
23 | | * @copyright 2022-2023 Arran Cudbard-Bell (a.cudbardb@freeradius.org) |
24 | | * @copyright 2000-2004,2006 The FreeRADIUS server project |
25 | | */ |
26 | | RCSID("$Id: 14bb1a11c213ce1e2dc1226f354533ddbee6ee1d $") |
27 | | |
28 | | #include <stdint.h> |
29 | | |
30 | | #include <freeradius-devel/server/log.h> |
31 | | #include <freeradius-devel/server/exec.h> |
32 | | #include <freeradius-devel/server/exec_priv.h> |
33 | | #include <freeradius-devel/server/util.h> |
34 | | |
35 | | #define MAX_ENVP 1024 |
36 | | |
37 | | static _Thread_local char *env_exec_arr[MAX_ENVP]; /* Avoid allocing 8k on the stack */ |
38 | | |
39 | | /** Flatten a list into individual "char *" argv-style array |
40 | | * |
41 | | * @param[in] ctx to allocate boxes in. |
42 | | * @param[out] argv_p where output strings go |
43 | | * @param[in] in boxes to flatten |
44 | | * @return |
45 | | * - >= 0 number of array elements in argv |
46 | | * - <0 on error |
47 | | */ |
48 | | int fr_exec_value_box_list_to_argv(TALLOC_CTX *ctx, char ***argv_p, fr_value_box_list_t const *in) |
49 | 0 | { |
50 | 0 | char **argv; |
51 | 0 | unsigned int i = 0; |
52 | 0 | size_t argc = fr_value_box_list_num_elements(in); |
53 | 0 | fr_value_box_t const *first; |
54 | | |
55 | | /* |
56 | | * Check that we're not trying to run a program from |
57 | | * a tainted source. |
58 | | */ |
59 | 0 | first = fr_value_box_list_head(in); |
60 | 0 | if (first->type == FR_TYPE_GROUP) first = fr_value_box_list_head(&first->vb_group); |
61 | 0 | if (!first) { |
62 | 0 | fr_strerror_const("No program to run"); |
63 | 0 | return -1; |
64 | 0 | } |
65 | 0 | if (first->tainted) { |
66 | 0 | fr_strerror_printf("Program to run comes from tainted source - %pV", first); |
67 | 0 | return -1; |
68 | 0 | } |
69 | | |
70 | 0 | argv = talloc_zero_array(ctx, char *, argc + 1); |
71 | 0 | if (!argv) return -1; |
72 | | |
73 | 0 | fr_value_box_list_foreach(in, vb) { |
74 | | /* |
75 | | * Print the children of each group into the argv array. |
76 | | */ |
77 | 0 | argv[i] = fr_value_box_list_aprint(argv, &vb->vb_group, NULL, NULL); |
78 | 0 | if (!argv[i]) { |
79 | 0 | talloc_free(argv); |
80 | 0 | return -1; |
81 | 0 | } |
82 | 0 | i++; |
83 | 0 | } |
84 | | |
85 | 0 | *argv_p = argv; |
86 | |
|
87 | 0 | return argc; |
88 | 0 | } |
89 | | |
90 | | /** Print debug information showing the arguments and environment for a process |
91 | | * |
92 | | * @param[in] request The current request, may be NULL. |
93 | | * @param[in] argv_in arguments to pass to process. |
94 | | * @param[in] env_in environment to pass to process. |
95 | | * @param[in] env_inherit print debug for the environment from the environment. |
96 | | */ |
97 | | static inline CC_HINT(always_inline) void exec_debug(request_t *request, char **argv_in, char **env_in, bool env_inherit) |
98 | 0 | { |
99 | 0 | char **p; |
100 | |
|
101 | 0 | if (argv_in) for (p = argv_in; *p; p++) ROPTIONAL(RDEBUG3, DEBUG3, "arg[%d] %s", (unsigned int)(p - argv_in), *p); |
102 | 0 | if (env_in) for (p = env_in; *p; p++) ROPTIONAL(RDEBUG3, DEBUG3, "export %s", *p); |
103 | 0 | if (env_inherit) for (p = environ; *p; p++) ROPTIONAL(RDEBUG3, DEBUG3, "export %s", *p); |
104 | 0 | } |
105 | | |
106 | | /** Convert pairs from a request and a list of pairs into environmental variables |
107 | | * |
108 | | * @param[out] env_p Where to write an array of \0 terminated strings. |
109 | | * @param[in] env_len Length of env_p. |
110 | | * @param[out] env_sbuff To write environmental variables too. Each variable |
111 | | * will be written to the buffer, and separated with |
112 | | * a '\0'. |
113 | | * @param[in] env_m an array of markers of the same length as env_len. |
114 | | * @param[in] request Will look for &control.Exec-Export items to convert to |
115 | | * env vars. |
116 | | * @param[in] env_pairs Other items to convert to environmental variables. |
117 | | * The dictionary attribute name will be converted to |
118 | | * uppercase, and all '-' converted to '_' and will form |
119 | | * the variable name. |
120 | | * @param[in] env_escape Wrap string values in double quotes, and apply doublequote |
121 | | * escaping to all environmental variable values. |
122 | | * @return |
123 | | * - The number of environmental variables created. |
124 | | * - -1 on failure. |
125 | | */ |
126 | | static inline CC_HINT(nonnull(1,3,4,5)) CC_HINT(always_inline) |
127 | | int exec_pair_to_env(char **env_p, size_t env_len, |
128 | | fr_sbuff_t *env_sbuff, fr_sbuff_marker_t env_m[], |
129 | | request_t *request, fr_pair_list_t *env_pairs, bool env_escape) |
130 | 0 | { |
131 | 0 | char *p; |
132 | 0 | size_t i, j; |
133 | 0 | fr_dcursor_t cursor; |
134 | 0 | fr_dict_attr_t const *da; |
135 | 0 | fr_sbuff_t sbuff = FR_SBUFF_BIND_CURRENT(env_sbuff); |
136 | |
|
137 | 0 | if (!env_pairs) { |
138 | 0 | env_p[0] = NULL; |
139 | 0 | return 0; |
140 | 0 | } |
141 | | |
142 | | /* |
143 | | * Set up the environment variables in the |
144 | | * parent, so we don't call libc functions that |
145 | | * hold mutexes. They might be locked when we fork, |
146 | | * and will remain locked in the child. |
147 | | */ |
148 | 0 | i = 0; |
149 | 0 | fr_pair_list_foreach_leaf(env_pairs, vp) { |
150 | 0 | fr_sbuff_marker(&env_m[i], &sbuff); |
151 | |
|
152 | 0 | if (fr_sbuff_in_strcpy(&sbuff, vp->da->name) <= 0) { |
153 | 0 | fr_strerror_printf("Out of buffer space adding attribute name"); |
154 | 0 | return -1; |
155 | 0 | } |
156 | | |
157 | | /* |
158 | | * POSIX only allows names to contain |
159 | | * uppercase chars, digits, and |
160 | | * underscores. Digits are not allowed |
161 | | * for the first char. |
162 | | */ |
163 | 0 | p = fr_sbuff_current(&env_m[i]); |
164 | 0 | if (isdigit((uint8_t)*p)) *p++ = '_'; |
165 | 0 | for (; p < fr_sbuff_current(&sbuff); p++) { |
166 | 0 | if (isalpha((uint8_t)*p)) *p = toupper((uint8_t) *p); |
167 | 0 | else if (*p == '-') *p = '_'; |
168 | 0 | else if (isdigit((uint8_t)*p)) goto next; |
169 | 0 | else *p = '_'; |
170 | 0 | } |
171 | | |
172 | 0 | if (fr_sbuff_in_char(&sbuff, '=') <= 0) { |
173 | 0 | fr_strerror_printf("Out of buffer space"); |
174 | 0 | return -1; |
175 | 0 | } |
176 | | |
177 | 0 | if (env_escape) { |
178 | 0 | if (fr_value_box_print_quoted(&sbuff, &vp->data, T_DOUBLE_QUOTED_STRING) < 0) { |
179 | 0 | fr_strerror_printf("Out of buffer space adding attribute value for %pV", &vp->data); |
180 | 0 | return -1; |
181 | 0 | } |
182 | 0 | } else { |
183 | | /* |
184 | | * This can be zero length for empty strings |
185 | | * |
186 | | * Note we don't do double quote escaping here, |
187 | | * we just escape unprintable chars. |
188 | | * |
189 | | * Environmental variable values are not |
190 | | * restricted we likely wouldn't need to do |
191 | | * any escaping if we weren't dealing with C |
192 | | * strings. |
193 | | * |
194 | | * If we end up passing binary data through |
195 | | * then the user can unescape the octal |
196 | | * sequences on the other side. |
197 | | * |
198 | | * We unfortunately still need to escape '\' |
199 | | * because of this. |
200 | | */ |
201 | 0 | if (fr_value_box_print(&sbuff, &vp->data, &fr_value_escape_unprintables) < 0) { |
202 | 0 | fr_strerror_printf("Out of buffer space adding attribute value for %pV", &vp->data); |
203 | 0 | return -1; |
204 | 0 | } |
205 | 0 | } |
206 | 0 | if (fr_sbuff_in_char(&sbuff, '\0') <= 0) { |
207 | 0 | fr_strerror_printf("Out of buffer space"); |
208 | 0 | return -1; |
209 | 0 | } |
210 | | |
211 | 0 | next: |
212 | 0 | i++; |
213 | 0 | if (i == (env_len - 1)) break; |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * Do this as a separate step so that if env_sbuff |
218 | | * is extended at any point during the conversion |
219 | | * the sbuff we use is the final one. |
220 | | */ |
221 | 0 | for (j = 0; j < i; j++) { |
222 | 0 | env_p[j] = fr_sbuff_current(&env_m[j]); |
223 | 0 | } |
224 | |
|
225 | 0 | da = fr_dict_attr_child_by_num(fr_dict_root(fr_dict_internal()), FR_EXEC_EXPORT); |
226 | 0 | if (da) { |
227 | 0 | fr_pair_t *vp; |
228 | |
|
229 | 0 | for (vp = fr_pair_dcursor_by_da_init(&cursor, &request->control_pairs, da); |
230 | 0 | vp; |
231 | 0 | vp = fr_dcursor_next(&cursor)) { |
232 | 0 | env_p[i++] = UNCONST(char *, vp->vp_strvalue); |
233 | 0 | } |
234 | 0 | } |
235 | |
|
236 | 0 | if (unlikely(i == (env_len - 1))) { |
237 | 0 | fr_strerror_printf("Out of space for environmental variables"); |
238 | 0 | return -1; |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * NULL terminate for execve |
243 | | */ |
244 | 0 | env_p[i] = NULL; |
245 | |
|
246 | 0 | return i; |
247 | 0 | } |
248 | | |
249 | | /** Convert env_pairs into an array of environmental variables using thread local buffers |
250 | | * |
251 | | * @param[in] request Will be searched for control.Exec-Export pairs. |
252 | | * @param[in] env_pairs env_pairs to put into into the environment. May be NULL. |
253 | | * @param[in] env_escape Wrap string values in double quotes, and apply doublequote |
254 | | * escaping to all environmental variable values. |
255 | | * @return |
256 | | * - An array of environmental variable definitions, valid until the next call |
257 | | * to fr_exec_pair_to_env within the same thread. |
258 | | * - NULL on error. Error retrievable fr_strerror(). |
259 | | */ |
260 | | char **fr_exec_pair_to_env(request_t *request, fr_pair_list_t *env_pairs, bool env_escape) |
261 | 0 | { |
262 | 0 | static _Thread_local char *env_arr[MAX_ENVP]; /* Avoid allocing 8k on the stack */ |
263 | 0 | static _Thread_local char env_buff[NUM_ELEMENTS(env_arr) * 128]; /* Avoid allocing 128k on the stack */ |
264 | 0 | static _Thread_local fr_sbuff_marker_t env_m[NUM_ELEMENTS(env_arr)]; |
265 | |
|
266 | 0 | if (exec_pair_to_env(env_arr, NUM_ELEMENTS(env_arr), |
267 | 0 | &FR_SBUFF_OUT(env_buff, sizeof(env_buff)), env_m, |
268 | 0 | request, env_pairs, env_escape) < 0) return NULL; |
269 | | |
270 | 0 | return env_arr; |
271 | 0 | } |
272 | | |
273 | | /** Start a child process |
274 | | * |
275 | | * We try to be fail-safe here. So if ANYTHING goes wrong, we exit with status 1. |
276 | | * |
277 | | * @param[in] argv array of arguments to pass to child. |
278 | | * @param[in] envp array of environment variables in form `<attr>=<val>` |
279 | | * @param[in] exec_wait if true, redirect child process' stdin, stdout, stderr |
280 | | * to the pipes provided, redirecting any to /dev/null |
281 | | * where no pipe was provided. If false redirect |
282 | | * stdin, and stdout to /dev/null. |
283 | | * @param[in] debug If true, and !exec_wait, don't molest stderr. |
284 | | * @param[in] stdin_pipe the pipe used to write data to the process. STDIN will |
285 | | * be set to stdin_pipe[0], stdin_pipe[1] will be closed. |
286 | | * @param[in] stdout_pipe the pipe used to read data from the process. |
287 | | * STDOUT will be set to stdout_pipe[1], stdout_pipe[0] |
288 | | * will be closed. |
289 | | * @param[in] stderr_pipe the pipe used to read error text from the process. |
290 | | * STDERR will be set to stderr_pipe[1], stderr_pipe[0] |
291 | | * will be closed. |
292 | | */ |
293 | | static NEVER_RETURNS void exec_child(char **argv, char **envp, |
294 | | bool exec_wait, bool debug, |
295 | | int stdin_pipe[static 2], int stdout_pipe[static 2], int stderr_pipe[static 2]) |
296 | 0 | { |
297 | 0 | int devnull; |
298 | | |
299 | | /* |
300 | | * Open STDIN to /dev/null |
301 | | */ |
302 | 0 | devnull = open("/dev/null", O_RDWR); |
303 | 0 | if (devnull < 0) { |
304 | 0 | fprintf(stderr, "Failed opening /dev/null: %s\n", fr_syserror(errno)); |
305 | | |
306 | | /* |
307 | | * Where the status code is interpreted as a module rcode |
308 | | * one is subtracted from it, to allow 0 to equal success |
309 | | * |
310 | | * 2 is RLM_MODULE_FAIL + 1 |
311 | | */ |
312 | 0 | exit(2); |
313 | 0 | } |
314 | | |
315 | | /* |
316 | | * Only massage the pipe handles if the parent |
317 | | * has created them. |
318 | | */ |
319 | 0 | if (exec_wait) { |
320 | 0 | if (stdin_pipe[1] >= 0) { |
321 | 0 | close(stdin_pipe[1]); |
322 | 0 | dup2(stdin_pipe[0], STDIN_FILENO); |
323 | 0 | } else { |
324 | 0 | dup2(devnull, STDIN_FILENO); |
325 | 0 | } |
326 | |
|
327 | 0 | if (stdout_pipe[1] >= 0) { |
328 | 0 | close(stdout_pipe[0]); |
329 | 0 | dup2(stdout_pipe[1], STDOUT_FILENO); |
330 | 0 | } else { |
331 | 0 | dup2(devnull, STDOUT_FILENO); |
332 | 0 | } |
333 | |
|
334 | 0 | if (stderr_pipe[1] >= 0) { |
335 | 0 | close(stderr_pipe[0]); |
336 | 0 | dup2(stderr_pipe[1], STDERR_FILENO); |
337 | 0 | } else { |
338 | 0 | dup2(devnull, STDERR_FILENO); |
339 | 0 | } |
340 | 0 | } else { /* no pipe, STDOUT should be /dev/null */ |
341 | 0 | dup2(devnull, STDIN_FILENO); |
342 | 0 | dup2(devnull, STDOUT_FILENO); |
343 | | |
344 | | /* |
345 | | * If we're not debugging, then we can't do |
346 | | * anything with the error messages, so we throw |
347 | | * them away. |
348 | | * |
349 | | * If we are debugging, then we want the error |
350 | | * messages to go to the STDERR of the server. |
351 | | */ |
352 | 0 | if (!debug) dup2(devnull, STDERR_FILENO); |
353 | 0 | } |
354 | |
|
355 | 0 | close(devnull); |
356 | | |
357 | | /* |
358 | | * The server may have MANY FD's open. We don't |
359 | | * want to leave dangling FD's for the child process |
360 | | * to play funky games with, so we close them. |
361 | | */ |
362 | 0 | fr_closefrom(STDERR_FILENO + 1); |
363 | | |
364 | | /* |
365 | | * Disarm the thread local destructors |
366 | | * |
367 | | * It's not safe to free memory between fork and exec. |
368 | | */ |
369 | 0 | fr_atexit_thread_local_disarm_all(); |
370 | | |
371 | | /* |
372 | | * Disarm the global destructors for the same reason |
373 | | */ |
374 | 0 | fr_atexit_global_disarm_all(); |
375 | | |
376 | | /* |
377 | | * I swear the signature for execve is wrong and should |
378 | | * take 'char const * const argv[]'. |
379 | | * |
380 | | * Note: execve(), unlike system(), treats all the space |
381 | | * delimited arguments as literals, so there's no need |
382 | | * to perform additional escaping. |
383 | | */ |
384 | 0 | execve(argv[0], argv, envp); |
385 | 0 | printf("Failed to execute \"%s\": %s", argv[0], fr_syserror(errno)); /* fork output will be captured */ |
386 | | |
387 | | /* |
388 | | * Where the status code is interpreted as a module rcode |
389 | | * one is subtracted from it, to allow 0 to equal success |
390 | | * |
391 | | * 2 is RLM_MODULE_FAIL + 1 |
392 | | */ |
393 | 0 | exit(2); |
394 | 0 | } |
395 | | |
396 | | /** Merge extra environmental variables and potentially the inherited environment |
397 | | * |
398 | | * @param[in] env_in to merge. |
399 | | * @param[in] env_inherit inherite environment from radiusd. |
400 | | * @return merged environmental variables. |
401 | | */ |
402 | | static |
403 | | char **exec_build_env(char **env_in, bool env_inherit) |
404 | 0 | { |
405 | 0 | size_t num_in, num_environ; |
406 | | |
407 | | /* |
408 | | * Not inheriting the radiusd environment, just return whatever we were given. |
409 | | */ |
410 | 0 | if (!env_inherit) { |
411 | 0 | return env_in; |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * No additional environment variables, just return the ones from radiusd. |
416 | | */ |
417 | 0 | if (!env_in) return environ; |
418 | | |
419 | 0 | for (num_environ = 0; environ[num_environ] != NULL; num_environ++) { |
420 | | /* nothing */ |
421 | 0 | } |
422 | | |
423 | | /* |
424 | | * No room to copy anything after the environment variables. |
425 | | */ |
426 | 0 | if (((num_environ + 1) >= NUM_ELEMENTS(env_exec_arr))) { |
427 | 0 | return environ; |
428 | 0 | } |
429 | | |
430 | | /* |
431 | | * Copy the radiusd environment to the local array |
432 | | */ |
433 | 0 | memcpy(env_exec_arr, environ, (num_environ + 1) * sizeof(environ[0])); |
434 | |
|
435 | 0 | for (num_in = 0; env_in[num_in] != NULL; num_in++) { |
436 | 0 | if ((num_environ + num_in + 1) >= NUM_ELEMENTS(env_exec_arr)) break; |
437 | 0 | } |
438 | |
|
439 | 0 | memcpy(env_exec_arr + num_environ, env_in, num_in * sizeof(environ[0])); |
440 | 0 | env_exec_arr[num_environ + num_in] = NULL; |
441 | |
|
442 | 0 | return env_exec_arr; |
443 | 0 | } |
444 | | |
445 | | /** Execute a program without waiting for the program to finish. |
446 | | * |
447 | | * @param[in] el event list to insert reaper child into. |
448 | | * @param[in] argv_in arg[0] is the path to the program, arg[...] are arguments |
449 | | * to pass to the program. |
450 | | * @param[in] env_in any additional environmental variables to pass to the program. |
451 | | * @param[in] env_inherit Inherit the environment from the current process. |
452 | | * This will be merged with any variables from env_pairs. |
453 | | * @param[in] debug If true, STDERR will be left open and pointing to the stderr |
454 | | * descriptor of the parent. |
455 | | * @return |
456 | | * - <0 on error. Error retrievable fr_strerror(). |
457 | | * - 0 on success |
458 | | * |
459 | | * @todo - maybe take an fr_dcursor_t instead of env_pairs? That |
460 | | * would allow finer-grained control over the attributes to put into |
461 | | * the environment. |
462 | | */ |
463 | | int fr_exec_fork_nowait(fr_event_list_t *el, char **argv_in, char **env_in, bool env_inherit, bool debug) |
464 | 0 | { |
465 | 0 | char **env; |
466 | 0 | pid_t pid; |
467 | |
|
468 | 0 | env = exec_build_env(env_in, env_inherit); |
469 | 0 | pid = fork(); |
470 | | /* |
471 | | * The child never returns from calling exec_child(); |
472 | | */ |
473 | 0 | if (pid == 0) { |
474 | 0 | int unused[2] = { -1, -1 }; |
475 | |
|
476 | 0 | exec_child(argv_in, env, false, debug, unused, unused, unused); |
477 | 0 | } |
478 | |
|
479 | 0 | if (pid < 0) { |
480 | 0 | fr_strerror_printf("Couldn't fork %s", argv_in[0]); |
481 | 0 | error: |
482 | 0 | return -1; |
483 | 0 | } |
484 | | |
485 | | /* |
486 | | * Ensure that we can clean up any child processes. We |
487 | | * don't want them left over as zombies. |
488 | | */ |
489 | 0 | if (fr_event_pid_reap(el, pid, NULL, NULL) < 0) { |
490 | 0 | int status; |
491 | | |
492 | | /* |
493 | | * Try and cleanup... really we have |
494 | | * no idea what state things are in. |
495 | | */ |
496 | 0 | kill(pid, SIGKILL); |
497 | 0 | waitpid(pid, &status, WNOHANG); |
498 | 0 | goto error; |
499 | 0 | } |
500 | | |
501 | 0 | return 0; |
502 | 0 | } |
503 | | |
504 | | /** Execute a program assuming that the caller waits for it to finish. |
505 | | * |
506 | | * The caller takes responsibility for calling waitpid() on the returned PID. |
507 | | * |
508 | | * The caller takes responsibility for reading from the returned FD, |
509 | | * and closing it. |
510 | | * |
511 | | * @param[out] pid_p The PID of the child |
512 | | * @param[out] stdin_fd The stdin FD of the child. |
513 | | * @param[out] stdout_fd The stdout FD of the child. |
514 | | * @param[out] stderr_fd The stderr FD of the child. |
515 | | * @param[in] argv_in arg[0] is the path to the program, arg[...] are arguments |
516 | | * to pass to the program. |
517 | | * @param[in] env_in Environmental variables to pass to the program. |
518 | | * @param[in] env_inherit Inherit the environment from the current process. |
519 | | * This will be merged with any variables from env_pairs. |
520 | | * @param[in] debug If true, STDERR will be left open and pointing to the stderr |
521 | | * descriptor of the parent, if no stderr_fd pointer is provided. |
522 | | * @return |
523 | | * - <0 on error. Error retrievable fr_strerror(). |
524 | | * - 0 on success. |
525 | | * |
526 | | * @todo - maybe take an fr_dcursor_t instead of env_pairs? That |
527 | | * would allow finer-grained control over the attributes to put into |
528 | | * the environment. |
529 | | */ |
530 | | int fr_exec_fork_wait(pid_t *pid_p, |
531 | | int *stdin_fd, int *stdout_fd, int *stderr_fd, |
532 | | char **argv_in, char **env_in, bool env_inherit, bool debug) |
533 | 0 | { |
534 | 0 | char **env; |
535 | 0 | pid_t pid; |
536 | 0 | int stdin_pipe[2] = {-1, -1}; |
537 | 0 | int stderr_pipe[2] = {-1, -1}; |
538 | 0 | int stdout_pipe[2] = {-1, -1}; |
539 | |
|
540 | 0 | if (stdin_fd) { |
541 | 0 | if (pipe(stdin_pipe) < 0) { |
542 | 0 | fr_strerror_const("Failed opening pipe to write to child"); |
543 | |
|
544 | 0 | error1: |
545 | 0 | return -1; |
546 | 0 | } |
547 | 0 | if (fr_nonblock(stdin_pipe[1]) < 0) { |
548 | 0 | fr_strerror_const("Error setting stdin to nonblock"); |
549 | 0 | goto error2; |
550 | 0 | } |
551 | 0 | } |
552 | | |
553 | 0 | if (stdout_fd) { |
554 | 0 | if (pipe(stdout_pipe) < 0) { |
555 | 0 | fr_strerror_const("Failed opening pipe to read from child"); |
556 | |
|
557 | 0 | error2: |
558 | 0 | close(stdin_pipe[0]); |
559 | 0 | close(stdin_pipe[1]); |
560 | 0 | goto error1; |
561 | 0 | } |
562 | 0 | if (fr_nonblock(stdout_pipe[0]) < 0) { |
563 | 0 | fr_strerror_const("Error setting stdout to nonblock"); |
564 | 0 | goto error3; |
565 | 0 | } |
566 | 0 | } |
567 | | |
568 | 0 | if (stderr_fd) { |
569 | 0 | if (pipe(stderr_pipe) < 0) { |
570 | 0 | fr_strerror_const("Failed opening pipe to read from child"); |
571 | |
|
572 | 0 | error3: |
573 | 0 | close(stdout_pipe[0]); |
574 | 0 | close(stdout_pipe[1]); |
575 | 0 | goto error2; |
576 | 0 | } |
577 | 0 | if (fr_nonblock(stderr_pipe[0]) < 0) { |
578 | 0 | fr_strerror_const("Error setting stderr to nonblock"); |
579 | 0 | close(stderr_pipe[0]); |
580 | 0 | close(stderr_pipe[1]); |
581 | 0 | goto error3; |
582 | 0 | } |
583 | 0 | } |
584 | | |
585 | 0 | env = exec_build_env(env_in, env_inherit); |
586 | 0 | pid = fork(); |
587 | | |
588 | | /* |
589 | | * The child never returns from calling exec_child(); |
590 | | */ |
591 | 0 | if (pid == 0) exec_child(argv_in, env, true, debug, stdin_pipe, stdout_pipe, stderr_pipe); |
592 | 0 | if (pid < 0) { |
593 | 0 | fr_strerror_printf("Couldn't fork %s", argv_in[0]); |
594 | 0 | *pid_p = -1; /* Ensure the PID is set even if the caller didn't check the return code */ |
595 | 0 | goto error3; |
596 | 0 | } |
597 | | |
598 | | /* |
599 | | * Tell the caller the childs PID, and the FD to read from. |
600 | | */ |
601 | 0 | *pid_p = pid; |
602 | |
|
603 | 0 | if (stdin_fd) { |
604 | 0 | *stdin_fd = stdin_pipe[1]; |
605 | 0 | close(stdin_pipe[0]); |
606 | 0 | } |
607 | |
|
608 | 0 | if (stdout_fd) { |
609 | 0 | *stdout_fd = stdout_pipe[0]; |
610 | 0 | close(stdout_pipe[1]); |
611 | 0 | } |
612 | |
|
613 | 0 | if (stderr_fd) { |
614 | 0 | *stderr_fd = stderr_pipe[0]; |
615 | 0 | close(stderr_pipe[1]); |
616 | 0 | } |
617 | |
|
618 | 0 | return 0; |
619 | 0 | } |
620 | | |
621 | | /** Similar to fr_exec_oneshot, but does not attempt to parse output |
622 | | * |
623 | | * @param[in] request currently being processed, may be NULL. |
624 | | * @param[in] args to call as a fr_value_box_list_t. Program will |
625 | | * be the first box and arguments in the subsequent boxes. |
626 | | * @param[in] env_pairs list of pairs to be presented as environment variables |
627 | | * to the child. |
628 | | * @param[in] env_escape Wrap string values in double quotes, and apply doublequote |
629 | | * escaping to all environmental variable values. |
630 | | * @param[in] env_inherit Inherit the environment from the current process. |
631 | | * This will be merged with any variables from env_pairs. |
632 | | * @return |
633 | | * - 0 on success. |
634 | | * - -1 on error. |
635 | | */ |
636 | | int fr_exec_oneshot_nowait(request_t *request, |
637 | | fr_value_box_list_t *args, fr_pair_list_t *env_pairs, |
638 | | bool env_escape, bool env_inherit) |
639 | 0 | { |
640 | 0 | char **argv = NULL; |
641 | 0 | char **env = NULL; |
642 | 0 | int ret; |
643 | |
|
644 | 0 | if (unlikely(fr_exec_value_box_list_to_argv(unlang_interpret_frame_talloc_ctx(request), &argv, args) < 0)) { |
645 | 0 | RPEDEBUG("Failed converting boxes to argument strings"); |
646 | 0 | return -1; |
647 | 0 | } |
648 | | |
649 | 0 | if (env_pairs) { |
650 | 0 | env = fr_exec_pair_to_env(request, env_pairs, env_escape); |
651 | 0 | if (unlikely(env == NULL)) { |
652 | 0 | RPEDEBUG("Failed creating environment pairs"); |
653 | 0 | return -1; |
654 | 0 | } |
655 | 0 | } |
656 | | |
657 | 0 | if (RDEBUG_ENABLED3) exec_debug(request, argv, env, env_inherit); |
658 | 0 | ret = fr_exec_fork_nowait(unlang_interpret_event_list(request), argv, env, |
659 | 0 | env_inherit, ROPTIONAL_ENABLED(RDEBUG_ENABLED2, DEBUG_ENABLED2)); |
660 | 0 | talloc_free(argv); |
661 | 0 | if (unlikely(ret < 0)) RPEDEBUG("Failed executing program"); |
662 | |
|
663 | 0 | return ret; |
664 | 0 | } |
665 | | |
666 | | /** Cleans up an exec'd process on error |
667 | | * |
668 | | * This function is intended to be called at any point after a successful |
669 | | * #fr_exec_oneshot call in order to release resources and cleanup |
670 | | * zombie processes. |
671 | | * |
672 | | * @param[in] exec state to cleanup. |
673 | | * @param[in] signal If non-zero, and we think the process is still |
674 | | * running, send it a signal to cause it to exit. |
675 | | * The PID reaper we insert here will cleanup its |
676 | | * state so it doesn't become a zombie. |
677 | | * |
678 | | */ |
679 | | void fr_exec_oneshot_cleanup(fr_exec_state_t *exec, int signal) |
680 | 0 | { |
681 | 0 | request_t *request = exec->request; |
682 | 0 | fr_event_list_t *el = unlang_interpret_event_list(request); |
683 | |
|
684 | 0 | if (exec->pid >= 0) { |
685 | 0 | RDEBUG3("Cleaning up exec state for PID %u", exec->pid); |
686 | |
|
687 | 0 | } else if (exec->failed != FR_EXEC_FAIL_NONE) { |
688 | 0 | RDEBUG3("Cleaning up failed exec"); |
689 | 0 | } |
690 | | |
691 | | /* |
692 | | * There's still an EV_PROC event installed |
693 | | * for the PID remove it (there's a destructor). |
694 | | */ |
695 | 0 | if (exec->ev_pid) { |
696 | 0 | talloc_const_free(exec->ev_pid); |
697 | 0 | fr_assert(!exec->ev_pid); /* Should be NULLified by destructor */ |
698 | 0 | } |
699 | |
|
700 | 0 | if (exec->stdout_fd >= 0) { |
701 | 0 | if (fr_event_fd_delete(el, exec->stdout_fd, FR_EVENT_FILTER_IO) < 0){ |
702 | 0 | RPERROR("Failed removing stdout handler"); |
703 | 0 | } |
704 | 0 | close(exec->stdout_fd); |
705 | 0 | exec->stdout_fd = -1; |
706 | 0 | } |
707 | |
|
708 | 0 | if (exec->stderr_fd >= 0) { |
709 | 0 | if (fr_event_fd_delete(el, exec->stderr_fd, FR_EVENT_FILTER_IO) < 0) { |
710 | 0 | RPERROR("Failed removing stderr handler"); |
711 | 0 | } |
712 | 0 | close(exec->stderr_fd); |
713 | 0 | exec->stderr_fd = -1; |
714 | 0 | } |
715 | |
|
716 | 0 | if (exec->pid >= 0) { |
717 | 0 | if (signal > 0) kill(exec->pid, signal); |
718 | |
|
719 | 0 | if (unlikely(fr_event_pid_reap(el, exec->pid, NULL, NULL) < 0)) { |
720 | 0 | int status; |
721 | |
|
722 | 0 | RPERROR("Failed setting up async PID reaper, PID %u may now be a zombie", exec->pid); |
723 | | |
724 | | /* |
725 | | * Try and cleanup... really we have |
726 | | * no idea what state things are in. |
727 | | */ |
728 | 0 | kill(exec->pid, SIGKILL); |
729 | 0 | waitpid(exec->pid, &status, WNOHANG); |
730 | 0 | } |
731 | 0 | exec->pid = -1; |
732 | 0 | } |
733 | |
|
734 | 0 | FR_TIMER_DELETE(&exec->ev); |
735 | 0 | } |
736 | | |
737 | | /* |
738 | | * Callback when exec has completed. Record the status and tidy up. |
739 | | */ |
740 | | static void exec_reap(fr_event_list_t *el, pid_t pid, int status, void *uctx) |
741 | 0 | { |
742 | 0 | fr_exec_state_t *exec = uctx; /* may not be talloced */ |
743 | 0 | request_t *request = exec->request; |
744 | 0 | int wait_status = 0; |
745 | 0 | int ret; |
746 | |
|
747 | 0 | if (!fr_cond_assert(pid == exec->pid)) RWDEBUG("Event PID %u and exec->pid %u do not match", pid, exec->pid); |
748 | | |
749 | | /* |
750 | | * Reap the process. This is needed so the processes |
751 | | * don't stick around indefinitely. libkqueue/kqueue |
752 | | * does not do this for us! |
753 | | */ |
754 | 0 | ret = waitpid(exec->pid, &wait_status, WNOHANG); |
755 | 0 | if (ret < 0) { |
756 | 0 | RWDEBUG("Failed reaping PID %i: %s", exec->pid, fr_syserror(errno)); |
757 | | /* |
758 | | * Either something cleaned up the process before us |
759 | | * (bad!), or the notification system is broken |
760 | | * (also bad!) |
761 | | * |
762 | | * This could be caused by 3rd party libraries. |
763 | | */ |
764 | 0 | } else if (ret == 0) { |
765 | 0 | RWDEBUG("Something reaped PID %d before us!", exec->pid); |
766 | 0 | wait_status = status; |
767 | 0 | } |
768 | | |
769 | | /* |
770 | | * kevent should be returning an identical status value |
771 | | * to waitpid. |
772 | | */ |
773 | 0 | if (wait_status != status) RWDEBUG("Exit status from waitpid (%d) and kevent (%d) disagree", |
774 | 0 | wait_status, status); |
775 | |
|
776 | 0 | if (WIFEXITED(wait_status)) { |
777 | 0 | RDEBUG("Program exited with status code %d", WEXITSTATUS(wait_status)); |
778 | 0 | exec->status = WEXITSTATUS(wait_status); |
779 | 0 | } else if (WIFSIGNALED(wait_status)) { |
780 | 0 | RDEBUG("Program exited due to signal with status code %d", WTERMSIG(wait_status)); |
781 | 0 | exec->status = -WTERMSIG(wait_status); |
782 | 0 | } else { |
783 | 0 | RDEBUG("Program exited due to unknown status %d", wait_status); |
784 | 0 | exec->status = -wait_status; |
785 | 0 | } |
786 | 0 | exec->pid = -1; /* pid_t is signed */ |
787 | |
|
788 | 0 | FR_TIMER_DELETE(&exec->ev); |
789 | | |
790 | | /* |
791 | | * Process exit notifications (EV_PROC) and file |
792 | | * descriptor read events (EV_READ) can race. |
793 | | * |
794 | | * So... If the process has exited, trigger the IO |
795 | | * handlers manually. |
796 | | * |
797 | | * This is icky, but the only other option is to |
798 | | * enhance our event loop so we can look for |
799 | | * pending events associated with file |
800 | | * descriptors... |
801 | | * |
802 | | * Even then we might get the file readable |
803 | | * notification and the process exited notification |
804 | | * in different kevent() calls on busy systems. |
805 | | */ |
806 | 0 | if (exec->stdout_fd >= 0) { |
807 | 0 | fr_event_fd_t *ef; |
808 | 0 | fr_event_fd_cb_t cb; |
809 | |
|
810 | 0 | ef = fr_event_fd_handle(el, exec->stdout_fd, FR_EVENT_FILTER_IO); |
811 | 0 | if (!fr_cond_assert_msg(ef, "no event associated with processes's stdout fd (%i)", |
812 | 0 | exec->stdout_fd)) goto close_stdout; |
813 | | |
814 | 0 | cb = fr_event_fd_cb(ef, EVFILT_READ, 0); |
815 | 0 | if (!fr_cond_assert_msg(cb, "no read callback associated with processes's stdout_fd (%i)", |
816 | 0 | exec->stdout_fd)) goto close_stdout; |
817 | | |
818 | | /* |
819 | | * Call the original read callback that |
820 | | * was setup here to ensure that there's |
821 | | * no pending data. |
822 | | */ |
823 | 0 | cb(el, exec->stdout_fd, 0, fr_event_fd_uctx(ef)); |
824 | | |
825 | | /* |
826 | | * ...and delete the event from the event |
827 | | * loop. This should also suppress the |
828 | | * EVFILT_READ event if there was one. |
829 | | */ |
830 | 0 | (void) fr_event_fd_delete(el, exec->stdout_fd, FR_EVENT_FILTER_IO); |
831 | 0 | close_stdout: |
832 | 0 | close(exec->stdout_fd); |
833 | 0 | exec->stdout_fd = -1; |
834 | 0 | } |
835 | | |
836 | 0 | if (exec->stderr_fd >= 0) { |
837 | 0 | fr_event_fd_t *ef; |
838 | 0 | fr_event_fd_cb_t cb; |
839 | |
|
840 | 0 | ef = fr_event_fd_handle(el, exec->stderr_fd, FR_EVENT_FILTER_IO); |
841 | 0 | if (!fr_cond_assert_msg(ef, "no event associated with processes's stderr fd (%i)", |
842 | 0 | exec->stderr_fd)) goto close_stderr; |
843 | | |
844 | 0 | cb = fr_event_fd_cb(ef, EVFILT_READ, 0); |
845 | 0 | if (!fr_cond_assert_msg(cb, "no read callback associated with processes's stderr_fd (%i)", |
846 | 0 | exec->stderr_fd)) goto close_stderr; |
847 | | |
848 | 0 | cb(el, exec->stderr_fd, 0, fr_event_fd_uctx(ef)); |
849 | 0 | (void) fr_event_fd_delete(el, exec->stderr_fd, FR_EVENT_FILTER_IO); |
850 | 0 | close_stderr: |
851 | 0 | close(exec->stderr_fd); |
852 | 0 | exec->stderr_fd = -1; |
853 | 0 | } |
854 | | |
855 | 0 | unlang_interpret_mark_runnable(exec->request); |
856 | 0 | } |
857 | | |
858 | | /* |
859 | | * Callback when an exec times out. |
860 | | */ |
861 | | static void exec_timeout(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx) |
862 | 0 | { |
863 | 0 | fr_exec_state_t *exec = uctx; /* may not be talloced */ |
864 | 0 | bool exit_timeout; |
865 | | |
866 | | /* |
867 | | * Some race conditions cause fr_exec_oneshot_cleanup to insert |
868 | | * a new event, which calls fr_strerror_clear(), resulting in |
869 | | * inconsistent error messages. |
870 | | * Recording the condition to drive the error message here and |
871 | | * then setting after tidying up keeps things consistent. |
872 | | */ |
873 | 0 | exit_timeout = (exec->stdout_fd < 0); |
874 | |
|
875 | 0 | exec->failed = FR_EXEC_FAIL_TIMEOUT; |
876 | 0 | fr_exec_oneshot_cleanup(exec, SIGKILL); |
877 | |
|
878 | 0 | if (exit_timeout) { |
879 | 0 | fr_strerror_const("Timeout waiting for program to exit"); |
880 | 0 | } else { |
881 | 0 | fr_strerror_const("Timeout running program"); |
882 | 0 | } |
883 | |
|
884 | 0 | unlang_interpret_mark_runnable(exec->request); |
885 | 0 | } |
886 | | |
887 | | /* |
888 | | * Callback to read stdout from an exec into the pre-prepared extensible sbuff |
889 | | */ |
890 | 0 | static void exec_stdout_read(UNUSED fr_event_list_t *el, int fd, int flags, void *uctx) { |
891 | 0 | fr_exec_state_t *exec = uctx; |
892 | 0 | request_t *request = exec->request; |
893 | 0 | ssize_t data_len, remaining; |
894 | 0 | fr_sbuff_marker_t start_m; |
895 | |
|
896 | 0 | fr_sbuff_marker(&start_m, &exec->stdout_buff); |
897 | |
|
898 | 0 | do { |
899 | | /* |
900 | | * Read in 128 byte chunks |
901 | | */ |
902 | 0 | remaining = fr_sbuff_extend_lowat(NULL, &exec->stdout_buff, 128); |
903 | | |
904 | | /* |
905 | | * Ran out of buffer space. |
906 | | */ |
907 | 0 | if (unlikely(!remaining)) { |
908 | 0 | REDEBUG("Too much output from program - killing it and failing the request"); |
909 | |
|
910 | 0 | error: |
911 | 0 | exec->failed = FR_EXEC_FAIL_TOO_MUCH_DATA; |
912 | 0 | fr_exec_oneshot_cleanup(exec, SIGKILL); |
913 | 0 | break; |
914 | 0 | } |
915 | | |
916 | 0 | data_len = read(fd, fr_sbuff_current(&exec->stdout_buff), remaining); |
917 | 0 | if (data_len < 0) { |
918 | 0 | if (errno == EINTR) continue; |
919 | | |
920 | | /* |
921 | | * This can happen when the callback is called |
922 | | * manually when we're reaping the process. |
923 | | * |
924 | | * It's pretty much an identical condition to |
925 | | * data_len == 0. |
926 | | */ |
927 | 0 | if (errno == EWOULDBLOCK) break; |
928 | | |
929 | 0 | REDEBUG("Error reading from child program - %s", fr_syserror(errno)); |
930 | 0 | goto error; |
931 | 0 | } |
932 | | |
933 | | /* |
934 | | * Even if we get 0 now the process may write more data later |
935 | | * before it completes, so we leave the fd handlers in place. |
936 | | */ |
937 | 0 | if (data_len == 0) break; |
938 | | |
939 | 0 | fr_sbuff_advance(&exec->stdout_buff, data_len); |
940 | 0 | } while (remaining == data_len); /* If process returned maximum output, loop again */ |
941 | | |
942 | 0 | if (flags & EV_EOF) { |
943 | | /* |
944 | | * We've received EOF - so the process has finished writing |
945 | | * Remove event and tidy up |
946 | | */ |
947 | 0 | (void) fr_event_fd_delete(unlang_interpret_event_list(exec->request), fd, FR_EVENT_FILTER_IO); |
948 | 0 | close(fd); |
949 | 0 | exec->stdout_fd = -1; |
950 | |
|
951 | 0 | if (exec->pid < 0) { |
952 | | /* |
953 | | * Child has already exited - unlang can resume |
954 | | */ |
955 | 0 | FR_TIMER_DELETE(&exec->ev); |
956 | 0 | unlang_interpret_mark_runnable(exec->request); |
957 | 0 | } |
958 | 0 | } |
959 | | |
960 | | /* |
961 | | * Only print if we got additional data |
962 | | */ |
963 | 0 | if (RDEBUG_ENABLED2 && fr_sbuff_behind(&start_m)) { |
964 | 0 | RDEBUG2("pid %u (stdout) - %pV", exec->pid, |
965 | 0 | fr_box_strvalue_len(fr_sbuff_current(&start_m), fr_sbuff_behind(&start_m))); |
966 | 0 | } |
967 | |
|
968 | 0 | fr_sbuff_marker_release(&start_m); |
969 | 0 | } |
970 | | |
971 | | /** Call an child program, optionally reading it's output |
972 | | * |
973 | | * @note If the caller set need_stdin = true, it is the caller's |
974 | | * responsibility to close exec->std_in and remove it from any event loops |
975 | | * if this function returns 0 (success). |
976 | | * |
977 | | * @param[in] ctx to allocate events in. |
978 | | * @param[in,out] exec structure holding the state of the external call. |
979 | | * @param[in] request currently being processed, may be NULL. |
980 | | * @param[in] args to call as a fr_value_box_list_t. Program will |
981 | | * be the first box and arguments in the subsequent boxes. |
982 | | * @param[in] env_pairs list of pairs to be presented as environment variables |
983 | | * to the child. |
984 | | * @param[in] env_escape Wrap string values in double quotes, and apply doublequote |
985 | | * escaping to all environmental variable values. |
986 | | * @param[in] env_inherit Inherit the environment from the current process. |
987 | | * This will be merged with any variables from env_pairs. |
988 | | * @param[in] need_stdin If true, allocate a pipe that will allow us to send data to the |
989 | | * process. |
990 | | * @param[in] store_stdout if true keep a copy of stdout in addition to logging |
991 | | * it if RDEBUG_ENABLED2. |
992 | | * @param[in] stdout_ctx ctx to alloc stdout data in. |
993 | | * @param[in] timeout to wait for child to complete. |
994 | | * @return |
995 | | * - 0 on success |
996 | | * - -1 on failure |
997 | | */ |
998 | | int fr_exec_oneshot(TALLOC_CTX *ctx, fr_exec_state_t *exec, request_t *request, |
999 | | fr_value_box_list_t *args, |
1000 | | fr_pair_list_t *env_pairs, bool env_escape, bool env_inherit, |
1001 | | bool need_stdin, |
1002 | | bool store_stdout, TALLOC_CTX *stdout_ctx, |
1003 | | fr_time_delta_t timeout) |
1004 | 0 | { |
1005 | 0 | int *stdout_fd = (store_stdout || RDEBUG_ENABLED2) ? &exec->stdout_fd : NULL; |
1006 | 0 | fr_event_list_t *el = unlang_interpret_event_list(request); |
1007 | 0 | char **env = NULL; |
1008 | 0 | char **argv; |
1009 | 0 | int ret; |
1010 | |
|
1011 | 0 | if (unlikely(fr_exec_value_box_list_to_argv(unlang_interpret_frame_talloc_ctx(request), &argv, args) < 0)) { |
1012 | 0 | RPEDEBUG("Failed converting boxes to argument strings"); |
1013 | 0 | return -1; |
1014 | 0 | } |
1015 | | |
1016 | 0 | if (env_pairs) { |
1017 | 0 | env = fr_exec_pair_to_env(request, env_pairs, env_escape); |
1018 | 0 | if (unlikely(!env)) { |
1019 | 0 | RPEDEBUG("Failed creating environment pairs"); |
1020 | 0 | return -1; |
1021 | 0 | } |
1022 | 0 | } |
1023 | | |
1024 | 0 | if (RDEBUG_ENABLED3) exec_debug(request, argv, env, env_inherit); |
1025 | 0 | *exec = (fr_exec_state_t){ |
1026 | 0 | .request = request, |
1027 | 0 | .env_pairs = env_pairs, |
1028 | 0 | .pid = -1, |
1029 | 0 | .stdout_fd = -1, |
1030 | 0 | .stderr_fd = -1, |
1031 | 0 | .stdin_fd = -1, |
1032 | 0 | .status = -1, /* default to program didn't work */ |
1033 | 0 | .stdin_used = need_stdin, |
1034 | 0 | .stdout_used = store_stdout, |
1035 | 0 | .stdout_ctx = stdout_ctx |
1036 | 0 | }; |
1037 | 0 | ret = fr_exec_fork_wait(&exec->pid, |
1038 | 0 | exec->stdin_used ? &exec->stdin_fd : NULL, |
1039 | 0 | stdout_fd, &exec->stderr_fd, |
1040 | 0 | argv, env, |
1041 | 0 | env_inherit, ROPTIONAL_ENABLED(RDEBUG_ENABLED2, DEBUG_ENABLED2)); |
1042 | 0 | talloc_free(argv); |
1043 | 0 | if (ret < 0) { |
1044 | 0 | fail: |
1045 | 0 | RPEDEBUG("Failed executing program"); |
1046 | | |
1047 | | /* |
1048 | | * Not done in fr_exec_oneshot_cleanup as it's |
1049 | | * usually the caller's responsibility. |
1050 | | */ |
1051 | 0 | if (exec->stdin_fd >= 0) { |
1052 | 0 | close(exec->stdin_fd); |
1053 | 0 | exec->stdin_fd = -1; |
1054 | 0 | } |
1055 | 0 | fr_exec_oneshot_cleanup(exec, 0); |
1056 | 0 | return -1; |
1057 | 0 | } |
1058 | | |
1059 | | /* |
1060 | | * First setup I/O events for the child process. This needs |
1061 | | * to be done before we call fr_event_pid_wait, as it may |
1062 | | * immediately trigger the PID callback if there's a race |
1063 | | * between kevent and the child exiting, and that callback |
1064 | | * will expect file descriptor events to have been created. |
1065 | | */ |
1066 | | |
1067 | | /* |
1068 | | * If we need to parse stdout, insert a special IO handler that |
1069 | | * aggregates all stdout data into an expandable buffer. |
1070 | | */ |
1071 | 0 | if (exec->stdout_used) { |
1072 | | /* |
1073 | | * Accept a maximum of 32k of data from the process. |
1074 | | */ |
1075 | 0 | fr_sbuff_init_talloc(exec->stdout_ctx, &exec->stdout_buff, &exec->stdout_tctx, 128, 32 * 1024); |
1076 | 0 | if (fr_event_fd_insert(ctx, NULL, el, exec->stdout_fd, exec_stdout_read, NULL, NULL, exec) < 0) { |
1077 | 0 | RPEDEBUG("Failed adding event listening to stdout"); |
1078 | 0 | goto fail_and_close; |
1079 | 0 | } |
1080 | | |
1081 | | /* |
1082 | | * If the caller doesn't want the output box, we still want to copy stdout |
1083 | | * into the request log if we're logging at a high enough level of verbosity. |
1084 | | */ |
1085 | 0 | } else if (RDEBUG_ENABLED2) { |
1086 | 0 | snprintf(exec->stdout_prefix, sizeof(exec->stdout_prefix), "pid %u (stdout)", exec->pid); |
1087 | 0 | exec->stdout_uctx = (log_fd_event_ctx_t) { |
1088 | 0 | .type = L_DBG, |
1089 | 0 | .lvl = L_DBG_LVL_2, |
1090 | 0 | .request = request, |
1091 | 0 | .prefix = exec->stdout_prefix |
1092 | 0 | }; |
1093 | |
|
1094 | 0 | if (fr_event_fd_insert(ctx, NULL, el, exec->stdout_fd, log_request_fd_event, |
1095 | 0 | NULL, NULL, &exec->stdout_uctx) < 0){ |
1096 | 0 | RPEDEBUG("Failed adding event listening to stdout"); |
1097 | 0 | goto fail_and_close; |
1098 | 0 | } |
1099 | 0 | } |
1100 | | |
1101 | | /* |
1102 | | * Send stderr to the request log as error messages with a custom prefix |
1103 | | */ |
1104 | 0 | snprintf(exec->stderr_prefix, sizeof(exec->stderr_prefix), "pid %u (stderr)", exec->pid); |
1105 | 0 | exec->stderr_uctx = (log_fd_event_ctx_t) { |
1106 | 0 | .type = L_DBG_ERR, |
1107 | 0 | .lvl = L_DBG_LVL_1, |
1108 | 0 | .request = request, |
1109 | 0 | .prefix = exec->stderr_prefix |
1110 | 0 | }; |
1111 | |
|
1112 | 0 | if (fr_event_fd_insert(ctx, NULL, el, exec->stderr_fd, log_request_fd_event, |
1113 | 0 | NULL, NULL, &exec->stderr_uctx) < 0) { |
1114 | 0 | RPEDEBUG("Failed adding event listening to stderr"); |
1115 | 0 | close(exec->stderr_fd); |
1116 | 0 | exec->stderr_fd = -1; |
1117 | 0 | goto fail; |
1118 | 0 | } |
1119 | | |
1120 | | /* |
1121 | | * Tell the event loop that it needs to wait for this PID |
1122 | | */ |
1123 | 0 | if (fr_event_pid_wait(ctx, el, &exec->ev_pid, exec->pid, exec_reap, exec) < 0) { |
1124 | 0 | exec->pid = -1; |
1125 | 0 | RPEDEBUG("Failed adding watcher for child process"); |
1126 | |
|
1127 | 0 | fail_and_close: |
1128 | | /* |
1129 | | * Avoid spurious errors in fr_exec_oneshot_cleanup |
1130 | | * when it tries to remove FDs from the |
1131 | | * event loop that were never added. |
1132 | | */ |
1133 | 0 | if (exec->stdout_fd >= 0) { |
1134 | 0 | close(exec->stdout_fd); |
1135 | 0 | exec->stdout_fd = -1; |
1136 | 0 | } |
1137 | |
|
1138 | 0 | if (exec->stderr_fd >= 0) { |
1139 | 0 | close(exec->stderr_fd); |
1140 | 0 | exec->stderr_fd = -1; |
1141 | 0 | } |
1142 | |
|
1143 | 0 | goto fail; |
1144 | 0 | } |
1145 | | |
1146 | | /* |
1147 | | * Setup event to kill the child process after a period of time. |
1148 | | */ |
1149 | 0 | if (fr_time_delta_ispos(timeout) && |
1150 | 0 | (fr_timer_in(ctx, el->tl, &exec->ev, timeout, true, exec_timeout, exec) < 0)) goto fail_and_close; |
1151 | | |
1152 | 0 | return 0; |
1153 | 0 | } |