Diff for /embedaddon/sudo/plugins/sudoers/sudoreplay.c between versions 1.1.1.5 and 1.1.1.6

version 1.1.1.5, 2013/10/14 07:56:35 version 1.1.1.6, 2014/06/15 16:12:54
Line 18 Line 18
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/uio.h>  #include <sys/uio.h>
 #ifdef HAVE_SYS_SYSMACROS_H  
 # include <sys/sysmacros.h>  
 #endif  
 #include <sys/stat.h>  #include <sys/stat.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 49 Line 43
 #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
 #ifndef HAVE_STRUCT_TIMESPEC  #ifndef HAVE_STRUCT_TIMESPEC
Line 95 Line 89
   
 #include <pathnames.h>  #include <pathnames.h>
   
   #include "gettext.h"            /* must be included before missing.h */
   
 #include "missing.h"  #include "missing.h"
 #include "alloc.h"  #include "alloc.h"
 #include "fatal.h"  #include "fatal.h"
 #include "gettext.h"  
 #include "logging.h"  #include "logging.h"
 #include "iolog.h"  #include "iolog.h"
   #include "queue.h"
 #include "sudo_plugin.h"  #include "sudo_plugin.h"
 #include "sudo_conf.h"  #include "sudo_conf.h"
 #include "sudo_debug.h"  #include "sudo_debug.h"
   #include "sudo_event.h"
   #include "sudo_util.h"
   
 #ifndef LINE_MAX  #ifndef LINE_MAX
 # define LINE_MAX 2048  # define LINE_MAX 2048
Line 124  struct log_info { Line 122  struct log_info {
     int cols;      int cols;
 };  };
   
   /* Closure for write_output */
   struct write_closure {
       struct sudo_event *wevent;
       struct iovec *iov;
       unsigned int iovcnt;
       size_t nbytes;
   };
   
 /*  /*
  * Handle expressions like:   * Handle expressions like:
  * ( user millert or user root ) and tty console and command /bin/sh   * ( user millert or user root ) and tty console and command /bin/sh
  */   */
static struct search_node {STAILQ_HEAD(search_node_list, search_node);
    struct search_node *next;struct search_node {
     STAILQ_ENTRY(search_node) entries;
 #define ST_EXPR         1  #define ST_EXPR         1
 #define ST_TTY          2  #define ST_TTY          2
 #define ST_USER         3  #define ST_USER         3
Line 140  static struct search_node { Line 147  static struct search_node {
 #define ST_TODATE       8  #define ST_TODATE       8
 #define ST_CWD          9  #define ST_CWD          9
     char type;      char type;
    char negated;    bool negated;
    char or;    bool or;
    char pad; 
     union {      union {
 #ifdef HAVE_REGCOMP  #ifdef HAVE_REGCOMP
         regex_t cmdre;          regex_t cmdre;
   #else
           char *pattern;
 #endif  #endif
         time_t tstamp;          time_t tstamp;
         char *cwd;          char *cwd;
         char *tty;          char *tty;
         char *user;          char *user;
         char *pattern;  
         char *runas_group;          char *runas_group;
         char *runas_user;          char *runas_user;
        struct search_node *expr;        struct search_node_list expr;
         void *ptr;          void *ptr;
     } u;      } u;
} *search_expr;};
   
#define STACK_NODE_SIZE 32static struct search_node_list search_expr = STAILQ_HEAD_INITIALIZER(search_expr);
static struct search_node *node_stack[32]; 
static int stack_top; 
   
static int timing_idx_adj = 0;static int timing_idx_adj;
   
   static double speed_factor = 1.0;
   
 static const char *session_dir = _PATH_SUDO_IO_LOGDIR;  static const char *session_dir = _PATH_SUDO_IO_LOGDIR;
   
 static const char short_opts[] =  "d:f:hlm:s:V";  static const char short_opts[] =  "d:f:hlm:s:V";
Line 179  static struct option long_opts[] = { Line 186  static struct option long_opts[] = {
     { NULL,             no_argument,            NULL,   '\0' },      { NULL,             no_argument,            NULL,   '\0' },
 };  };
   
extern time_t get_date(char *);/* XXX move to separate header? */
 extern char *get_timestr(time_t, int);  extern char *get_timestr(time_t, int);
extern int term_raw(int, int);extern time_t get_date(char *);
extern int term_restore(int, int); 
extern void get_ttysize(int *rowp, int *colp); 
   
 static int list_sessions(int, char **, const char *, const char *, const char *);  static int list_sessions(int, char **, const char *, const char *, const char *);
 static int parse_expr(struct search_node **, char **);  
 static void check_input(int, double *);  
 static void delay(double);  
 static void help(void) __attribute__((__noreturn__));  
 static void usage(int);  
 static int open_io_fd(char *path, int len, struct io_log_file *iol);  static int open_io_fd(char *path, int len, struct io_log_file *iol);
   static int parse_expr(struct search_node_list *, char **, bool);
 static int parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes);  static int parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes);
 static struct log_info *parse_logfile(char *logfile);  static struct log_info *parse_logfile(char *logfile);
   static void check_input(int fd, int what, void *v);
 static void free_log_info(struct log_info *li);  static void free_log_info(struct log_info *li);
static size_t atomic_writev(int fd, struct iovec *iov, int iovcnt);static void help(void) __attribute__((__noreturn__));
static void sudoreplay_handler(int);static void replay_session(const double max_wait, const char *decimal);
 static void sudoreplay_cleanup(void);  static void sudoreplay_cleanup(void);
   static void sudoreplay_handler(int);
   static void usage(int);
   static void write_output(int fd, int what, void *v);
   
 #ifdef HAVE_REGCOMP  #ifdef HAVE_REGCOMP
 # define REGEX_T        regex_t  # define REGEX_T        regex_t
Line 225  int Line 230  int
 main(int argc, char *argv[])  main(int argc, char *argv[])
 {  {
     int ch, idx, plen, exitcode = 0, rows = 0, cols = 0;      int ch, idx, plen, exitcode = 0, rows = 0, cols = 0;
    bool interactive = false, listonly = false, need_nlcr = false;    bool def_filter = true, listonly = false;
    bool def_filter = true; 
     const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL;      const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL;
    char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;    char *cp, *ep, path[PATH_MAX];
    double seconds, to_wait, speed = 1.0, max_wait = 0; 
    sigaction_t sa; 
    size_t len, nbytes, nread; 
     struct log_info *li;      struct log_info *li;
    struct iovec *iov = NULL;    double max_wait = 0;
    int iovcnt = 0, iovmax = 0; 
     debug_decl(main, SUDO_DEBUG_MAIN)      debug_decl(main, SUDO_DEBUG_MAIN)
   
 #if defined(SUDO_DEVEL) && defined(__OpenBSD__)  #if defined(SUDO_DEVEL) && defined(__OpenBSD__)
Line 244  main(int argc, char *argv[]) Line 244  main(int argc, char *argv[])
     }        }  
 #endif  #endif
   
