26RCSID(
"$Id: 23b020ffd837224e65e81dc21802c582f2846d22 $")
 
   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> 
   52        size_t                  argc = fr_value_box_list_num_elements(
in);
 
   59        first = fr_value_box_list_head(
in);
 
   60        if (first->type == 
FR_TYPE_GROUP) first = fr_value_box_list_head(&first->vb_group);
 
   66        argv = talloc_zero_array(ctx, 
char *, argc + 1);
 
 
   93static inline CC_HINT(always_inline) 
void exec_debug(
request_t *request, 
char **argv_in, 
char **env_in, 
bool env_inherit)
 
   97        if (argv_in) 
for (p = argv_in; *p; p++) 
ROPTIONAL(
RDEBUG3, 
DEBUG3, 
"arg[%d] %s", (
unsigned int)(p - argv_in), *p);
 
 
  122static inline CC_HINT(
nonnull(1,3,4,5)) CC_HINT(always_inline)
 
  146                fr_sbuff_marker(&env_m[i], &sbuff);
 
  160                if (isdigit((
uint8_t)*p)) *p++ = 
'_';
 
  163                        else if (*p == 
'-') *p = 
'_';
 
  164                        else if (isdigit((
uint8_t)*p)) 
goto next;
 
  209                if (i == (env_len - 1)) 
break;
 
  217        for (j = 0; j < i; j++) {
 
  228                        env_p[i++] = 
UNCONST(
char *, 
vp->vp_strvalue);
 
 
  258        static _Thread_local 
char *env_arr[
MAX_ENVP];                           
 
  259        static _Thread_local 
char env_buff[
NUM_ELEMENTS(env_arr) * 128];        
 
  264                             request, env_pairs, env_escape) < 0) 
return NULL;
 
 
  290                                     bool exec_wait, 
bool debug,
 
  298        devnull = open(
"/dev/null", O_RDWR);
 
  300                fprintf(stderr, 
"Failed opening /dev/null: %s\n", 
fr_syserror(errno));
 
  316                if (stdin_pipe[1] >= 0) {
 
  317                        close(stdin_pipe[1]);
 
  318                        dup2(stdin_pipe[0], STDIN_FILENO);
 
  348                if (!debug) dup2(
devnull, STDERR_FILENO);
 
  380        execve(argv[0], argv, envp);
 
  381        printf(
"Failed to execute \"%s\": %s", argv[0], 
fr_syserror(errno)); 
 
 
  401        size_t num_in, num_environ;
 
  413        if (!env_in) 
return environ;
 
  415        for (num_environ = 0; environ[num_environ] != NULL; num_environ++) {
 
  431        for (num_in = 0; env_in[num_in] != NULL; num_in++) {
 
 
  470                int unused[2] = { -1, -1 };
 
  472                exec_child(argv_in, env, 
false, debug, unused, unused, unused);
 
  493                waitpid(pid, &status, WNOHANG);
 
 
  528                      char **argv_in, 
char **env_in, 
bool env_inherit, 
bool debug)
 
  532        int             stdin_pipe[2] = {-1, -1};
 
  537                if (pipe(stdin_pipe) < 0) {
 
  551                        close(stdin_pipe[0]);
 
  552                        close(stdin_pipe[1]);
 
  589                *stdin_fd = stdin_pipe[1];
 
  590                close(stdin_pipe[0]);
 
 
  623                           bool env_escape, 
bool env_inherit)
 
  630                RPEDEBUG(
"Failed converting boxes to argument strings");
 
  637                        RPEDEBUG(
"Failed creating environment pairs");
 
 
  669        if (exec->
pid >= 0) {
 
  670                RDEBUG3(
"Cleaning up exec state for PID %u", exec->
pid);
 
  672                RDEBUG3(
"Cleaning up failed exec");
 
  686                        RPERROR(
"Failed removing stdout handler");
 
  694                        RPERROR(
"Failed removing stderr handler");
 
  700        if (exec->
pid >= 0) {
 
  701                if (signal > 0) kill(exec->
pid, signal);
 
  706                        RPERROR(
"Failed setting up async PID reaper, PID %u may now be a zombie", exec->
pid);
 
  712                        kill(exec->
pid, SIGKILL);
 
 
  738        ret = 
waitpid(exec->
pid, &wait_status, WNOHANG);
 
  748        } 
else if (ret == 0) {
 
  749                RWDEBUG(
"Something reaped PID %d before us!", exec->
pid);
 
  750                wait_status = status;
 
  757        if (wait_status != status) 
RWDEBUG(
"Exit status from waitpid (%d) and kevent (%d) disagree",
 
  758                                           wait_status, 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);
 
  767                RDEBUG(
"Program exited due to unknown status %d", wait_status);
 
  768                exec->
status = -wait_status;
 
  799                if (!
fr_cond_assert_msg(cb, 
"no read callback associated with processes's stdout_fd (%i)",
 
  829                if (!
fr_cond_assert_msg(cb, 
"no read callback associated with processes's stderr_fd (%i)",
 
 
  892                        REDEBUG(
"Too much output from program - killing it and failing the request");
 
  902                        if (errno == EINTR) 
continue;
 
  911                        if (errno == EWOULDBLOCK) 
break;
 
  921                if (data_len == 0) 
break;
 
  924        } 
while (remaining == data_len);        
 
  926        if (flags & EV_EOF) {
 
  952        fr_sbuff_marker_release(&start_m);
 
 
  983                    fr_value_box_list_t *
args,
 
  996                RPEDEBUG(
"Failed converting boxes to argument strings");
 
 1003                        RPEDEBUG(
"Failed creating environment pairs");
 
 1011                .env_pairs = env_pairs,
 
 1017                .stdin_used = need_stdin,
 
 1018                .stdout_used = store_stdout,
 
 1029                RPEDEBUG(
"Failed executing program");
 
 1061                        RPEDEBUG(
"Failed adding event listening to stdout");
 
 1062                        goto fail_and_close;
 
 1080                        RPEDEBUG(
"Failed adding event listening to stdout");
 
 1081                        goto fail_and_close;
 
 1098                RPEDEBUG(
"Failed adding event listening to stderr");
 
 1109                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.
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.
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(...)
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.
static void exec_timeout(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
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]
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.
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_timer_t * ev
for timing out the child
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.
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
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_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.
#define FR_TIMER_DELETE(_ev_p)
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)