Add support for restarting I/O log transfers.

This commit is contained in:
Todd C. Miller
2019-10-24 20:04:30 -06:00
parent 8a16e62a88
commit e76d2e8201
6 changed files with 330 additions and 64 deletions

View File

@@ -108,7 +108,8 @@ SHELL = @SHELL@
PROGS = logsrvd sendlog PROGS = logsrvd sendlog
LOGSRVD_OBJS = iolog_writer.o logsrvd.o log_server.pb-c.o protobuf-c.o LOGSRVD_OBJS = iolog_util.o iolog_writer.o logsrvd.o log_server.pb-c.o \
protobuf-c.o
SENDLOG_OBJS = sendlog.o iolog_util.o log_server.pb-c.o protobuf-c.o SENDLOG_OBJS = sendlog.o iolog_util.o log_server.pb-c.o protobuf-c.o
@@ -232,14 +233,16 @@ iolog_writer.o: $(srcdir)/iolog_writer.c $(devdir)/log_server.pb-c.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \
$(sudoers_srcdir)/iolog.h $(top_builddir)/config.h $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \
$(top_builddir)/config.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_writer.c $(CC) -c $(CPPFLAGS) $(CFLAGS) $(ASAN_CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(srcdir)/iolog_writer.c
iolog_writer.i: $(srcdir)/iolog_writer.c $(devdir)/log_server.pb-c.h \ iolog_writer.i: $(srcdir)/iolog_writer.c $(devdir)/log_server.pb-c.h \
$(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \ $(incdir)/compat/stdbool.h $(incdir)/sudo_compat.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \ $(incdir)/sudo_debug.h $(incdir)/sudo_fatal.h \
$(incdir)/sudo_queue.h $(incdir)/sudo_util.h \ $(incdir)/sudo_queue.h $(incdir)/sudo_util.h \
$(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \ $(srcdir)/logsrvd.h $(srcdir)/protobuf-c/protobuf-c.h \
$(sudoers_srcdir)/iolog.h $(top_builddir)/config.h $(sudoers_srcdir)/iolog.h $(sudoers_srcdir)/iolog_util.h \
$(top_builddir)/config.h
$(CC) -E -o $@ $(CPPFLAGS) $< $(CC) -E -o $@ $(CPPFLAGS) $<
iolog_writer.plog: iolog_writer.i iolog_writer.plog: iolog_writer.i
rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_writer.c --i-file $< --output-file $@ rm -f $@; pvs-studio --cfg $(PVS_CFG) --sourcetree-root $(top_srcdir) --skip-cl-exe yes --source-file $(srcdir)/iolog_writer.c --i-file $< --output-file $@

View File

@@ -42,7 +42,7 @@
#include "sudo_debug.h" #include "sudo_debug.h"
#include "sudo_util.h" #include "sudo_util.h"
#include "sudo_fatal.h" #include "sudo_fatal.h"
#include "iolog.h" #include "iolog_util.h"
#include "logsrvd.h" #include "logsrvd.h"
/* I/O log file names relative to iolog_dir. */ /* I/O log file names relative to iolog_dir. */
@@ -294,7 +294,7 @@ create_iolog_dir(struct iolog_details *details, struct connection_closure *closu
closure->iolog_dir_fd = open(closure->iolog_dir, O_RDONLY); closure->iolog_dir_fd = open(closure->iolog_dir, O_RDONLY);
if (closure->iolog_dir_fd == -1) { if (closure->iolog_dir_fd == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO, sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"%s", path); "%s", closure->iolog_dir);
goto bad; goto bad;
} }
@@ -414,6 +414,151 @@ iolog_init(ExecMessage *msg, struct connection_closure *closure)
debug_return_bool(true); debug_return_bool(true);
} }
/*
* Read the next record from the timing file.
* Return 0 on success, 1 on EOF and -1 on error.
*/
static int
read_timing_record(FILE *fp, struct timing_closure *timing)
{
char line[LINE_MAX];
debug_decl(read_timing_record, SUDO_DEBUG_UTIL)
/* Read next record from timing file. */
if (fgets(line, sizeof(line), fp) == NULL) {
/* EOF or error reading timing file, we are done. */
if (feof(fp))
debug_return_int(1); /* EOF */
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"error reading timing file");
debug_return_int(-1);
}
/* Parse timing file record. */
line[strcspn(line, "\n")] = '\0';
if (!parse_timing(line, timing)) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"invalid timing file line: %s", line);
debug_return_int(-1);
}
debug_return_int(0);
}
bool
iolog_restart(RestartMessage *msg, struct connection_closure *closure)
{
struct timespec target;
struct timing_closure timing;
FILE *fp = NULL;
int i, fd;
off_t length;
debug_decl(iolog_init, SUDO_DEBUG_UTIL)
target.tv_sec = msg->resume_point->tv_sec;
target.tv_nsec = msg->resume_point->tv_nsec;
/* We use iolog_dir_fd in calls to openat(2) */
if ((closure->iolog_dir = strdup(msg->log_id)) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"strdup");
goto bad;
}
closure->iolog_dir_fd = open(closure->iolog_dir, O_RDONLY);
if (closure->iolog_dir_fd == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"%s", closure->iolog_dir);
goto bad;
}
/* Open existing I/O log files. */
for (i = 0; i < IOFD_MAX; i++) {
closure->io_fds[i] = openat(closure->iolog_dir_fd, iolog_names[i],
O_RDWR, 0600);
}
/* Dup IOFD_TIMING to a stream for easier processing. */
if (closure->io_fds[IOFD_TIMING] == -1)
goto bad;
if ((fd = dup(closure->io_fds[IOFD_TIMING])) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"dup");
goto bad;
}
if ((fp = fdopen(fd, "r")) == NULL) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"fdopen");
goto bad;
}
/* Parse timing file until we reach the target point. */
/* XXX - split up */
for (;;) {
if (read_timing_record(fp, &timing) != 0)
goto bad;
sudo_timespecadd(&timing.delay, &closure->elapsed_time,
&closure->elapsed_time);
if (timing.event < IOFD_TIMING) {
if (closure->io_fds[timing.event] == -1) {
/* Missing log file. */
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"iofd %d referenced but not open", timing.event);
goto bad;
}
length = lseek(closure->io_fds[timing.event], timing.u.nbytes,
SEEK_CUR);
if (length == -1) {
sudo_debug_printf(
SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"lseek(%d, %lld, SEEK_CUR", closure->io_fds[timing.event],
(long long)timing.u.nbytes);
goto bad;
}
if (ftruncate(closure->io_fds[timing.event], length) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"ftruncate(%d, %lld)", closure->io_fds[IOFD_TIMING], length);
goto bad;
}
}
if (sudo_timespeccmp(&closure->elapsed_time, &target, >=)) {
if (sudo_timespeccmp(&closure->elapsed_time, &target, ==))
break;
/* Mismatch between resume point and stored log. */
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"resume point mismatch, target [%lld, %ld], have [%lld, %ld]",
(long long)target.tv_sec, target.tv_nsec,
(long long)closure->elapsed_time.tv_sec,
closure->elapsed_time.tv_nsec);
goto bad;
}
}
/* Update timing file position after determining resume point. */
#ifdef HAVE_FSEEKO
length = ftello(fp);
#else
length = ftell(fp);
#endif
fclose(fp);
if (lseek(closure->io_fds[IOFD_TIMING], length, SEEK_SET) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"lseek(%d, %lld, SEEK_SET", closure->io_fds[IOFD_TIMING], length);
goto bad;
}
if (ftruncate(closure->io_fds[IOFD_TIMING], length) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO,
"ftruncate(%d, %lld)", closure->io_fds[IOFD_TIMING], length);
goto bad;
}
/* Ready to log I/O buffers. */
debug_return_bool(true);
bad:
if (fp != NULL)
fclose(fp);
debug_return_bool(false);
}
static bool static bool
iolog_write(int iofd, void *buf, size_t len, struct connection_closure *closure) iolog_write(int iofd, void *buf, size_t len, struct connection_closure *closure)
{ {

View File

@@ -156,6 +156,18 @@ fmt_log_id_message(const char *id, struct connection_buffer *buf)
debug_return_bool(fmt_server_message(buf, &msg)); debug_return_bool(fmt_server_message(buf, &msg));
} }
static bool
fmt_error_message(const char *errstr, struct connection_buffer *buf)
{
ServerMessage msg = SERVER_MESSAGE__INIT;
debug_decl(fmt_error_message, SUDO_DEBUG_UTIL)
msg.error = (char *)errstr;
msg.type_case = SERVER_MESSAGE__TYPE_ERROR;
debug_return_bool(fmt_server_message(buf, &msg));
}
/* /*
* Parse an ExecMessage * Parse an ExecMessage
*/ */
@@ -211,6 +223,7 @@ handle_exit(ExitMessage *msg, struct connection_closure *closure)
"unexpected state %d", closure->state); "unexpected state %d", closure->state);
debug_return_bool(false); debug_return_bool(false);
} }
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received ExitMessage", __func__);
/* Sudo I/O logs don't store this info. */ /* Sudo I/O logs don't store this info. */
if (msg->signal != NULL && msg->signal[0] != '\0') { if (msg->signal != NULL && msg->signal[0] != '\0') {
@@ -250,12 +263,26 @@ handle_restart(RestartMessage *msg, struct connection_closure *closure)
"unexpected state %d", closure->state); "unexpected state %d", closure->state);
debug_return_bool(false); debug_return_bool(false);
} }
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: received RestartMessage for %s",
__func__, msg->log_id);
/* TODO */ if (!iolog_restart(msg, closure)) {
closure->state = RESTARTING; sudo_debug_printf(SUDO_DEBUG_WARN, "%s: unable to restart I/O log", __func__);
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, /* XXX - structured error message so client can send from beginning */
"server restart not implemented"); if (!fmt_error_message("unable to restart log", &closure->write_buf))
debug_return_bool(false); debug_return_bool(false);
sudo_ev_del(NULL, closure->read_ev);
if (sudo_ev_add(NULL, closure->write_ev, NULL, false) == -1) {
sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO,
"unable to add server write event");
debug_return_bool(false);
}
closure->state = ERROR;
debug_return_bool(true);
}
closure->state = RUNNING;
debug_return_bool(true);
} }
static bool static bool
@@ -491,7 +518,8 @@ server_msg_cb(int fd, int what, void *v)
buf->off = 0; buf->off = 0;
buf->len = 0; buf->len = 0;
sudo_ev_del(NULL, closure->write_ev); sudo_ev_del(NULL, closure->write_ev);
if (closure->state == FLUSHED || closure->state == SHUTDOWN) if (closure->state == FLUSHED || closure->state == SHUTDOWN ||
closure->state == ERROR)
goto finished; goto finished;
} }
debug_return; debug_return;

View File

@@ -66,10 +66,10 @@ struct iolog_details {
enum connection_status { enum connection_status {
INITIAL, INITIAL,
RUNNING, RUNNING,
RESTARTING,
EXITED, EXITED,
SHUTDOWN, SHUTDOWN,
FLUSHED FLUSHED,
ERROR
}; };
union sockaddr_union { union sockaddr_union {
@@ -109,6 +109,7 @@ struct connection_closure {
/* iolog.c */ /* iolog.c */
bool iolog_init(ExecMessage *msg, struct connection_closure *closure); bool iolog_init(ExecMessage *msg, struct connection_closure *closure);
bool iolog_restart(RestartMessage *msg, struct connection_closure *closure);
int store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure); int store_iobuf(int iofd, IoBuffer *msg, struct connection_closure *closure);
int store_suspend(CommandSuspend *msg, struct connection_closure *closure); int store_suspend(CommandSuspend *msg, struct connection_closure *closure);
int store_winsize(ChangeWindowSize *msg, struct connection_closure *closure); int store_winsize(ChangeWindowSize *msg, struct connection_closure *closure);

View File

@@ -59,7 +59,7 @@
static gzFile io_fds[IOFD_MAX]; static gzFile io_fds[IOFD_MAX];
/* I/O log file names relative to iolog_dir. */ /* I/O log file names relative to the iolog dir. */
/* XXX - duplicated with server */ /* XXX - duplicated with server */
static const char *iolog_names[] = { static const char *iolog_names[] = {
"stdin", /* IOFD_STDIN */ "stdin", /* IOFD_STDIN */
@@ -74,8 +74,8 @@ static const char *iolog_names[] = {
static void static void
usage(void) usage(void)
{ {
fprintf(stderr, "usage: %s [-h host] [-p port] /path/to/iolog\n", fprintf(stderr, "usage: %s [-h host] [-i iolog-id] [-p port] "
getprogname()); "[-r restart-point] /path/to/iolog\n", getprogname());
exit(1); exit(1);
} }
@@ -138,7 +138,7 @@ connect_server(const char *host, const char *port)
} }
/* /*
* Free client closure allocated by client_closure_alloc() * Free client closure contents.
*/ */
static void static void
client_closure_free(struct client_closure *closure) client_closure_free(struct client_closure *closure)
@@ -151,7 +151,8 @@ client_closure_free(struct client_closure *closure)
free(closure->read_buf.data); free(closure->read_buf.data);
free(closure->write_buf.data); free(closure->write_buf.data);
free(closure->buf); free(closure->buf);
free(closure); // XXX - avoid use after free
//free(closure);
} }
debug_return; debug_return;
@@ -431,6 +432,42 @@ done:
debug_return_bool(ret); debug_return_bool(ret);
} }
/*
* Build and format a RestartMessage wrapped in a ClientMessage.
* Stores the wire format message in the closure's write buffer.
* Returns true on success, false on failure.
*/
static bool
fmt_restart_message(struct client_closure *closure)
{
ClientMessage client_msg = CLIENT_MESSAGE__INIT;
RestartMessage restart_msg = RESTART_MESSAGE__INIT;
TimeSpec tv = TIME_SPEC__INIT;
bool ret = false;
debug_decl(fmt_restart_message, SUDO_DEBUG_UTIL)
sudo_debug_printf(SUDO_DEBUG_INFO,
"%s: sending RestartMessage, [%lld, %ld]", __func__,
(long long)closure->restart->tv_sec, closure->restart->tv_nsec);
tv.tv_sec = closure->restart->tv_sec;
tv.tv_nsec = closure->restart->tv_nsec;
restart_msg.resume_point = &tv;
restart_msg.log_id = (char *)closure->iolog_id;
/* Schedule ClientMessage */
client_msg.restart_msg = &restart_msg;
client_msg.type_case = CLIENT_MESSAGE__TYPE_RESTART_MSG;
ret = fmt_client_message(&closure->write_buf, &client_msg);
if (ret) {
if (sudo_ev_add(NULL, closure->write_ev, NULL, false) == -1)
ret = false;
}
ret = true;
debug_return_bool(ret);
}
/* /*
* Build and format an ExitMessage wrapped in a ClientMessage. * Build and format an ExitMessage wrapped in a ClientMessage.
* Stores the wire format message in the closure's write buffer. * Stores the wire format message in the closure's write buffer.
@@ -603,6 +640,7 @@ fmt_next_iolog(struct client_closure *closure)
} }
/* TODO: fill write buffer with multiple messages */ /* TODO: fill write buffer with multiple messages */
again:
switch (read_timing_record(timing)) { switch (read_timing_record(timing)) {
case 0: case 0:
/* OK */ /* OK */
@@ -616,6 +654,16 @@ fmt_next_iolog(struct client_closure *closure)
debug_return_bool(false); debug_return_bool(false);
} }
/* Track elapsed time for comparison with commit points. */
sudo_timespecadd(&timing->delay, &closure->elapsed, &closure->elapsed);
/* If we have a restart point, ignore records until we hit it. */
if (closure->restart != NULL) {
if (sudo_timespeccmp(closure->restart, &closure->elapsed, >=))
goto again;
closure->restart = NULL; /* caught up */
}
switch (timing->event) { switch (timing->event) {
case IO_EVENT_STDIN: case IO_EVENT_STDIN:
ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDIN_BUF, closure, buf); ret = fmt_io_buf(CLIENT_MESSAGE__TYPE_STDIN_BUF, closure, buf);
@@ -643,9 +691,6 @@ fmt_next_iolog(struct client_closure *closure)
break; break;
} }
/* Track elapsed time for comparison with commit points. */
sudo_timespecadd(&timing->delay, &closure->elapsed, &closure->elapsed);
debug_return_bool(ret); debug_return_bool(ret);
} }
@@ -660,6 +705,7 @@ client_message_completion(struct client_closure *closure)
switch (closure->state) { switch (closure->state) {
case SEND_EXEC: case SEND_EXEC:
case SEND_RESTART:
closure->state = SEND_IO; closure->state = SEND_IO;
/* FALLTHROUGH */ /* FALLTHROUGH */
case SEND_IO: case SEND_IO:
@@ -744,7 +790,7 @@ handle_log_id(char *id, struct client_closure *closure)
debug_decl(handle_log_id, SUDO_DEBUG_UTIL) debug_decl(handle_log_id, SUDO_DEBUG_UTIL)
printf("remote log ID: %s\n", id); printf("remote log ID: %s\n", id);
if ((closure->iolog_dir = strdup(id)) == NULL) if ((closure->iolog_id = strdup(id)) == NULL)
sudo_fatal(NULL); sudo_fatal(NULL);
debug_return_bool(true); debug_return_bool(true);
} }
@@ -797,8 +843,13 @@ handle_server_message(uint8_t *buf, size_t len,
switch (msg->type_case) { switch (msg->type_case) {
case SERVER_MESSAGE__TYPE_HELLO: case SERVER_MESSAGE__TYPE_HELLO:
if ((ret = handle_server_hello(msg->hello, closure))) { if ((ret = handle_server_hello(msg->hello, closure))) {
closure->state = SEND_EXEC; if (sudo_timespecisset(closure->restart)) {
ret = fmt_exec_message(closure); closure->state = SEND_RESTART;
ret = fmt_restart_message(closure);
} else {
closure->state = SEND_EXEC;
ret = fmt_exec_message(closure);
}
} }
break; break;
case SERVER_MESSAGE__TYPE_COMMIT_POINT: case SERVER_MESSAGE__TYPE_COMMIT_POINT:
@@ -930,20 +981,23 @@ bad:
} }
/* /*
* Allocate a new connection closure. * Initialize a new client closure
*/ */
static struct client_closure * static bool
client_closure_alloc(int sock, struct iolog_info *log_info) client_closure_fill(struct client_closure *closure, int sock,
struct timespec *restart, const char *iolog_id,
struct iolog_info *log_info)
{ {
struct client_closure *closure; debug_decl(client_closure_fill, SUDO_DEBUG_UTIL)
debug_decl(client_closure_alloc, SUDO_DEBUG_UTIL)
if ((closure = calloc(1, sizeof(*closure))) == NULL) memset(closure, 0, sizeof(*closure));
debug_return_ptr(NULL);
closure->state = RECV_HELLO; closure->state = RECV_HELLO;
closure->log_info = log_info; closure->log_info = log_info;
closure->restart = restart;
closure->iolog_id = iolog_id;
closure->bufsize = 10240; closure->bufsize = 10240;
closure->buf = malloc(closure->bufsize); closure->buf = malloc(closure->bufsize);
if (closure->buf == NULL) if (closure->buf == NULL)
@@ -969,10 +1023,10 @@ client_closure_alloc(int sock, struct iolog_info *log_info)
if (closure->write_ev == NULL) if (closure->write_ev == NULL)
goto bad; goto bad;
debug_return_ptr(closure); debug_return_bool(true);
bad: bad:
client_closure_free(closure); client_closure_free(closure);
debug_return_ptr(NULL); debug_return_bool(false);
} }
/* /*
@@ -1003,16 +1057,55 @@ iolog_open_all(const char *iolog_path)
debug_return_bool(true); debug_return_bool(true);
} }
/*
* Parse a timespec on the command line of the form
* seconds[,nanoseconds]
*/
static bool
parse_timespec(struct timespec *ts, const char *strval)
{
long long tv_sec;
long tv_nsec;
char *ep;
debug_decl(parse_timespec, SUDO_DEBUG_UTIL)
errno = 0;
tv_sec = strtoll(strval, &ep, 10);
if (strval == ep || (*ep != ',' && *ep != '\0'))
debug_return_bool(false);
#if TIME_T_MAX != LLONG_MAX
if (tv_sec > TIME_T_MAX)
debug_return_bool(false);
#endif
if (tv_sec < 0 || (errno == ERANGE && tv_sec == LLONG_MAX))
debug_return_bool(false);
strval = ep + 1;
errno = 0;
tv_nsec = strtol(strval, &ep, 10);
if (strval == ep || *ep != '\0')
debug_return_bool(false);
if (tv_nsec < 0 || (errno == ERANGE && tv_nsec == LONG_MAX))
debug_return_bool(false);
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: parsed timespec [%lld, %ld]",
__func__, tv_sec, tv_nsec);
ts->tv_sec = (time_t)tv_sec;
ts->tv_nsec = tv_nsec;
debug_return_bool(true);
}
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
struct client_closure *closure; struct client_closure closure;
struct sudo_event_base *evbase; struct sudo_event_base *evbase;
struct iolog_info *log_info; struct iolog_info *log_info;
const char *host = "localhost"; const char *host = "localhost";
const char *port = DEFAULT_PORT_STR; const char *port = DEFAULT_PORT_STR;
char fname[PATH_MAX]; struct timespec restart = { 0, 0 };
char *iolog_path; char fname[PATH_MAX], *iolog_path;
const char *iolog_id = NULL;
int ch, sock; int ch, sock;
debug_decl_vars(main, SUDO_DEBUG_MAIN) debug_decl_vars(main, SUDO_DEBUG_MAIN)
@@ -1030,14 +1123,21 @@ main(int argc, char *argv[])
if (protobuf_c_version_number() < 1003000) if (protobuf_c_version_number() < 1003000)
sudo_fatalx("Protobuf-C version 1.3 or higher required"); sudo_fatalx("Protobuf-C version 1.3 or higher required");
while ((ch = getopt(argc, argv, "h:p:")) != -1) { while ((ch = getopt(argc, argv, "h:i:p:r:")) != -1) {
switch (ch) { switch (ch) {
case 'h': case 'h':
host = optarg; host = optarg;
break; break;
case 'i':
iolog_id = optarg;
break;
case 'p': case 'p':
port = optarg; port = optarg;
break; break;
case 'r':
if (!parse_timespec(&restart, optarg))
goto bad;
break;
default: default:
usage(); usage();
} }
@@ -1045,6 +1145,11 @@ main(int argc, char *argv[])
argc -= optind; argc -= optind;
argv += optind; argv += optind;
if (sudo_timespecisset(&restart) != (iolog_id != NULL)) {
sudo_warnx("must specify both restart point and iolog ID");
usage();
}
/* Remaining arg should be path to I/O log file to send. */ /* Remaining arg should be path to I/O log file to send. */
if (argc != 1) if (argc != 1)
usage(); usage();
@@ -1071,22 +1176,20 @@ main(int argc, char *argv[])
sudo_fatal(NULL); sudo_fatal(NULL);
sudo_ev_base_setdef(evbase); sudo_ev_base_setdef(evbase);
if ((closure = client_closure_alloc(sock, log_info)) == NULL) if (!client_closure_fill(&closure, sock, &restart, iolog_id, log_info))
goto bad; goto bad;
/* Add read event for the server hello message and enter event loop. */ /* Add read event for the server hello message and enter event loop. */
if (sudo_ev_add(evbase, closure->read_ev, NULL, false) == -1) if (sudo_ev_add(evbase, closure.read_ev, NULL, false) == -1)
goto bad; goto bad;
sudo_ev_dispatch(evbase); sudo_ev_dispatch(evbase);
if (!sudo_timespeccmp(&closure->elapsed, &closure->committed, ==)) { if (closure.state != FINISHED) {
sudo_warnx("commit point mismatch, expected [%lld, %ld], got [%lld, %ld]", sudo_warnx("exited prematurely with state %d", closure.state);
(long long)closure->elapsed.tv_sec, closure->elapsed.tv_nsec, sudo_warnx("elapsed time sent to server [%lld, %ld]",
(long long)closure->committed.tv_sec, closure->committed.tv_nsec); (long long)closure.elapsed.tv_sec, closure.elapsed.tv_nsec);
} sudo_warnx("commit point received from server [%lld, %ld]",
(long long)closure.committed.tv_sec, closure.committed.tv_nsec);
if (closure->state != FINISHED) {
sudo_warnx("exited prematurely with state %d", closure->state);
goto bad; goto bad;
} }
printf("I/O log transmitted successfully\n"); printf("I/O log transmitted successfully\n");

View File

@@ -33,25 +33,10 @@
#define IOFD_TIMING 5 #define IOFD_TIMING 5
#define IOFD_MAX 6 #define IOFD_MAX 6
#if 0
struct timing_closure {
struct timespec delay;
int event;
union {
struct {
int lines;
int columns;
} winsize;
size_t nbytes;
} u;
char *buf;
size_t bufsize;
};
#endif
enum client_state { enum client_state {
ERROR, ERROR,
RECV_HELLO, RECV_HELLO,
SEND_RESTART,
SEND_EXEC, SEND_EXEC,
SEND_IO, SEND_IO,
SEND_EXIT, SEND_EXIT,
@@ -68,6 +53,7 @@ struct connection_buffer {
}; };
struct client_closure { struct client_closure {
struct timespec *restart;
struct timespec elapsed; struct timespec elapsed;
struct timespec committed; struct timespec committed;
struct timing_closure timing; struct timing_closure timing;
@@ -76,7 +62,7 @@ struct client_closure {
struct sudo_event *read_ev; struct sudo_event *read_ev;
struct sudo_event *write_ev; struct sudo_event *write_ev;
struct iolog_info *log_info; struct iolog_info *log_info;
char *iolog_dir; const char *iolog_id;
char *buf; /* XXX */ char *buf; /* XXX */
size_t bufsize; /* XXX */ size_t bufsize; /* XXX */
enum client_state state; enum client_state state;