#if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME)    initprogname(argc > 0 ? argv[0] : "sudoreplay");
    setprogname(argc > 0 ? argv[0] : "sudoreplay");    setlocale(LC_ALL, "");
#endif 
 
    sudoers_setlocale(SUDOERS_LOCALE_USER, NULL); 
     decimal = localeconv()->decimal_point;      decimal = localeconv()->decimal_point;
     bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have sudoreplay domain */      bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have sudoreplay domain */
     textdomain("sudoers");      textdomain("sudoers");
Line 275  main(int argc, char *argv[]) Line 272  main(int argc, char *argv[])
                 else if (strcmp(cp, "ttyout") == 0)                  else if (strcmp(cp, "ttyout") == 0)
                     io_log_files[IOFD_TTYOUT].enabled = true;                      io_log_files[IOFD_TTYOUT].enabled = true;
                 else                  else
                    fatalx(_("invalid filter option: %s"), optarg);                    fatalx(U_("invalid filter option: %s"), optarg);
             }              }
             break;              break;
         case 'h':          case 'h':
Line 288  main(int argc, char *argv[]) Line 285  main(int argc, char *argv[])
             errno = 0;              errno = 0;
             max_wait = strtod(optarg, &ep);              max_wait = strtod(optarg, &ep);
             if (*ep != '\0' || errno != 0)              if (*ep != '\0' || errno != 0)
                fatalx(_("invalid max wait: %s"), optarg);                fatalx(U_("invalid max wait: %s"), optarg);
             break;              break;
         case 's':          case 's':
             errno = 0;              errno = 0;
            speed = strtod(optarg, &ep);            speed_factor = strtod(optarg, &ep);
             if (*ep != '\0' || errno != 0)              if (*ep != '\0' || errno != 0)
                fatalx(_("invalid speed factor: %s"), optarg);                fatalx(U_("invalid speed factor: %s"), optarg);
             break;              break;
         case 'V':          case 'V':
             (void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION);              (void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION);
Line 328  main(int argc, char *argv[]) Line 325  main(int argc, char *argv[])
     if (VALID_ID(id)) {      if (VALID_ID(id)) {
         plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing",          plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing",
             session_dir, id, &id[2], &id[4]);              session_dir, id, &id[2], &id[4]);
        if (plen <= 0 || plen >= sizeof(path))        if (plen <= 0 || (size_t)plen >= sizeof(path))
            fatalx(_("%s/%.2s/%.2s/%.2s/timing: %s"), session_dir,            fatalx(U_("%s/%.2s/%.2s/%.2s/timing: %s"), session_dir,
                 id, &id[2], &id[4], strerror(ENAMETOOLONG));                  id, &id[2], &id[4], strerror(ENAMETOOLONG));
     } else {      } else {
         plen = snprintf(path, sizeof(path), "%s/%s/timing",          plen = snprintf(path, sizeof(path), "%s/%s/timing",
             session_dir, id);              session_dir, id);
        if (plen <= 0 || plen >= sizeof(path))        if (plen <= 0 || (size_t)plen >= sizeof(path))
            fatalx(_("%s/%s/timing: %s"), session_dir,            fatalx(U_("%s/%s/timing: %s"), session_dir,
                 id, strerror(ENAMETOOLONG));                  id, strerror(ENAMETOOLONG));
     }      }
     plen -= 7;      plen -= 7;
Line 343  main(int argc, char *argv[]) Line 340  main(int argc, char *argv[])
     /* Open files for replay, applying replay filter for the -f flag. */      /* Open files for replay, applying replay filter for the -f flag. */
     for (idx = 0; idx < IOFD_MAX; idx++) {      for (idx = 0; idx < IOFD_MAX; idx++) {
         if (open_io_fd(path, plen, &io_log_files[idx]) == -1)           if (open_io_fd(path, plen, &io_log_files[idx]) == -1) 
            fatal(_("unable to open %s"), path);            fatal(U_("unable to open %s"), path);
     }      }
   
     /* Parse log file. */      /* Parse log file. */
