26 RCSID(
"$Id: c32377b47d41e19fd92ea59479c6839c11b98b90 $")
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>
54 size_t argc = fr_value_box_list_num_elements(
in);
61 first = fr_value_box_list_head(
in);
62 if (first->type ==
FR_TYPE_GROUP) first = fr_value_box_list_head(&first->vb_group);
68 argv = talloc_zero_array(ctx,
char *, argc + 1);
95 static inline CC_HINT(always_inline)
void exec_debug(
request_t *request,
char **argv_in,
char **env_in,
bool env_inherit)
99 if (argv_in)
for (p = argv_in; *p; p++)
ROPTIONAL(
RDEBUG3,
DEBUG3,
"arg[%d] %s", (
unsigned int)(p - argv_in), *p);
124 static inline CC_HINT(
nonnull(1,3,4,5)) CC_HINT(always_inline)
148 fr_sbuff_marker(&env_m[i], &sbuff);
162 if (isdigit((
uint8_t)*p)) *p++ =
'_';
165 else if (*p ==
'-') *p =
'_';
211 if (i == (env_len - 1))
break;
219 for (j = 0; j < i; j++) {
230 env_p[i++] =
UNCONST(
char *,
vp->vp_strvalue);
260 static _Thread_local
char *env_arr[
MAX_ENVP];
261 static _Thread_local
char env_buff[
NUM_ELEMENTS(env_arr) * 128];
266 request, env_pairs, env_escape) < 0)
return NULL;
292 bool exec_wait,
bool debug,
300 devnull = open(
"/dev/null", O_RDWR);
302 fprintf(stderr,
"Failed opening /dev/null: %s\n",
fr_syserror(errno));
318 if (stdin_pipe[1] >= 0) {
319 close(stdin_pipe[1]);
320 dup2(stdin_pipe[0], STDIN_FILENO);
350 if (!debug) dup2(
devnull, STDERR_FILENO);
382 execve(argv[0], argv, envp);
383 printf(
"Failed to execute \"%s\": %s", argv[0],
fr_syserror(errno));
403 size_t num_in, num_environ;
415 if (!env_in)
return environ;
417 for (num_environ = 0; environ[num_environ] != NULL; num_environ++) {
433 for (num_in = 0; env_in[num_in] != NULL; num_in++) {
472 int unused[2] = { -1, -1 };
474 exec_child(argv_in, env,
false, debug, unused, unused, unused);
495 waitpid(pid, &status, WNOHANG);
530 char **argv_in,
char **env_in,
bool env_inherit,
bool debug)
534 int stdin_pipe[2] = {-1, -1};
539 if (pipe(stdin_pipe) < 0) {
553 close(stdin_pipe[0]);
554 close(stdin_pipe[1]);
591 *stdin_fd = stdin_pipe[1];
592 close(stdin_pipe[0]);
625 bool env_escape,
bool env_inherit)
632 RPEDEBUG(
"Failed converting boxes to argument strings");
639 RPEDEBUG(
"Failed creating environment pairs");
671 if (exec->
pid >= 0) {
672 RDEBUG3(
"Cleaning up exec state for PID %u", exec->
pid);
674 RDEBUG3(
"Cleaning up failed exec");
688 RPERROR(
"Failed removing stdout handler");
696 RPERROR(
"Failed removing stderr handler");
702 if (exec->
pid >= 0) {
703 if (signal > 0) kill(exec->
pid, signal);
708 RPERROR(
"Failed setting up async PID reaper, PID %u may now be a zombie", exec->
pid);
714 kill(exec->
pid, SIGKILL);
740 ret =
waitpid(exec->
pid, &wait_status, WNOHANG);
750 }
else if (ret == 0) {
751 RWDEBUG(
"Something reaped PID %d before us!", exec->
pid);
752 wait_status = status;
759 if (wait_status != status)
RWDEBUG(
"Exit status from waitpid (%d) and kevent (%d) disagree",
760 wait_status, 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);
769 RDEBUG(
"Program exited due to unknown status %d", wait_status);
770 exec->
status = -wait_status;
801 if (!
fr_cond_assert_msg(cb,
"no read callback associated with processes's stdout_fd (%i)",
831 if (!
fr_cond_assert_msg(cb,
"no read callback associated with processes's stderr_fd (%i)",
894 REDEBUG(
"Too much output from program - killing it and failing the request");
904 if (errno == EINTR)
continue;
913 if (errno == EWOULDBLOCK)
break;
923 if (data_len == 0)
break;
926 }
while (remaining == data_len);
928 if (flags & EV_EOF) {
954 fr_sbuff_marker_release(&start_m);
985 fr_value_box_list_t *
args,
998 RPEDEBUG(
"Failed converting boxes to argument strings");
1005 RPEDEBUG(
"Failed creating environment pairs");
1013 .env_pairs = env_pairs,
1019 .stdin_used = need_stdin,
1020 .stdout_used = store_stdout,
1031 RPEDEBUG(
"Failed executing program");
1063 RPEDEBUG(
"Failed adding event listening to stdout");
1064 goto fail_and_close;
1082 RPEDEBUG(
"Failed adding event listening to stdout");
1083 goto fail_and_close;
1100 RPEDEBUG(
"Failed adding event listening to stderr");
1111 RPEDEBUG(
"Failed adding watcher for child process");
void fr_atexit_global_disarm_all(void)
Remove all global destructors (without executing them)
#define fr_atexit_thread_local_disarm_all(...)
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define NEVER_RETURNS
Should be placed before the function return type.
fr_dcursor_eval_t void const * uctx
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
static fr_time_delta_t timeout
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
fr_dict_t const * fr_dict_internal(void)
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.
#define fr_event_fd_insert(...)
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.
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
#define fr_event_pid_reap(...)
#define fr_event_pid_wait(...)
#define fr_event_timer_in(...)
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.
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.
static void exec_stdout_read(UNUSED fr_event_list_t *el, int fd, int flags, void *uctx)
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.
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.
static void exec_reap(fr_event_list_t *el, pid_t pid, int status, void *uctx)
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.
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.
static char ** exec_build_env(char **env_in, bool env_inherit)
Merge extra environmental variables and potentially the inherited environment.
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.
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.
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.
void fr_exec_oneshot_cleanup(fr_exec_state_t *exec, int signal)
Cleans up an exec'd process on error.
static _Thread_local char * env_exec_arr[MAX_ENVP]
static void exec_timeout(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
fr_event_pid_t const * ev_pid
for cleaning up the process
request_t * request
request this exec is related to
char stderr_prefix[sizeof("pid -9223372036854775808 (stderr)")]
log_fd_event_ctx_t stdout_uctx
Config for the stdout logger.
int stderr_fd
for producing error messages.
log_fd_event_ctx_t stderr_uctx
Config for the stderr logger.
TALLOC_CTX * stdout_ctx
ctx to allocate output buffers
int stdout_fd
for reading from the child.
int stdin_fd
for writing to the child.
@ FR_EXEC_FAIL_TOO_MUCH_DATA
char stdout_prefix[sizeof("pid -9223372036854775808 (stdout)")]
fr_sbuff_t stdout_buff
Expandable buffer to store process output.
bool stdout_used
use stdout fd?
fr_sbuff_uctx_talloc_t stdout_tctx
sbuff talloc ctx data.
int status
return code of the program
bool stdin_used
use stdin fd?
fr_exec_fail_t failed
what kind of failure
fr_event_timer_t const * ev
for timing out the child
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
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.
#define DEBUG_ENABLED2
True if global debug level 1-2 messages are enabled.
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
#define ROPTIONAL_ENABLED(_e_request, _e_global)
Check if a debug level is set by the request (if !NULL) or by the global log.
#define RPEDEBUG(fmt,...)
fr_log_type_t type
What type of log message it is.
Context structure for the log fd event function.
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.
void * fr_event_fd_uctx(fr_event_fd_t *ef)
Returns the uctx associated with an fr_event_fd_t handle.
int fr_event_timer_delete(fr_event_timer_t const **ev_p)
Delete a timer event from the event list.
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.
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.
A file descriptor/filter event.
Stores all information relating to an event list.
static FILE * devnull
File handle for /dev/null.
static int stdout_fd
The original unmolested stdout file descriptor.
static int stdout_pipe[2]
Pipe we use to transport stdout data.
static int stderr_pipe[2]
Pipe we use to transport stderr data.
static fr_log_fd_event_ctx_t stdout_ctx
Logging ctx for stdout.
static int stderr_fd
The original unmolested stderr file descriptor.
@ L_DBG_LVL_1
Highest priority debug messages (-x).
@ L_DBG_LVL_2
2nd highest priority debug messages (-xx | -X).
@ L_DBG_ERR
Error only displayed when debugging is enabled.
@ L_DBG
Only displayed when debugging is enabled.
@ FR_TYPE_GROUP
A grouping of other attributes.
int fr_nonblock(UNUSED int fd)
#define RDEBUG_ENABLED2()
#define WIFEXITED(stat_val)
#define WEXITSTATUS(stat_val)
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
#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)
Stores an attribute, a value and various bits of other data.
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
static int talloc_const_free(void const *ptr)
Free const'd memory.
#define fr_time_delta_ispos(_a)
A time delta, a difference in time measured in nanoseconds.
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.
#define fr_pair_list_foreach_leaf(_list_head, _iter)
Iterate over the leaf nodes of a fr_pair_list_t.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const(_msg)
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.
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.
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)
fr_sbuff_escape_rules_t fr_value_escape_unprintables
#define fr_box_strvalue_len(_val, _len)
#define fr_value_box_list_foreach(_list_head, _iter)