/*
- * 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");
* 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;
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);
}
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;
}
/*
* 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
/*
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;
}
* until the message is full.
*/
done = 0;
- left = sizeof(answer) - 1;
gettimeofday(&start, NULL);
while (1) {
int rcode;
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;
* 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.
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);