]> granicus.if.org Git - sudo/commitdiff
Sync sudoreplay with trunk
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 15 Jun 2010 20:28:28 +0000 (16:28 -0400)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 15 Jun 2010 20:28:28 +0000 (16:28 -0400)
--HG--
branch : 1.7

sudoreplay.c
sudoreplay.cat
sudoreplay.man.in
sudoreplay.pod

index bd2ba44e999e5248d3585f517c114acc18d91fbc..357f1a2a5444eb6db84d09e95567807f42a6f917 100644 (file)
 #include "error.h"
 #include "missing.h"
 
+/* Must match the defines in iolog.c */
+#define IOFD_STDIN      0
+#define IOFD_STDOUT     1
+#define IOFD_STDERR     2
+#define IOFD_TTYIN      3
+#define IOFD_TTYOUT     4
+#define IOFD_TIMING     5
+#define IOFD_MAX        6
+
+/* Bitmap of iofds to be replayed */
+unsigned int replay_filter = (1 << IOFD_STDOUT) | (1 << IOFD_STDERR) |
+                            (1 << IOFD_TTYOUT);
+
 /* For getopt(3) */
 extern char *optarg;
 extern int optind;
 
 int Argc;
 char **Argv;
-const char *session_dir = _PATH_SUDO_IO_LOGDIR;
+
+union io_fd {
+    FILE *f;
+#ifdef HAVE_ZLIB
+    gzFile g;
+#endif
+    void *v;
+};
 
 /*
  * Info present in the I/O log file
@@ -148,9 +168,21 @@ struct search_node {
 static struct search_node *node_stack[32];
 static int stack_top;
 
+static const char *session_dir = _PATH_SUDO_IO_LOGDIR;
+
+static union io_fd io_fds[IOFD_MAX];
+static const char *io_fnames[IOFD_MAX] = {
+    "/stdin",
+    "/stdout",
+    "/stderr",
+    "/ttyin",
+    "/ttyout",
+    "/timing"
+};
+
 extern time_t get_date __P((char *));
 extern char *get_timestr __P((time_t, int));
-extern int term_raw __P((int, int, int));
+extern int term_raw __P((int, int));
 extern int term_restore __P((int, int));
 extern void zero_bytes __P((volatile void *, size_t));
 void cleanup __P((int));
@@ -160,6 +192,7 @@ static int parse_expr __P((struct search_node **, char **));
 static void check_input __P((int, double *));
 static void delay __P((double));
 static void usage __P((void));
+static void *open_io_fd __P((char *pathbuf, int len, const char *suffix));
 
 #ifdef HAVE_REGCOMP
 # define REGEX_T       regex_t
@@ -175,35 +208,41 @@ static void usage __P((void));
 int
 main(argc, argv)
     int argc;
-    char **argv;
+    char *argv[];
 {
     int ch, plen, nready, interactive = 0, listonly = 0;
     const char *id, *user = NULL, *pattern = NULL, *tty = NULL;
     char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;
+    double seconds, to_wait, speed = 1.0, max_wait = 0;
     FILE *lfile;
-#ifdef HAVE_ZLIB
-    gzFile tfile, sfile;
-#else
-    FILE *tfile, *sfile;
-#endif
-    fd_set *fdsr;
+    fd_set *fdsw;
     sigaction_t sa;
-    unsigned long nbytes;
-    size_t len, nread;
+    unsigned long nbytes, idx;
+    size_t len, nread, off;
     ssize_t nwritten;
-    double seconds;
-    double speed = 1.0;
-    double max_wait = 0;
-    double to_wait;
 
     Argc = argc;
     Argv = argv;
 
-    while ((ch = getopt(argc, argv, "d:lm:s:V")) != -1) {
+    while ((ch = getopt(argc, argv, "d:f:lm:s:V")) != -1) {
        switch(ch) {
        case 'd':
            session_dir = optarg;
            break;
+       case 'f':
+           /* Set the replay filter. */
+           replay_filter = 0;
+           for (cp = strtok(optarg, ","); cp; cp = strtok(NULL, ",")) {
+               if (strcmp(cp, "stdout") == 0)
+                   SET(replay_filter, 1 << IOFD_STDOUT);
+               else if (strcmp(cp, "stderr") == 0)
+                   SET(replay_filter, 1 << IOFD_STDERR);
+               else if (strcmp(cp, "ttyout") == 0)
+                   SET(replay_filter, 1 << IOFD_TTYOUT);
+               else
+                   errorx(1, "invalid filter option: %s", optarg);
+           }
+           break;
        case 'l':
            listonly = 1;
            break;
