26RCSID(
"$Id: e8e3588871d022ad4e871a654ff87046926063d4 $")
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/main_config.h>
34#include <freeradius-devel/server/util.h>
35#include <freeradius-devel/util/file.h>
54 size_t argc = fr_value_box_list_num_elements(
in);
62 first = fr_value_box_list_head(
in);
68 if (first->type ==
FR_TYPE_GROUP) first = fr_value_box_list_head(&first->vb_group);
69 if (!first)
goto missing;
75 argv = talloc_zero_array(ctx,
char *, argc + 1);
106 if (argv_in)
for (p = argv_in; *p; p++)
ROPTIONAL(
RDEBUG3,
DEBUG3,
"arg[%u] %s", (
unsigned int)(p - argv_in), *p);
131static inline CC_HINT(
nonnull(1,3,4,5)) CC_HINT(always_inline)
155 fr_sbuff_marker(&env_m[i], &sbuff);
169 if (isdigit((
uint8_t)*p)) *p++ =
'_';
172 else if (*p ==
'-') *p =
'_';
173 else if (isdigit((
uint8_t)*p))
goto next;
218 if (i == (env_len - 1))
break;
226 for (j = 0; j < i; j++) {
237 env_p[i++] =
UNCONST(
char *,
vp->vp_strvalue);
273 request, env_pairs, env_escape) < 0)
return NULL;
299 bool exec_wait,
bool debug,
307 devnull = open(
"/dev/null", O_RDWR);
309 fprintf(stderr,
"Failed opening /dev/null: %s\n",
fr_syserror(errno));
325 if (stdin_pipe[1] >= 0) {
326 close(stdin_pipe[1]);
327 dup2(stdin_pipe[0], STDIN_FILENO);
357 if (!debug) dup2(
devnull, STDERR_FILENO);
389 execve(argv[0], argv, envp);
390 printf(
"Failed to execute \"%s\": %s", argv[0],
fr_syserror(errno));
410 size_t num_in, num_environ;
422 if (!env_in)
return environ;
424 for (num_environ = 0; environ[num_environ] != NULL; num_environ++) {
438 memcpy(
env_exec_arr, environ, (num_environ + 1) *
sizeof(environ[0]));
440 for (num_in = 0; env_in[num_in] != NULL; num_in++) {
444 memcpy(
env_exec_arr + num_environ, env_in, num_in *
sizeof(environ[0]));
458 end = filename + strlen(filename);
463 PERROR(
"Invalid program %s", filename);
480 EDEBUG(
"Not allowed to run program %s - the exec { ... } section is empty", filename);
485 EDEBUG(
"Not allowed to run program %s - it is outside of allowed list in exec { ... }", filename);
523 int unused[2] = { -1, -1 };
525 exec_child(argv_in, env,
false, debug, unused, unused, unused);
546 waitpid(pid, &status, WNOHANG);
581 char **argv_in,
char **env_in,
bool env_inherit,
bool debug)
585 int stdin_pipe[2] = {-1, -1};
592 if (pipe(stdin_pipe) < 0) {
609 close(stdin_pipe[0]);
610 close(stdin_pipe[1]);
659 *stdin_fd = stdin_pipe[1];
660 close(stdin_pipe[0]);
693 bool env_escape,
bool env_inherit)
700 RPEDEBUG(
"Failed converting boxes to argument strings");
707 RPEDEBUG(
"Failed creating environment pairs");
739 if (exec->
pid >= 0) {
740 RDEBUG3(
"Cleaning up exec state for PID %ld", (
long)exec->
pid);
743 RDEBUG3(
"Cleaning up failed exec");
757 RPERROR(
"Failed removing stdout handler");
765 RPERROR(
"Failed removing stderr handler");
771 if (exec->
pid >= 0) {
772 if (signal > 0) kill(exec->
pid, signal);
777 RPERROR(
"Failed setting up async PID reaper, PID %ld may now be a zombie", (
long)exec->
pid);
783 kill(exec->
pid, SIGKILL);
784 waitpid(exec->
pid, &status, WNOHANG);
809 ret = waitpid(exec->
pid, &wait_status, WNOHANG);
819 }
else if (ret == 0) {
820 RWDEBUG(
"Something reaped PID %d before us!", exec->
pid);
821 wait_status = status;
828 if (wait_status != status)
RWDEBUG(
"Exit status from waitpid (%d) and kevent (%d) disagree",
829 wait_status, status);
834 }
else if (WIFSIGNALED(wait_status)) {
835 RDEBUG(
"Program exited due to signal with status code %d", WTERMSIG(wait_status));
836 exec->
status = -WTERMSIG(wait_status);
838 RDEBUG(
"Program exited due to unknown status %d", wait_status);
839 exec->
status = -wait_status;
870 if (!
fr_cond_assert_msg(cb,
"no read callback associated with processes's stdout_fd (%i)",
900 if (!
fr_cond_assert_msg(cb,
"no read callback associated with processes's stderr_fd (%i)",
963 REDEBUG(
"Too much output from program - killing it and failing the request");
973 if (errno == EINTR)
continue;
982 if (errno == EWOULDBLOCK)
break;
992 if (data_len == 0)
break;
995 }
while (remaining == data_len);
997 if (flags & EV_EOF) {
1006 if (exec->
pid < 0) {
1019 RDEBUG2(
"pid %ld (stdout) - %pV", (
long)exec->
pid,
1023 fr_sbuff_marker_release(&start_m);
1054 fr_value_box_list_t *
args,
1067 RPEDEBUG(
"Failed converting boxes to argument strings");
1074 RPEDEBUG(
"Failed creating environment pairs");
1082 .env_pairs = env_pairs,
1088 .stdin_used = need_stdin,
1089 .stdout_used = store_stdout,
1100 RPEDEBUG(
"Failed executing program");
1132 RPEDEBUG(
"Failed adding event listening to stdout");
1133 goto fail_and_close;
1151 RPEDEBUG(
"Failed adding event listening to stdout");
1152 goto fail_and_close;
1169 RPEDEBUG(
"Failed adding event listening to stderr");
1180 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(...)
static uint8_t request[4]
#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.
#define fr_event_pid_reap(...)
#define fr_event_pid_wait(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
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)
static bool fr_exec_allowed(char const *filename)
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)")]
@ FR_EXEC_FAIL_TOO_MUCH_DATA
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.
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 DEBUG_ENABLED
True if global debug level 1 messages are enabled.
#define RPEDEBUG(fmt,...)
fr_log_type_t type
What type of log message it is.
Context structure for the log fd event function.
void * fr_event_fd_uctx(fr_event_fd_t *ef)
Returns the uctx associated with an fr_event_fd_t handle.
fr_event_fd_cb_t fr_event_fd_cb(fr_event_fd_t *ef, int kq_filter, uint32_t kq_fflags)
Returns the appropriate callback function for a given event.
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.
bool fr_filename_ok(char const *filename, char const *end, bool allow_dir)
See if a filename is OK.
bool fr_filename_allowed_by_list(char const *filename, char const *end, char const *const *array)
See if a filename is allowed by a 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.
main_config_t const * main_config
Main server configuration.
main_config_limit_t limit
limit files, exec, etc.
bool exec_is_set
if we have a limit { exec { ... } } section.
char const ** exec
where exec() is limited to
@ 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.
fr_sbuff_escape_rules_t const fr_value_escape_unprintables
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)
static fr_sbuff_err_t char size_t fr_sbuff_t * in
#define fr_box_strvalue_len(_val, _len)
#define fr_value_box_is_safe_for(_box, _safe_for)
#define fr_value_box_list_foreach(_list_head, _iter)