Diff for /embedaddon/sudo/src/exec_pty.c between versions 1.1.1.4 and 1.1.1.5

version 1.1.1.4, 2013/07/22 10:46:13 version 1.1.1.5, 2014/06/15 16:12:55
Line 17 Line 17
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #ifdef HAVE_SYS_SYSMACROS_H  
 # include <sys/sysmacros.h>  
 #endif  
 #include <sys/socket.h>  #include <sys/socket.h>
 #include <sys/time.h>  #include <sys/time.h>
 #include <sys/wait.h>  #include <sys/wait.h>
 #include <sys/ioctl.h>  #include <sys/ioctl.h>
 #ifdef HAVE_SYS_SELECT_H  
 # include <sys/select.h>  
 #endif /* HAVE_SYS_SELECT_H */  
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
 # include <stdlib.h>  # include <stdlib.h>
Line 48 Line 42
 #ifdef HAVE_UNISTD_H  #ifdef HAVE_UNISTD_H
 # include <unistd.h>  # include <unistd.h>
 #endif /* HAVE_UNISTD_H */  #endif /* HAVE_UNISTD_H */
#if TIME_WITH_SYS_TIME#ifdef TIME_WITH_SYS_TIME
 # include <time.h>  # include <time.h>
 #endif  #endif
 #include <errno.h>  #include <errno.h>
Line 57 Line 51
 #include <termios.h>  #include <termios.h>
   
 #include "sudo.h"  #include "sudo.h"
   #include "sudo_event.h"
 #include "sudo_exec.h"  #include "sudo_exec.h"
 #include "sudo_plugin.h"  #include "sudo_plugin.h"
 #include "sudo_plugin_int.h"  #include "sudo_plugin_int.h"
Line 68 Line 63
 #define SFD_SLAVE       4  #define SFD_SLAVE       4
 #define SFD_USERTTY     5  #define SFD_USERTTY     5
   
   /* Evaluates to true if the event has /dev/tty as its fd. */
   #define USERTTY_EVENT(_ev)      (sudo_ev_get_fd((_ev)) == io_fds[SFD_USERTTY])
   
 #define TERM_COOKED     0  #define TERM_COOKED     0
 #define TERM_RAW        1  #define TERM_RAW        1
   
Line 79 Line 77
 #endif  #endif
   
 struct io_buffer {  struct io_buffer {
    struct io_buffer *next;    SLIST_ENTRY(io_buffer) entries;
     struct sudo_event *revent;
     struct sudo_event *wevent;
     bool (*action)(const char *buf, unsigned int len);
     int len; /* buffer length (how much produced) */      int len; /* buffer length (how much produced) */
     int off; /* write position (how much already consumed) */      int off; /* write position (how much already consumed) */
     int rfd;  /* reader (producer) */  
     int wfd; /* writer (consumer) */  
     bool (*action)(const char *buf, unsigned int len);  
     char buf[32 * 1024];      char buf[32 * 1024];
 };  };
   
   SLIST_HEAD(io_buffer_list, io_buffer);
   
 static char slavename[PATH_MAX];  static char slavename[PATH_MAX];
 static bool foreground, pipeline, tty_initialized;  static bool foreground, pipeline, tty_initialized;
 static int io_fds[6] = { -1, -1, -1, -1, -1, -1};  static int io_fds[6] = { -1, -1, -1, -1, -1, -1};
 static int ttymode = TERM_COOKED;  static int ttymode = TERM_COOKED;
 static pid_t ppgrp, cmnd_pgrp, mon_pgrp;  static pid_t ppgrp, cmnd_pgrp, mon_pgrp;
 static sigset_t ttyblock;  static sigset_t ttyblock;
static struct io_buffer *iobufs;static struct io_buffer_list iobufs;
   
static void flush_output(void);static void del_io_events(void);
 static int exec_monitor(struct command_details *details, int backchannel);  static int exec_monitor(struct command_details *details, int backchannel);
 static void exec_pty(struct command_details *details,  static void exec_pty(struct command_details *details,
    struct command_status *cstat, int *errfd);    struct command_status *cstat, int errfd);
 static void sigwinch(int s);  static void sigwinch(int s);
 static void sync_ttysize(int src, int dst);  static void sync_ttysize(int src, int dst);
 static void deliver_signal(pid_t pid, int signo, bool from_parent);  static void deliver_signal(pid_t pid, int signo, bool from_parent);
 static int safe_close(int fd);  static int safe_close(int fd);
   static void ev_free_by_fd(struct sudo_event_base *evbase, int fd);
 static void check_foreground(void);  static void check_foreground(void);
   
 /*  /*
Line 114  pty_cleanup(void) Line 115  pty_cleanup(void)
 {  {
     debug_decl(cleanup, SUDO_DEBUG_EXEC);      debug_decl(cleanup, SUDO_DEBUG_EXEC);
   
    if (!tq_empty(&io_plugins) && io_fds[SFD_USERTTY] != -1) {    if (!TAILQ_EMPTY(&io_plugins) && io_fds[SFD_USERTTY] != -1)
        check_foreground();        term_restore(io_fds[SFD_USERTTY], 0);
        if (foreground) 
            term_restore(io_fds[SFD_USERTTY], 0); 
    } 
 #ifdef HAVE_SELINUX  #ifdef HAVE_SELINUX
     selinux_restore_tty();      selinux_restore_tty();
 #endif  #endif
Line 132  pty_cleanup(void) Line 130  pty_cleanup(void)
  * The other end of signal_pipe is checked in the monitor event loop.   * The other end of signal_pipe is checked in the monitor event loop.
  */   */
 #ifdef SA_SIGINFO  #ifdef SA_SIGINFO
voidstatic void
 mon_handler(int s, siginfo_t *info, void *context)  mon_handler(int s, siginfo_t *info, void *context)
 {  {
     unsigned char signo = (unsigned char)s;      unsigned char signo = (unsigned char)s;
Line 153  mon_handler(int s, siginfo_t *info, void *context) Line 151  mon_handler(int s, siginfo_t *info, void *context)
     ignore_result(write(signal_pipe[1], &signo, sizeof(signo)));      ignore_result(write(signal_pipe[1], &signo, sizeof(signo)));
 }  }
 #else  #else
