version 1.1.1.5, 2013/10/14 07:56:35
|
version 1.1.1.6, 2014/06/15 16:12:54
|
Line 18
|
Line 18
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
#include <sys/uio.h> |
#include <sys/uio.h> |
#ifdef HAVE_SYS_SYSMACROS_H |
|
# include <sys/sysmacros.h> |
|
#endif |
|
#include <sys/stat.h> |
#include <sys/stat.h> |
#include <sys/time.h> |
#include <sys/time.h> |
#include <sys/wait.h> |
#include <sys/wait.h> |
#include <sys/ioctl.h> |
#include <sys/ioctl.h> |
#ifdef HAVE_SYS_SELECT_H |
|
#include <sys/select.h> |
|
#endif /* HAVE_SYS_SELECT_H */ |
|
#include <stdio.h> |
#include <stdio.h> |
#ifdef STDC_HEADERS |
#ifdef STDC_HEADERS |
# include <stdlib.h> |
# include <stdlib.h> |
Line 49
|
Line 43
|
#ifdef HAVE_UNISTD_H |
#ifdef HAVE_UNISTD_H |
# include <unistd.h> |
# include <unistd.h> |
#endif /* HAVE_UNISTD_H */ |
#endif /* HAVE_UNISTD_H */ |
#if TIME_WITH_SYS_TIME | #ifdef TIME_WITH_SYS_TIME |
# include <time.h> |
# include <time.h> |
#endif |
#endif |
#ifndef HAVE_STRUCT_TIMESPEC |
#ifndef HAVE_STRUCT_TIMESPEC |
Line 95
|
Line 89
|
|
|
#include <pathnames.h> |
#include <pathnames.h> |
|
|
|
#include "gettext.h" /* must be included before missing.h */ |
|
|
#include "missing.h" |
#include "missing.h" |
#include "alloc.h" |
#include "alloc.h" |
#include "fatal.h" |
#include "fatal.h" |
#include "gettext.h" |
|
#include "logging.h" |
#include "logging.h" |
#include "iolog.h" |
#include "iolog.h" |
|
#include "queue.h" |
#include "sudo_plugin.h" |
#include "sudo_plugin.h" |
#include "sudo_conf.h" |
#include "sudo_conf.h" |
#include "sudo_debug.h" |
#include "sudo_debug.h" |
|
#include "sudo_event.h" |
|
#include "sudo_util.h" |
|
|
#ifndef LINE_MAX |
#ifndef LINE_MAX |
# define LINE_MAX 2048 |
# define LINE_MAX 2048 |
Line 124 struct log_info {
|
Line 122 struct log_info {
|
int cols; |
int cols; |
}; |
}; |
|
|
|
/* Closure for write_output */ |
|
struct write_closure { |
|
struct sudo_event *wevent; |
|
struct iovec *iov; |
|
unsigned int iovcnt; |
|
size_t nbytes; |
|
}; |
|
|
/* |
/* |
* Handle expressions like: |
* Handle expressions like: |
* ( user millert or user root ) and tty console and command /bin/sh |
* ( user millert or user root ) and tty console and command /bin/sh |
*/ |
*/ |
static struct search_node { | STAILQ_HEAD(search_node_list, search_node); |
struct search_node *next; | struct search_node { |
| STAILQ_ENTRY(search_node) entries; |
#define ST_EXPR 1 |
#define ST_EXPR 1 |
#define ST_TTY 2 |
#define ST_TTY 2 |
#define ST_USER 3 |
#define ST_USER 3 |
Line 140 static struct search_node {
|
Line 147 static struct search_node {
|
#define ST_TODATE 8 |
#define ST_TODATE 8 |
#define ST_CWD 9 |
#define ST_CWD 9 |
char type; |
char type; |
char negated; | bool negated; |
char or; | bool or; |
char pad; | |
union { |
union { |
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
regex_t cmdre; |
regex_t cmdre; |
|
#else |
|
char *pattern; |
#endif |
#endif |
time_t tstamp; |
time_t tstamp; |
char *cwd; |
char *cwd; |
char *tty; |
char *tty; |
char *user; |
char *user; |
char *pattern; |
|
char *runas_group; |
char *runas_group; |
char *runas_user; |
char *runas_user; |
struct search_node *expr; | struct search_node_list expr; |
void *ptr; |
void *ptr; |
} u; |
} u; |
} *search_expr; | }; |
|
|
#define STACK_NODE_SIZE 32 | static struct search_node_list search_expr = STAILQ_HEAD_INITIALIZER(search_expr); |
static struct search_node *node_stack[32]; | |
static int stack_top; | |
|
|
static int timing_idx_adj = 0; | static int timing_idx_adj; |
|
|
|
static double speed_factor = 1.0; |
|
|
static const char *session_dir = _PATH_SUDO_IO_LOGDIR; |
static const char *session_dir = _PATH_SUDO_IO_LOGDIR; |
|
|
static const char short_opts[] = "d:f:hlm:s:V"; |
static const char short_opts[] = "d:f:hlm:s:V"; |
Line 179 static struct option long_opts[] = {
|
Line 186 static struct option long_opts[] = {
|
{ NULL, no_argument, NULL, '\0' }, |
{ NULL, no_argument, NULL, '\0' }, |
}; |
}; |
|
|
extern time_t get_date(char *); | /* XXX move to separate header? */ |
extern char *get_timestr(time_t, int); |
extern char *get_timestr(time_t, int); |
extern int term_raw(int, int); | extern time_t get_date(char *); |
extern int term_restore(int, int); | |
extern void get_ttysize(int *rowp, int *colp); | |
|
|
static int list_sessions(int, char **, const char *, const char *, const char *); |
static int list_sessions(int, char **, const char *, const char *, const char *); |
static int parse_expr(struct search_node **, char **); |
|
static void check_input(int, double *); |
|
static void delay(double); |
|
static void help(void) __attribute__((__noreturn__)); |
|
static void usage(int); |
|
static int open_io_fd(char *path, int len, struct io_log_file *iol); |
static int open_io_fd(char *path, int len, struct io_log_file *iol); |
|
static int parse_expr(struct search_node_list *, char **, bool); |
static int parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes); |
static int parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes); |
static struct log_info *parse_logfile(char *logfile); |
static struct log_info *parse_logfile(char *logfile); |
|
static void check_input(int fd, int what, void *v); |
static void free_log_info(struct log_info *li); |
static void free_log_info(struct log_info *li); |
static size_t atomic_writev(int fd, struct iovec *iov, int iovcnt); | static void help(void) __attribute__((__noreturn__)); |
static void sudoreplay_handler(int); | static void replay_session(const double max_wait, const char *decimal); |
static void sudoreplay_cleanup(void); |
static void sudoreplay_cleanup(void); |
|
static void sudoreplay_handler(int); |
|
static void usage(int); |
|
static void write_output(int fd, int what, void *v); |
|
|
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
# define REGEX_T regex_t |
# define REGEX_T regex_t |
Line 225 int
|
Line 230 int
|
main(int argc, char *argv[]) |
main(int argc, char *argv[]) |
{ |
{ |
int ch, idx, plen, exitcode = 0, rows = 0, cols = 0; |
int ch, idx, plen, exitcode = 0, rows = 0, cols = 0; |
bool interactive = false, listonly = false, need_nlcr = false; | bool def_filter = true, listonly = false; |
bool def_filter = true; | |
const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL; |
const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL; |
char path[PATH_MAX], buf[LINE_MAX], *cp, *ep; | char *cp, *ep, path[PATH_MAX]; |
double seconds, to_wait, speed = 1.0, max_wait = 0; | |
sigaction_t sa; | |
size_t len, nbytes, nread; | |
struct log_info *li; |
struct log_info *li; |
struct iovec *iov = NULL; | double max_wait = 0; |
int iovcnt = 0, iovmax = 0; | |
debug_decl(main, SUDO_DEBUG_MAIN) |
debug_decl(main, SUDO_DEBUG_MAIN) |
|
|
#if defined(SUDO_DEVEL) && defined(__OpenBSD__) |
#if defined(SUDO_DEVEL) && defined(__OpenBSD__) |
Line 244 main(int argc, char *argv[])
|
Line 244 main(int argc, char *argv[])
|
} |
} |
#endif |
#endif |
|
|
#if !defined(HAVE_GETPROGNAME) && !defined(HAVE___PROGNAME) | initprogname(argc > 0 ? argv[0] : "sudoreplay"); |
setprogname(argc > 0 ? argv[0] : "sudoreplay"); | setlocale(LC_ALL, ""); |
#endif | |
| |
sudoers_setlocale(SUDOERS_LOCALE_USER, NULL); | |
decimal = localeconv()->decimal_point; |
decimal = localeconv()->decimal_point; |
bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have sudoreplay domain */ |
bindtextdomain("sudoers", LOCALEDIR); /* XXX - should have sudoreplay domain */ |
textdomain("sudoers"); |
textdomain("sudoers"); |
Line 275 main(int argc, char *argv[])
|
Line 272 main(int argc, char *argv[])
|
else if (strcmp(cp, "ttyout") == 0) |
else if (strcmp(cp, "ttyout") == 0) |
io_log_files[IOFD_TTYOUT].enabled = true; |
io_log_files[IOFD_TTYOUT].enabled = true; |
else |
else |
fatalx(_("invalid filter option: %s"), optarg); | fatalx(U_("invalid filter option: %s"), optarg); |
} |
} |
break; |
break; |
case 'h': |
case 'h': |
Line 288 main(int argc, char *argv[])
|
Line 285 main(int argc, char *argv[])
|
errno = 0; |
errno = 0; |
max_wait = strtod(optarg, &ep); |
max_wait = strtod(optarg, &ep); |
if (*ep != '\0' || errno != 0) |
if (*ep != '\0' || errno != 0) |
fatalx(_("invalid max wait: %s"), optarg); | fatalx(U_("invalid max wait: %s"), optarg); |
break; |
break; |
case 's': |
case 's': |
errno = 0; |
errno = 0; |
speed = strtod(optarg, &ep); | speed_factor = strtod(optarg, &ep); |
if (*ep != '\0' || errno != 0) |
if (*ep != '\0' || errno != 0) |
fatalx(_("invalid speed factor: %s"), optarg); | fatalx(U_("invalid speed factor: %s"), optarg); |
break; |
break; |
case 'V': |
case 'V': |
(void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION); |
(void) printf(_("%s version %s\n"), getprogname(), PACKAGE_VERSION); |
Line 328 main(int argc, char *argv[])
|
Line 325 main(int argc, char *argv[])
|
if (VALID_ID(id)) { |
if (VALID_ID(id)) { |
plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing", |
plen = snprintf(path, sizeof(path), "%s/%.2s/%.2s/%.2s/timing", |
session_dir, id, &id[2], &id[4]); |
session_dir, id, &id[2], &id[4]); |
if (plen <= 0 || plen >= sizeof(path)) | if (plen <= 0 || (size_t)plen >= sizeof(path)) |
fatalx(_("%s/%.2s/%.2s/%.2s/timing: %s"), session_dir, | fatalx(U_("%s/%.2s/%.2s/%.2s/timing: %s"), session_dir, |
id, &id[2], &id[4], strerror(ENAMETOOLONG)); |
id, &id[2], &id[4], strerror(ENAMETOOLONG)); |
} else { |
} else { |
plen = snprintf(path, sizeof(path), "%s/%s/timing", |
plen = snprintf(path, sizeof(path), "%s/%s/timing", |
session_dir, id); |
session_dir, id); |
if (plen <= 0 || plen >= sizeof(path)) | if (plen <= 0 || (size_t)plen >= sizeof(path)) |
fatalx(_("%s/%s/timing: %s"), session_dir, | fatalx(U_("%s/%s/timing: %s"), session_dir, |
id, strerror(ENAMETOOLONG)); |
id, strerror(ENAMETOOLONG)); |
} |
} |
plen -= 7; |
plen -= 7; |
Line 343 main(int argc, char *argv[])
|
Line 340 main(int argc, char *argv[])
|
/* Open files for replay, applying replay filter for the -f flag. */ |
/* Open files for replay, applying replay filter for the -f flag. */ |
for (idx = 0; idx < IOFD_MAX; idx++) { |
for (idx = 0; idx < IOFD_MAX; idx++) { |
if (open_io_fd(path, plen, &io_log_files[idx]) == -1) |
if (open_io_fd(path, plen, &io_log_files[idx]) == -1) |
fatal(_("unable to open %s"), path); | fatal(U_("unable to open %s"), path); |
} |
} |
|
|
/* Parse log file. */ |
/* Parse log file. */ |
Line 366 main(int argc, char *argv[])
|
Line 363 main(int argc, char *argv[])
|
free_log_info(li); |
free_log_info(li); |
li = NULL; |
li = NULL; |
|
|
|
/* Replay session corresponding to io_log_files[]. */ |
|
replay_session(max_wait, decimal); |
|
|
|
term_restore(STDIN_FILENO, 1); |
|
done: |
|
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode); |
|
exit(exitcode); |
|
} |
|
|
|
static void |
|
replay_session(const double max_wait, const char *decimal) |
|
{ |
|
struct sudo_event *input_ev, *output_ev; |
|
unsigned int i, iovcnt = 0, iovmax = 0; |
|
struct sudo_event_base *evbase; |
|
struct iovec iovb, *iov = &iovb; |
|
bool interactive; |
|
struct write_closure wc; |
|
char buf[LINE_MAX]; |
|
sigaction_t sa; |
|
int idx; |
|
debug_decl(replay_session, SUDO_DEBUG_UTIL) |
|
|
|
/* Restore tty settings if interupted. */ |
fflush(stdout); |
fflush(stdout); |
memset(&sa, 0, sizeof(sa)); |
memset(&sa, 0, sizeof(sa)); |
sigemptyset(&sa.sa_mask); |
sigemptyset(&sa.sa_mask); |
sa.sa_flags = SA_RESETHAND; |
sa.sa_flags = SA_RESETHAND; |
sa.sa_handler = sudoreplay_handler; |
sa.sa_handler = sudoreplay_handler; |
(void) sigaction(SIGINT, &sa, NULL); |
(void) sigaction(SIGINT, &sa, NULL); |
(void) sigaction(SIGKILL, &sa, NULL); |
|
(void) sigaction(SIGTERM, &sa, NULL); |
(void) sigaction(SIGTERM, &sa, NULL); |
(void) sigaction(SIGHUP, &sa, NULL); |
(void) sigaction(SIGHUP, &sa, NULL); |
|
(void) sigaction(SIGQUIT, &sa, NULL); |
|
|
|
/* Don't suspend as we cannot restore the screen on resume. */ |
sa.sa_flags = SA_RESTART; |
sa.sa_flags = SA_RESTART; |
sa.sa_handler = SIG_IGN; |
sa.sa_handler = SIG_IGN; |
(void) sigaction(SIGTSTP, &sa, NULL); |
(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 */ |
/* 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 */ |
/* Set stdin to raw mode if it is a tty */ |
interactive = isatty(STDIN_FILENO); |
interactive = isatty(STDIN_FILENO); |
if (interactive) { |
if (interactive) { |
ch = fcntl(STDIN_FILENO, F_GETFL, 0); | idx = fcntl(STDIN_FILENO, F_GETFL, 0); |
if (ch != -1) | if (idx != -1) |
(void) fcntl(STDIN_FILENO, F_SETFL, ch | O_NONBLOCK); | (void) fcntl(STDIN_FILENO, F_SETFL, idx | O_NONBLOCK); |
if (!term_raw(STDIN_FILENO, 1)) |
if (!term_raw(STDIN_FILENO, 1)) |
fatal(_("unable to set tty to raw mode")); | fatal(U_("unable to set tty to raw mode")); |
iovmax = 32; | |
iov = ecalloc(iovmax, sizeof(*iov)); | |
} |
} |
|
|
|
/* Setup event base and input/output events. */ |
|
evbase = sudo_ev_base_alloc(); |
|
if (evbase == NULL) |
|
fatal(NULL); |
|
input_ev = sudo_ev_alloc(STDIN_FILENO, interactive ? SUDO_EV_READ : |
|
SUDO_EV_TIMEOUT, check_input, sudo_ev_self_cbarg()); |
|
if (input_ev == NULL) |
|
fatal(NULL); |
|
output_ev = sudo_ev_alloc(STDIN_FILENO, SUDO_EV_WRITE, write_output, &wc); |
|
if (output_ev == NULL) |
|
fatal(NULL); |
|
|
/* |
/* |
* Timing file consists of line of the format: "%f %d\n" | * Read each line of the timing file, displaying the output streams. |
*/ |
*/ |
#ifdef HAVE_ZLIB_H |
#ifdef HAVE_ZLIB_H |
while (gzgets(io_log_files[IOFD_TIMING].fd.g, buf, sizeof(buf)) != NULL) { |
while (gzgets(io_log_files[IOFD_TIMING].fd.g, buf, sizeof(buf)) != NULL) { |
#else |
#else |
while (fgets(buf, sizeof(buf), io_log_files[IOFD_TIMING].fd.f) != NULL) { |
while (fgets(buf, sizeof(buf), io_log_files[IOFD_TIMING].fd.f) != NULL) { |
#endif |
#endif |
|
size_t len, nbytes, nread; |
|
double seconds, to_wait; |
|
struct timeval timeout; |
|
bool need_nlcr = false; |
char last_char = '\0'; |
char last_char = '\0'; |
|
|
|
buf[strcspn(buf, "\n")] = '\0'; |
if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) |
if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes)) |
fatalx(_("invalid timing file line: %s"), buf); | fatalx(U_("invalid timing file line: %s"), buf); |
|
|
if (interactive) |
|
check_input(STDIN_FILENO, &speed); |
|
|
|
/* Adjust delay using speed factor and clamp to max_wait */ |
/* Adjust delay using speed factor and clamp to max_wait */ |
to_wait = seconds / speed; | to_wait = seconds / speed_factor; |
if (max_wait && to_wait > max_wait) |
if (max_wait && to_wait > max_wait) |
to_wait = max_wait; |
to_wait = max_wait; |
delay(to_wait); |
|
|
|
|
/* Convert delay to a timeval. */ |
|
timeout.tv_sec = to_wait; |
|
timeout.tv_usec = (to_wait - timeout.tv_sec) * 1000000.0; |
|
|
|
/* Run event event loop to delay and get keyboard input. */ |
|
sudo_ev_add(evbase, input_ev, &timeout, false); |
|
sudo_ev_loop(evbase, 0); |
|
|
/* Even if we are not replaying, we still have to delay. */ |
/* Even if we are not replaying, we still have to delay. */ |
if (io_log_files[idx].fd.v == NULL) |
if (io_log_files[idx].fd.v == NULL) |
continue; |
continue; |
Line 424 main(int argc, char *argv[])
|
Line 465 main(int argc, char *argv[])
|
need_nlcr = (idx == IOFD_STDOUT || idx == IOFD_STDERR); |
need_nlcr = (idx == IOFD_STDOUT || idx == IOFD_STDERR); |
|
|
/* All output is sent to stdout. */ |
/* All output is sent to stdout. */ |
|
/* XXX - assumes no wall clock time spent writing output. */ |
while (nbytes != 0) { |
while (nbytes != 0) { |
if (nbytes > sizeof(buf)) |
if (nbytes > sizeof(buf)) |
len = sizeof(buf); |
len = sizeof(buf); |
Line 440 main(int argc, char *argv[])
|
Line 482 main(int argc, char *argv[])
|
if (need_nlcr) { |
if (need_nlcr) { |
size_t remainder = nread; |
size_t remainder = nread; |
size_t linelen; |
size_t linelen; |
iovcnt = 0; | char *cp = buf; |
cp = buf; | char *ep = buf - 1; |
ep = cp - 1; | |
/* Handle a "\r\n" pair that spans a buffer. */ |
/* Handle a "\r\n" pair that spans a buffer. */ |
if (last_char == '\r' && buf[0] == '\n') { |
if (last_char == '\r' && buf[0] == '\n') { |
ep++; |
ep++; |
remainder--; |
remainder--; |
} |
} |
|
|
|
iovcnt = 0; |
while ((ep = memchr(ep + 1, '\n', remainder)) != NULL) { |
while ((ep = memchr(ep + 1, '\n', remainder)) != NULL) { |
/* Is there already a carriage return? */ |
/* Is there already a carriage return? */ |
if (cp != ep && ep[-1] == '\r') { |
if (cp != ep && ep[-1] == '\r') { |
Line 457 main(int argc, char *argv[])
|
Line 501 main(int argc, char *argv[])
|
|
|
/* Store the line in iov followed by \r\n pair. */ |
/* Store the line in iov followed by \r\n pair. */ |
if (iovcnt + 3 > iovmax) { |
if (iovcnt + 3 > iovmax) { |
iovmax <<= 1; | iov = iovmax ? |
iov = erealloc3(iov, iovmax, sizeof(*iov)); | erealloc3(iov, iovmax <<= 1, sizeof(*iov)) : |
| emalloc2(iovmax = 32, sizeof(*iov)); |
} |
} |
linelen = (size_t)(ep - cp) + 1; |
linelen = (size_t)(ep - cp) + 1; |
iov[iovcnt].iov_base = cp; |
iov[iovcnt].iov_base = cp; |
Line 470 main(int argc, char *argv[])
|
Line 515 main(int argc, char *argv[])
|
cp = ep + 1; |
cp = ep + 1; |
remainder -= linelen; |
remainder -= linelen; |
} |
} |
if (cp - buf != nread) { | if ((size_t)(cp - buf) != nread) { |
/* |
/* |
* Partial line without a linefeed or multiple lines |
* Partial line without a linefeed or multiple lines |
* with \r\n pairs. |
* with \r\n pairs. |
Line 486 main(int argc, char *argv[])
|
Line 531 main(int argc, char *argv[])
|
iov[0].iov_len = nread; |
iov[0].iov_len = nread; |
iovcnt = 1; |
iovcnt = 1; |
} |
} |
if (atomic_writev(STDOUT_FILENO, iov, iovcnt) == -1) |
|
fatal(_("writing to standard output")); |
|
} |
|
} |
|
term_restore(STDIN_FILENO, 1); |
|
done: |
|
sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, exitcode); |
|
exit(exitcode); |
|
} |
|
|
|
static void | /* Setup closure for write_output. */ |
delay(double secs) | wc.wevent = output_ev; |
{ | wc.iov = iov; |
struct timespec ts, rts; | wc.iovcnt = iovcnt; |
int rval; | wc.nbytes = 0; |
| for (i = 0; i < iovcnt; i++) |
| wc.nbytes += iov[i].iov_len; |
|
|
/* | /* Run event event loop to write output. */ |
* Typical max resolution is 1/HZ but we can't portably check that. | /* XXX - should use a single event loop with a circular buffer. */ |
* If the interval is small enough, just ignore it. | sudo_ev_add(evbase, output_ev, NULL, false); |
*/ | sudo_ev_loop(evbase, 0); |
if (secs < 0.0001) | } |
return; | |
| |
rts.tv_sec = secs; | |
rts.tv_nsec = (secs - (double) rts.tv_sec) * 1000000000.0; | |
do { | |
memcpy(&ts, &rts, sizeof(ts)); | |
rval = nanosleep(&ts, &rts); | |
} while (rval == -1 && errno == EINTR); | |
if (rval == -1) { | |
fatal_nodebug("nanosleep: tv_sec %lld, tv_nsec %ld", | |
(long long)ts.tv_sec, (long)ts.tv_nsec); | |
} |
} |
|
debug_return; |
} |
} |
|
|
static int |
static int |
Line 539 open_io_fd(char *path, int len, struct io_log_file *io
|
Line 567 open_io_fd(char *path, int len, struct io_log_file *io
|
debug_return_int(iol->fd.v ? 0 : -1); |
debug_return_int(iol->fd.v ? 0 : -1); |
} |
} |
|
|
/* | static void |
* Call writev(), restarting as needed and handling EAGAIN since | write_output(int fd, int what, void *v) |
* fd may be in non-blocking mode. | |
*/ | |
static size_t | |
atomic_writev(int fd, struct iovec *iov, int iovcnt) | |
{ |
{ |
ssize_t n, nwritten = 0; | struct write_closure *wc = v; |
size_t count, remainder, nbytes = 0; | ssize_t nwritten; |
int i; | size_t count, remainder; |
debug_decl(atomic_writev, SUDO_DEBUG_UTIL) | unsigned int i; |
| debug_decl(write_output, SUDO_DEBUG_UTIL) |
|
|
for (i = 0; i < iovcnt; i++) | nwritten = writev(STDOUT_FILENO, wc->iov, wc->iovcnt); |
nbytes += iov[i].iov_len; | switch (nwritten) { |
| case -1: |
| if (errno != EINTR && errno != EAGAIN) |
| fatal(U_("unable to write to %s"), "stdout"); |
| break; |
| case 0: |
| break; |
| default: |
| remainder = wc->nbytes - nwritten; |
| if (remainder == 0) { |
| /* writev completed */ |
| debug_return; |
| } |
|
|
for (;;) { | /* short writev, adjust iov so we can write the remainder. */ |
n = writev(STDOUT_FILENO, iov, iovcnt); | count = 0; |
if (n > 0) { | i = wc->iovcnt; |
nwritten += n; | while (i--) { |
remainder = nbytes - nwritten; | count += wc->iov[i].iov_len; |
if (remainder == 0) | if (count == remainder) { |
| wc->iov += i; |
| wc->iovcnt -= i; |
break; |
break; |
/* short writev, adjust iov and do the rest. */ |
|
count = 0; |
|
i = iovcnt; |
|
while (i--) { |
|
count += iov[i].iov_len; |
|
if (count == remainder) { |
|
iov += i; |
|
iovcnt -= i; |
|
break; |
|
} |
|
if (count > remainder) { |
|
size_t off = (count - remainder); |
|
/* XXX - side effect prevents iov from being const */ |
|
iov[i].iov_base = (char *)iov[i].iov_base + off; |
|
iov[i].iov_len -= off; |
|
iov += i; |
|
iovcnt -= i; |
|
break; |
|
} |
|
} |
} |
continue; | if (count > remainder) { |
| size_t off = (count - remainder); |
| wc->iov[i].iov_base = (char *)wc->iov[i].iov_base + off; |
| wc->iov[i].iov_len -= off; |
| wc->iov += i; |
| wc->iovcnt -= i; |
| break; |
| } |
} |
} |
if (n == 0 || errno == EAGAIN) { |
|
int nready; |
|
fd_set fdsw; |
|
FD_ZERO(&fdsw); |
|
FD_SET(STDOUT_FILENO, &fdsw); |
|
do { |
|
nready = select(STDOUT_FILENO + 1, NULL, &fdsw, NULL, NULL); |
|
} while (nready == -1 && errno == EINTR); |
|
if (nready == 1) |
|
continue; |
|
} |
|
if (errno == EINTR) |
|
continue; |
|
nwritten = -1; |
|
break; |
break; |
} |
} |
debug_return_size_t(nwritten); | |
| /* Reschedule event to write remainder. */ |
| sudo_ev_add(sudo_ev_get_base(wc->wevent), wc->wevent, NULL, false); |
| debug_return; |
} |
} |
|
|
/* |
/* |
* Build expression list from search args |
* Build expression list from search args |
*/ |
*/ |
static int |
static int |
parse_expr(struct search_node **headp, char *argv[]) | parse_expr(struct search_node_list *head, char *argv[], bool sub_expr) |
{ |
{ |
struct search_node *sn, *newsn; | bool or = false, not = false; |
char or = 0, not = 0, type, **av; | struct search_node *sn; |
| char type, **av; |
debug_decl(parse_expr, SUDO_DEBUG_UTIL) |
debug_decl(parse_expr, SUDO_DEBUG_UTIL) |
|
|
sn = *headp; | for (av = argv; *av != NULL; av++) { |
for (av = argv; *av; av++) { | |
switch (av[0][0]) { |
switch (av[0][0]) { |
case 'a': /* and (ignore) */ |
case 'a': /* and (ignore) */ |
if (strncmp(*av, "and", strlen(*av)) != 0) |
if (strncmp(*av, "and", strlen(*av)) != 0) |
Line 622 parse_expr(struct search_node **headp, char *argv[])
|
Line 638 parse_expr(struct search_node **headp, char *argv[])
|
case 'o': /* or */ |
case 'o': /* or */ |
if (strncmp(*av, "or", strlen(*av)) != 0) |
if (strncmp(*av, "or", strlen(*av)) != 0) |
goto bad; |
goto bad; |
or = 1; | or = true; |
continue; |
continue; |
case '!': /* negate */ |
case '!': /* negate */ |
if (av[0][1] != '\0') |
if (av[0][1] != '\0') |
goto bad; |
goto bad; |
not = 1; | not = true; |
continue; |
continue; |
case 'c': /* command */ |
case 'c': /* command */ |
if (av[0][1] == '\0') |
if (av[0][1] == '\0') |
fatalx(_("ambiguous expression \"%s\""), *av); | fatalx(U_("ambiguous expression \"%s\""), *av); |
if (strncmp(*av, "cwd", strlen(*av)) == 0) |
if (strncmp(*av, "cwd", strlen(*av)) == 0) |
type = ST_CWD; |
type = ST_CWD; |
else if (strncmp(*av, "command", strlen(*av)) == 0) |
else if (strncmp(*av, "command", strlen(*av)) == 0) |
Line 656 parse_expr(struct search_node **headp, char *argv[])
|
Line 672 parse_expr(struct search_node **headp, char *argv[])
|
break; |
break; |
case 't': /* tty or to date */ |
case 't': /* tty or to date */ |
if (av[0][1] == '\0') |
if (av[0][1] == '\0') |
fatalx(_("ambiguous expression \"%s\""), *av); | fatalx(U_("ambiguous expression \"%s\""), *av); |
if (strncmp(*av, "todate", strlen(*av)) == 0) |
if (strncmp(*av, "todate", strlen(*av)) == 0) |
type = ST_TODATE; |
type = ST_TODATE; |
else if (strncmp(*av, "tty", strlen(*av)) == 0) |
else if (strncmp(*av, "tty", strlen(*av)) == 0) |
Line 672 parse_expr(struct search_node **headp, char *argv[])
|
Line 688 parse_expr(struct search_node **headp, char *argv[])
|
case '(': /* start sub-expression */ |
case '(': /* start sub-expression */ |
if (av[0][1] != '\0') |
if (av[0][1] != '\0') |
goto bad; |
goto bad; |
if (stack_top + 1 == STACK_NODE_SIZE) { |
|
fatalx(_("too many parenthesized expressions, max %d"), |
|
STACK_NODE_SIZE); |
|
} |
|
node_stack[stack_top++] = sn; |
|
type = ST_EXPR; |
type = ST_EXPR; |
break; |
break; |
case ')': /* end sub-expression */ |
case ')': /* end sub-expression */ |
if (av[0][1] != '\0') |
if (av[0][1] != '\0') |
goto bad; |
goto bad; |
/* pop */ | if (!sub_expr) |
if (--stack_top < 0) | fatalx(U_("unmatched ')' in expression")); |
fatalx(_("unmatched ')' in expression")); | |
if (node_stack[stack_top]) | |
sn->next = node_stack[stack_top]->next; | |
debug_return_int(av - argv + 1); |
debug_return_int(av - argv + 1); |
bad: |
bad: |
default: |
default: |
fatalx(_("unknown search term \"%s\""), *av); | fatalx(U_("unknown search term \"%s\""), *av); |
/* NOTREACHED */ |
/* NOTREACHED */ |
} |
} |
|
|
/* Allocate new search node */ |
/* Allocate new search node */ |
newsn = ecalloc(1, sizeof(*newsn)); | sn = ecalloc(1, sizeof(*sn)); |
newsn->type = type; | sn->type = type; |
newsn->or = or; | sn->or = or; |
newsn->negated = not; | sn->negated = not; |
/* newsn->next = NULL; */ | |
if (type == ST_EXPR) { |
if (type == ST_EXPR) { |
av += parse_expr(&newsn->u.expr, av + 1); | STAILQ_INIT(&sn->u.expr); |
| av += parse_expr(&sn->u.expr, av + 1, true); |
} else { |
} else { |
if (*(++av) == NULL) |
if (*(++av) == NULL) |
fatalx(_("%s requires an argument"), av[-1]); | fatalx(U_("%s requires an argument"), av[-1]); |
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
if (type == ST_PATTERN) { |
if (type == ST_PATTERN) { |
if (regcomp(&newsn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0) | if (regcomp(&sn->u.cmdre, *av, REG_EXTENDED|REG_NOSUB) != 0) |
fatalx(_("invalid regular expression: %s"), *av); | fatalx(U_("invalid regular expression: %s"), *av); |
} else |
} else |
#endif |
#endif |
if (type == ST_TODATE || type == ST_FROMDATE) { |
if (type == ST_TODATE || type == ST_FROMDATE) { |
newsn->u.tstamp = get_date(*av); | sn->u.tstamp = get_date(*av); |
if (newsn->u.tstamp == -1) | if (sn->u.tstamp == -1) |
fatalx(_("could not parse date \"%s\""), *av); | fatalx(U_("could not parse date \"%s\""), *av); |
} else { |
} else { |
newsn->u.ptr = *av; | sn->u.ptr = *av; |
} |
} |
} |
} |
not = or = 0; /* reset state */ | not = or = false; /* reset state */ |
if (sn) | STAILQ_INSERT_TAIL(head, sn, entries); |
sn->next = newsn; | |
else | |
*headp = newsn; | |
sn = newsn; | |
} |
} |
if (stack_top) | if (sub_expr) |
fatalx(_("unmatched '(' in expression")); | fatalx(U_("unmatched '(' in expression")); |
if (or) |
if (or) |
fatalx(_("illegal trailing \"or\"")); | fatalx(U_("illegal trailing \"or\"")); |
if (not) |
if (not) |
fatalx(_("illegal trailing \"!\"")); | fatalx(U_("illegal trailing \"!\"")); |
|
|
debug_return_int(av - argv); |
debug_return_int(av - argv); |
} |
} |
|
|
static bool |
static bool |
match_expr(struct search_node *head, struct log_info *log) | match_expr(struct search_node_list *head, struct log_info *log, bool last_match) |
{ |
{ |
struct search_node *sn; |
struct search_node *sn; |
bool matched = true; | bool res, matched = last_match; |
int rc; |
int rc; |
debug_decl(match_expr, SUDO_DEBUG_UTIL) |
debug_decl(match_expr, SUDO_DEBUG_UTIL) |
|
|
for (sn = head; sn; sn = sn->next) { | STAILQ_FOREACH(sn, head, entries) { |
/* If we have no match, skip ahead to the next OR entry. */ | |
if (!matched && !sn->or) | |
continue; | |
| |
switch (sn->type) { |
switch (sn->type) { |
case ST_EXPR: |
case ST_EXPR: |
matched = match_expr(sn->u.expr, log); | res = match_expr(&sn->u.expr, log, matched); |
break; |
break; |
case ST_CWD: |
case ST_CWD: |
matched = strcmp(sn->u.cwd, log->cwd) == 0; | res = strcmp(sn->u.cwd, log->cwd) == 0; |
break; |
break; |
case ST_TTY: |
case ST_TTY: |
matched = strcmp(sn->u.tty, log->tty) == 0; | res = strcmp(sn->u.tty, log->tty) == 0; |
break; |
break; |
case ST_RUNASGROUP: |
case ST_RUNASGROUP: |
matched = strcmp(sn->u.runas_group, log->runas_group) == 0; | res = strcmp(sn->u.runas_group, log->runas_group) == 0; |
break; |
break; |
case ST_RUNASUSER: |
case ST_RUNASUSER: |
matched = strcmp(sn->u.runas_user, log->runas_user) == 0; | res = strcmp(sn->u.runas_user, log->runas_user) == 0; |
break; |
break; |
case ST_USER: |
case ST_USER: |
matched = strcmp(sn->u.user, log->user) == 0; | res = strcmp(sn->u.user, log->user) == 0; |
break; |
break; |
case ST_PATTERN: |
case ST_PATTERN: |
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
Line 776 match_expr(struct search_node *head, struct log_info *
|
Line 776 match_expr(struct search_node *head, struct log_info *
|
regerror(rc, &sn->u.cmdre, buf, sizeof(buf)); |
regerror(rc, &sn->u.cmdre, buf, sizeof(buf)); |
fatalx("%s", buf); |
fatalx("%s", buf); |
} |
} |
matched = rc == REG_NOMATCH ? 0 : 1; | res = rc == REG_NOMATCH ? 0 : 1; |
#else |
#else |
matched = strstr(log.cmd, sn->u.pattern) != NULL; | res = strstr(log.cmd, sn->u.pattern) != NULL; |
#endif |
#endif |
break; |
break; |
case ST_FROMDATE: |
case ST_FROMDATE: |
matched = log->tstamp >= sn->u.tstamp; | res = log->tstamp >= sn->u.tstamp; |
break; |
break; |
case ST_TODATE: |
case ST_TODATE: |
matched = log->tstamp <= sn->u.tstamp; | res = log->tstamp <= sn->u.tstamp; |
break; |
break; |
|
default: |
|
fatalx(U_("unknown search type %d"), sn->type); |
|
/* NOTREACHED */ |
} |
} |
if (sn->negated) |
if (sn->negated) |
matched = !matched; | res = !res; |
| matched = sn->or ? (res || last_match) : (res && last_match); |
| last_match = matched; |
} |
} |
debug_return_bool(matched); |
debug_return_bool(matched); |
} |
} |
Line 799 parse_logfile(char *logfile)
|
Line 804 parse_logfile(char *logfile)
|
{ |
{ |
FILE *fp; |
FILE *fp; |
char *buf = NULL, *cp, *ep; |
char *buf = NULL, *cp, *ep; |
|
const char *errstr; |
size_t bufsize = 0, cwdsize = 0, cmdsize = 0; |
size_t bufsize = 0, cwdsize = 0, cmdsize = 0; |
struct log_info *li = NULL; |
struct log_info *li = NULL; |
debug_decl(list_session, SUDO_DEBUG_UTIL) | debug_decl(parse_logfile, SUDO_DEBUG_UTIL) |
|
|
fp = fopen(logfile, "r"); |
fp = fopen(logfile, "r"); |
if (fp == NULL) { |
if (fp == NULL) { |
warning(_("unable to open %s"), logfile); | warning(U_("unable to open %s"), logfile); |
goto bad; |
goto bad; |
} |
} |
|
|
Line 819 parse_logfile(char *logfile)
|
Line 825 parse_logfile(char *logfile)
|
if (getline(&buf, &bufsize, fp) == -1 || |
if (getline(&buf, &bufsize, fp) == -1 || |
getline(&li->cwd, &cwdsize, fp) == -1 || |
getline(&li->cwd, &cwdsize, fp) == -1 || |
getline(&li->cmd, &cmdsize, fp) == -1) { |
getline(&li->cmd, &cmdsize, fp) == -1) { |
|
warning(U_("%s: invalid log file"), logfile); |
goto bad; |
goto bad; |
} |
} |
|
|
Line 829 parse_logfile(char *logfile)
|
Line 836 parse_logfile(char *logfile)
|
/* |
/* |
* Crack the log line (rows and cols not present in old versions). |
* Crack the log line (rows and cols not present in old versions). |
* timestamp:user:runas_user:runas_group:tty:rows:cols |
* timestamp:user:runas_user:runas_group:tty:rows:cols |
|
* XXX - probably better to use strtok and switch on the state. |
*/ |
*/ |
buf[strcspn(buf, "\n")] = '\0'; |
buf[strcspn(buf, "\n")] = '\0'; |
|
cp = buf; |
|
|
/* timestamp */ |
/* timestamp */ |
if ((ep = strchr(buf, ':')) == NULL) | if ((ep = strchr(cp, ':')) == NULL) { |
| warning(U_("%s: time stamp field is missing"), logfile); |
goto bad; |
goto bad; |
if ((li->tstamp = atoi(buf)) == 0) | } |
| *ep = '\0'; |
| li->tstamp = sizeof(time_t) == 4 ? strtonum(cp, INT_MIN, INT_MAX, &errstr) : |
| strtonum(cp, LLONG_MIN, LLONG_MAX, &errstr); |
| if (errstr != NULL) { |
| warning(U_("%s: time stamp %s: %s"), logfile, cp, errstr); |
goto bad; |
goto bad; |
|
} |
|
|
/* user */ |
/* user */ |
cp = ep + 1; |
cp = ep + 1; |
if ((ep = strchr(cp, ':')) == NULL) | if ((ep = strchr(cp, ':')) == NULL) { |
| warning(U_("%s: user field is missing"), logfile); |
goto bad; |
goto bad; |
|
} |
li->user = estrndup(cp, (size_t)(ep - cp)); |
li->user = estrndup(cp, (size_t)(ep - cp)); |
|
|
/* runas user */ |
/* runas user */ |
cp = ep + 1; |
cp = ep + 1; |
if ((ep = strchr(cp, ':')) == NULL) | if ((ep = strchr(cp, ':')) == NULL) { |
| warning(U_("%s: runas user field is missing"), logfile); |
goto bad; |
goto bad; |
|
} |
li->runas_user = estrndup(cp, (size_t)(ep - cp)); |
li->runas_user = estrndup(cp, (size_t)(ep - cp)); |
|
|
/* runas group */ |
/* runas group */ |
cp = ep + 1; |
cp = ep + 1; |
if ((ep = strchr(cp, ':')) == NULL) | if ((ep = strchr(cp, ':')) == NULL) { |
| warning(U_("%s: runas group field is missing"), logfile); |
goto bad; |
goto bad; |
|
} |
if (cp != ep) |
if (cp != ep) |
li->runas_group = estrndup(cp, (size_t)(ep - cp)); |
li->runas_group = estrndup(cp, (size_t)(ep - cp)); |
|
|
/* tty, followed by optional rows + columns */ |
/* tty, followed by optional rows + columns */ |
cp = ep + 1; |
cp = ep + 1; |
if ((ep = strchr(cp, ':')) == NULL) { |
if ((ep = strchr(cp, ':')) == NULL) { |
|
/* just the tty */ |
li->tty = estrdup(cp); |
li->tty = estrdup(cp); |
} else { |
} else { |
|
/* tty followed by rows + columns */ |
li->tty = estrndup(cp, (size_t)(ep - cp)); |
li->tty = estrndup(cp, (size_t)(ep - cp)); |
cp = ep + 1; |
cp = ep + 1; |
li->rows = atoi(cp); | /* need to NULL out separator to use strtonum() */ |
if ((ep = strchr(cp, ':')) != NULL) { |
if ((ep = strchr(cp, ':')) != NULL) { |
|
*ep = '\0'; |
|
} |
|
li->rows = strtonum(cp, 1, INT_MAX, &errstr); |
|
if (errstr != NULL) { |
|
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
|
"%s: tty rows %s: %s", logfile, cp, errstr); |
|
} |
|
if (ep != NULL) { |
cp = ep + 1; |
cp = ep + 1; |
li->cols = atoi(cp); | li->cols = strtonum(cp, 1, INT_MAX, &errstr); |
| if (errstr != NULL) { |
| sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, |
| "%s: tty cols %s: %s", logfile, cp, errstr); |
| } |
} |
} |
} |
} |
fclose(fp); |
fclose(fp); |
Line 900 static int
|
Line 936 static int
|
list_session(char *logfile, REGEX_T *re, const char *user, const char *tty) |
list_session(char *logfile, REGEX_T *re, const char *user, const char *tty) |
{ |
{ |
char idbuf[7], *idstr, *cp; |
char idbuf[7], *idstr, *cp; |
|
const char *timestr; |
struct log_info *li; |
struct log_info *li; |
int rval = -1; |
int rval = -1; |
debug_decl(list_session, SUDO_DEBUG_UTIL) |
debug_decl(list_session, SUDO_DEBUG_UTIL) |
Line 908 list_session(char *logfile, REGEX_T *re, const char *u
|
Line 945 list_session(char *logfile, REGEX_T *re, const char *u
|
goto done; |
goto done; |
|
|
/* Match on search expression if there is one. */ |
/* Match on search expression if there is one. */ |
if (search_expr && !match_expr(search_expr, li)) | if (!STAILQ_EMPTY(&search_expr) && !match_expr(&search_expr, li, true)) |
goto done; |
goto done; |
|
|
/* Convert from /var/log/sudo-sessions/00/00/01/log to 000001 */ |
/* Convert from /var/log/sudo-sessions/00/00/01/log to 000001 */ |
Line 928 list_session(char *logfile, REGEX_T *re, const char *u
|
Line 965 list_session(char *logfile, REGEX_T *re, const char *u
|
idstr = cp; |
idstr = cp; |
} |
} |
/* XXX - print rows + cols? */ |
/* XXX - print rows + cols? */ |
|
timestr = get_timestr(li->tstamp, 1); |
printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ", |
printf("%s : %s : TTY=%s ; CWD=%s ; USER=%s ; ", |
get_timestr(li->tstamp, 1), li->user, li->tty, li->cwd, li->runas_user); | timestr ? timestr : "invalid date", |
| li->user, li->tty, li->cwd, li->runas_user); |
if (li->runas_group) |
if (li->runas_group) |
printf("GROUP=%s ; ", li->runas_group); |
printf("GROUP=%s ; ", li->runas_group); |
printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd); |
printf("TSID=%s ; COMMAND=%s\n", idstr, li->cmd); |
Line 957 find_sessions(const char *dir, REGEX_T *re, const char
|
Line 996 find_sessions(const char *dir, REGEX_T *re, const char
|
struct dirent *dp; |
struct dirent *dp; |
struct stat sb; |
struct stat sb; |
size_t sdlen, sessions_len = 0, sessions_size = 36*36; |
size_t sdlen, sessions_len = 0, sessions_size = 36*36; |
int i, len; | unsigned int i; |
| int len; |
char pathbuf[PATH_MAX], **sessions = NULL; |
char pathbuf[PATH_MAX], **sessions = NULL; |
#ifdef HAVE_STRUCT_DIRENT_D_TYPE |
#ifdef HAVE_STRUCT_DIRENT_D_TYPE |
bool checked_type = true; |
bool checked_type = true; |
Line 968 find_sessions(const char *dir, REGEX_T *re, const char
|
Line 1008 find_sessions(const char *dir, REGEX_T *re, const char
|
|
|
d = opendir(dir); |
d = opendir(dir); |
if (d == NULL) |
if (d == NULL) |
fatal(_("unable to open %s"), dir); | fatal(U_("unable to open %s"), dir); |
|
|
/* XXX - would be faster to chdir and use relative names */ |
/* XXX - would be faster to chdir and use relative names */ |
sdlen = strlcpy(pathbuf, dir, sizeof(pathbuf)); |
sdlen = strlcpy(pathbuf, dir, sizeof(pathbuf)); |
Line 1011 find_sessions(const char *dir, REGEX_T *re, const char
|
Line 1051 find_sessions(const char *dir, REGEX_T *re, const char
|
for (i = 0; i < sessions_len; i++) { |
for (i = 0; i < sessions_len; i++) { |
len = snprintf(&pathbuf[sdlen], sizeof(pathbuf) - sdlen, |
len = snprintf(&pathbuf[sdlen], sizeof(pathbuf) - sdlen, |
"%s/log", sessions[i]); |
"%s/log", sessions[i]); |
if (len <= 0 || len >= sizeof(pathbuf) - sdlen) { | if (len <= 0 || (size_t)len >= sizeof(pathbuf) - sdlen) { |
errno = ENAMETOOLONG; |
errno = ENAMETOOLONG; |
fatal("%s/%s/log", dir, sessions[i]); |
fatal("%s/%s/log", dir, sessions[i]); |
} |
} |
Line 1041 list_sessions(int argc, char **argv, const char *patte
|
Line 1081 list_sessions(int argc, char **argv, const char *patte
|
debug_decl(list_sessions, SUDO_DEBUG_UTIL) |
debug_decl(list_sessions, SUDO_DEBUG_UTIL) |
|
|
/* Parse search expression if present */ |
/* Parse search expression if present */ |
parse_expr(&search_expr, argv); | parse_expr(&search_expr, argv, false); |
|
|
#ifdef HAVE_REGCOMP |
#ifdef HAVE_REGCOMP |
/* optional regex */ |
/* optional regex */ |
if (pattern) { |
if (pattern) { |
re = &rebuf; |
re = &rebuf; |
if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0) |
if (regcomp(re, pattern, REG_EXTENDED|REG_NOSUB) != 0) |
fatalx(_("invalid regular expression: %s"), pattern); | fatalx(U_("invalid regular expression: %s"), pattern); |
} |
} |
#else |
#else |
re = (char *) pattern; |
re = (char *) pattern; |
Line 1058 list_sessions(int argc, char **argv, const char *patte
|
Line 1098 list_sessions(int argc, char **argv, const char *patte
|
} |
} |
|
|
/* |
/* |
* Check input for ' ', '<', '>' | * Check input for ' ', '<', '>', return |
* pause, slow, fast | * pause, slow, fast, next |
*/ |
*/ |
static void |
static void |
check_input(int ttyfd, double *speed) | check_input(int fd, int what, void *v) |
{ |
{ |
fd_set *fdsr; | struct sudo_event *ev = v; |
int nready, paused = 0; | struct sudo_event_base *evbase = sudo_ev_get_base(ev); |
struct timeval tv; | struct timeval tv, *timeout = NULL; |
| static bool paused = 0; |
char ch; |
char ch; |
ssize_t n; |
|
debug_decl(check_input, SUDO_DEBUG_UTIL) |
debug_decl(check_input, SUDO_DEBUG_UTIL) |
|
|
fdsr = ecalloc(howmany(ttyfd + 1, NFDBITS), sizeof(fd_mask)); | if (ISSET(what, SUDO_EV_READ)) { |
for (;;) { | switch (read(fd, &ch, 1)) { |
FD_SET(ttyfd, fdsr); | case -1: |
tv.tv_sec = 0; | if (errno != EINTR && errno != EAGAIN) |
tv.tv_usec = 0; | fatal(U_("unable to read %s"), "stdin"); |
| |
nready = select(ttyfd + 1, fdsr, NULL, NULL, paused ? NULL : &tv); | |
if (nready != 1) | |
break; |
break; |
n = read(ttyfd, &ch, 1); | case 0: |
if (n == 1) { | /* Ignore EOF. */ |
| break; |
| case 1: |
if (paused) { |
if (paused) { |
paused = 0; | /* Any key will unpause, event is finished. */ |
continue; | /* XXX - pause time could be less than timeout */ |
| paused = false; |
| debug_return; /* XXX */ |
} |
} |
switch (ch) { |
switch (ch) { |
case ' ': |
case ' ': |
paused = 1; | paused = true; |
break; |
break; |
case '<': |
case '<': |
*speed /= 2; | speed_factor /= 2; |
break; |
break; |
case '>': |
case '>': |
*speed *= 2; | speed_factor *= 2; |
break; |
break; |
|
case '\r': |
|
case '\n': |
|
debug_return; /* XXX */ |
} |
} |
|
break; |
} |
} |
|
if (!paused) { |
|
/* Determine remaining timeout, if any. */ |
|
sudo_ev_get_timeleft(ev, &tv); |
|
if (!sudo_timevalisset(&tv)) { |
|
/* No time left, event is done. */ |
|
debug_return; |
|
} |
|
timeout = &tv; |
|
} |
|
/* Re-enable event. */ |
|
sudo_ev_add(evbase, ev, timeout, false); |
} |
} |
free(fdsr); |
|
debug_return; |
debug_return; |
} |
} |
|
|
Line 1111 check_input(int ttyfd, double *speed)
|
Line 1166 check_input(int ttyfd, double *speed)
|
* Returns 1 on success and 0 on failure. |
* Returns 1 on success and 0 on failure. |
*/ |
*/ |
static int |
static int |
parse_timing(buf, decimal, idx, seconds, nbytes) | parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, |
const char *buf; | size_t *nbytes) |
const char *decimal; | |
int *idx; | |
double *seconds; | |
size_t *nbytes; | |
{ |
{ |
unsigned long ul; |
unsigned long ul; |
long l; |
long l; |
Line 1126 parse_timing(buf, decimal, idx, seconds, nbytes)
|
Line 1177 parse_timing(buf, decimal, idx, seconds, nbytes)
|
|
|
/* Parse index */ |
/* Parse index */ |
ul = strtoul(buf, &ep, 10); |
ul = strtoul(buf, &ep, 10); |
|
if (ep == buf || !isspace((unsigned char) *ep)) |
|
goto bad; |
if (ul >= IOFD_TIMING) { |
if (ul >= IOFD_TIMING) { |
if (ul != 6) |
if (ul != 6) |
goto bad; |
goto bad; |
Line 1144 parse_timing(buf, decimal, idx, seconds, nbytes)
|
Line 1197 parse_timing(buf, decimal, idx, seconds, nbytes)
|
*/ |
*/ |
errno = 0; |
errno = 0; |
l = strtol(cp, &ep, 10); |
l = strtol(cp, &ep, 10); |
if ((errno == ERANGE && (l == LONG_MAX || l == LONG_MIN)) || | if (ep == cp || (*ep != '.' && strncmp(ep, decimal, strlen(decimal)) != 0)) |
l < 0 || l > INT_MAX || | |
(*ep != '.' && strncmp(ep, decimal, strlen(decimal)) != 0)) { | |
goto bad; |
goto bad; |
} | if (l < 0 || l > INT_MAX || (errno == ERANGE && l == LONG_MAX)) |
| goto bad; |
*seconds = (double)l; |
*seconds = (double)l; |
cp = ep + (*ep == '.' ? 1 : strlen(decimal)); |
cp = ep + (*ep == '.' ? 1 : strlen(decimal)); |
d = 10.0; |
d = 10.0; |
Line 1163 parse_timing(buf, decimal, idx, seconds, nbytes)
|
Line 1215 parse_timing(buf, decimal, idx, seconds, nbytes)
|
|
|
errno = 0; |
errno = 0; |
ul = strtoul(cp, &ep, 10); |
ul = strtoul(cp, &ep, 10); |
if (errno == ERANGE && ul == ULONG_MAX) | if (ep == cp || *ep != '\0' || (errno == ERANGE && ul == ULONG_MAX)) |
goto bad; |
goto bad; |
*nbytes = (size_t)ul; |
*nbytes = (size_t)ul; |
|
|
Line 1211 sudoreplay_cleanup(void)
|
Line 1263 sudoreplay_cleanup(void)
|
} |
} |
|
|
/* |
/* |
* Signal handler for SIGINT, SIGKILL, SIGTERM, SIGHUP | * Signal handler for SIGINT, SIGTERM, SIGHUP, SIGQUIT |
* Must be installed with SA_RESETHAND enabled. |
* Must be installed with SA_RESETHAND enabled. |
*/ |
*/ |
static void |
static void |