From: Phil Mayers Date: Tue, 19 Apr 2011 14:10:11 +0000 (+0100) Subject: split exec functionality into 3 parts X-Git-Tag: release_3_0_0_beta0~840 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=195c31acfd4864b99516951eb16daa0faed73406;p=thirdparty%2Ffreeradius-server.git split exec functionality into 3 parts --- diff --git a/src/include/radiusd.h b/src/include/radiusd.h index 9e07dc61ab3..503fb584c43 100644 --- a/src/include/radiusd.h +++ b/src/include/radiusd.h @@ -641,6 +641,13 @@ int rad_authenticate (REQUEST *); int rad_postauth(REQUEST *); /* exec.c */ +pid_t radius_start_program(const char *cmd, REQUEST *request, + int exec_wait, + int *input_fd, + int *output_fd, + VALUE_PAIR *input_pairs, + int shell_escape); +int radius_readfrom_program(int fd, pid_t pid, int timeout, char *answer, int left); int radius_exec_program(const char *, REQUEST *, int, char *user_msg, int msg_len, VALUE_PAIR *input_pairs, diff --git a/src/main/exec.c b/src/main/exec.c index 3c50269e487..196cdfc29b6 100644 --- a/src/main/exec.c +++ b/src/main/exec.c @@ -66,41 +66,29 @@ static void tv_sub(struct timeval *end, struct timeval *start, /* - * Execute a program on successful authentication. - * Return 0 if exec_wait == 0. - * Return the exit code of the called program if exec_wait != 0. - * Return -1 on fork/other errors in the parent process. + * Start a process */ -int radius_exec_program(const char *cmd, REQUEST *request, +pid_t radius_start_program(const char *cmd, REQUEST *request, int exec_wait, - char *user_msg, int msg_len, + int *input_fd, + int *output_fd, VALUE_PAIR *input_pairs, - VALUE_PAIR **output_pairs, int shell_escape) { VALUE_PAIR *vp; char mycmd[1024]; const char *from; char *p, *to; - int pd[2]; - pid_t pid, child_pid; + int to_child[2] = {-1, -1}; + int from_child[2] = {-1, -1}; + pid_t pid; int argc = -1; - int comma = 0; - int status; int i; - int n, left, done; + int n, left; char *argv[MAX_ARGV]; - char answer[4096]; char argv_buf[4096]; #define MAX_ENVP 1024 char *envp[MAX_ENVP]; - struct timeval start; -#ifdef O_NONBLOCK - int nonblock = TRUE; -#endif - - if (user_msg) *user_msg = '\0'; - if (output_pairs) *output_pairs = NULL; if (strlen(cmd) > (sizeof(mycmd) - 1)) { radlog(L_ERR|L_CONS, "Command line is too long"); @@ -239,18 +227,23 @@ int radius_exec_program(const char *cmd, REQUEST *request, * Open a pipe for child/parent communication, if necessary. */ if (exec_wait) { - if (pipe(pd) != 0) { - radlog(L_ERR|L_CONS, "Couldn't open pipe: %s", - strerror(errno)); - return -1; + if (input_fd) { + if (pipe(to_child) != 0) { + radlog(L_ERR|L_CONS, "Couldn't open pipe to child: %s", + strerror(errno)); + return -1; + } + } + if (output_fd) { + if (pipe(from_child) != 0) { + radlog(L_ERR|L_CONS, "Couldn't open pipe from child: %s", + strerror(errno)); + /* safe because these either need closing or are == -1 */ + close(to_child[0]); + close(to_child[1]); + return -1; + } } - } else { - /* - * We're not waiting, so we don't look for a - * message, or VP's. - */ - user_msg = NULL; - output_pairs = NULL; } envp[0] = NULL; @@ -322,34 +315,29 @@ int radius_exec_program(const char *cmd, REQUEST *request, strerror(errno)); exit(1); } - dup2(devnull, STDIN_FILENO); /* * Only massage the pipe handles if the parent * has created them. */ if (exec_wait) { - /* - * pd[0] is the FD the child will read from, - * which we don't want. - */ - if (close(pd[0]) != 0) { - radlog(L_ERR|L_CONS, "Can't close pipe: %s", - strerror(errno)); - exit(1); + + if (input_fd) { + close(to_child[1]); + dup2(to_child[0], STDIN_FILENO); + } else { + dup2(devnull, STDIN_FILENO); } - /* - * pd[1] is the FD that the child will write to, - * so we make it STDOUT. - */ - if (dup2(pd[1], STDOUT_FILENO) != 1) { - radlog(L_ERR|L_CONS, "Can't dup stdout: %s", - strerror(errno)); - exit(1); + if (output_fd) { + close(from_child[0]); + dup2(from_child[1], STDOUT_FILENO); + } else { + dup2(devnull, STDOUT_FILENO); } } else { /* no pipe, STDOUT should be /dev/null */ + dup2(devnull, STDIN_FILENO); dup2(devnull, STDOUT_FILENO); } @@ -393,8 +381,11 @@ int radius_exec_program(const char *cmd, REQUEST *request, radlog(L_ERR|L_CONS, "Couldn't fork %s: %s", argv[0], strerror(errno)); if (exec_wait) { - close(pd[0]); - close(pd[1]); + /* safe because these either need closing or are == -1 */ + close(to_child[0]); + close(to_child[1]); + close(from_child[0]); + close(from_child[0]); } return -1; } @@ -402,21 +393,36 @@ int radius_exec_program(const char *cmd, REQUEST *request, /* * We're not waiting, exit, and ignore any child's status. */ - if (!exec_wait) { - return 0; + if (exec_wait) { + /* + * Close the ends of the pipe(s) the child is using + * return the ends of the pipe(s) our caller wants + * + */ + if (input_fd) { + *input_fd = to_child[1]; + close(to_child[0]); + } + if (output_fd) { + *output_fd = from_child[0]; + close(from_child[1]); + } } - /* - * Close the FD to which the child writes it's data. - * - * If we can't close it, then we close pd[0], and return an - * error. - */ - if (close(pd[1]) != 0) { - radlog(L_ERR|L_CONS, "Can't close pipe: %s", strerror(errno)); - close(pd[0]); - return -1; - } + return pid; +} + +/* + * read from the child process into buffer "answer" of length "left" + */ +int radius_readfrom_program(int fd, pid_t pid, int timeout, char *answer, int left) { + + int done; + int status; + struct timeval start; +#ifdef O_NONBLOCK + int nonblock = TRUE; +#endif #ifdef O_NONBLOCK /* @@ -425,13 +431,13 @@ int radius_exec_program(const char *cmd, REQUEST *request, do { int flags; - if ((flags = fcntl(pd[0], F_GETFL, NULL)) < 0) { + if ((flags = fcntl(fd, F_GETFL, NULL)) < 0) { nonblock = FALSE; break; } flags |= O_NONBLOCK; - if( fcntl(pd[0], F_SETFL, flags) < 0) { + if( fcntl(fd, F_SETFL, flags) < 0) { nonblock = FALSE; break; } @@ -444,7 +450,6 @@ int radius_exec_program(const char *cmd, REQUEST *request, * until the message is full. */ done = 0; - left = sizeof(answer) - 1; gettimeofday(&start, NULL); while (1) { int rcode; @@ -452,28 +457,28 @@ int radius_exec_program(const char *cmd, REQUEST *request, struct timeval when, elapsed, wake; FD_ZERO(&fds); - FD_SET(pd[0], &fds); + FD_SET(fd, &fds); gettimeofday(&when, NULL); tv_sub(&when, &start, &elapsed); - if (elapsed.tv_sec >= 10) goto too_long; + if (elapsed.tv_sec >= timeout) goto too_long; - when.tv_sec = 10; + when.tv_sec = timeout; when.tv_usec = 0; tv_sub(&when, &elapsed, &wake); - rcode = select(pd[0] + 1, &fds, NULL, NULL, &wake); + rcode = select(fd + 1, &fds, NULL, NULL, &wake); if (rcode == 0) { too_long: radlog(L_ERR, "Child PID %u is taking too much time: forcing failure and killing child.", pid); kill(pid, SIGTERM); - close(pd[0]); /* should give SIGPIPE to child, too */ + close(fd); /* should give SIGPIPE to child, too */ /* * Clean up the child entry. */ rad_waitpid(pid, &status); - return 1; + return -1; } if (rcode < 0) { if (errno == EINTR) continue; @@ -486,13 +491,13 @@ int radius_exec_program(const char *cmd, REQUEST *request, * will return the number of bytes available. */ if (nonblock) { - status = read(pd[0], answer + done, left); + status = read(fd, answer + done, left); } else #endif /* * There's at least 1 byte ready: read it. */ - status = read(pd[0], answer + done, 1); + status = read(fd, answer + done, 1); /* * Nothing more to read: stop. @@ -524,13 +529,58 @@ int radius_exec_program(const char *cmd, REQUEST *request, left -= status; if (left <= 0) break; } + + return done; +} + +/* + * Execute a program on successful authentication. + * Return 0 if exec_wait == 0. + * Return the exit code of the called program if exec_wait != 0. + * Return -1 on fork/other errors in the parent process. + */ +int radius_exec_program(const char *cmd, REQUEST *request, + int exec_wait, + char *user_msg, int msg_len, + VALUE_PAIR *input_pairs, + VALUE_PAIR **output_pairs, + int shell_escape) +{ + VALUE_PAIR *vp; + char *p; + int from_child; + pid_t pid, child_pid; + int comma = 0; + int status; + int n, done; + char answer[4096]; + + pid = radius_start_program(cmd, request, exec_wait, NULL, &from_child, input_pairs, shell_escape); + if (pid < 0) { + return -1; + } + + if (!exec_wait) + return 0; + + done = radius_readfrom_program(from_child, pid, 10, answer, sizeof(answer)); + if (done < 0) { + /* + * failure - radius_readfrom_program will + * have called close(from_child) for us + */ + DEBUG("failed to read from child output"); + return 1; + + } answer[done] = 0; + /* * Make sure that the writer can't block while writing to * a pipe that no one is reading from anymore. */ - close(pd[0]); + close(from_child); DEBUG2("Exec-Program output: %s", answer);