26 RCSID(
"$Id: fd495658da941b48252a0f9604904d782b2ca309 $")
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/rad_assert.h>
36 #ifdef HAVE_SYS_WAIT_H
37 # include <sys/wait.h>
40 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
43 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
46 #define MAX_ARGV (256)
51 static void tv_sub(
struct timeval *end,
struct timeval *start,
52 struct timeval *elapsed)
54 elapsed->tv_sec = end->tv_sec - start->tv_sec;
55 if (elapsed->tv_sec > 0) {
57 elapsed->tv_usec =
USEC;
61 elapsed->tv_usec += end->tv_usec;
62 elapsed->tv_usec -= start->tv_usec;
64 if (elapsed->tv_usec >=
USEC) {
65 elapsed->tv_usec -=
USEC;
88 int *input_fd,
int *output_fd,
95 int to_child[2] = {-1, -1};
96 int from_child[2] = {-1, -1};
102 char *argv[
MAX_ARGV], **argv_start = argv;
104 #define MAX_ENVP 1024
107 TALLOC_CTX *input_ctx = NULL;
115 memcpy(&argv_p, &argv_start,
sizeof(argv_p));
118 ERROR(
"Invalid command '%s'", cmd);
123 for (i = 0; i < argc; i++)
DEBUG3(
"arg[%d] %s", i, argv[i]);
132 if (pipe(to_child) != 0) {
138 if (pipe(from_child) != 0) {
154 input_ctx = talloc_new(request);
163 vp && (envlen < ((
sizeof(envp) /
sizeof(*envp)) - 1));
170 snprintf(buffer,
sizeof(buffer),
"%s=", vp->da->name);
172 for (p = buffer; *p !=
'='; p++) {
175 }
else if (isalpha((
int) *p)) {
184 DEBUG3(
"export %s", buffer);
185 envp[envlen++] = talloc_strdup(input_ctx, buffer);
189 while ((envlen < ((
sizeof(envp) /
sizeof(*envp)) - 1)) &&
191 DEBUG3(
"export %s", vp->vp_strvalue);
192 memcpy(&envp[envlen++], &vp->vp_strvalue,
sizeof(*envp));
220 devnull = open(
"/dev/null", O_RDWR);
240 dup2(to_child[0], STDIN_FILENO);
242 dup2(devnull, STDIN_FILENO);
246 close(from_child[0]);
247 dup2(from_child[1], STDOUT_FILENO);
249 dup2(devnull, STDOUT_FILENO);
253 dup2(devnull, STDIN_FILENO);
254 dup2(devnull, STDOUT_FILENO);
266 dup2(devnull, STDERR_FILENO);
285 execve(argv[0], argv, envp);
286 printf(
"Failed to execute \"%s\": %s", argv[0],
fr_syserror(errno));
300 talloc_free(input_ctx);
311 close(from_child[0]);
312 close(from_child[1]);
327 *input_fd = to_child[1];
331 *output_fd = from_child[0];
332 close(from_child[1]);
339 ERROR(
"Wait is not supported");
368 _spawnve(_P_NOWAIT, argv[0], argv, envp);
387 char *answer,
int left)
392 struct timeval start;
394 bool nonblock =
true;
404 if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) {
410 if( fcntl(fd, F_SETFL, flags) < 0) {
422 gettimeofday(&start, NULL);
426 struct timeval when, elapsed, wake;
431 gettimeofday(&when, NULL);
432 tv_sub(&when, &start, &elapsed);
433 if (elapsed.tv_sec >= timeout)
goto too_long;
437 tv_sub(&when, &elapsed, &wake);
439 rcode = select(fd + 1, &fds, NULL, NULL, &wake);
442 DEBUG(
"Child PID %u is taking too much time: forcing failure and killing child.", pid);
453 if (errno == EINTR)
continue;
463 status = read(fd, answer + done, left);
469 status = read(fd, answer + done, 1);
485 if (errno == EINTR) {
499 if (left <= 0)
break;
504 while ((done > 0) && (answer[done - 1] ==
'\n')) {
505 answer[--
done] =
'\0';
533 bool exec_wait,
bool shell_escape,
int timeout)
547 RDEBUG2(
"Executing: %s:", cmd);
549 if (out) *out =
'\0';
551 pid =
radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape);
567 RERROR(
"Failed to read from child output");
593 for (p = answer; *p; p++) {
595 *p = comma ?
' ' :
',';
607 if (answer[len - 1] ==
',') {
608 answer[--len] =
'\0';
632 if (child_pid == 0) {
633 RERROR(
"Timeout waiting for child");
638 if (child_pid == pid) {
641 if ((status != 0) || (ret < 0)) {
642 RERROR(
"Program returned code (%d) and output '%s'", status, answer);
644 RDEBUG2(
"Program returned code (%d) and output '%s'", status, answer);
647 return ret < 0 ? ret : status;
672 exec_trigger_main = cs;
706 attr = strrchr(name,
'.');
720 if (!subcs && exec_trigger_main && (cs != exec_trigger_main)) {
728 ERROR(
"No such item in trigger section: %s", attr);
733 ERROR(
"Trigger is not a configuration variable: %s", attr);
742 ERROR(
"Trigger has no value: %s", name);
773 time_t now = time(NULL);
774 if (*last_time == now)
return;
788 DEBUG(
"Trigger %s -> %s", name, value);
792 if (alloc) talloc_free(request);
void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, bool quench)
Execute a trigger - call an executable to process an event.
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
VALUE_PAIR * fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag)
Iterate over a collection of VALUE_PAIRs of a given type in the pairlist.
void * rad_malloc(size_t size)
VALUE_PAIR ** radius_list(REQUEST *request, pair_lists_t list)
Resolve attribute pair_lists_t value to an attribute list.
#define WEXITSTATUS(stat_val)
void size_t fr_pair_value_snprint(char *out, size_t outlen, VALUE_PAIR const *vp, char quote)
Print the value of an attribute to a string.
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
#define rad_waitpid(a, b)
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
char const * cf_pair_value(CONF_PAIR const *pair)
void exec_trigger_set_conf(CONF_SECTION *cs)
Set the global trigger section exec_trigger will search in.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *item)
Cast a CONF_ITEM to a CONF_PAIR.
static CONF_SECTION * exec_trigger_subcs
Stores an attribute, a value and various bits of other data.
int radius_exec_program(TALLOC_CTX *ctx, char *out, size_t outlen, VALUE_PAIR **output_pairs, REQUEST *request, char const *cmd, VALUE_PAIR *input_pairs, bool exec_wait, bool shell_escape, int timeout)
Execute a program.
bool cf_item_is_pair(CONF_ITEM const *item)
int cf_data_add(CONF_SECTION *, char const *, void *, void(*)(void *))
Configuration AVP similar to a VALUE_PAIR.
#define WIFEXITED(stat_val)
char const * fr_strerror(void)
Get the last library error.
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
log_lvl_t rad_debug_lvl
Global debugging level.
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
RADIUS_PACKET * packet
Incoming request.
static void time_free(void *data)
static CONF_SECTION * exec_trigger_main
pid_t radius_start_program(char const *cmd, REQUEST *request, bool exec_wait, int *input_fd, int *output_fd, VALUE_PAIR *input_pairs, bool shell_escape)
Start a process.
static void tv_sub(struct timeval *end, struct timeval *start, struct timeval *elapsed)
int radius_readfrom_program(int fd, pid_t pid, int timeout, char *answer, int left)
Read from the child process.
int rad_expand_xlat(REQUEST *request, char const *cmd, int max_argc, char const *argv[], bool can_fail, size_t argv_buflen, char *argv_buf)
Split string into words and expand each one.
size_t strlcpy(char *dst, char const *src, size_t siz)
Attributes that change the behaviour of modules.
void * cf_data_find(CONF_SECTION const *, char const *)
FR_TOKEN fr_pair_list_afrom_str(TALLOC_CTX *ctx, char const *buffer, VALUE_PAIR **head)
Read one line of attribute/value pairs into a list.
REQUEST * request_alloc(TALLOC_CTX *ctx)
Create a new REQUEST data structure.
CONF_ITEM * cf_reference_item(CONF_SECTION const *parentcs, CONF_SECTION *outercs, char const *ptr)