char buf[16 * 1024];
};
-static int script_fds[3];
+static int script_fds[3] = { -1, -1, -1 };
static int ttyout;
/* XXX - use an array of signals instead of just a single variable */
int relaysig = 0, sv[2];
fd_set *fdsr, *fdsw;
int rbac_enabled = 0;
- int maxfd;
+ int log_io, maxfd;
cstat->type = 0; /* XXX */
+ log_io = !tq_empty(&io_plugins);
+
#ifdef HAVE_SELINUX
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
if (rbac_enabled) {
selinux_prefork(user_role, user_type, script_fds[SFD_SLAVE]);
- /* Re-open slave fd after it has been relabeled */
- close(script_fds[SFD_SLAVE]);
- script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
- if (script_fds[SFD_SLAVE] == -1)
+ if (log_io) {
+ /* Re-open slave fd after it has been relabeled */
+ close(script_fds[SFD_SLAVE]);
+ script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
+ if (script_fds[SFD_SLAVE] == -1)
error(1, "cannot open %s", slavename);
+ }
}
#endif
- /* Are we the foreground process? */
parent = getpid(); /* so child can pass signals back to us */
- foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
- /* So we can block tty-generated signals */
- sigemptyset(&ttyblock);
- sigaddset(&ttyblock, SIGINT);
- sigaddset(&ttyblock, SIGQUIT);
- sigaddset(&ttyblock, SIGTSTP);
- sigaddset(&ttyblock, SIGTTIN);
- sigaddset(&ttyblock, SIGTTOU);
+ /*
+ * We communicate with the child over a bi-directional pipe.
+ * Parent sends signal info to child and child sends back wait status.
+ */
+ if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
+ error(1, "cannot create sockets");
- /* Setup signal handlers window size changes and child stop/exit */
zero_bytes(&sa, sizeof(sa));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
- sa.sa_handler = sigwinch;
- sigaction(SIGWINCH, &sa, NULL);
/* XXX - now get command status via sv (still need to detect child death) */
sa.sa_handler = sigchild;
sa.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sa, NULL);
+ if (log_io) {
+ sa.sa_handler = sigwinch;
+ sigaction(SIGWINCH, &sa, NULL);
+
+ /* So we can block tty-generated signals */
+ sigemptyset(&ttyblock);
+ sigaddset(&ttyblock, SIGINT);
+ sigaddset(&ttyblock, SIGQUIT);
+ sigaddset(&ttyblock, SIGTSTP);
+ sigaddset(&ttyblock, SIGTTIN);
+ sigaddset(&ttyblock, SIGTTOU);
+
+ /* Are we the foreground process? */
+ foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
+
+ /* If stdout is not a tty we handle post-processing differently. */
+ ttyout = isatty(STDOUT_FILENO);
+ }
+
/* Signals to relay from parent to child. */
sa.sa_flags = 0; /* do not restart syscalls for these */
sa.sa_handler = handler;
sigaction(SIGINT, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGTSTP, &sa, NULL);
-#if 0 /* XXX - keep? */
+#if 0 /* XXX - keep these? */
sigaction(SIGTTIN, &sa, NULL);
sigaction(SIGTTOU, &sa, NULL);
#endif
- /* If stdout is not a tty we handle post-processing differently. */
- ttyout = isatty(STDOUT_FILENO);
-
- /*
- * We communicate with the child over a bi-directional pipe.
- * Parent sends signal info to child and child sends back wait status.
- */
- if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
- error(1, "cannot create sockets");
-
- if (foreground) {
+ if (log_io && foreground) {
/* Copy terminal attrs from user tty -> pty slave. */
if (term_copy(script_fds[SFD_USERTTY], script_fds[SFD_SLAVE], ttyout)) {
tty_initialized = 1;
close(sv[0]);
if (exec_setup(details) == 0) {
/* headed for execve() */
- script_child(details->command, argv, envp, sv[1], rbac_enabled);
+ if (log_io)
+ script_child(details->command, argv, envp, sv[1], rbac_enabled);
+ else {
+#ifdef HAVE_SELINUX
+ if (rbac_enabled)
+ selinux_execve(details->command, argv, envp);
+ else
+#endif
+ execve(details->command, argv, envp);
+ }
+ /* XXX - fallback to sh */
}
cstat->type = CMD_ERRNO;
cstat->val = errno;
}
close(sv[1]);
- n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
- if (n != -1) {
- n |= O_NONBLOCK;
- (void) fcntl(script_fds[SFD_MASTER], F_SETFL, n);
- }
- n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
- if (n != -1) {
- n |= O_NONBLOCK;
- (void) fcntl(script_fds[SFD_USERTTY], F_SETFL, n);
- }
- n = fcntl(STDOUT_FILENO, F_GETFL, 0);
- if (n != -1) {
- n |= O_NONBLOCK;
- (void) fcntl(STDOUT_FILENO, F_SETFL, n);
- }
-
/* Max fd we will be selecting on. */
maxfd = sv[0];
- if (maxfd < script_fds[SFD_MASTER])
- maxfd = script_fds[SFD_MASTER];
- if (maxfd < script_fds[SFD_USERTTY])
- maxfd = script_fds[SFD_USERTTY];
+
+ if (log_io) {
+ if (maxfd < script_fds[SFD_MASTER])
+ maxfd = script_fds[SFD_MASTER];
+ if (maxfd < script_fds[SFD_USERTTY])
+ maxfd = script_fds[SFD_USERTTY];
+
+ n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
+ if (n != -1) {
+ n |= O_NONBLOCK;
+ (void) fcntl(script_fds[SFD_MASTER], F_SETFL, n);
+ }
+ n = fcntl(script_fds[SFD_USERTTY], F_GETFL, 0);
+ if (n != -1) {
+ n |= O_NONBLOCK;
+ (void) fcntl(script_fds[SFD_USERTTY], F_SETFL, n);
+ }
+ n = fcntl(STDOUT_FILENO, F_GETFL, 0);
+ if (n != -1) {
+ n |= O_NONBLOCK;
+ (void) fcntl(STDOUT_FILENO, F_SETFL, n);
+ }
+ }
/*
* In the event loop we pass input from user tty to master
recvsig = 0;
}
- if (input.off == input.len)
- input.off = input.len = 0;
- if (output.off == output.len)
- output.off = output.len = 0;
+ /* If not logging I/O and child has exited we are done. */
+ if (!log_io && recvsig == SIGCHLD) {
+ cstat->type = CMD_WSTATUS;
+ cstat->val = child_status;
+ break;
+ }
zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
- if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
- FD_SET(script_fds[SFD_USERTTY], fdsr);
- if (output.len != sizeof(output.buf))
- FD_SET(script_fds[SFD_MASTER], fdsr);
- if (output.len > output.off)
- FD_SET(STDOUT_FILENO, fdsw);
- if (input.len > input.off)
- FD_SET(script_fds[SFD_MASTER], fdsw);
+ if (log_io) {
+ if (input.off == input.len)
+ input.off = input.len = 0;
+ if (output.off == output.len)
+ output.off = output.len = 0;
+
+ if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
+ FD_SET(script_fds[SFD_USERTTY], fdsr);
+ if (output.len != sizeof(output.buf))
+ FD_SET(script_fds[SFD_MASTER], fdsr);
+ if (output.len > output.off)
+ FD_SET(STDOUT_FILENO, fdsw);
+ if (input.len > input.off)
+ FD_SET(script_fds[SFD_MASTER], fdsw);
+ }
FD_SET(sv[0], fdsr);
if (relaysig)
FD_SET(sv[0], fdsw);
break; /* should not happen */
}
}
+ if (!log_io)
+ continue;
+
if (FD_ISSET(script_fds[SFD_USERTTY], fdsr)) {
n = read(script_fds[SFD_USERTTY], input.buf + input.len,
sizeof(input.buf) - input.len);
}
}
- /* Flush any remaining output to stdout (already sent to I/O modules). */
- n = fcntl(STDOUT_FILENO, F_GETFL, 0);
- if (n != -1) {
- n &= ~O_NONBLOCK;
- (void) fcntl(STDOUT_FILENO, F_SETFL, n);
- }
- flush_output(&output);
+ if (log_io) {
+ /* Flush any remaining output to stdout (plugin already got it) */
+ n = fcntl(STDOUT_FILENO, F_GETFL, 0);
+ if (n != -1) {
+ n &= ~O_NONBLOCK;
+ (void) fcntl(STDOUT_FILENO, F_SETFL, n);
+ }
+ flush_output(&output);
#ifdef HAVE_STRSIGNAL
- if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
- int signo = WTERMSIG(cstat->val);
- if (signo && signo != SIGINT && signo != SIGPIPE) {
- char *reason = strsignal(signo);
- write(STDOUT_FILENO, reason, strlen(reason));
- if (WCOREDUMP(cstat->val))
- write(STDOUT_FILENO, " (core dumped)", 14);
- write(STDOUT_FILENO, "\n", 1);
+ if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
+ int signo = WTERMSIG(cstat->val);
+ if (signo && signo != SIGINT && signo != SIGPIPE) {
+ char *reason = strsignal(signo);
+ write(STDOUT_FILENO, reason, strlen(reason));
+ if (WCOREDUMP(cstat->val))
+ write(STDOUT_FILENO, " (core dumped)", 14);
+ write(STDOUT_FILENO, "\n", 1);
+ }
}
- }
#endif
-
- do {
- n = term_restore(script_fds[SFD_USERTTY], 0);
- } while (!n && errno == EINTR);
+ do {
+ n = term_restore(script_fds[SFD_USERTTY], 0);
+ } while (!n && errno == EINTR);
+ }
return cstat->type == CMD_ERRNO ? -1 : 0;
}
sigchild(int s)
{
pid_t pid;
+ int flags = WNOHANG;
int serrno = errno;
+ if (!tq_empty(&io_plugins))
+ flags |= WUNTRACED;
+ else
+ recvsig = SIGCHLD;
do {
- pid = waitpid(child, &child_status, WNOHANG | WUNTRACED);
+ pid = waitpid(child, &child_status, flags);
} while (pid == -1 && errno == EINTR);
if (pid == child) {
if (WIFSTOPPED(child_status))
/*
* XXX - missing closefrom(), may not be possible in new scheme
* also no background support
- * or selinux...
*/
/* If there are I/O plugins, allocate a pty and exec */
if (!tq_empty(&io_plugins)) {
sudo_debug(8, "script mode");
script_setup(details->euid);
- script_execve(details, argv, envp, &cstat);
- } else {
- pid_t child, pid;
- int nready, sv[2];
- ssize_t nread;
- sigaction_t sa;
- fd_set *fdsr;
-
- zero_bytes(&sa, sizeof(sa));
- sigemptyset(&sa.sa_mask);
- sa.sa_flags = SA_RESTART;
-
- /* Want select() to be interrupted when child dies. */
- sa.sa_handler = sigchild;
- sigaction(SIGCHLD, &sa, NULL);
-
- /* Ignore SIGPIPE from other end of socketpair. */
- sa.sa_handler = SIG_IGN;
- sigaction(SIGPIPE, &sa, NULL);
-
- if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
- error(1, "cannot create sockets");
-
- child = fork();
- if (child == -1)
- error(1, "unable to fork");
-
- if (child == 0) {
- /* child */
- close(sv[0]);
- if (exec_setup(details) == 0) {
- /* XXX - fallback via /bin/sh */
- execve(details->command, argv, envp);
- }
- cstat.type = CMD_ERRNO;
- cstat.val = errno;
- write(sv[1], &cstat, sizeof(cstat));
- _exit(1);
- }
- close(sv[1]);
- sudo_debug(9, "waiting for child");
-
- /* wait for child to complete or for data on sv[0] */
- fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
- zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
- FD_SET(sv[0], fdsr);
- for (;;) {
- /* XXX - we may get SIGCILD before the wait status from the child */
- if (sigchld) {
- /* Note: this is the child, not the command we are waiting on */
- sigchld = 0;
- do {
- pid = waitpid(child, &cstat.val, WNOHANG);
- if (pid == child)
- cstat.type = CMD_WSTATUS;
- } while (pid == -1 && errno == EINTR);
- if (cstat.type == CMD_WSTATUS) {
- /* command terminated, we're done */
- break;
- }
- }
- nready = select(sv[0] + 1, fdsr, NULL, NULL, NULL);
- if (nready == -1) {
- if (errno == EINTR)
- continue;
- error(1, "select failed");
- }
- if (FD_ISSET(sv[0], fdsr)) {
- /* read child status */
- nread = recv(sv[0], &cstat, sizeof(cstat), 0);
- if (nread == -1) {
- /* XXX - could be interrupted by SIGCHLD */
- if (errno == EINTR)
- continue;
- /* XXX - init cstat for failure case */
- }
- sudo_debug(9, "cmdtype %d, val %d", cstat.type, cstat.val);
- break; /* XXX */
- }
- }
}
+ script_execve(details, argv, envp, &cstat);
switch (cstat.type) {
case CMD_ERRNO: