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