Line 366  main(int argc, char *argv[]) Line 363  main(int argc, char *argv[])
     free_log_info(li);      free_log_info(li);
     li = NULL;      li = NULL;
   
       /* Replay session corresponding to io_log_files[]. */
       replay_session(max_wait, decimal);
   
       term_restore(STDIN_FILENO, 1);
   done:
       sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);
       exit(exitcode);
   }
   
   static void
   replay_session(const double max_wait, const char *decimal)
   {
       struct sudo_event *input_ev, *output_ev;
       unsigned int i, iovcnt = 0, iovmax = 0;
       struct sudo_event_base *evbase;
       struct iovec iovb, *iov = &iovb;
       bool interactive;
       struct write_closure wc;
       char buf[LINE_MAX];
       sigaction_t sa;
       int idx;
       debug_decl(replay_session, SUDO_DEBUG_UTIL)
   
       /* Restore tty settings if interupted. */
     fflush(stdout);      fflush(stdout);
     memset(&sa, 0, sizeof(sa));      memset(&sa, 0, sizeof(sa));
     sigemptyset(&sa.sa_mask);      sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_RESETHAND;      sa.sa_flags = SA_RESETHAND;
     sa.sa_handler = sudoreplay_handler;      sa.sa_handler = sudoreplay_handler;
     (void) sigaction(SIGINT, &sa, NULL);      (void) sigaction(SIGINT, &sa, NULL);
     (void) sigaction(SIGKILL, &sa, NULL);  
     (void) sigaction(SIGTERM, &sa, NULL);      (void) sigaction(SIGTERM, &sa, NULL);
     (void) sigaction(SIGHUP, &sa, NULL);      (void) sigaction(SIGHUP, &sa, NULL);
       (void) sigaction(SIGQUIT, &sa, NULL);
   
       /* Don't suspend as we cannot restore the screen on resume. */
     sa.sa_flags = SA_RESTART;      sa.sa_flags = SA_RESTART;
     sa.sa_handler = SIG_IGN;      sa.sa_handler = SIG_IGN;
     (void) sigaction(SIGTSTP, &sa, NULL);      (void) sigaction(SIGTSTP, &sa, NULL);
     (void) sigaction(SIGQUIT, &sa, NULL);  
   
     /* XXX - read user input from /dev/tty and set STDOUT to raw if not a pipe */      /* XXX - read user input from /dev/tty and set STDOUT to raw if not a pipe */
     /* Set stdin to raw mode if it is a tty */      /* Set stdin to raw mode if it is a tty */
     interactive = isatty(STDIN_FILENO);      interactive = isatty(STDIN_FILENO);
     if (interactive) {      if (interactive) {
        ch = fcntl(STDIN_FILENO, F_GETFL, 0);        idx = fcntl(STDIN_FILENO, F_GETFL, 0);
        if (ch != -1)        if (idx != -1)
            (void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK);            (void) fcntl(STDIN_FILENO, F_SETFL, idx | O_NONBLOCK);
         if (!term_raw(STDIN_FILENO, 1))          if (!term_raw(STDIN_FILENO, 1))
            fatal(_("unable to set tty to raw mode"));            fatal(U_("unable to set tty to raw mode"));
        iovmax = 32; 
        iov = ecalloc(iovmax, sizeof(*iov)); 
     }      }
   
       /* Setup event base and input/output events. */
       evbase = sudo_ev_base_alloc();
       if (evbase == NULL)
           fatal(NULL);
       input_ev = sudo_ev_alloc(STDIN_FILENO, interactive ? SUDO_EV_READ :
           SUDO_EV_TIMEOUT, check_input, sudo_ev_self_cbarg());
       if (input_ev == NULL)
           fatal(NULL);
       output_ev = sudo_ev_alloc(STDIN_FILENO, SUDO_EV_WRITE, write_output, &wc);
       if (output_ev == NULL)
           fatal(NULL);
   
     /*      /*
     * Timing file consists of line of the format: "%f %d\n"     * Read each line of the timing file, displaying the output streams.
      */       */
 #ifdef HAVE_ZLIB_H  #ifdef HAVE_ZLIB_H
     while (gzgets(io_log_files[IOFD_TIMING].fd.g, buf, sizeof(buf)) != NULL) {      while (gzgets(io_log_files[IOFD_TIMING].fd.g, buf, sizeof(buf)) != NULL) {
 #else  #else
     while (fgets(buf, sizeof(buf), io_log_files[IOFD_TIMING].fd.f) != NULL) {      while (fgets(buf, sizeof(buf), io_log_files[IOFD_TIMING].fd.f) != NULL) {
 #endif  #endif
           size_t len, nbytes, nread;
           double seconds, to_wait;
           struct timeval timeout;
           bool need_nlcr = false;
         char last_char = '\0';          char last_char = '\0';
   
           buf[strcspn(buf, "\n")] = '\0';
         if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes))          if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes))
            fatalx(_("invalid timing file line: %s"), buf);            fatalx(U_("invalid timing file line: %s"), buf);
   
         if (interactive)  
             check_input(STDIN_FILENO, &speed);  
   
         /* Adjust delay using speed factor and clamp to max_wait */          /* Adjust delay using speed factor and clamp to max_wait */
        to_wait = seconds / speed;        to_wait = seconds / speed_factor;
         if (max_wait && to_wait > max_wait)          if (max_wait && to_wait > max_wait)
             to_wait = max_wait;              to_wait = max_wait;
         delay(to_wait);  
   
           /* Convert delay to a timeval. */
           timeout.tv_sec = to_wait;
           timeout.tv_usec = (to_wait - timeout.tv_sec) * 1000000.0;
   
           /* Run event event loop to delay and get keyboard input. */
           sudo_ev_add(evbase, input_ev, &timeout, false);
           sudo_ev_loop(evbase, 0);
   
         /* Even if we are not replaying, we still have to delay. */          /* Even if we are not replaying, we still have to delay. */
         if (io_log_files[idx].fd.v == NULL)          if (io_log_files[idx].fd.v == NULL)
             continue;              continue;
Line 424  main(int argc, char *argv[]) Line 465  main(int argc, char *argv[])
             need_nlcr = (idx == IOFD_STDOUT || idx == IOFD_STDERR);              need_nlcr = (idx == IOFD_STDOUT || idx == IOFD_STDERR);
   
         /* All output is sent to stdout. */          /* All output is sent to stdout. */
           /* XXX - assumes no wall clock time spent writing output. */
         while (nbytes != 0) {          while (nbytes != 0) {
             if (nbytes > sizeof(buf))              if (nbytes > sizeof(buf))
                 len = sizeof(buf);                  len = sizeof(buf);
Line 440  main(int argc, char *argv[]) Line 482  main(int argc, char *argv[])
             if (need_nlcr) {              if (need_nlcr) {
                 size_t remainder = nread;                  size_t remainder = nread;
                 size_t linelen;                  size_t linelen;
                iovcnt = 0;                char *cp = buf;
                cp = buf;                char *ep = buf - 1;
                ep = cp - 1;
                 /* Handle a "\r\n" pair that spans a buffer. */                  /* Handle a "\r\n" pair that spans a buffer. */
                 if (last_char == '\r' && buf[0] == '\n') {                  if (last_char == '\r' && buf[0] == '\n') {
                     ep++;                      ep++;
                     remainder--;                      remainder--;
                 }                  }
   
                   iovcnt = 0;
                 while ((ep = memchr(ep + 1, '\n', remainder)) != NULL) {                  while ((ep = memchr(ep + 1, '\n', remainder)) != NULL) {
                     /* Is there already a carriage return? */                      /* Is there already a carriage return? */
                     if (cp != ep && ep[-1] == '\r') {                      if (cp != ep && ep[-1] == '\r') {
Line 457  main(int argc, char *argv[]) Line 501  main(int argc, char *argv[])
   
                     /* Store the line in iov followed by \r\n pair. */                      /* Store the line in iov followed by \r\n pair. */
                     if (iovcnt + 3 > iovmax) {                      if (iovcnt + 3 > iovmax) {
                        iovmax <<= 1;                        iov = iovmax ?
                        iov = erealloc3(iov, iovmax, sizeof(*iov));                            erealloc3(iov, iovmax <<= 1, sizeof(*iov)) :
                             emalloc2(iovmax = 32, sizeof(*iov));
                     }                      }
                     linelen = (size_t)(ep - cp) + 1;                      linelen = (size_t)(ep - cp) + 1;
                     iov[iovcnt].iov_base = cp;                      iov[iovcnt].iov_base = cp;
Line 470  main(int argc, char *argv[]) Line 515  main(int argc, char *argv[])
                     cp = ep + 1;                      cp = ep + 1;
                     remainder -= linelen;                      remainder -= linelen;
                 }                  }
                if (cp - buf != nread) {                if ((size_t)(cp - buf) != nread) {
                     /*                      /*
                      * Partial line without a linefeed or multiple lines                       * Partial line without a linefeed or multiple lines
                      * with \r\n pairs.                       * with \r\n pairs.
Line 486  main(int argc, char *argv[]) Line 531  main(int argc, char *argv[])
                 iov[0].iov_len = nread;                  iov[0].iov_len = nread;
                 iovcnt = 1;                  iovcnt = 1;
             }              }
             if (atomic_writev(STDOUT_FILENO, iov, iovcnt) == -1)  
                 fatal(_("writing to standard output"));  
         }  
     }  
     term_restore(STDIN_FILENO, 1);  
 done:  
     sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode);  
     exit(exitcode);  
 }  
   
static void            /* Setup closure for write_output. */
delay(double secs)            wc.wevent = output_ev;
{            wc.iov = iov;
    struct timespec ts, rts;            wc.iovcnt = iovcnt;
    int rval;            wc.nbytes = 0;
             for (i = 0; i < iovcnt; i++)
                 wc.nbytes += iov[i].iov_len;
   
    /*            /* Run event event loop to write output. */
     * Typical max resolution is 1/HZ but we can't portably check that.            /* XXX - should use a single event loop with a circular buffer. */
     * If the interval is small enough, just ignore it.            sudo_ev_add(evbase, output_ev, NULL, false);
     */            sudo_ev_loop(evbase, 0);
    if (secs < 0.0001)        }
        return; 
 
    rts.tv_sec = secs; 
    rts.tv_nsec = (secs - (double) rts.tv_sec) * 1000000000.0; 
    do { 
      memcpy(&ts, &rts, sizeof(ts)); 
      rval = nanosleep(&ts, &rts); 
    } while (rval == -1 && errno == EINTR); 
    if (rval == -1) { 
        fatal_nodebug("nanosleep: tv_sec %lld, tv_nsec %ld", 
            (long long)ts.tv_sec, (long)ts.tv_nsec); 
     }      }
       debug_return;
 }  }
   
 static int  static int
Line 539  open_io_fd(char *path, int len, struct io_log_file *io Line 567  open_io_fd(char *path, int len, struct io_log_file *io
     debug_return_int(iol->fd.v ? 0 : -1);      debug_return_int(iol->fd.v ? 0 : -1);
 }  }
   
/*static void
 * Call writev(), restarting as needed and handling EAGAIN sincewrite_output(int fd, int what, void *v)
 * fd may be in non-blocking mode. 
 */ 
static size_t 
atomic_writev(int fd, struct iovec *iov, int iovcnt) 
 {  {
    ssize_t n, nwritten = 0;    struct write_closure *wc = v;
    size_t count, remainder, nbytes = 0;    ssize_t nwritten;
    int i;    size_t count, remainder;
    debug_decl(atomic_writev, SUDO_DEBUG_UTIL)    unsigned int i;
     debug_decl(write_output, SUDO_DEBUG_UTIL)
   
    for (i = 0; i < iovcnt; i++)    nwritten = writev(STDOUT_FILENO, wc->iov, wc->iovcnt);
        nbytes += iov[i].iov_len;    switch (nwritten) {
     case -1:
         if (errno != EINTR && errno != EAGAIN)
             fatal(U_("unable to write to %s"), "stdout");
         break;
     case 0:
         break;
     default:
         remainder = wc->nbytes - nwritten;
         if (remainder == 0) {
             /* writev completed */
             debug_return;
         }
   
    for (;;) {        /* short writev, adjust iov so we can write the remainder. */
        n = writev(STDOUT_FILENO, iov, iovcnt);        count = 0;
        if (n > 0) {        i = wc->iovcnt;
            nwritten += n;        while (i--) {
            remainder = nbytes - nwritten;            count += wc->iov[i].iov_len;
            if (remainder == 0)            if (count == remainder) {
                 wc->iov += i;
                 wc->iovcnt -= i;
                 break;                  break;
             /* short writev, adjust iov and do the rest. */  
             count = 0;  
             i = iovcnt;  
             while (i--) {  
                 count += iov[i].iov_len;  
                 if (count == remainder) {  
                     iov += i;  
                     iovcnt -= i;  
                     break;  
                 }  
                 if (count > remainder) {  
                     size_t off = (count - remainder);  
                     /* XXX - side effect prevents iov from being const */  
                     iov[i].iov_base = (char *)iov[i].iov_base + off;  
                     iov[i].iov_len -= off;  
                     iov += i;  
                     iovcnt -= i;  
                     break;  
                 }  
             }              }
            continue;            if (count > remainder) {
                 size_t off = (count - remainder);
                 wc->iov[i].iov_base = (char *)wc->iov[i].iov_base + off;
                 wc->iov[i].iov_len -= off;
                 wc->iov += i;
                 wc->iovcnt -= i;
                 break;
             }
         }          }
         if (n == 0 || errno == EAGAIN) {  
             int nready;  
             fd_set fdsw;  
             FD_ZERO(&fdsw);  
             FD_SET(STDOUT_FILENO, &fdsw);  
             do {  
                 nready = select(STDOUT_FILENO + 1, NULL, &fdsw, NULL, NULL);  
             } while (nready == -1 && errno == EINTR);  
             if (nready == 1)  
                 continue;  
         }  
         if (errno == EINTR)  
             continue;  
         nwritten = -1;  
         break;          break;
     }      }
    debug_return_size_t(nwritten);
     /* Reschedule event to write remainder. */
     sudo_ev_add(sudo_ev_get_base(wc->wevent), wc->wevent, NULL, false);
     debug_return;
 }  }
   
 /*  /*
  * Build expression list from search args   * Build expression list from search args
  */   */
 static int  static int
parse_expr(struct search_node **headp, char *argv[])parse_expr(struct search_node_list *head, char *argv[], bool sub_expr)
 {  {
    struct search_node *sn, *newsn;    bool or = false, not = false;
    char or = 0, not = 0, type, **av;    struct search_node *sn;
     char type, **av;
     debug_decl(parse_expr, SUDO_DEBUG_UTIL)      debug_decl(parse_expr, SUDO_DEBUG_UTIL)
   
    sn = *headp;    for (av = argv; *av != NULL; av++) {
    for (av = argv; *av; av++) { 
         switch (av[0][0]) {          switch (av[0][0]) {
         case 'a': /* and (ignore) */          case 'a': /* and (ignore) */
             if (strncmp(*av, "and", strlen(*av)) != 0)              if (strncmp(*av, "and", strlen(*av)) != 0)
Line 622  parse_expr(struct search_node **headp, char *argv[]) Line 638  parse_expr(struct search_node **headp, char *argv[])
         case 'o': /* or */          case 'o': /* or */
             if (strncmp(*av, "or", strlen(*av)) != 0)              if (strncmp(*av, "or", strlen(*av)) != 0)
                 goto bad;                  goto bad;
            or = 1;            or = true;
             continue;              continue;
         case '!': /* negate */          case '!': /* negate */
             if (av[0][1] != '\0')              if (av[0][1] != '\0')
                 goto bad;                  goto bad;
            not = 1;            not = true;
             continue;              continue;
         case 'c': /* command */          case 'c': /* command */
             if (av[0][1] == '\0')              if (av[0][1] == '\0')
                fatalx(_("ambiguous expression \"%s\""), *av);                fatalx(U_("ambiguous expression \"%s\""), *av);
             if (strncmp(*av, "cwd", strlen(*av)) == 0)              if (strncmp(*av, "cwd", strlen(*av)) == 0)
                 type = ST_CWD;                  type = ST_CWD;
             else if (strncmp(*av, "command", strlen(*av)) == 0)              else if (strncmp(*av, "command", strlen(*av)) == 0)
Line 656  parse_expr(struct search_node **headp, char *argv[]) Line 672  parse_expr(struct search_node **headp, char *argv[])
             break;              break;
         case 't': /* tty or to date */          case 't': /* tty or to date */
             if (av[0][1] == '\0')              if (av[0][1] == '\0')
                fatalx(_("ambiguous expression \"%s\""), *av);                fatalx(U_("ambiguous expression \"%s\""), *av);
             if (strncmp(*av, "todate", strlen(*av)) == 0)              if (strncmp(*av, "todate", strlen(*av)) == 0)
                 type = ST_TODATE;                  type = ST_TODATE;
             else if (strncmp(*av, "tty", strlen(*av)) == 0)              else if (strncmp(*av, "tty", strlen(*av)) == 0)
Line 672  parse_expr(struct search_node **headp, char *argv[]) Line 688  parse_expr(struct search_node **headp, char *argv[])
         case '(': /* start sub-expression */          case '(': /* start sub-expression */
             if (av[0][1] != '\0')              if (av[0][1] != '\0')
                 goto bad;                  goto bad;
             if (stack_top + 1 == STACK_NODE_SIZE) {  
                 fatalx(_("too many parenthesized expressions, max %d"),  
                     STACK_NODE_SIZE);  
             }  
             node_stack[stack_top++] = sn;  
             type = ST_EXPR;              type = ST_EXPR;
             break;              break;
         case ')': /* end sub-expression */          case ')': /* end sub-expression */
             if (av[0][1] != '\0')              if (av[0][1] != '\0')
                 goto bad;                  goto bad;
            /* pop */            if (!sub_expr)
            if (--stack_top < 0)                fatalx(U_("unmatched ')' in expression"));
                fatalx(_("unmatched ')' in expression")); 
            if (node_stack[stack_top]) 
                sn->next = node_stack[stack_top]->next; 
             debug_return_int(av - argv + 1);              debug_return_int(av - argv + 1);
         bad:          bad:
         default:          default:
            fatalx(_("unknown search term \"%s\""), *av);            fatalx(U_("unknown search term \"%s\""), *av);
             /* NOTREACHED */              /* NOTREACHED */
         }          }
   
         /* Allocate new search node */          /* Allocate new search node */
        newsn = ecalloc(1, sizeof(*newsn));        sn = ecalloc(1, sizeof(*sn));
        newsn->type = type;        sn->type = type;
        newsn->or = or;        sn->or = or;
        newsn->negated = not;        sn->negated = not;
        /* newsn->next = NULL; */ 
         if (type == ST_EXPR) {          if (type == ST_EXPR) {
            av += parse_expr(&newsn->u.expr, av + 1);            STAILQ_INIT(&sn->u.expr);
             av += parse_expr(&sn->u.expr, av + 1, true);
         } else {          } else {
             if (*(++av) == NULL)              if (*(++av) == NULL)
                fatalx(_("%s requires an argument"), av[-1]);                fatalx(U_("%s requires an argument"), av[-1]);
 #ifdef HAVE_REGCOMP  #ifdef HAVE_REGCOMP
             if (type == ST_PATTERN) {              if (type == ST_PATTERN) {
                if (regcomp(&newsn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0)                if (regcomp(&sn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0)
                    fatalx(_("invalid regular expression: %s"), *av);                    fatalx(U_("invalid regular expression: %s"), *av);
             } else              } else
 #endif  #endif
             if (type == ST_TODATE || type == ST_FROMDATE) {              if (type == ST_TODATE || type == ST_FROMDATE) {
                newsn->u.tstamp = get_date(*av);                sn->u.tstamp = get_date(*av);
                if (newsn->u.tstamp == -1)                if (sn->u.tstamp == -1)
                    fatalx(_("could not parse date \"%s\""), *av);                    fatalx(U_("could not parse date \"%s\""), *av);
             } else {              } else {
                newsn->u.ptr = *av;                sn->u.ptr = *av;
             }              }
         }          }
        not = or = 0; /* reset state */        not = or = false; /* reset state */
        if (sn)        STAILQ_INSERT_TAIL(head, sn, entries);
            sn->next = newsn; 
        else 
            *headp = newsn; 
        sn = newsn; 
     }      }
    if (stack_top)    if (sub_expr)
        fatalx(_("unmatched '(' in expression"));        fatalx(U_("unmatched '(' in expression"));
     if (or)      if (or)
        fatalx(_("illegal trailing \"or\""));        fatalx(U_("illegal trailing \"or\""));
     if (not)      if (not)
        fatalx(_("illegal trailing \"!\""));        fatalx(U_("illegal trailing \"!\""));
   
     debug_return_int(av - argv);      debug_return_int(av - argv);
 }  }
   
 static bool  static bool
match_expr(struct search_node *head, struct log_info *log)match_expr(struct search_node_list *head, struct log_info *log, bool last_match)
 {  {
     struct search_node *sn;      struct search_node *sn;
    bool matched = true;    bool res, matched = last_match;
     int rc;      int rc;
     debug_decl(match_expr, SUDO_DEBUG_UTIL)      debug_decl(match_expr, SUDO_DEBUG_UTIL)
   
    for (sn = head; sn; sn = sn->next) {    STAILQ_FOREACH(sn, head, entries) {
        /* If we have no match, skip ahead to the next OR entry. */ 
        if (!matched && !sn->or) 
            continue; 
 
         switch (sn->type) {          switch (sn->type) {
         case ST_EXPR:          case ST_EXPR:
            matched = match_expr(sn->u.expr, log);            res = match_expr(&sn->u.expr, log, matched);
             break;              break;
         case ST_CWD:          case ST_CWD:
            matched = strcmp(sn->u.cwd, log->cwd) == 0;            res = strcmp(sn->u.cwd, log->cwd) == 0;
             break;              break;
         case ST_TTY:          case ST_TTY:
            matched = strcmp(sn->u.tty, log->tty) == 0;            res = strcmp(sn->u.tty, log->tty) == 0;
             break;              break;
         case ST_RUNASGROUP:          case ST_RUNASGROUP:
            matched = strcmp(sn->u.runas_group, log->runas_group) == 0;            res = strcmp(sn->u.runas_group, log->runas_group) == 0;
             break;              break;
         case ST_RUNASUSER:          case ST_RUNASUSER:
            matched = strcmp(sn->u.runas_user, log->runas_user) == 0;            res = strcmp(sn->u.runas_user, log->runas_user) == 0;
             break;              break;
         case ST_USER:          case ST_USER:
            matched = strcmp(sn->u.user, log->user) == 0;            res = strcmp(sn->u.user, log->user) == 0;
             break;              break;
         case ST_PATTERN:          case ST_PATTERN:
 #ifdef HAVE_REGCOMP  #ifdef HAVE_REGCOMP
Line 776  match_expr(struct search_node *head, struct log_info * Line 776  match_expr(struct search_node *head, struct log_info *
                 regerror(rc, &sn->u.cmdre, buf, sizeof(buf));                  regerror(rc, &sn->u.cmdre, buf, sizeof(buf));
                 fatalx("%s", buf);                  fatalx("%s", buf);
             }              }
            matched = rc == REG_NOMATCH ? 0 : 1;            res = rc == REG_NOMATCH ? 0 : 1;
 #else  #else
            matched = strstr(log.cmd, sn->u.pattern) != NULL;            res = strstr(log.cmd, sn->u.pattern) != NULL;
 #endif  #endif
             break;              break;
         case ST_FROMDATE:          case ST_FROMDATE:
            matched = log->tstamp >= sn->u.tstamp;            res = log->tstamp >= sn->u.tstamp;
             break;              break;
         case ST_TODATE:          case ST_TODATE:
            matched = log->tstamp <= sn->u.tstamp;            res = log->tstamp <= sn->u.tstamp;
             break;              break;
           default:
               fatalx(U_("unknown search type %d"), sn->type);
               /* NOTREACHED */
         }          }
         if (sn->negated)          if (sn->negated)
            matched = !matched;            res = !res;
         matched = sn->or ? (res || last_match) : (res && last_match);
         last_match = matched;
     }      }
     debug_return_bool(matched);      debug_return_bool(matched);
 }  }
Line 799  parse_logfile(char *logfile) Line 804  parse_logfile(char *logfile)
 {  {
     FILE *fp;      FILE *fp;
     char *buf = NULL, *cp, *ep;      char *buf = NULL, *cp, *ep;
       const char *errstr;
     size_t bufsize = 0, cwdsize = 0, cmdsize = 0;      size_t bufsize = 0, cwdsize = 0, cmdsize = 0;
     struct log_info *li = NULL;      struct log_info *li = NULL;
    debug_decl(list_session, SUDO_DEBUG_UTIL)    debug_decl(parse_logfile, SUDO_DEBUG_UTIL)
   
     fp = fopen(logfile, "r");      fp = fopen(logfile, "r");
     if (fp == NULL) {      if (fp == NULL) {
        warning(_("unable to open %s"), logfile);        warning(U_("unable to open %s"), logfile);
         goto bad;          goto bad;
     }      }
   
Line 819  parse_logfile(char *logfile) Line 825  parse_logfile(char *logfile)
     if (getline(&buf, &bufsize, fp) == -1 ||      if (getline(&buf, &bufsize, fp) == -1 ||
         getline(&li->cwd, &cwdsize, fp) == -1 ||          getline(&li->cwd, &cwdsize, fp) == -1 ||
         getline(&li->cmd, &cmdsize, fp) == -1) {          getline(&li->cmd, &cmdsize, fp) == -1) {
           warning(U_("%s: invalid log file"), logfile);
         goto bad;          goto bad;
     }      }
   
Line 829  parse_logfile(char *logfile) Line 836  parse_logfile(char *logfile)
     /*      /*
      * Crack the log line (rows and cols not present in old versions).       * Crack the log line (rows and cols not present in old versions).
      *  timestamp:user:runas_user:runas_group:tty:rows:cols       *  timestamp:user:runas_user:runas_group:tty:rows:cols
        * XXX - probably better to use strtok and switch on the state.
      */       */
     buf[strcspn(buf, "\n")] = '\0';      buf[strcspn(buf, "\n")] = '\0';
       cp = buf;
   
     /* timestamp */      /* timestamp */
    if ((ep = strchr(buf, ':')) == NULL)    if ((ep = strchr(cp, ':')) == NULL) {
         warning(U_("%s: time stamp field is missing"), logfile);
         goto bad;          goto bad;
    if ((li->tstamp = atoi(buf)) == 0)    }
     *ep = '\0';
     li->tstamp = sizeof(time_t) == 4 ? strtonum(cp, INT_MIN, INT_MAX, &errstr) :
         strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr);
     if (errstr != NULL) {
         warning(U_("%s: time stamp %s: %s"), logfile, cp, errstr);
         goto bad;          goto bad;
       }
   
     /* user */      /* user */
     cp = ep + 1;      cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL)    if ((ep = strchr(cp, ':')) == NULL) {
         warning(U_("%s: user field is missing"), logfile);
         goto bad;          goto bad;
       }
     li->user = estrndup(cp, (size_t)(ep - cp));      li->user = estrndup(cp, (size_t)(ep - cp));
   
     /* runas user */      /* runas user */
     cp = ep + 1;      cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL)    if ((ep = strchr(cp, ':')) == NULL) {
         warning(U_("%s: runas user field is missing"), logfile);
         goto bad;          goto bad;
       }
     li->runas_user = estrndup(cp, (size_t)(ep - cp));      li->runas_user = estrndup(cp, (size_t)(ep - cp));
   
     /* runas group */      /* runas group */
     cp = ep + 1;      cp = ep + 1;
    if ((ep = strchr(cp, ':')) == NULL)    if ((ep = strchr(cp, ':')) == NULL) {
         warning(U_("%s: runas group field is missing"), logfile);
         goto bad;          goto bad;
       }
     if (cp != ep)      if (cp != ep)
         li->runas_group = estrndup(cp, (size_t)(ep - cp));          li->runas_group = estrndup(cp, (size_t)(ep - cp));
   
     /* tty, followed by optional rows + columns */      /* tty, followed by optional rows + columns */
     cp = ep + 1;      cp = ep + 1;
     if ((ep = strchr(cp, ':')) == NULL) {      if ((ep = strchr(cp, ':')) == NULL) {
           /* just the tty */
         li->tty = estrdup(cp);          li->tty = estrdup(cp);
     } else {      } else {
           /* tty followed by rows + columns */
         li->tty = estrndup(cp, (size_t)(ep - cp));          li->tty = estrndup(cp, (size_t)(ep - cp));
         cp = ep + 1;          cp = ep + 1;
        li->rows = atoi(cp);        /* need to NULL out separator to use strtonum() */
         if ((ep = strchr(cp, ':')) != NULL) {          if ((ep = strchr(cp, ':')) != NULL) {
               *ep = '\0';
           }
           li->rows = strtonum(cp, 1, INT_MAX, &errstr);
           if (errstr != NULL) {
               sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
                   "%s: tty rows %s: %s", logfile, cp, errstr);
           }
           if (ep != NULL) {
             cp = ep + 1;              cp = ep + 1;
            li->cols = atoi(cp);            li->cols = strtonum(cp, 1, INT_MAX, &errstr);
             if (errstr != NULL) {
                 sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
                     "%s: tty cols %s: %s", logfile, cp, errstr);
             }
         }          }
     }      }
     fclose(fp);      fclose(fp);
Line 900  static int Line 936  static int
 list_session(char *logfile, REGEX_T *re, const char *user, const char *tty)  list_session(char *logfile, REGEX_T *re, const char *user, const char *tty)
 {  {
     char idbuf[7], *idstr, *cp;      char idbuf[7], *idstr, *cp;
       const char *timestr;
     struct log_info *li;      struct log_info *li;
     int rval = -1;      int rval = -1;
     debug_decl(list_session, SUDO_DEBUG_UTIL)      debug_decl(list_session, SUDO_DEBUG_UTIL)
Line 908  list_session(char *logfile, REGEX_T *re, const char *u Line 945  list_session(char *logfile, REGEX_T *re, const char *u
         goto done;          goto done;
   
     /* Match on search expression if there is one. */      /* Match on search expression if there is one. */
    if (search_expr && !match_expr(search_expr, li))    if (!STAILQ_EMPTY(&search_expr) && !match_expr(&search_expr, li, true))
         goto done;          goto done;
   
     /* Convert from /var/log/sudo-sessions/00/00/01/log to 000001 */      /* Convert from /var/log/sudo-sessions/00/00/01/log to 000001 */
Line 928  list_session(char *logfile, REGEX_T *re, const char *u Line 965  list_session(char *logfile, REGEX_T *re, const char *u
         idstr = cp;          idstr = cp;
     }      }
     /* XXX - print rows + cols? */      /* XXX - print rows + cols? */
       timestr = get_timestr(li->tstamp, 1);
     printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ",      printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ",
        get_timestr(li->tstamp, 1), li->user, li->tty, li->cwd, li->runas_user);        timestr ? timestr : "invalid date",
         li->user, li->tty, li->cwd, li->runas_user);
     if (li->runas_group)      if (li->runas_group)
         printf("GROUP=%s ; ", li->runas_group);          printf("GROUP=%s ; ", li->runas_group);
     printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd);      printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd);
Line 957  find_sessions(const char *dir, REGEX_T *re, const char Line 996  find_sessions(const char *dir, REGEX_T *re, const char
     struct dirent *dp;      struct dirent *dp;
     struct stat sb;      struct stat sb;
     size_t sdlen, sessions_len = 0, sessions_size = 36*36;      size_t sdlen, sessions_len = 0, sessions_size = 36*36;
    int i, len;    unsigned int i;
     int len;
     char pathbuf[PATH_MAX], **sessions = NULL;      char pathbuf[PATH_MAX], **sessions = NULL;
 #ifdef HAVE_STRUCT_DIRENT_D_TYPE  #ifdef HAVE_STRUCT_DIRENT_D_TYPE
     bool checked_type = true;      bool checked_type = true;
Line 968  find_sessions(const char *dir, REGEX_T *re, const char Line 1008  find_sessions(const char *dir, REGEX_T *re, const char
   
     d = opendir(dir);      d = opendir(dir);
     if (d == NULL)      if (d == NULL)
        fatal(_("unable to open %s"), dir);        fatal(U_("unable to open %s"), dir);
   
     /* XXX - would be faster to chdir and use relative names */      /* XXX - would be faster to chdir and use relative names */
     sdlen = strlcpy(pathbuf, dir, sizeof(pathbuf));      sdlen = strlcpy(pathbuf, dir, sizeof(pathbuf));
Line 1011  find_sessions(const char *dir, REGEX_T *re, const char Line 1051  find_sessions(const char *dir, REGEX_T *re, const char
     for (i = 0; i < sessions_len; i++) {      for (i = 0; i < sessions_len; i++) {
         len = snprintf(&pathbuf[sdlen], sizeof(pathbuf) - sdlen,          len = snprintf(&pathbuf[sdlen], sizeof(pathbuf) - sdlen,
             "%s/log", sessions[i]);              "%s/log", sessions[i]);
        if (len <= 0 || len >= sizeof(pathbuf) - sdlen) {        if (len <= 0 || (size_t)len >= sizeof(pathbuf) - sdlen) {
             errno = ENAMETOOLONG;              errno = ENAMETOOLONG;
             fatal("%s/%s/log", dir, sessions[i]);              fatal("%s/%s/log", dir, sessions[i]);
         }          }
Line 1041  list_sessions(int argc, char **argv, const char *patte Line 1081  list_sessions(int argc, char **argv, const char *patte
     debug_decl(list_sessions, SUDO_DEBUG_UTIL)      debug_decl(list_sessions, SUDO_DEBUG_UTIL)
   
     /* Parse search expression if present */      /* Parse search expression if present */
    parse_expr(&search_expr, argv);    parse_expr(&search_expr, argv, false);
   
 #ifdef HAVE_REGCOMP  #ifdef HAVE_REGCOMP
     /* optional regex */      /* optional regex */
     if (pattern) {      if (pattern) {
         re = &rebuf;          re = &rebuf;
         if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0)          if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0)
            fatalx(_("invalid regular expression: %s"), pattern);            fatalx(U_("invalid regular expression: %s"), pattern);
     }      }
 #else  #else
     re = (char *) pattern;      re = (char *) pattern;
Line 1058  list_sessions(int argc, char **argv, const char *patte Line 1098  list_sessions(int argc, char **argv, const char *patte
 }  }
   
 /*  /*
 * Check input for ' ', '<', '>' * Check input for ' ', '<', '>', return
 * pause, slow, fast * pause, slow, fast, next
  */   */
 static void  static void
check_input(int ttyfd, double *speed)check_input(int fd, int what, void *v)
 {  {
    fd_set *fdsr;    struct sudo_event *ev = v;
    int nready, paused = 0;    struct sudo_event_base *evbase = sudo_ev_get_base(ev);
    struct timeval tv;    struct timeval tv, *timeout = NULL;
     static bool paused = 0;
     char ch;      char ch;
     ssize_t n;  
     debug_decl(check_input, SUDO_DEBUG_UTIL)      debug_decl(check_input, SUDO_DEBUG_UTIL)
   
    fdsr = ecalloc(howmany(ttyfd + 1, NFDBITS), sizeof(fd_mask));    if (ISSET(what, SUDO_EV_READ)) {
    for (;;) {        switch (read(fd, &ch, 1)) {
        FD_SET(ttyfd, fdsr);        case -1:
        tv.tv_sec = 0;            if (errno != EINTR && errno != EAGAIN)
        tv.tv_usec = 0;                fatal(U_("unable to read %s"), "stdin");
 
        nready = select(ttyfd + 1, fdsr, NULL, NULL, paused ? NULL : &tv); 
        if (nready != 1) 
             break;              break;
        n = read(ttyfd, &ch, 1);        case 0:
        if (n == 1) {            /* Ignore EOF. */
             break;
         case 1:
             if (paused) {              if (paused) {
                paused = 0;                /* Any key will unpause, event is finished. */
                continue;                /* XXX - pause time could be less than timeout */
                 paused = false;
                 debug_return; /* XXX */
             }              }
             switch (ch) {              switch (ch) {
             case ' ':              case ' ':
                paused = 1;                paused = true;
                 break;                  break;
             case '<':              case '<':
                *speed /= 2;                speed_factor /= 2;
                 break;                  break;
             case '>':              case '>':
                *speed *= 2;                speed_factor *= 2;
                 break;                  break;
               case '\r':
               case '\n':
                   debug_return; /* XXX */
             }              }
               break;
         }          }
           if (!paused) {
               /* Determine remaining timeout, if any. */
               sudo_ev_get_timeleft(ev, &tv);
               if (!sudo_timevalisset(&tv)) {
                   /* No time left, event is done. */
                   debug_return;
               }
               timeout = &tv;
           }
           /* Re-enable event. */
           sudo_ev_add(evbase, ev, timeout, false);
     }      }
     free(fdsr);  
     debug_return;      debug_return;
 }  }
   
Line 1111  check_input(int ttyfd, double *speed) Line 1166  check_input(int ttyfd, double *speed)
  * Returns 1 on success and 0 on failure.   * Returns 1 on success and 0 on failure.
  */   */
 static int  static int
parse_timing(buf, decimal, idx, seconds, nbytes)parse_timing(const char *buf, const char *decimal, int *idx, double *seconds,
    const char *buf;    size_t *nbytes)
    const char *decimal; 
    int *idx; 
    double *seconds; 
    size_t *nbytes; 
 {  {
     unsigned long ul;      unsigned long ul;
     long l;      long l;
Line 1126  parse_timing(buf, decimal, idx, seconds, nbytes) Line 1177  parse_timing(buf, decimal, idx, seconds, nbytes)
   
     /* Parse index */      /* Parse index */
     ul = strtoul(buf, &ep, 10);      ul = strtoul(buf, &ep, 10);
       if (ep == buf || !isspace((unsigned char) *ep))
           goto bad;
     if (ul >= IOFD_TIMING) {      if (ul >= IOFD_TIMING) {
         if (ul != 6)          if (ul != 6)
             goto bad;              goto bad;
Line 1144  parse_timing(buf, decimal, idx, seconds, nbytes) Line 1197  parse_timing(buf, decimal, idx, seconds, nbytes)
      */       */
     errno = 0;      errno = 0;
     l = strtol(cp, &ep, 10);      l = strtol(cp, &ep, 10);
    if ((errno == ERANGE && (l == LONG_MAX || l == LONG_MIN)) ||    if (ep == cp || (*ep != '.' && strncmp(ep, decimal, strlen(decimal)) != 0))
        l < 0 || l > INT_MAX || 
        (*ep != '.' && strncmp(ep, decimal, strlen(decimal)) != 0)) { 
         goto bad;          goto bad;
    }    if (l < 0 || l > INT_MAX || (errno == ERANGE && l == LONG_MAX))
         goto bad;
     *seconds = (double)l;      *seconds = (double)l;
     cp = ep + (*ep == '.' ? 1 : strlen(decimal));      cp = ep + (*ep == '.' ? 1 : strlen(decimal));
     d = 10.0;      d = 10.0;
Line 1163  parse_timing(buf, decimal, idx, seconds, nbytes) Line 1215  parse_timing(buf, decimal, idx, seconds, nbytes)
   
     errno = 0;      errno = 0;
     ul = strtoul(cp, &ep, 10);      ul = strtoul(cp, &ep, 10);
    if (errno == ERANGE && ul == ULONG_MAX)    if (ep == cp || *ep != '\0' || (errno == ERANGE && ul == ULONG_MAX))
         goto bad;          goto bad;
     *nbytes = (size_t)ul;      *nbytes = (size_t)ul;
   
Line 1211  sudoreplay_cleanup(void) Line 1263  sudoreplay_cleanup(void)
 }  }
   
 /*  /*
 * Signal handler for SIGINT, SIGKILL, SIGTERM, SIGHUP * Signal handler for SIGINT, SIGTERM, SIGHUP, SIGQUIT
  * Must be installed with SA_RESETHAND enabled.   * Must be installed with SA_RESETHAND enabled.
  */   */
 static void  static void

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


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