voidstatic void
 mon_handler(int s)  mon_handler(int s)
 {  {
     unsigned char signo = (unsigned char)s;      unsigned char signo = (unsigned char)s;
Line 180  pty_setup(uid_t uid, const char *tty, const char *utmp Line 178  pty_setup(uid_t uid, const char *tty, const char *utmp
     if (io_fds[SFD_USERTTY] != -1) {      if (io_fds[SFD_USERTTY] != -1) {
         if (!get_pty(&io_fds[SFD_MASTER], &io_fds[SFD_SLAVE],          if (!get_pty(&io_fds[SFD_MASTER], &io_fds[SFD_SLAVE],
             slavename, sizeof(slavename), uid))              slavename, sizeof(slavename), uid))
            fatal(_("unable to allocate pty"));            fatal(U_("unable to allocate pty"));
         /* Add entry to utmp/utmpx? */          /* Add entry to utmp/utmpx? */
         if (utmp_user != NULL)          if (utmp_user != NULL)
             utmp_login(tty, slavename, io_fds[SFD_SLAVE], utmp_user);              utmp_login(tty, slavename, io_fds[SFD_SLAVE], utmp_user);
Line 199  log_ttyin(const char *buf, unsigned int n) Line 197  log_ttyin(const char *buf, unsigned int n)
     debug_decl(log_ttyin, SUDO_DEBUG_EXEC);      debug_decl(log_ttyin, SUDO_DEBUG_EXEC);
   
     sigprocmask(SIG_BLOCK, &ttyblock, &omask);      sigprocmask(SIG_BLOCK, &ttyblock, &omask);
    tq_foreach_fwd(&io_plugins, plugin) {    TAILQ_FOREACH(plugin, &io_plugins, entries) {
         if (plugin->u.io->log_ttyin) {          if (plugin->u.io->log_ttyin) {
             if (!plugin->u.io->log_ttyin(buf, n)) {              if (!plugin->u.io->log_ttyin(buf, n)) {
                 rval = false;                  rval = false;
Line 222  log_stdin(const char *buf, unsigned int n) Line 220  log_stdin(const char *buf, unsigned int n)
     debug_decl(log_stdin, SUDO_DEBUG_EXEC);      debug_decl(log_stdin, SUDO_DEBUG_EXEC);
   
     sigprocmask(SIG_BLOCK, &ttyblock, &omask);      sigprocmask(SIG_BLOCK, &ttyblock, &omask);
    tq_foreach_fwd(&io_plugins, plugin) {    TAILQ_FOREACH(plugin, &io_plugins, entries) {
         if (plugin->u.io->log_stdin) {          if (plugin->u.io->log_stdin) {
             if (!plugin->u.io->log_stdin(buf, n)) {              if (!plugin->u.io->log_stdin(buf, n)) {
                 rval = false;                  rval = false;
Line 245  log_ttyout(const char *buf, unsigned int n) Line 243  log_ttyout(const char *buf, unsigned int n)
     debug_decl(log_ttyout, SUDO_DEBUG_EXEC);      debug_decl(log_ttyout, SUDO_DEBUG_EXEC);
   
     sigprocmask(SIG_BLOCK, &ttyblock, &omask);      sigprocmask(SIG_BLOCK, &ttyblock, &omask);
    tq_foreach_fwd(&io_plugins, plugin) {    TAILQ_FOREACH(plugin, &io_plugins, entries) {
         if (plugin->u.io->log_ttyout) {          if (plugin->u.io->log_ttyout) {
             if (!plugin->u.io->log_ttyout(buf, n)) {              if (!plugin->u.io->log_ttyout(buf, n)) {
                 rval = false;                  rval = false;
Line 268  log_stdout(const char *buf, unsigned int n) Line 266  log_stdout(const char *buf, unsigned int n)
     debug_decl(log_stdout, SUDO_DEBUG_EXEC);      debug_decl(log_stdout, SUDO_DEBUG_EXEC);
   
     sigprocmask(SIG_BLOCK, &ttyblock, &omask);      sigprocmask(SIG_BLOCK, &ttyblock, &omask);
    tq_foreach_fwd(&io_plugins, plugin) {    TAILQ_FOREACH(plugin, &io_plugins, entries) {
         if (plugin->u.io->log_stdout) {          if (plugin->u.io->log_stdout) {
             if (!plugin->u.io->log_stdout(buf, n)) {              if (!plugin->u.io->log_stdout(buf, n)) {
                 rval = false;                  rval = false;
Line 291  log_stderr(const char *buf, unsigned int n) Line 289  log_stderr(const char *buf, unsigned int n)
     debug_decl(log_stderr, SUDO_DEBUG_EXEC);      debug_decl(log_stderr, SUDO_DEBUG_EXEC);
   
     sigprocmask(SIG_BLOCK, &ttyblock, &omask);      sigprocmask(SIG_BLOCK, &ttyblock, &omask);
    tq_foreach_fwd(&io_plugins, plugin) {    TAILQ_FOREACH(plugin, &io_plugins, entries) {
         if (plugin->u.io->log_stderr) {          if (plugin->u.io->log_stderr) {
             if (!plugin->u.io->log_stderr(buf, n)) {              if (!plugin->u.io->log_stderr(buf, n)) {
                 rval = false;                  rval = false;
Line 362  suspend_parent(int signo) Line 360  suspend_parent(int signo)
         /* FALLTHROUGH */          /* FALLTHROUGH */
     case SIGSTOP:      case SIGSTOP:
     case SIGTSTP:      case SIGTSTP:
        /* Flush any remaining output before suspending. */        /* Flush any remaining output and deschedule I/O events. */
        flush_output();        del_io_events();
   
         /* Restore original tty mode before suspending. */          /* Restore original tty mode before suspending. */
        if (ttymode != TERM_COOKED) {        if (ttymode != TERM_COOKED)
            do {            term_restore(io_fds[SFD_USERTTY], 0);
                n = term_restore(io_fds[SFD_USERTTY], 0); 
            } while (!n && errno == EINTR); 
        } 
   
         if (sig2str(signo, signame) == -1)          if (sig2str(signo, signame) == -1)
             snprintf(signame, sizeof(signame), "%d", signo);              snprintf(signame, sizeof(signame), "%d", signo);
Line 454  terminate_command(pid_t pid, bool use_pgrp) Line 449  terminate_command(pid_t pid, bool use_pgrp)
     debug_return;      debug_return;
 }  }
   
 static struct io_buffer *  
 io_buf_new(int rfd, int wfd, bool (*action)(const char *, unsigned int),  
     struct io_buffer *head)  
 {  
     struct io_buffer *iob;  
     debug_decl(io_buf_new, SUDO_DEBUG_EXEC);  
   
     iob = ecalloc(1, sizeof(*iob));  
     iob->rfd = rfd;  
     iob->wfd = wfd;  
     iob->action = action;  
     iob->next = head;  
   
     debug_return_ptr(iob);  
 }  
   
 /*  /*
 * Read/write iobufs depending on fdsr and fdsw. * Read/write an iobuf that is ready.
 * Returns the number of errors. 
  */   */
intstatic void
perform_io(fd_set *fdsr, fd_set *fdsw, struct command_status *cstat)io_callback(int fd, int what, void *v)
 {  {
    struct io_buffer *iob;    struct io_buffer *iob = v;
    int n, errors = 0;    struct sudo_event_base *evbase;
    debug_decl(perform_io, SUDO_DEBUG_EXEC);    int n;
     debug_decl(io_callback, SUDO_DEBUG_EXEC);
   
    for (iob = iobufs; iob; iob = iob->next) {    if (ISSET(what, SUDO_EV_READ)) {
        if (iob->rfd != -1 && FD_ISSET(iob->rfd, fdsr)) {        evbase = sudo_ev_get_base(iob->revent);
            do {        do {
                n = read(iob->rfd, iob->buf + iob->len,            n = read(fd, iob->buf + iob->len, sizeof(iob->buf) - iob->len);
                    sizeof(iob->buf) - iob->len);        } while (n == -1 && errno == EINTR);
            } while (n == -1 && errno == EINTR);        switch (n) {
            switch (n) {            case -1:
                case -1:                if (errno == EAGAIN)
                    if (errno != EAGAIN) { 
                        /* treat read error as fatal and close the fd */ 
                        sudo_debug_printf(SUDO_DEBUG_ERROR, 
                            "error reading fd %d: %s", iob->rfd, 
                            strerror(errno)); 
                        safe_close(iob->rfd); 
                        iob->rfd = -1; 
                    } 
                     break;                      break;
                case 0:                /* treat read error as fatal and close the fd */
                    /* got EOF or pty has gone away */                sudo_debug_printf(SUDO_DEBUG_ERROR,
                     "error reading fd %d: %s", fd, strerror(errno));
                 /* FALLTHROUGH */
             case 0:
                 /* got EOF or pty has gone away */
                 if (n == 0) {
                     sudo_debug_printf(SUDO_DEBUG_INFO,                      sudo_debug_printf(SUDO_DEBUG_INFO,
                        "read EOF from fd %d", iob->rfd);                        "read EOF from fd %d", fd);
                    safe_close(iob->rfd); 
                    iob->rfd = -1; 
                    break; 
                default: 
                    sudo_debug_printf(SUDO_DEBUG_INFO, 
                        "read %d bytes from fd %d", n, iob->rfd); 
                    if (!iob->action(iob->buf + iob->len, n)) 
                        terminate_command(cmnd_pid, true); 
                    iob->len += n; 
                    break; 
            } 
        } 
        if (iob->wfd != -1 && FD_ISSET(iob->wfd, fdsw)) { 
            do { 
                n = write(iob->wfd, iob->buf + iob->off, 
                    iob->len - iob->off); 
            } while (n == -1 && errno == EINTR); 
            if (n == -1) { 
                if (errno == EPIPE || errno == ENXIO || errno == EIO || errno == EBADF) { 
                    sudo_debug_printf(SUDO_DEBUG_INFO, 
                        "unable to write %d bytes to fd %d", 
                            iob->len - iob->off, iob->wfd); 
                    /* other end of pipe closed or pty revoked */ 
                    if (iob->rfd != -1) { 
                        safe_close(iob->rfd); 
                        iob->rfd = -1; 
                    } 
                    safe_close(iob->wfd); 
                    iob->wfd = -1; 
                    continue; 
                 }                  }
                if (errno != EAGAIN) {                safe_close(fd);
                    errors++;                ev_free_by_fd(evbase, fd);
                    sudo_debug_printf(SUDO_DEBUG_ERROR,                /* If writer already consumed the buffer, close it too. */
                        "error writing fd %d: %s", iob->wfd, strerror(errno));                if (iob->wevent != NULL && iob->off == iob->len) {
                     safe_close(sudo_ev_get_fd(iob->wevent));
                     ev_free_by_fd(evbase, sudo_ev_get_fd(iob->wevent));
                     iob->off = iob->len = 0;
                 }                  }
            } else {                break;
             default:
                 sudo_debug_printf(SUDO_DEBUG_INFO,                  sudo_debug_printf(SUDO_DEBUG_INFO,
                    "wrote %d bytes to fd %d", n, iob->wfd);                    "read %d bytes from fd %d", n, fd);
                iob->off += n;                if (!iob->action(iob->buf + iob->len, n))
                     terminate_command(cmnd_pid, true);
                 iob->len += n;
                 /* Enable writer if not /dev/tty or we are foreground pgrp. */
                 if (iob->wevent != NULL &&
                     (foreground || !USERTTY_EVENT(iob->wevent))) {
                     if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
                         fatal(U_("unable to add event to queue"));
                 }
                 /* Re-enable reader if buffer is not full. */
                 if (iob->len != sizeof(iob->buf)) {
                     if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
                         fatal(U_("unable to add event to queue"));
                 }
                 break;
         }
     }
     if (ISSET(what, SUDO_EV_WRITE)) {
         evbase = sudo_ev_get_base(iob->wevent);
         do {
             n = write(fd, iob->buf + iob->off, iob->len - iob->off);
         } while (n == -1 && errno == EINTR);
         if (n == -1) {
             switch (errno) {
             case EPIPE:
             case ENXIO:
             case EIO:
             case EBADF:
                 /* other end of pipe closed or pty revoked */
                 sudo_debug_printf(SUDO_DEBUG_INFO,
                     "unable to write %d bytes to fd %d",
                     iob->len - iob->off, fd);
                 if (iob->revent != NULL) {
                     safe_close(sudo_ev_get_fd(iob->revent));
                     ev_free_by_fd(evbase, sudo_ev_get_fd(iob->revent));
                 }
                 safe_close(fd);
                 ev_free_by_fd(evbase, fd);
                 break;
             case EAGAIN:
                 /* not an error */
                 break;
             default:
 #if 0 /* XXX -- how to set cstat? stash in iobufs instead? */
                 if (cstat != NULL) {
                     cstat->type = CMD_ERRNO;
                     cstat->val = errno;
                 }
 #endif /* XXX */
                 sudo_debug_printf(SUDO_DEBUG_ERROR,
                     "error writing fd %d: %s", fd, strerror(errno));
                 sudo_ev_loopbreak(evbase);
                 break;
             }              }
           } else {
               sudo_debug_printf(SUDO_DEBUG_INFO,
                   "wrote %d bytes to fd %d", n, fd);
               iob->off += n;
               /* Reset buffer if fully consumed. */
               if (iob->off == iob->len) {
                   iob->off = iob->len = 0;
                   /* Forward the EOF from reader to writer. */
                   if (iob->revent == NULL) {
                       safe_close(fd);
                       ev_free_by_fd(evbase, fd);
                   }
               }
               /* Re-enable writer if buffer is not empty. */
               if (iob->len > iob->off) {
                   if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
                       fatal(U_("unable to add event to queue"));
               }
               /* Enable reader if buffer is not full. */
               if (iob->revent != NULL &&
                   (ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) {
                   if (iob->len != sizeof(iob->buf)) {
                       if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
                           fatal(U_("unable to add event to queue"));
                   }
               }
         }          }
     }      }
     if (errors && cstat != NULL) {  
         cstat->type = CMD_ERRNO;  
         cstat->val = errno;  
     }  
     debug_return_int(errors);  
 }  }
   
   static void
   io_buf_new(int rfd, int wfd, bool (*action)(const char *, unsigned int),
       struct io_buffer_list *head)
   {
       int n;
       struct io_buffer *iob;
       debug_decl(io_buf_new, SUDO_DEBUG_EXEC);
   
       /* Set non-blocking mode. */
       n = fcntl(rfd, F_GETFL, 0);
       if (n != -1 && !ISSET(n, O_NONBLOCK))
           (void) fcntl(rfd, F_SETFL, n | O_NONBLOCK);
       n = fcntl(wfd, F_GETFL, 0);
       if (n != -1 && !ISSET(n, O_NONBLOCK))
           (void) fcntl(wfd, F_SETFL, n | O_NONBLOCK);
   
       /* Allocate and add to head of list. */
       iob = emalloc(sizeof(*iob));
       iob->revent = sudo_ev_alloc(rfd, SUDO_EV_READ, io_callback, iob);
       iob->wevent = sudo_ev_alloc(wfd, SUDO_EV_WRITE, io_callback, iob);
       iob->len = 0;
       iob->off = 0;
       iob->action = action;
       iob->buf[0] = '\0';
       if (iob->revent == NULL || iob->wevent == NULL)
           fatal(NULL);
       SLIST_INSERT_HEAD(head, iob, entries);
   
       debug_return;
   }
   
 /*  /*
  * Fork a monitor process which runs the actual command as its own child   * Fork a monitor process which runs the actual command as its own child
  * process with std{in,out,err} hooked up to the pty or pipes as appropriate.   * process with std{in,out,err} hooked up to the pty or pipes as appropriate.
  * Returns the child pid.   * Returns the child pid.
  */   */
 int  int
fork_pty(struct command_details *details, int sv[], int *maxfd, sigset_t *omask)fork_pty(struct command_details *details, int sv[], sigset_t *omask)
 {  {
     struct command_status cstat;      struct command_status cstat;
     struct io_buffer *iob;  
     int io_pipe[3][2], n;      int io_pipe[3][2], n;
     sigaction_t sa;      sigaction_t sa;
     sigset_t mask;      sigset_t mask;
Line 599  fork_pty(struct command_details *details, int sv[], in Line 652  fork_pty(struct command_details *details, int sv[], in
     if (io_fds[SFD_USERTTY] != -1) {      if (io_fds[SFD_USERTTY] != -1) {
         /* Read from /dev/tty, write to pty master */          /* Read from /dev/tty, write to pty master */
         if (!ISSET(details->flags, CD_BACKGROUND)) {          if (!ISSET(details->flags, CD_BACKGROUND)) {
            iobufs = io_buf_new(io_fds[SFD_USERTTY], io_fds[SFD_MASTER],            io_buf_new(io_fds[SFD_USERTTY], io_fds[SFD_MASTER],
                log_ttyin, iobufs);                log_ttyin, &iobufs);
         }          }
   
         /* Read from pty master, write to /dev/tty */          /* Read from pty master, write to /dev/tty */
        iobufs = io_buf_new(io_fds[SFD_MASTER], io_fds[SFD_USERTTY],        io_buf_new(io_fds[SFD_MASTER], io_fds[SFD_USERTTY],
            log_ttyout, iobufs);            log_ttyout, &iobufs);
   
         /* Are we the foreground process? */          /* Are we the foreground process? */
         foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;          foreground = tcgetpgrp(io_fds[SFD_USERTTY]) == ppgrp;
Line 620  fork_pty(struct command_details *details, int sv[], in Line 673  fork_pty(struct command_details *details, int sv[], in
         sudo_debug_printf(SUDO_DEBUG_INFO, "stdin not a tty, creating a pipe");          sudo_debug_printf(SUDO_DEBUG_INFO, "stdin not a tty, creating a pipe");
         pipeline = true;          pipeline = true;
         if (pipe(io_pipe[STDIN_FILENO]) != 0)          if (pipe(io_pipe[STDIN_FILENO]) != 0)
            fatal(_("unable to create pipe"));            fatal(U_("unable to create pipe"));
        iobufs = io_buf_new(STDIN_FILENO, io_pipe[STDIN_FILENO][1],        io_buf_new(STDIN_FILENO, io_pipe[STDIN_FILENO][1],
            log_stdin, iobufs);            log_stdin, &iobufs);
         io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0];          io_fds[SFD_STDIN] = io_pipe[STDIN_FILENO][0];
     }      }
     if (io_fds[SFD_STDOUT] == -1 || !isatty(STDOUT_FILENO)) {      if (io_fds[SFD_STDOUT] == -1 || !isatty(STDOUT_FILENO)) {
         sudo_debug_printf(SUDO_DEBUG_INFO, "stdout not a tty, creating a pipe");          sudo_debug_printf(SUDO_DEBUG_INFO, "stdout not a tty, creating a pipe");
         pipeline = true;          pipeline = true;
         if (pipe(io_pipe[STDOUT_FILENO]) != 0)          if (pipe(io_pipe[STDOUT_FILENO]) != 0)
            fatal(_("unable to create pipe"));            fatal(U_("unable to create pipe"));
        iobufs = io_buf_new(io_pipe[STDOUT_FILENO][0], STDOUT_FILENO,        io_buf_new(io_pipe[STDOUT_FILENO][0], STDOUT_FILENO,
            log_stdout, iobufs);            log_stdout, &iobufs);
         io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1];          io_fds[SFD_STDOUT] = io_pipe[STDOUT_FILENO][1];
     }      }
     if (io_fds[SFD_STDERR] == -1 || !isatty(STDERR_FILENO)) {      if (io_fds[SFD_STDERR] == -1 || !isatty(STDERR_FILENO)) {
         sudo_debug_printf(SUDO_DEBUG_INFO, "stderr not a tty, creating a pipe");          sudo_debug_printf(SUDO_DEBUG_INFO, "stderr not a tty, creating a pipe");
         if (pipe(io_pipe[STDERR_FILENO]) != 0)          if (pipe(io_pipe[STDERR_FILENO]) != 0)
            fatal(_("unable to create pipe"));            fatal(U_("unable to create pipe"));
        iobufs = io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO,        io_buf_new(io_pipe[STDERR_FILENO][0], STDERR_FILENO,
            log_stderr, iobufs);            log_stderr, &iobufs);
         io_fds[SFD_STDERR] = io_pipe[STDERR_FILENO][1];          io_fds[SFD_STDERR] = io_pipe[STDERR_FILENO][1];
     }      }
   
Line 673  fork_pty(struct command_details *details, int sv[], in Line 726  fork_pty(struct command_details *details, int sv[], in
                 n = term_raw(io_fds[SFD_USERTTY], 0);                  n = term_raw(io_fds[SFD_USERTTY], 0);
             } while (!n && errno == EINTR);              } while (!n && errno == EINTR);
             if (!n)              if (!n)
                fatal(_("unable to set terminal to raw mode"));                fatal(U_("unable to set terminal to raw mode"));
         }          }
     }      }
   
Line 682  fork_pty(struct command_details *details, int sv[], in Line 735  fork_pty(struct command_details *details, int sv[], in
      * or certain pam modules won't be able to track their state.       * or certain pam modules won't be able to track their state.
      */       */
     if (policy_init_session(details) != true)      if (policy_init_session(details) != true)
        fatalx(_("policy plugin failed session initialization"));        fatalx(U_("policy plugin failed session initialization"));
   
     /*      /*
      * Block some signals until cmnd_pid is set in the parent to avoid a       * Block some signals until cmnd_pid is set in the parent to avoid a
Line 698  fork_pty(struct command_details *details, int sv[], in Line 751  fork_pty(struct command_details *details, int sv[], in
     child = sudo_debug_fork();      child = sudo_debug_fork();
     switch (child) {      switch (child) {
     case -1:      case -1:
        fatal(_("unable to fork"));        fatal(U_("unable to fork"));
         break;          break;
     case 0:      case 0:
         /* child */          /* child */
Line 729  fork_pty(struct command_details *details, int sv[], in Line 782  fork_pty(struct command_details *details, int sv[], in
     if (io_pipe[STDERR_FILENO][1])      if (io_pipe[STDERR_FILENO][1])
         close(io_pipe[STDERR_FILENO][1]);          close(io_pipe[STDERR_FILENO][1]);
   
     for (iob = iobufs; iob; iob = iob->next) {  
         /* Determine maxfd */  
         if (iob->rfd > *maxfd)  
             *maxfd = iob->rfd;  
         if (iob->wfd > *maxfd)  
             *maxfd = iob->wfd;  
   
         /* Set non-blocking mode. */  
         n = fcntl(iob->rfd, F_GETFL, 0);  
         if (n != -1 && !ISSET(n, O_NONBLOCK))  
             (void) fcntl(iob->rfd, F_SETFL, n | O_NONBLOCK);  
         n = fcntl(iob->wfd, F_GETFL, 0);  
         if (n != -1 && !ISSET(n, O_NONBLOCK))  
             (void) fcntl(iob->wfd, F_SETFL, n | O_NONBLOCK);  
     }  
   
     debug_return_int(child);      debug_return_int(child);
 }  }
   
 void  void
 pty_close(struct command_status *cstat)  pty_close(struct command_status *cstat)
 {  {
       struct io_buffer *iob;
     int n;      int n;
     debug_decl(pty_close, SUDO_DEBUG_EXEC);      debug_decl(pty_close, SUDO_DEBUG_EXEC);
   
Line 762  pty_close(struct command_status *cstat) Line 800  pty_close(struct command_status *cstat)
             (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n);              (void) fcntl(io_fds[SFD_USERTTY], F_SETFL, n);
         }          }
     }      }
    flush_output();    del_io_events();
   
    if (io_fds[SFD_USERTTY] != -1) {    /* Free I/O buffers. */
        check_foreground();    while ((iob = SLIST_FIRST(&iobufs)) != NULL) {
        if (foreground) {        SLIST_REMOVE_HEAD(&iobufs, entries);
            do {        efree(iob);
                n = term_restore(io_fds[SFD_USERTTY], 0); 
            } while (!n && errno == EINTR); 
        } 
     }      }
   
       /* Restore terminal settings. */
       if (io_fds[SFD_USERTTY] != -1)
           term_restore(io_fds[SFD_USERTTY], 0);
   
     /* If child was signalled, write the reason to stdout like the shell. */      /* If child was signalled, write the reason to stdout like the shell. */
     if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {      if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
         int signo = WTERMSIG(cstat->val);          int signo = WTERMSIG(cstat->val);
Line 793  pty_close(struct command_status *cstat) Line 832  pty_close(struct command_status *cstat)
 }  }
   
 /*  /*
 * Fill in fdsr and fdsw based on the io buffers list. * Schedule I/O events before starting the main event loop or
 * Called prior to select(). * resuming from suspend.
  */   */
 void  void
fd_set_iobs(fd_set *fdsr, fd_set *fdsw)add_io_events(struct sudo_event_base *evbase)
 {  {
     struct io_buffer *iob;      struct io_buffer *iob;
    debug_decl(fd_set_iobs, SUDO_DEBUG_EXEC);    debug_decl(add_io_events, SUDO_DEBUG_EXEC);
   
    for (iob = iobufs; iob; iob = iob->next) {    /*
        if (iob->rfd == -1 && iob->wfd == -1)     * Schedule all readers as long as the buffer is not full.
            continue;     * Schedule writers that contain buffered data.
        if (iob->off == iob->len) {     * Normally, write buffers are added on demand when data is read.
            iob->off = iob->len = 0;     */
            /* Forward the EOF from reader to writer. */    SLIST_FOREACH(iob, &iobufs, entries) {
            if (iob->rfd == -1) {        /* Don't read/write from /dev/tty if we are not in the foreground. */
                safe_close(iob->wfd);        if (iob->revent != NULL &&
                iob->wfd = -1;            (ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) {
             if (iob->len != sizeof(iob->buf)) {
                 sudo_debug_printf(SUDO_DEBUG_INFO,
                     "added I/O revent %p, fd %d, events %d",
                     iob->revent, iob->revent->fd, iob->revent->events);
                 if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
                     fatal(U_("unable to add event to queue"));
             }              }
         }          }
        /* Don't read/write /dev/tty if we are not in the foreground. */        if (iob->wevent != NULL &&
        if (iob->rfd != -1 &&            (foreground || !USERTTY_EVENT(iob->wevent))) {
            (ttymode == TERM_RAW || iob->rfd != io_fds[SFD_USERTTY])) {            if (iob->len > iob->off) {
            if (iob->len != sizeof(iob->buf))                sudo_debug_printf(SUDO_DEBUG_INFO,
                FD_SET(iob->rfd, fdsr);                    "added I/O wevent %p, fd %d, events %d",
                     iob->wevent, iob->wevent->fd, iob->wevent->events);
                 if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
                     fatal(U_("unable to add event to queue"));
             }
         }          }
        if (iob->wfd != -1 &&    }
            (foreground || iob->wfd != io_fds[SFD_USERTTY])) {    debug_return;
            if (iob->len > iob->off)}
                FD_SET(iob->wfd, fdsw);
 /*
  * Flush any output buffered in iobufs or readable from fds other
  * than /dev/tty.  Removes I/O events from the event base when done.
  */
 static void
 del_io_events(void)
 {
     struct io_buffer *iob;
     struct sudo_event_base *evbase;
     debug_decl(del_io_events, SUDO_DEBUG_EXEC);
 
     /* Remove iobufs from existing event base. */
     SLIST_FOREACH(iob, &iobufs, entries) {
         if (iob->revent != NULL) {
             sudo_debug_printf(SUDO_DEBUG_INFO,
                 "deleted I/O revent %p, fd %d, events %d",
                 iob->revent, iob->revent->fd, iob->revent->events);
             sudo_ev_del(NULL, iob->revent);
         }          }
           if (iob->wevent != NULL) {
               sudo_debug_printf(SUDO_DEBUG_INFO,
                   "deleted I/O wevent %p, fd %d, events %d",
                   iob->wevent, iob->wevent->fd, iob->wevent->events);
               sudo_ev_del(NULL, iob->wevent);
           }
     }      }
   
       /* Create temporary event base for flushing. */
       evbase = sudo_ev_base_alloc();
       if (evbase == NULL)
           fatal(NULL);
   
       /* Avoid reading from /dev/tty, just flush existing data. */
       SLIST_FOREACH(iob, &iobufs, entries) {
           /* Don't read from /dev/tty while flushing. */
           if (iob->revent != NULL && !USERTTY_EVENT(iob->revent)) {
               if (iob->len != sizeof(iob->buf)) {
                   if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
                       fatal(U_("unable to add event to queue"));
               }
           }
           /* Flush any write buffers with data in them. */
           if (iob->wevent != NULL) {
               if (iob->len > iob->off) {
                   if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
                       fatal(U_("unable to add event to queue"));
               }
           }
       }
   
       (void) sudo_ev_loop(evbase, SUDO_EVLOOP_NONBLOCK);
   
       /* Free temporary event base, removing its events. */
       sudo_ev_base_free(evbase);
   
     debug_return;      debug_return;
 }  }
   
Line 953  handle_sigchld(int backchannel, struct command_status  Line 1055  handle_sigchld(int backchannel, struct command_status 
     debug_return_bool(alive);      debug_return_bool(alive);
 }  }
   
   struct monitor_closure {
       struct sudo_event_base *evbase;
       struct sudo_event *errpipe_event;
       struct sudo_event *backchannel_event;
       struct sudo_event *signal_pipe_event;
       struct command_status *cstat;
       int backchannel;
       bool alive;
   };
   
   static void
   mon_signal_pipe_cb(int fd, int what, void *v)
   {
       struct monitor_closure *mc = v;
       unsigned char signo;
       ssize_t n;
       debug_decl(mon_signal_pipe_cb, SUDO_DEBUG_EXEC);
   
       n = read(fd, &signo, sizeof(signo));
       if (n == -1) {
           if (errno != EINTR && errno != EAGAIN) {
               warning(U_("error reading from signal pipe"));
               sudo_ev_loopbreak(mc->evbase);
           }
       } else {
           /*
            * Handle SIGCHLD specially and deliver other signals
            * directly to the command.
            */
           if (signo == SIGCHLD) {
               mc->alive = handle_sigchld(mc->backchannel, mc->cstat);
               if (!mc->alive) {
                   /* Remove all but the errpipe event. */
                   sudo_ev_del(mc->evbase, mc->backchannel_event);
                   sudo_ev_del(mc->evbase, mc->signal_pipe_event);
               }
           } else {
               deliver_signal(cmnd_pid, signo, false);
           }
       }
       debug_return;
   }
   
   static void
   mon_errpipe_cb(int fd, int what, void *v)
   {
       struct monitor_closure *mc = v;
       ssize_t n;
       debug_decl(mon_errpipe_cb, SUDO_DEBUG_EXEC);
   
       /* read errno or EOF from command pipe */
       n = read(fd, mc->cstat, sizeof(struct command_status));
       if (n == -1) {
           if (errno != EINTR && errno != EAGAIN) {
               warning(U_("error reading from pipe"));
               sudo_ev_loopbreak(mc->evbase);
           }
       } else {
           /* Got errno or EOF, either way we are done with errpipe. */
           sudo_ev_del(mc->evbase, mc->errpipe_event);
           close(fd);
       }
       debug_return;
   }
   
   static void
   mon_backchannel_cb(int fd, int what, void *v)
   {
       struct monitor_closure *mc = v;
       struct command_status cstmp;
       ssize_t n;
       debug_decl(mon_backchannel_cb, SUDO_DEBUG_EXEC);
   
       /* read command from backchannel, should be a signal */
       n = recv(fd, &cstmp, sizeof(cstmp), MSG_WAITALL);
       if (n != sizeof(cstmp)) {
           if (n == -1) {
               if (errno == EINTR || errno == EAGAIN)
                   debug_return;
               warning(U_("error reading from socketpair"));
           } else {
               /* short read or EOF, parent process died? */
           }
           sudo_ev_loopbreak(mc->evbase);
       } else {
           if (cstmp.type == CMD_SIGNO) {
               deliver_signal(cmnd_pid, cstmp.val, true);
           } else {
               warningx(U_("unexpected reply type on backchannel: %d"), cstmp.type);
           }
       }
       debug_return;
   }
   
 /*  /*
  * Monitor process that creates a new session with the controlling tty,   * Monitor process that creates a new session with the controlling tty,
  * resets signal handlers and forks a child to call exec_pty().   * resets signal handlers and forks a child to call exec_pty().
Line 964  static int Line 1160  static int
 exec_monitor(struct command_details *details, int backchannel)  exec_monitor(struct command_details *details, int backchannel)
 {  {
     struct command_status cstat;      struct command_status cstat;
    struct timeval tv;    struct sudo_event_base *evbase;
    fd_set *fdsr;    struct monitor_closure mc;
     sigaction_t sa;      sigaction_t sa;
    int errpipe[2], maxfd, n;    int errpipe[2], n;
    bool alive = true; 
    unsigned char signo; 
     debug_decl(exec_monitor, SUDO_DEBUG_EXEC);      debug_decl(exec_monitor, SUDO_DEBUG_EXEC);
   
     /* Close unused fds. */      /* Close unused fds. */
Line 980  exec_monitor(struct command_details *details, int back Line 1174  exec_monitor(struct command_details *details, int back
   
     /*      /*
      * We use a pipe to atomically handle signal notification within       * We use a pipe to atomically handle signal notification within
     * the select() loop.     * the event loop.
      */       */
     if (pipe_nonblock(signal_pipe) != 0)      if (pipe_nonblock(signal_pipe) != 0)
        fatal(_("unable to create pipe"));        fatal(U_("unable to create pipe"));
   
     /* Reset SIGWINCH and SIGALRM. */      /* Reset SIGWINCH and SIGALRM. */
     memset(&sa, 0, sizeof(sa));      memset(&sa, 0, sizeof(sa));
Line 1001  exec_monitor(struct command_details *details, int back Line 1195  exec_monitor(struct command_details *details, int back
     /* Block all signals in mon_handler(). */      /* Block all signals in mon_handler(). */
     sigfillset(&sa.sa_mask);      sigfillset(&sa.sa_mask);
   
    /* Note: HP-UX select() will not be interrupted if SA_RESTART set */    /* Note: HP-UX poll() will not be interrupted if SA_RESTART is set. */
     sa.sa_flags = SA_INTERRUPT;      sa.sa_flags = SA_INTERRUPT;
 #ifdef SA_SIGINFO  #ifdef SA_SIGINFO
     sa.sa_flags |= SA_SIGINFO;      sa.sa_flags |= SA_SIGINFO;
Line 1039  exec_monitor(struct command_details *details, int back Line 1233  exec_monitor(struct command_details *details, int back
     if (io_fds[SFD_SLAVE] != -1) {      if (io_fds[SFD_SLAVE] != -1) {
 #ifdef TIOCSCTTY  #ifdef TIOCSCTTY
         if (ioctl(io_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)          if (ioctl(io_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
            fatal(_("unable to set controlling tty"));            fatal(U_("unable to set controlling tty"));
 #else  #else
         /* Set controlling tty by reopening slave. */          /* Set controlling tty by reopening slave. */
         if ((n = open(slavename, O_RDWR)) >= 0)          if ((n = open(slavename, O_RDWR)) >= 0)
Line 1060  exec_monitor(struct command_details *details, int back Line 1254  exec_monitor(struct command_details *details, int back
   
     /* Start command and wait for it to stop or exit */      /* Start command and wait for it to stop or exit */
     if (pipe(errpipe) == -1)      if (pipe(errpipe) == -1)
        fatal(_("unable to create pipe"));        fatal(U_("unable to create pipe"));
     cmnd_pid = sudo_debug_fork();      cmnd_pid = sudo_debug_fork();
     if (cmnd_pid == -1) {      if (cmnd_pid == -1) {
        warning(_("unable to fork"));        warning(U_("unable to fork"));
         goto bad;          goto bad;
     }      }
     if (cmnd_pid == 0) {      if (cmnd_pid == 0) {
Line 1076  exec_monitor(struct command_details *details, int back Line 1270  exec_monitor(struct command_details *details, int back
         restore_signals();          restore_signals();
   
         /* setup tty and exec command */          /* setup tty and exec command */
        exec_pty(details, &cstat, &errpipe[1]);        exec_pty(details, &cstat, errpipe[1]);
         ignore_result(write(errpipe[1], &cstat, sizeof(cstat)));          ignore_result(write(errpipe[1], &cstat, sizeof(cstat)));
         _exit(1);          _exit(1);
     }      }
Line 1106  exec_monitor(struct command_details *details, int back Line 1300  exec_monitor(struct command_details *details, int back
         } while (n == -1 && errno == EINTR);          } while (n == -1 && errno == EINTR);
     }      }
   
    /* Wait for errno on pipe, signal on backchannel or for SIGCHLD */    /*
    maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel);     * Create new event base and register read events for the
    fdsr = ecalloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));     * signal pipe, error pipe, and backchannel.
      */
     evbase = sudo_ev_base_alloc();
     if (evbase == NULL)
         fatal(NULL);
 
     memset(&cstat, 0, sizeof(cstat));      memset(&cstat, 0, sizeof(cstat));
    tv.tv_sec = 0;    mc.cstat = &cstat;
    tv.tv_usec = 0;    mc.evbase = evbase;
    for (;;) {    mc.backchannel = backchannel;
        /* Check for signal on backchannel or errno on errpipe. */    mc.alive = true;
        FD_SET(backchannel, fdsr); 
        FD_SET(signal_pipe[0], fdsr); 
        if (errpipe[0] != -1) 
            FD_SET(errpipe[0], fdsr); 
        maxfd = MAX(MAX(errpipe[0], signal_pipe[0]), backchannel); 
   
        /* If command exited we just poll, there may be data on errpipe. */    mc.signal_pipe_event = sudo_ev_alloc(signal_pipe[0],
        n = select(maxfd + 1, fdsr, NULL, NULL, alive ? NULL : &tv);        SUDO_EV_READ|SUDO_EV_PERSIST, mon_signal_pipe_cb, &mc);
        if (n <= 0) {    if (mc.signal_pipe_event == NULL)
            if (n == 0)        fatal(NULL);
                goto done;    if (sudo_ev_add(evbase, mc.signal_pipe_event, NULL, false) == -1)
            if (errno == EINTR || errno == ENOMEM)        fatal(U_("unable to add event to queue"));
                continue; 
            warning("monitor: %s", _("select failed")); 
            break; 
        } 
   
        if (FD_ISSET(signal_pipe[0], fdsr)) {    mc.errpipe_event = sudo_ev_alloc(errpipe[0],
            n = read(signal_pipe[0], &signo, sizeof(signo));        SUDO_EV_READ|SUDO_EV_PERSIST, mon_errpipe_cb, &mc);
            if (n == -1) {    if (mc.errpipe_event == NULL)
                if (errno == EINTR || errno == EAGAIN)        fatal(NULL);
                    continue;    if (sudo_ev_add(evbase, mc.errpipe_event, NULL, false) == -1)
                warning(_("error reading from signal pipe"));        fatal(U_("unable to add event to queue"));
                goto done; 
            } 
            /* 
             * Handle SIGCHLD specially and deliver other signals 
             * directly to the command. 
             */ 
            if (signo == SIGCHLD) { 
                if (!handle_sigchld(backchannel, &cstat)) 
                    alive = false; 
            } else { 
                deliver_signal(cmnd_pid, signo, false); 
            } 
            continue; 
        } 
        if (errpipe[0] != -1 && FD_ISSET(errpipe[0], fdsr)) { 
            /* read errno or EOF from command pipe */ 
            n = read(errpipe[0], &cstat, sizeof(cstat)); 
            if (n == -1) { 
                if (errno == EINTR) 
                    continue; 
                warning(_("error reading from pipe")); 
                goto done; 
            } 
            /* Got errno or EOF, either way we are done with errpipe. */ 
            FD_CLR(errpipe[0], fdsr); 
            close(errpipe[0]); 
            errpipe[0] = -1; 
        } 
        if (FD_ISSET(backchannel, fdsr)) { 
            struct command_status cstmp; 
   
            /* read command from backchannel, should be a signal */    mc.backchannel_event = sudo_ev_alloc(backchannel,
            n = recv(backchannel, &cstmp, sizeof(cstmp), 0);        SUDO_EV_READ|SUDO_EV_PERSIST, mon_backchannel_cb, &mc);
            if (n == -1) {    if (mc.backchannel_event == NULL)
                if (errno == EINTR)        fatal(NULL);
                    continue;    if (sudo_ev_add(evbase, mc.backchannel_event, NULL, false) == -1)
                warning(_("error reading from socketpair"));        fatal(U_("unable to add event to queue"));
                goto done; 
            } 
            if (cstmp.type != CMD_SIGNO) { 
                warningx(_("unexpected reply type on backchannel: %d"), 
                    cstmp.type); 
                continue; 
            } 
            deliver_signal(cmnd_pid, cstmp.val, true); 
        } 
    } 
   
done:    /*
    if (alive) {     * Wait for errno on pipe, signal on backchannel or for SIGCHLD.
        /* XXX An error occurred, should send an error back. */     * The event loop ends when the child is no longer running and
      * the error pipe is closed.
      */
     (void) sudo_ev_loop(evbase, 0);
     if (mc.alive) {
         /* XXX An error occurred, should send a message back. */
         sudo_debug_printf(SUDO_DEBUG_ERROR,
             "Command still running after event loop exit, sending SIGKILL");
         kill(cmnd_pid, SIGKILL);          kill(cmnd_pid, SIGKILL);
     } else {      } else {
         /* Send parent status. */          /* Send parent status. */
Line 1201  bad: Line 1358  bad:
 }  }
   
 /*  /*
  * Flush any output buffered in iobufs or readable from the fds.  
  * Does not read from /dev/tty.  
  */  
 static void  
 flush_output(void)  
 {  
     struct io_buffer *iob;  
     struct timeval tv;  
     fd_set *fdsr, *fdsw;  
     int nready, nwriters, maxfd = -1;  
     debug_decl(flush_output, SUDO_DEBUG_EXEC);  
   
     /* Determine maxfd */  
     for (iob = iobufs; iob; iob = iob->next) {  
         if (iob->rfd > maxfd)  
             maxfd = iob->rfd;  
         if (iob->wfd > maxfd)  
             maxfd = iob->wfd;  
     }  
     if (maxfd == -1)  
         debug_return;  
   
     fdsr = emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));  
     fdsw = emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));  
     for (;;) {  
         memset(fdsw, 0, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));  
         memset(fdsr, 0, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));  
   
         nwriters = 0;  
         for (iob = iobufs; iob; iob = iob->next) {  
             /* Don't read from /dev/tty while flushing. */  
             if (io_fds[SFD_USERTTY] != -1 && iob->rfd == io_fds[SFD_USERTTY])  
                 continue;  
             if (iob->rfd == -1 && iob->wfd == -1)  
                 continue;  
             if (iob->off == iob->len) {  
                 iob->off = iob->len = 0;  
                 /* Forward the EOF from reader to writer. */  
                 if (iob->rfd == -1) {  
                     safe_close(iob->wfd);  
                     iob->wfd = -1;  
                 }  
             }  
             if (iob->rfd != -1) {  
                 if (iob->len != sizeof(iob->buf))  
                     FD_SET(iob->rfd, fdsr);  
             }  
             if (iob->wfd != -1) {  
                 if (iob->len > iob->off) {  
                     nwriters++;  
                     FD_SET(iob->wfd, fdsw);  
                 }  
             }  
         }  
   
         /* Don't sleep in select if there are no buffers that need writing. */  
         tv.tv_sec = 0;  
         tv.tv_usec = 0;  
         nready = select(maxfd + 1, fdsr, fdsw, NULL, nwriters ? NULL : &tv);  
         if (nready <= 0) {  
             if (nready == 0)  
                 break; /* all I/O flushed */  
             if (errno == EINTR || errno == ENOMEM)  
                 continue;  
             warning(_("select failed"));  
         }  
         if (perform_io(fdsr, fdsw, NULL) != 0 || nready == -1)  
             break;  
     }  
     efree(fdsr);  
     efree(fdsw);  
     debug_return;  
 }  
   
 /*  
  * Sets up std{in,out,err} and executes the actual command.   * Sets up std{in,out,err} and executes the actual command.
  * Returns only if execve() fails.   * Returns only if execve() fails.
  */   */
 static void  static void
 exec_pty(struct command_details *details,  exec_pty(struct command_details *details,
    struct command_status *cstat, int *errfd)    struct command_status *cstat, int errfd)
 {  {
     pid_t self = getpid();      pid_t self = getpid();
     debug_decl(exec_pty, SUDO_DEBUG_EXEC);      debug_decl(exec_pty, SUDO_DEBUG_EXEC);
Line 1354  sigwinch(int s) Line 1436  sigwinch(int s)
 }  }
   
 /*  /*
    * Remove and free any events associated with the specified
    * file descriptor present in the I/O buffers list.
    */
   static void
   ev_free_by_fd(struct sudo_event_base *evbase, int fd)
   {
       struct io_buffer *iob;
       debug_decl(ev_free_by_fd, SUDO_DEBUG_EXEC);
   
       /* Deschedule any users of the fd and free up the events. */
       SLIST_FOREACH(iob, &iobufs, entries) {
           if (iob->revent != NULL) {
               if (sudo_ev_get_fd(iob->revent) == fd) {
                   sudo_debug_printf(SUDO_DEBUG_INFO,
                       "%s: deleting and freeing revent %p with fd %d",
                       __func__, iob->revent, fd);
                   sudo_ev_del(evbase, iob->revent);
                   sudo_ev_free(iob->revent);
                   iob->revent = NULL;
               }
           }
           if (iob->wevent != NULL) {
               if (sudo_ev_get_fd(iob->wevent) == fd) {
                   sudo_debug_printf(SUDO_DEBUG_INFO,
                       "%s: deleting and freeing wevent %p with fd %d",
                       __func__, iob->wevent, fd);
                   sudo_ev_del(evbase, iob->wevent);
                   sudo_ev_free(iob->wevent);
                   iob->wevent = NULL;
               }
           }
       }
       debug_return;
   }
   
   /*
  * Only close the fd if it is not /dev/tty or std{in,out,err}.   * Only close the fd if it is not /dev/tty or std{in,out,err}.
 * Return value is the same as send(2). * Return value is the same as close(2).
  */   */
 static int  static int
 safe_close(int fd)  safe_close(int fd)
Line 1364  safe_close(int fd) Line 1482  safe_close(int fd)
   
     /* Avoid closing /dev/tty or std{in,out,err}. */      /* Avoid closing /dev/tty or std{in,out,err}. */
     if (fd < 3 || fd == io_fds[SFD_USERTTY]) {      if (fd < 3 || fd == io_fds[SFD_USERTTY]) {
           sudo_debug_printf(SUDO_DEBUG_INFO,
               "%s: not closing fd %d (/dev/tty)", __func__, fd);
         errno = EINVAL;          errno = EINVAL;
         debug_return_int(-1);          debug_return_int(-1);
     }      }
       sudo_debug_printf(SUDO_DEBUG_INFO, "%s: closing fd %d", __func__, fd);
     debug_return_int(close(fd));      debug_return_int(close(fd));
 }  }

Removed from v.1.1.1.4  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>