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
.\"
.\" 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
.\" 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
.\" 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
.if n .ad l
.SH "NAME"
@@ -30,7 +30,7 @@
[\fB\-f\fR\ \fIfilter\fR]
[\fB\-m\fR\ \fInum\fR]
[\fB\-s\fR\ \fInum\fR]
ID
ID[\fI@offset\fR]
.HP 11n
\fBsudoreplay\fR
[\fB\-h\fR]
@@ -52,6 +52,16 @@ should either be a six character sequence of digits and
upper case letters, e.g.,
\fR0100A5\fR
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
\fI@iolog_dir@\fR
(unless overridden by the

View File

@@ -1,7 +1,7 @@
.\"
.\" 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
.\" 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
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.Dd May 26, 2021
.Dd August 13, 2021
.Dt SUDOREPLAY @mansectsu@
.Os Sudo @PACKAGE_VERSION@
.Sh NAME
@@ -28,7 +28,7 @@
.Op Fl f Ar filter
.Op Fl m Ar num
.Op Fl s Ar num
ID
.No ID Ns Op Ar @offset
.Pp
.Nm
.Op Fl h
@@ -50,6 +50,16 @@ should either be a six character sequence of digits and
upper case letters, e.g.,
.Li 0100A5
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
.Pa @iolog_dir@
(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. */
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,
"invalid characters after seconds: %s", ep);
debug_return_ptr(NULL);
@@ -152,6 +157,7 @@ iolog_parse_delay(const char *cp, struct timespec *delay,
}
delay->tv_nsec = (long)llval;
done:
/* Advance to the next field. */
while (isspace((unsigned char)*ep))
ep++;

View File

@@ -1,7 +1,7 @@
/*
* 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
* 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 *sigterm_ev;
struct sudo_event *sigtstp_ev;
struct timespec *offset;
struct timespec *max_delay;
struct timing_closure timing;
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 help(void) __attribute__((__noreturn__));
static int replay_session(int iolog_dir_fd, const char *iolog_dir,
struct timespec *max_wait, const char *decimal, bool interactive,
bool suspend_wait);
struct timespec *offset, struct timespec *max_wait, const char *decimal,
bool interactive, bool suspend_wait);
static void sudoreplay_cleanup(void);
static void usage(int);
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]) && \
(s)[5] == '/' && \
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[]);
@@ -207,6 +208,7 @@ main(int argc, char *argv[])
bool interactive = true, suspend_wait = false, resize = true;
const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL;
char *cp, *ep, iolog_dir[PATH_MAX];
struct timespec offset = { 0, 0};
struct eventlog *evlog;
struct timespec max_delay_storage, *max_delay = NULL;
double dval;
@@ -323,8 +325,16 @@ main(int argc, char *argv[])
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];
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)) {
len = snprintf(iolog_dir, sizeof(iolog_dir), "%s/%.2s/%.2s/%.2s",
session_dir, id, &id[2], &id[4]);
@@ -376,8 +386,8 @@ main(int argc, char *argv[])
evlog = NULL;
/* Replay session corresponding to iolog_files[]. */
exitcode = replay_session(iolog_dir_fd, iolog_dir, max_delay, decimal,
interactive, suspend_wait);
exitcode = replay_session(iolog_dir_fd, iolog_dir, &offset, max_delay,
decimal, interactive, suspend_wait);
restore_terminal_size();
sudo_term_restore(ttyfd, true);
@@ -774,6 +784,16 @@ get_timing_record(struct replay_closure *closure)
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) {
/* Already waited, fire immediately. */
timing->delay.tv_sec = 0;
@@ -957,8 +977,8 @@ signal_cb(int signo, int what, void *v)
static struct replay_closure *
replay_closure_alloc(int iolog_dir_fd, const char *iolog_dir,
struct timespec *max_delay, const char *decimal, bool interactive,
bool suspend_wait)
struct timespec *offset, struct timespec *max_delay, const char *decimal,
bool interactive, bool suspend_wait)
{
struct replay_closure *closure;
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 = iolog_dir;
closure->interactive = interactive;
closure->offset = offset;
closure->suspend_wait = suspend_wait;
closure->max_delay = max_delay;
closure->timing.decimal = decimal;
@@ -1042,7 +1063,7 @@ bad:
}
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,
bool suspend_wait)
{
@@ -1051,8 +1072,8 @@ replay_session(int iolog_dir_fd, const char *iolog_dir,
debug_decl(replay_session, SUDO_DEBUG_UTIL);
/* Allocate the delay closure and read the first timing record. */
closure = replay_closure_alloc(iolog_dir_fd, iolog_dir, max_delay, decimal,
interactive, suspend_wait);
closure = replay_closure_alloc(iolog_dir_fd, iolog_dir, offset, max_delay,
decimal, interactive, suspend_wait);
if (get_timing_record(closure) != 0) {
ret = 1;
goto done;