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:
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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++;
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user