@@ -242,37 +281,28 @@ main(argc, argv)
     if (!VALID_ID(id))
        errorx(1, "invalid ID %s", id);
 
-    plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s.tim",
+    plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing",
        session_dir, id, &id[2], &id[4]);
     if (plen <= 0 || plen >= sizeof(path))
-       errorx(1, "%s/%.2s/%.2s/%.2s/%.2s.tim: %s", session_dir,
+       errorx(1, "%s/%.2s/%.2s/%.2s/%.2s/timing: %s", session_dir,
            id, &id[2], &id[4], strerror(ENAMETOOLONG));
+    plen -= 7;
+
+    /* Open files for replay, applying replay filter for the -f flag. */
+    for (idx = 0; idx < IOFD_MAX; idx++) {
+       if (ISSET(replay_filter, 1 << idx) || idx == IOFD_TIMING) {
+           io_fds[idx].v = open_io_fd(path, plen, io_fnames[idx]);
+           if (io_fds[idx].v == NULL)
+               error(1, "unable to open %s", path);
+       }
+    }
 
-    /* timing file */
-#ifdef HAVE_ZLIB
-    tfile = gzopen(path, "r");
-#else
-    tfile = fopen(path, "r");
-#endif
-    if (tfile == NULL)
-       error(1, "unable to open %s", path);
-
-    /* script file */
-    memcpy(&path[plen - 3], "scr", 3);
-#ifdef HAVE_ZLIB
-    sfile = gzopen(path, "r");
-#else
-    sfile = fopen(path, "r");
-#endif
-    if (sfile == NULL)
-       error(1, "unable to open %s", path);
-
-    /* log file */
-    path[plen - 4] = '\0';
+    /* Read log file. */
+    path[plen] = '\0';
+    strlcat(path, "/log", sizeof(path));
     lfile = fopen(path, "r");
     if (lfile == NULL)
        error(1, "unable to open %s", path);
-
     cp = NULL;
     getline(&cp, &len, lfile); /* log */
     getline(&cp, &len, lfile); /* cwd */
@@ -295,32 +325,40 @@ main(argc, argv)
     (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 */
     /* Set stdin to raw mode if it is a tty */
     interactive = isatty(STDIN_FILENO);
     if (interactive) {
        ch = fcntl(STDIN_FILENO, F_GETFL, 0);
        if (ch != -1)
            (void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK);
-       if (!term_raw(STDIN_FILENO, 0, 1))
+       if (!term_raw(STDIN_FILENO, 1))
            error(1, "cannot set tty to raw mode");
     }
-    fdsr = (fd_set *)emalloc2(howmany(STDOUT_FILENO + 1, NFDBITS),
+    fdsw = (fd_set *)emalloc2(howmany(STDOUT_FILENO + 1, NFDBITS),
        sizeof(fd_mask));
 
     /*
      * Timing file consists of line of the format: "%f %d\n"
      */
 #ifdef HAVE_ZLIB
-    while (gzgets(tfile, buf, sizeof(buf)) != NULL) {
+    while (gzgets(io_fds[IOFD_TIMING].g, buf, sizeof(buf)) != NULL) {
 #else
-    while (fgets(buf, sizeof(buf), tfile) != NULL) {
+    while (fgets(buf, sizeof(buf), io_fds[IOFD_TIMING].f) != NULL) {
 #endif
+       idx = strtoul(buf, &ep, 10);
+       if (idx > IOFD_MAX)
+           error(1, "invalid timing file index: %s", cp);
+       for (cp = ep + 1; isspace((unsigned char) *cp); cp++)
+           continue;
+
        errno = 0;
-       seconds = strtod(buf, &ep);
+       seconds = strtod(cp, &ep);
        if (errno != 0 || !isspace((unsigned char) *ep))
            error(1, "invalid timing file line: %s", buf);
        for (cp = ep + 1; isspace((unsigned char) *cp); cp++)
            continue;
+
        errno = 0;
        nbytes = strtoul(cp, &ep, 10);
        if (errno == ERANGE && nbytes == ULONG_MAX)
@@ -335,38 +373,44 @@ main(argc, argv)
            to_wait = max_wait;
        delay(to_wait);
 
+       /* Even if we are not relaying, we still have to delay. */
+       if (io_fds[idx].v == NULL)
+           continue;
+
+       /* All output is sent to stdout. */
        while (nbytes != 0) {
            if (nbytes > sizeof(buf))
                len = sizeof(buf);
            else
                len = nbytes;
 #ifdef HAVE_ZLIB
-           nread = gzread(sfile, buf, len);
+           nread = gzread(io_fds[idx].g, buf, len);
 #else
-           nread = fread(buf, 1, len, sfile);
+           nread = fread(buf, 1, len, io_fds[idx].f);
 #endif
            nbytes -= nread;
+           off = 0;
            do {
                /* no stdio, must be unbuffered */
-               nwritten = write(STDOUT_FILENO, buf, nread);
+               nwritten = write(STDOUT_FILENO, buf + off, nread - off);
                if (nwritten == -1) {
                    if (errno == EINTR)
                        continue;
                    if (errno == EAGAIN) {
-                       FD_SET(STDOUT_FILENO, fdsr);
+                       FD_SET(STDOUT_FILENO, fdsw);
                        do {
-                           nready = select(STDOUT_FILENO + 1, fdsr, NULL, NULL, NULL);
+                           nready = select(STDOUT_FILENO + 1, NULL, fdsw, NULL, NULL);
                        } while (nready == -1 && errno == EINTR);
                        if (nready == 1)
                            continue;
                    }
                    error(1, "writing to standard output");
                }
-               nread -= nwritten;
-           } while (nread);
+               off += nwritten;
+           } while (nread > off);
        }
     }
-    term_restore(STDOUT_FILENO, 0);
+    term_restore(STDIN_FILENO, 1);
     exit(0);
 }
 
@@ -394,13 +438,26 @@ delay(secs)
        error(1, "nanosleep: tv_sec %ld, tv_nsec %ld", ts.tv_sec, ts.tv_nsec);
 }
 
+static void *
+open_io_fd(char *path, int len, const char *suffix)
+{
+    path[len] = '\0';
+    strlcat(path, suffix, PATH_MAX);
+
+#ifdef HAVE_ZLIB
+    return gzopen(path, "r");
+#else
+    return fopen(path, "r");
+#endif
+}
+
 /*
  * Build expression list from search args
  */
 static int
 parse_expr(headp, argv)
     struct search_node **headp;
-    char **argv;
+    char *argv[];
 {
     struct search_node *sn, *newsn;
     char or = 0, not = 0, type, **av;
@@ -831,7 +888,7 @@ void
 cleanup(signo)
     int signo;
 {
-    term_restore(STDOUT_FILENO, 0);
+    term_restore(STDIN_FILENO, 0);
     if (signo)
        kill(getpid(), signo);
 }
index 26f906af6437b14935bcc9f73bde844f3aebc172..3329634891dd6a430271425263d7f86a246ed5c9 100644 (file)
@@ -8,7 +8,8 @@ N\bNA\bAM\bME\bE
        sudoreplay - replay sudo session logs
 
 S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS
-       s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by [-\b-d\bd _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by] [-\b-m\bm _\bm_\ba_\bx_\b__\bw_\ba_\bi_\bt] [-\b-s\bs _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br] ID
+       s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by [-\b-d\bd _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by] [-\b-f\bf _\bf_\bi_\bl_\bt_\be_\br] [-\b-m\bm _\bm_\ba_\bx_\b__\bw_\ba_\bi_\bt] [-\b-s\bs _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br]
+       ID
 
        s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by [-\b-d\bd _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by] -l [search expression]
 
@@ -40,6 +41,12 @@ O\bOP\bPT\bTI\bIO\bON\bNS\bS
                    Use _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by to for the session logs instead of the
                    default, _\b/_\bv_\ba_\br_\b/_\bl_\bo_\bg_\b/_\bs_\bu_\bd_\bo_\b-_\bi_\bo.
 
+       -f _\bf_\bi_\bl_\bt_\be_\br   By default, s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by will play back the command's
+                   standard output, standard error and tty output.  The _\b-_\bf
+                   option can be used to select which of these to output.  The
+                   _\bf_\bi_\bl_\bt_\be_\br argument is a comma-separated list, consisting of
+                   one or more of following: _\bs_\bt_\bd_\bo_\bu_\bt, _\bs_\bt_\bd_\be_\br_\br, and _\bt_\bt_\by_\bo_\bu_\bt.
+
        -l          Enable "list mode".  In this mode, s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by will list
                    available session IDs.  If a _\bs_\be_\ba_\br_\bc_\bh _\be_\bx_\bp_\br_\be_\bs_\bs_\bi_\bo_\bn is
                    specified, it will be used to restrict the IDs that are
@@ -51,24 +58,24 @@ O\bOP\bPT\bTI\bIO\bON\bNS\bS
                            _\bc_\bo_\bm_\bm_\ba_\bn_\bd _\bp_\ba_\bt_\bt_\be_\br_\bn.  On systems with POSIX regular
                            expression support, the pattern may be an extended
                            regular expression.  On systems without POSIX
-                           regular expression support, a simple substring
-                           match is performed instead.
 
-                   cwd _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by
-                           Evaluates to true if the command was run with the
-                           specified current working directory.
 
 
+1.7.3b3                   June 15, 2010                         1
 
 
-1.7.3b3                   June  3, 2010                         1
 
 
 
+SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 
 
-SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
+                           regular expression support, a simple substring
+                           match is performed instead.
 
+                   cwd _\bd_\bi_\br_\be_\bc_\bt_\bo_\br_\by
+                           Evaluates to true if the command was run with the
+                           specified current working directory.
 
                    fromdate _\bd_\ba_\bt_\be
                            Evaluates to true if the command was run on or
@@ -117,17 +124,10 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
                    session includes long pauses.  When the _\b-_\bm option is
                    specified, s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by will limit these pauses to at most
                    _\bm_\ba_\bx_\b__\bw_\ba_\bi_\bt seconds.  The value may be specified as a floating
-                   point number, .e.g. _\b2_\b._\b5.
 
-       -s _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br
-                   This option causes s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by to adjust the number of
-                   seconds it will wait between key presses or program output.
-                   This can be used to slow down or speed up the display.  For
-                   example, a _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br of _\b2 would make the output twice as
 
 
-
-1.7.3b3                   June  3, 2010                         2
+1.7.3b3                   June 15, 2010                         2
 
 
 
@@ -136,6 +136,13 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 
 
+                   point number, .e.g. _\b2_\b._\b5.
+
+       -s _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br
+                   This option causes s\bsu\bud\bdo\bor\bre\bep\bpl\bla\bay\by to adjust the number of
+                   seconds it will wait between key presses or program output.
+                   This can be used to slow down or speed up the display.  For
+                   example, a _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br of _\b2 would make the output twice as
                    fast whereas a _\bs_\bp_\be_\be_\bd_\b__\bf_\ba_\bc_\bt_\bo_\br of <.5> would make the output
                    twice as slow.
 
@@ -182,18 +189,11 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
        next Friday
                The first second of the next Friday.
 
-       this week
-               The current time but the first day of the coming week.
-
-       a fortnight ago
-               The current time but 14 days ago.
 
-       10:01 am 9/17/2009
-               10:01 am, September 17, 2009.
 
 
 
-1.7.3b3                   June  3, 2010                         3
+1.7.3b3                   June 15, 2010                         3
 
 
 
@@ -202,6 +202,15 @@ SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 
 
+       this week
+               The current time but the first day of the coming week.
+
+       a fortnight ago
+               The current time but 14 days ago.
+
+       10:01 am 9/17/2009
+               10:01 am, September 17, 2009.
+
        10:01 am
                10:01 am on the current day.
 
@@ -247,26 +256,27 @@ E\bEX\bXA\bAM\bMP\bPL\bLE\bES\bS
 
        List sessions run by user _\bb_\bo_\bb with a command containing the string vi:
 
-        sudoreplay -l user bob command vi
 
-       List sessions run by user _\bj_\be_\bf_\bf that match a regular expression:
 
-        sudoreplay -l user jeff command '/bin/[a-z]*sh'
 
-       List sessions run by jeff or bob on the console:
+1.7.3b3                   June 15, 2010                         4
 
-        sudoreplay -l ( user jeff or user bob ) tty console
 
 
 
-1.7.3b3                   June  3, 2010                         4
 
+SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
 
 
+        sudoreplay -l user bob command vi
 
+       List sessions run by user _\bj_\be_\bf_\bf that match a regular expression:
 
-SUDOREPLAY(1m)         MAINTENANCE COMMANDS        SUDOREPLAY(1m)
+        sudoreplay -l user jeff command '/bin/[a-z]*sh'
+
+       List sessions run by jeff or bob on the console:
 
+        sudoreplay -l ( user jeff or user bob ) tty console
 
 S\bSE\bEE\bE A\bAL\bLS\bSO\bO
        _\bs_\bu_\bd_\bo(1m), _\bs_\bc_\br_\bi_\bp_\bt(1)
@@ -315,16 +325,6 @@ D\bDI\bIS\bSC\bCL\bLA\bAI\bIM\bME\bER\bR
 
 
 
-
-
-
-
-
-
-
-
-
-
-1.7.3b3                   June  3, 2010                         5
+1.7.3b3                   June 15, 2010                         5
 
 
index 11d17bcd539cba572b9d58da1d6f927f8cce4703..d1f40de5f63a63f3600bdfdda5a0cb275d73dd1b 100644 (file)
 .\" ========================================================================
 .\"
 .IX Title "SUDOREPLAY @mansectsu@"
-.TH SUDOREPLAY @mansectsu@ "June  3, 2010" "1.7.3b3" "MAINTENANCE COMMANDS"
+.TH SUDOREPLAY @mansectsu@ "June 15, 2010" "1.7.3b3" "MAINTENANCE COMMANDS"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
 sudoreplay \- replay sudo session logs
 .SH "SYNOPSIS"
 .IX Header "SYNOPSIS"
-\&\fBsudoreplay\fR [\fB\-d\fR \fIdirectory\fR] [\fB\-m\fR \fImax_wait\fR] [\fB\-s\fR \fIspeed_factor\fR] \s-1ID\s0
+\&\fBsudoreplay\fR [\fB\-d\fR \fIdirectory\fR] [\fB\-f\fR \fIfilter\fR] [\fB\-m\fR \fImax_wait\fR] [\fB\-s\fR \fIspeed_factor\fR] \s-1ID\s0
 .PP
 \&\fBsudoreplay\fR [\fB\-d\fR \fIdirectory\fR] \-l [search expression]
 .SH "DESCRIPTION"
@@ -180,6 +180,13 @@ Double the playback speed.
 .IX Item "-d directory"
 Use \fIdirectory\fR to for the session logs instead of the default,
 \&\fI/var/log/sudo\-io\fR.
+.IP "\-f \fIfilter\fR" 12
+.IX Item "-f filter"
+By default, \fBsudoreplay\fR will play back the command's standard
+output, standard error and tty output.  The \fI\-f\fR option can be
+used to select which of these to output.  The \fIfilter\fR argument
+is a comma-separated list, consisting of one or more of following:
+\&\fIstdout\fR, \fIstderr\fR, and \fIttyout\fR.
 .IP "\-l" 12
 .IX Item "-l"
 Enable \*(L"list mode\*(R".  In this mode, \fBsudoreplay\fR will list available
index 568de5ce133cd8ad7bcdbc8e493eeb8090529506..c36f9133e49c56e394c1a0122ec5cbf8acbadd30 100644 (file)
@@ -21,7 +21,7 @@ sudoreplay - replay sudo session logs
 
 =head1 SYNOPSIS
 
-B<sudoreplay> [B<-d> I<directory>] [B<-m> I<max_wait>] [B<-s> I<speed_factor>] ID
+B<sudoreplay> [B<-d> I<directory>] [B<-f> I<filter>] [B<-m> I<max_wait>] [B<-s> I<speed_factor>] ID
 
 B<sudoreplay> [B<-d> I<directory>] -l [search expression]
 
@@ -68,6 +68,14 @@ B<sudoreplay> accepts the following command line options:
 Use I<directory> to for the session logs instead of the default,
 F</var/log/sudo-io>.
 
+=item -f I<filter>
+
+By default, B<sudoreplay> will play back the command's standard
+output, standard error and tty output.  The I<-f> option can be
+used to select which of these to output.  The I<filter> argument
+is a comma-separated list, consisting of one or more of following:
+I<stdout>, I<stderr>, and I<ttyout>.
+
 =item -l
 
 Enable "list mode".  In this mode, B<sudoreplay> will list available