Add support for an optional offset when parsing the ID to replay.

The offset is a suffix in the form of @sec[.nanosec]
This commit is contained in:
Todd C. Miller
2021-08-13 16:00:00 -06:00
parent 064981fb14
commit 695f4bea05
4 changed files with 65 additions and 18 deletions

View File

@@ -2,7 +2,7 @@
.\" .\"
.\" SPDX-License-Identifier: ISC .\" SPDX-License-Identifier: ISC
.\" .\"
.\" Copyright (c) 2009-2020 Todd C. Miller <Todd.Miller@sudo.ws> .\" Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws>
.\" .\"
.\" Permission to use, copy, modify, and distribute this software for any .\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above .\" purpose with or without fee is hereby granted, provided that the above
@@ -16,7 +16,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.TH "SUDOREPLAY" "@mansectsu@" "May 26, 2021" "Sudo @PACKAGE_VERSION@" "System Manager's Manual" .TH "SUDOREPLAY" "@mansectsu@" "August 13, 2021" "Sudo @PACKAGE_VERSION@" "System Manager's Manual"
.nh .nh
.if n .ad l .if n .ad l
.SH "NAME" .SH "NAME"
@@ -30,7 +30,7 @@
[\fB\-f\fR\ \fIfilter\fR] [\fB\-f\fR\ \fIfilter\fR]
[\fB\-m\fR\ \fInum\fR] [\fB\-m\fR\ \fInum\fR]
[\fB\-s\fR\ \fInum\fR] [\fB\-s\fR\ \fInum\fR]
ID ID[\fI@offset\fR]
.HP 11n .HP 11n
\fBsudoreplay\fR \fBsudoreplay\fR
[\fB\-h\fR] [\fB\-h\fR]
@@ -52,6 +52,16 @@ should either be a six character sequence of digits and
upper case letters, e.g., upper case letters, e.g.,
\fR0100A5\fR \fR0100A5\fR
or a path name. or a path name.
The
\fIID\fR
may include an optional
\fI@offset\fR
suffix which may be used to start replaying at a specific time offset.
The
\fI@offset\fR
is specified as a number in seconds since the start of the session
with an optional decimal fraction.
.PP
Path names may be relative to the I/O log directory Path names may be relative to the I/O log directory
\fI@iolog_dir@\fR \fI@iolog_dir@\fR
(unless overridden by the (unless overridden by the

View File

@@ -1,7 +1,7 @@
.\" .\"
.\" SPDX-License-Identifier: ISC .\" SPDX-License-Identifier: ISC
.\" .\"
.\" Copyright (c) 2009-2020 Todd C. Miller <Todd.Miller@sudo.ws> .\" Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws>
.\" .\"
.\" Permission to use, copy, modify, and distribute this software for any .\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above .\" purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd May 26, 2021 .Dd August 13, 2021
.Dt SUDOREPLAY @mansectsu@ .Dt SUDOREPLAY @mansectsu@
.Os Sudo @PACKAGE_VERSION@ .Os Sudo @PACKAGE_VERSION@
.Sh NAME .Sh NAME
@@ -28,7 +28,7 @@
.Op Fl f Ar filter .Op Fl f Ar filter
.Op Fl m Ar num .Op Fl m Ar num
.Op Fl s Ar num .Op Fl s Ar num
ID .No ID Ns Op Ar @offset
.Pp .Pp
.Nm .Nm
.Op Fl h .Op Fl h
@@ -50,6 +50,16 @@ should either be a six character sequence of digits and
upper case letters, e.g., upper case letters, e.g.,
.Li 0100A5 .Li 0100A5
or a path name. or a path name.
The
.Em ID
may include an optional
.Ar @offset
suffix which may be used to start replaying at a specific time offset.
The
.Ar @offset
is specified as a number in seconds since the start of the session
with an optional decimal fraction.
.Pp
Path names may be relative to the I/O log directory Path names may be relative to the I/O log directory
.Pa @iolog_dir@ .Pa @iolog_dir@
(unless overridden by the (unless overridden by the

View File

@@ -114,6 +114,11 @@ iolog_parse_delay(const char *cp, struct timespec *delay,
/* Radix may be in user's locale for sudo < 1.7.4 so accept that too. */ /* Radix may be in user's locale for sudo < 1.7.4 so accept that too. */
if (*ep != '.' && *ep != *decimal_point) { if (*ep != '.' && *ep != *decimal_point) {
if (*ep == '\0' || isspace((unsigned char)*ep)) {
/* No fractional part. */
delay->tv_nsec = 0;
goto done;
}
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"invalid characters after seconds: %s", ep); "invalid characters after seconds: %s", ep);
debug_return_ptr(NULL); debug_return_ptr(NULL);
@@ -152,6 +157,7 @@ iolog_parse_delay(const char *cp, struct timespec *delay,
} }
delay->tv_nsec = (long)llval; delay->tv_nsec = (long)llval;
done:
/* Advance to the next field. */ /* Advance to the next field. */
while (isspace((unsigned char)*ep)) while (isspace((unsigned char)*ep))
ep++; ep++;

View File

@@ -1,7 +1,7 @@
/* /*
* SPDX-License-Identifier: ISC * SPDX-License-Identifier: ISC
* *
* Copyright (c) 2009-2020 Todd C. Miller <Todd.Miller@sudo.ws> * Copyright (c) 2009-2021 Todd C. Miller <Todd.Miller@sudo.ws>
* *
* Permission to use, copy, modify, and distribute this software for any * Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
@@ -82,6 +82,7 @@ struct replay_closure {
struct sudo_event *sigquit_ev; struct sudo_event *sigquit_ev;
struct sudo_event *sigterm_ev; struct sudo_event *sigterm_ev;
struct sudo_event *sigtstp_ev; struct sudo_event *sigtstp_ev;
struct timespec *offset;
struct timespec *max_delay; struct timespec *max_delay;
struct timing_closure timing; struct timing_closure timing;
int iolog_dir_fd; int iolog_dir_fd;
@@ -176,8 +177,8 @@ static int parse_expr(struct search_node_list *, char **, bool);
static void read_keyboard(int fd, int what, void *v); static void read_keyboard(int fd, int what, void *v);
static void help(void) __attribute__((__noreturn__)); static void help(void) __attribute__((__noreturn__));
static int replay_session(int iolog_dir_fd, const char *iolog_dir, static int replay_session(int iolog_dir_fd, const char *iolog_dir,
struct timespec *max_wait, const char *decimal, bool interactive, struct timespec *offset, struct timespec *max_wait, const char *decimal,
bool suspend_wait); bool interactive, bool suspend_wait);
static void sudoreplay_cleanup(void); static void sudoreplay_cleanup(void);
static void usage(int); static void usage(int);
static void write_output(int fd, int what, void *v); static void write_output(int fd, int what, void *v);
@@ -195,7 +196,7 @@ static void setup_terminal(struct eventlog *evlog, bool interactive, bool resize
isalnum((unsigned char)(s)[3]) && isalnum((unsigned char)(s)[4]) && \ isalnum((unsigned char)(s)[3]) && isalnum((unsigned char)(s)[4]) && \
(s)[5] == '/' && \ (s)[5] == '/' && \
isalnum((unsigned char)(s)[6]) && isalnum((unsigned char)(s)[7]) && \ isalnum((unsigned char)(s)[6]) && isalnum((unsigned char)(s)[7]) && \
(s)[8] == '\0') (s)[6] == '\0')
sudo_dso_public int main(int argc, char *argv[]); sudo_dso_public int main(int argc, char *argv[]);
@@ -207,6 +208,7 @@ main(int argc, char *argv[])
bool interactive = true, suspend_wait = false, resize = true; bool interactive = true, suspend_wait = false, resize = true;
const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL; const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL;
char *cp, *ep, iolog_dir[PATH_MAX]; char *cp, *ep, iolog_dir[PATH_MAX];
struct timespec offset = { 0, 0};
struct eventlog *evlog; struct eventlog *evlog;
struct timespec max_delay_storage, *max_delay = NULL; struct timespec max_delay_storage, *max_delay = NULL;
double dval; double dval;
@@ -323,8 +325,16 @@ main(int argc, char *argv[])
iolog_files[IOFD_TTYOUT].enabled = true; iolog_files[IOFD_TTYOUT].enabled = true;
} }
/* 6 digit ID in base 36, e.g. 01G712AB or free-form name */ /* Check for offset in @sec.nsec form at the end of the id. */
id = argv[0]; id = argv[0];
if ((cp = strchr(id, '@')) != NULL) {
ep = iolog_parse_delay(cp + 1, &offset, decimal);
if (ep == NULL || *ep != '\0')
sudo_fatalx(U_("invalid time offset %s"), cp + 1);
*cp = '\0';
}
/* 6 digit ID in base 36, e.g. 01G712AB or free-form name */
if (VALID_ID(id)) { if (VALID_ID(id)) {
len = snprintf(iolog_dir, sizeof(iolog_dir), "%s/%.2s/%.2s/%.2s", len = snprintf(iolog_dir, sizeof(iolog_dir), "%s/%.2s/%.2s/%.2s",
session_dir, id, &id[2], &id[4]); session_dir, id, &id[2], &id[4]);
@@ -376,8 +386,8 @@ main(int argc, char *argv[])
evlog = NULL; evlog = NULL;
/* Replay session corresponding to iolog_files[]. */ /* Replay session corresponding to iolog_files[]. */
exitcode = replay_session(iolog_dir_fd, iolog_dir, max_delay, decimal, exitcode = replay_session(iolog_dir_fd, iolog_dir, &offset, max_delay,
interactive, suspend_wait); decimal, interactive, suspend_wait);
restore_terminal_size(); restore_terminal_size();
sudo_term_restore(ttyfd, true); sudo_term_restore(ttyfd, true);
@@ -774,6 +784,16 @@ get_timing_record(struct replay_closure *closure)
closure->iobuf.toread = timing->u.nbytes; closure->iobuf.toread = timing->u.nbytes;
} }
if (sudo_timespecisset(closure->offset)) {
if (sudo_timespeccmp(&timing->delay, closure->offset, >)) {
sudo_timespecsub(&timing->delay, closure->offset, &timing->delay);
sudo_timespecclear(closure->offset);
} else {
sudo_timespecsub(closure->offset, &timing->delay, closure->offset);
sudo_timespecclear(&timing->delay);
}
}
if (nodelay) { if (nodelay) {
/* Already waited, fire immediately. */ /* Already waited, fire immediately. */
timing->delay.tv_sec = 0; timing->delay.tv_sec = 0;
@@ -957,8 +977,8 @@ signal_cb(int signo, int what, void *v)
static struct replay_closure * static struct replay_closure *
replay_closure_alloc(int iolog_dir_fd, const char *iolog_dir, replay_closure_alloc(int iolog_dir_fd, const char *iolog_dir,
struct timespec *max_delay, const char *decimal, bool interactive, struct timespec *offset, struct timespec *max_delay, const char *decimal,
bool suspend_wait) bool interactive, bool suspend_wait)
{ {
struct replay_closure *closure; struct replay_closure *closure;
debug_decl(replay_closure_alloc, SUDO_DEBUG_UTIL); debug_decl(replay_closure_alloc, SUDO_DEBUG_UTIL);
@@ -969,6 +989,7 @@ replay_closure_alloc(int iolog_dir_fd, const char *iolog_dir,
closure->iolog_dir_fd = iolog_dir_fd; closure->iolog_dir_fd = iolog_dir_fd;
closure->iolog_dir = iolog_dir; closure->iolog_dir = iolog_dir;
closure->interactive = interactive; closure->interactive = interactive;
closure->offset = offset;
closure->suspend_wait = suspend_wait; closure->suspend_wait = suspend_wait;
closure->max_delay = max_delay; closure->max_delay = max_delay;
closure->timing.decimal = decimal; closure->timing.decimal = decimal;
@@ -1042,7 +1063,7 @@ bad:
} }
static int static int
replay_session(int iolog_dir_fd, const char *iolog_dir, replay_session(int iolog_dir_fd, const char *iolog_dir, struct timespec *offset,
struct timespec *max_delay, const char *decimal, bool interactive, struct timespec *max_delay, const char *decimal, bool interactive,
bool suspend_wait) bool suspend_wait)
{ {
@@ -1051,8 +1072,8 @@ replay_session(int iolog_dir_fd, const char *iolog_dir,
debug_decl(replay_session, SUDO_DEBUG_UTIL); debug_decl(replay_session, SUDO_DEBUG_UTIL);
/* Allocate the delay closure and read the first timing record. */ /* Allocate the delay closure and read the first timing record. */
closure = replay_closure_alloc(iolog_dir_fd, iolog_dir, max_delay, decimal, closure = replay_closure_alloc(iolog_dir_fd, iolog_dir, offset, max_delay,
interactive, suspend_wait); decimal, interactive, suspend_wait);
if (get_timing_record(closure) != 0) { if (get_timing_record(closure) != 0) {
ret = 1; ret = 1;
goto done; goto done;