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