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