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