#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
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));
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
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;
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 */
(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)
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);
}
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;
cleanup(signo)
int signo;
{
- term_restore(STDOUT_FILENO, 0);
+ term_restore(STDIN_FILENO, 0);
if (signo)
kill(getpid(), signo);
}
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]
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
_\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
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
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.
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
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.
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)
-
-
-
-
-
-
-
-
-
-
-1.7.3b3 June 3, 2010 5
+1.7.3b3 June 15, 2010 5