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