Add support for libevent-style timed events. Adding a timed event

is currently O(n).  The only consumer of timed events is sudoreplay
which only used a singled one so O(n) == O(1) for now.  This also
allows us to remove the nanosleep compat function as we now use a
timeout event instead.
This commit is contained in:
Todd C. Miller
2013-10-28 10:00:09 -06:00
parent d8b368b503
commit 8861e01d16
16 changed files with 282 additions and 312 deletions

View File

@@ -80,7 +80,6 @@ compat/mksiglist.h
compat/mksigname.c
compat/mksigname.h
compat/mktemp.c
compat/nanosleep.c
compat/nss_dbdefs.h
compat/pw_dup.c
compat/regress/fnmatch/fnm_test.c

View File

@@ -17,6 +17,7 @@
#include <config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
@@ -58,6 +59,7 @@ sudo_ev_base_alloc(void)
base = ecalloc(1, sizeof(*base));
TAILQ_INIT(&base->events);
TAILQ_INIT(&base->timeouts);
if (sudo_ev_base_alloc_impl(base) != 0) {
efree(base);
base = NULL;
@@ -88,6 +90,8 @@ sudo_ev_alloc(int fd, short events, sudo_ev_callback_t callback, void *closure)
struct sudo_event *ev;
debug_decl(sudo_ev_alloc, SUDO_DEBUG_EVENT)
/* XXX - sanity check events value */
ev = ecalloc(1, sizeof(*ev));
ev->fd = fd;
ev->events = events;
@@ -111,11 +115,12 @@ sudo_ev_free(struct sudo_event *ev)
}
int
sudo_ev_add(struct sudo_event_base *base, struct sudo_event *ev, bool tohead)
sudo_ev_add(struct sudo_event_base *base, struct sudo_event *ev,
struct timeval *timo, bool tohead)
{
debug_decl(sudo_ev_add, SUDO_DEBUG_EVENT)
/* Don't add an event twice; revisit if we want to support timeouts. */
/* Only add new events to the events list. */
if (ev->base == NULL) {
if (sudo_ev_add_impl(base, ev) != 0)
debug_return_int(-1);
@@ -125,6 +130,37 @@ sudo_ev_add(struct sudo_event_base *base, struct sudo_event *ev, bool tohead)
} else {
TAILQ_INSERT_TAIL(&base->events, ev, entries);
}
} else {
/* If no base specified, use existing one. */
if (base == NULL)
base = ev->base;
/* If event no longer has a timeout, remove from timeouts queue. */
if (timo == NULL && timevalisset(&ev->timeout)) {
timevalclear(&ev->timeout);
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
}
}
/* Timeouts can be changed for existing events. */
if (timo != NULL) {
struct sudo_event *evtmp;
if (timevalisset(&ev->timeout)) {
/* Remove from timeouts list, then add back. */
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
}
/* Convert to absolute time and insert in sorted order; O(n). */
gettimeofday(&ev->timeout, NULL);
ev->timeout.tv_sec += timo->tv_sec;
ev->timeout.tv_usec += timo->tv_usec;
TAILQ_FOREACH(evtmp, &base->timeouts, timeouts_entries) {
if (timevalcmp(timo, &evtmp->timeout, <))
break;
}
if (evtmp != NULL) {
TAILQ_INSERT_BEFORE(evtmp, ev, timeouts_entries);
} else {
TAILQ_INSERT_TAIL(&base->timeouts, ev, timeouts_entries);
}
}
/* Clear pending delete so adding from callback works properly. */
CLR(ev->flags, SUDO_EV_DELETE);
@@ -162,6 +198,10 @@ sudo_ev_del(struct sudo_event_base *base, struct sudo_event *ev)
/* Unlink from event list. */
TAILQ_REMOVE(&base->events, ev, entries);
/* Unlink from timeouts list. */
if (ISSET(ev->events, SUDO_EV_TIMEOUT) && timevalisset(&ev->timeout))
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
/* Unlink from active list and update base pointers as needed. */
if (ISSET(ev->flags, SUDO_EV_ACTIVE)) {
TAILQ_REMOVE(&base->active, ev, active_entries);
@@ -186,7 +226,9 @@ sudo_ev_del(struct sudo_event_base *base, struct sudo_event *ev)
int
sudo_ev_loop(struct sudo_event_base *base, int flags)
{
int rc;
struct timeval now;
struct sudo_event *ev;
int nready, rc = 0;
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
/*
@@ -195,7 +237,7 @@ sudo_ev_loop(struct sudo_event_base *base, int flags)
* All other base flags are ignored unless we are running events.
*/
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT))
flags |= SUDO_EVLOOP_ONCE;
SET(flags, SUDO_EVLOOP_ONCE);
base->flags = 0;
for (;;) {
@@ -206,12 +248,32 @@ rescan:
break;
}
/* Call backend to setup the active queue. */
/* Call backend to scan for I/O events. */
TAILQ_INIT(&base->active);
rc = sudo_ev_loop_impl(base, flags);
if (rc == -1) {
nready = sudo_ev_scan_impl(base, flags);
switch (nready) {
case -1:
if (errno == EINTR || errno == ENOMEM)
continue;
rc = -1;
goto done;
case 0:
/* Timed out, activate timeout events. */
gettimeofday(&now, NULL);
while ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
if (timevalcmp(&ev->timeout, &now, >))
break;
/* Remove from timeouts list. */
timevalclear(&ev->timeout);
TAILQ_REMOVE(&base->timeouts, ev, timeouts_entries);
/* Make event active. */
ev->revents = SUDO_EV_TIMEOUT;
SET(ev->flags, SUDO_EV_ACTIVE);
TAILQ_INSERT_TAIL(&base->active, ev, active_entries);
}
break;
default:
/* I/O events active, sudo_ev_scan_impl() already added them. */
break;
}
@@ -225,7 +287,7 @@ rescan:
if (!ISSET(base->cur->events, SUDO_EV_PERSIST))
SET(base->cur->flags, SUDO_EV_DELETE);
base->cur->callback(base->cur->fd, base->cur->revents,
base->cur->closure);
base->cur->closure == sudo_ev_self_cbarg() ? base->cur : base->cur->closure);
if (base->cur != NULL) {
CLR(base->cur->flags, SUDO_EV_ACTIVE);
if (ISSET(base->cur->flags, SUDO_EV_DELETE))
@@ -233,21 +295,23 @@ rescan:
}
if (ISSET(base->flags, SUDO_EVBASE_LOOPBREAK)) {
/* stop processing events immediately */
base->flags |= SUDO_EVBASE_GOT_BREAK;
SET(base->flags, SUDO_EVBASE_GOT_BREAK);
base->pending = NULL;
goto done;
}
if (ISSET(base->flags, SUDO_EVBASE_LOOPCONT)) {
/* rescan events and start polling again */
CLR(base->flags, SUDO_EVBASE_LOOPCONT);
if (!ISSET(flags, SUDO_EVLOOP_ONCE)) {
base->pending = NULL;
goto rescan;
}
}
}
base->pending = NULL;
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT)) {
/* exit loop after once through */
base->flags |= SUDO_EVBASE_GOT_EXIT;
SET(base->flags, SUDO_EVBASE_GOT_EXIT);
goto done;
}
if (flags & (SUDO_EVLOOP_ONCE | SUDO_EVLOOP_NONBLOCK))
@@ -262,7 +326,7 @@ void
sudo_ev_loopexit(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopexit, SUDO_DEBUG_EVENT)
base->flags |= SUDO_EVBASE_LOOPEXIT;
SET(base->flags, SUDO_EVBASE_LOOPEXIT);
debug_return;
}
@@ -270,7 +334,7 @@ void
sudo_ev_loopbreak(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopbreak, SUDO_DEBUG_EVENT)
base->flags |= SUDO_EVBASE_LOOPBREAK;
SET(base->flags, SUDO_EVBASE_LOOPBREAK);
debug_return;
}
@@ -278,7 +342,7 @@ void
sudo_ev_loopcontinue(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopcontinue, SUDO_DEBUG_EVENT)
base->flags |= SUDO_EVBASE_LOOPCONT;
SET(base->flags, SUDO_EVBASE_LOOPCONT);
debug_return;
}

View File

@@ -17,6 +17,7 @@
#include <config.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
@@ -133,22 +134,33 @@ sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
int
sudo_ev_loop_impl(struct sudo_event_base *base, int flags)
{
const int timeout = (flags & SUDO_EVLOOP_NONBLOCK) ? 0 : -1;
struct sudo_event *ev;
int nready;
int nready, timeout;
struct timeval now;
debug_decl(sudo_ev_loop_impl, SUDO_DEBUG_EVENT)
if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
struct timeval *timo = &ev->timeout;
gettimeofday(&now, NULL);
timeout = ((timo->tv_sec - now.tv_sec) * 1000) +
((timo->tv_usec - now.tv_usec) / 1000);
if (timeout <= 0)
debug_return_int(0);
} else {
timeout = (flags & SUDO_EVLOOP_NONBLOCK) ? 0 : -1;
}
nready = poll(base->pfds, base->pfd_high + 1, timeout);
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
switch (nready) {
case -1:
/* error or interrupted by signal */
/* Error or interrupted by signal. */
debug_return_int(-1);
case 0:
/* timeout or no events */
/* Front end will activate timeout events. */
break;
default:
/* Activate each event that fired. */
/* Activate each I/O event that fired. */
TAILQ_FOREACH(ev, &base->events, entries) {
if (ev->pfd_idx != -1 && base->pfds[ev->pfd_idx].revents) {
int what = 0;

View File

@@ -16,9 +16,10 @@
#include <config.h>
#include <sys/types.h>
#include <sys/param.h> /* for howmany() on Linux */
#include <sys/time.h>
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h>
# include <sys/sysmacros.h> /* for howmany() on Solaris */
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
@@ -108,20 +109,28 @@ sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
}
int
sudo_ev_loop_impl(struct sudo_event_base *base, int flags)
sudo_ev_scan_impl(struct sudo_event_base *base, int flags)
{
struct timeval tv, *timeout;
struct timeval now, tv, *timeout;
struct sudo_event *ev;
int nready, highfd = 0;
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) {
gettimeofday(&now, NULL);
tv = ev->timeout;
timevalsub(&tv, &now);
if (tv.tv_sec < 0 || tv.tv_usec < 0)
debug_return_int(0);
timeout = &tv;
} else {
if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
tv.tv_sec = 0;
tv.tv_usec = 0;
timevalclear(&tv);
timeout = &tv;
} else {
timeout = NULL;
}
}
/* For select we need to redo readfds and writefds each time. */
memset(base->readfds, 0, howmany(base->maxfd + 1, NFDBITS) * sizeof(fd_mask));
@@ -148,13 +157,13 @@ sudo_ev_loop_impl(struct sudo_event_base *base, int flags)
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready);
switch (nready) {
case -1:
/* error or interrupted by signal */
/* Error or interrupted by signal. */
debug_return_int(-1);
case 0:
/* timeout or no events */
/* Front end will activate timeout events. */
break;
default:
/* Activate each event that fired. */
/* Activate each I/O event that fired. */
TAILQ_FOREACH(ev, &base->events, entries) {
int what = 0;
if (FD_ISSET(ev->fd, base->readfds))
@@ -170,5 +179,5 @@ sudo_ev_loop_impl(struct sudo_event_base *base, int flags)
}
break;
}
debug_return_int(0);
debug_return_int(nready);
}

View File

@@ -209,9 +209,6 @@ mksigname.lo: $(srcdir)/mksigname.c $(top_builddir)/config.h \
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mksigname.c
mktemp.lo: $(srcdir)/mktemp.c $(top_builddir)/config.h $(incdir)/missing.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/mktemp.c
nanosleep.lo: $(srcdir)/nanosleep.c $(top_builddir)/config.h \
$(top_srcdir)/compat/timespec.h $(incdir)/missing.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/nanosleep.c
pw_dup.lo: $(srcdir)/pw_dup.c $(top_builddir)/config.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/pw_dup.c
sig2str.lo: $(srcdir)/sig2str.c $(top_builddir)/config.h $(incdir)/missing.h

View File

@@ -1,57 +0,0 @@
/*
* Copyright (c) 2009-2011, 2013 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#ifndef HAVE_NANOSLEEP
#include <sys/types.h>
#include <sys/time.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#if TIME_WITH_SYS_TIME
# include <time.h>
#endif
#ifndef HAVE_STRUCT_TIMESPEC
# include "compat/timespec.h"
#endif
#include <errno.h>
#include "missing.h"
int
nanosleep(const struct timespec *ts, struct timespec *rts)
{
struct timeval timeout, endtime, now;
int rval;
timeout.tv_sec = ts->tv_sec;
timeout.tv_usec = ts->tv_nsec / 1000;
if (rts != NULL) {
gettimeofday(&endtime, NULL);
timevaladd(&endtime, &timeout);
}
rval = select(0, NULL, NULL, NULL, &timeout);
if (rts != NULL && rval == -1 && errno == EINTR) {
gettimeofday(&now, NULL);
timevalsub(&endtime, &now);
rts->tv_sec = endtime.tv_sec;
rts->tv_nsec = endtime.tv_usec * 1000;
}
return rval;
}
#endif /* HAVE_NANOSLEEP */

View File

@@ -397,9 +397,6 @@
/* Define to 1 if you have the <mps/ldap_ssl.h> header file. */
#undef HAVE_MPS_LDAP_SSL_H
/* Define to 1 if you have the `nanosleep' function. */
#undef HAVE_NANOSLEEP
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H

62
configure vendored
View File

@@ -17653,68 +17653,6 @@ esac
fi
for ac_func in nanosleep
do :
ac_fn_c_check_func "$LINENO" "nanosleep" "ac_cv_func_nanosleep"
if test "x$ac_cv_func_nanosleep" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_NANOSLEEP 1
_ACEOF
else
# On Solaris, nanosleep is in librt
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for nanosleep in -lrt" >&5
$as_echo_n "checking for nanosleep in -lrt... " >&6; }
if ${ac_cv_lib_rt_nanosleep+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
LIBS="-lrt $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
builtin and then its argument prototype would still apply. */
#ifdef __cplusplus
extern "C"
#endif
char nanosleep ();
int
main ()
{
return nanosleep ();
;
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
ac_cv_lib_rt_nanosleep=yes
else
ac_cv_lib_rt_nanosleep=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_rt_nanosleep" >&5
$as_echo "$ac_cv_lib_rt_nanosleep" >&6; }
if test "x$ac_cv_lib_rt_nanosleep" = xyes; then :
REPLAY_LIBS="${REPLAY_LIBS} -lrt"
else
case " $LIBOBJS " in
*" nanosleep.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS nanosleep.$ac_objext"
;;
esac
fi
fi
done
for ac_func in getopt_long
do :
ac_fn_c_check_func "$LINENO" "getopt_long" "ac_cv_func_getopt_long"

View File

@@ -2370,10 +2370,6 @@ SUDO_FUNC_FNMATCH([AC_DEFINE(HAVE_FNMATCH)], [AC_LIBOBJ(fnmatch)
])
SUDO_FUNC_ISBLANK
AC_REPLACE_FUNCS(memrchr memset_s pw_dup strlcpy strlcat)
AC_CHECK_FUNCS(nanosleep, [], [
# On Solaris, nanosleep is in librt
AC_CHECK_LIB(rt, nanosleep, [REPLAY_LIBS="${REPLAY_LIBS} -lrt"], [AC_LIBOBJ(nanosleep)])
])
AC_CHECK_FUNCS(getopt_long, [], [AC_LIBOBJ(getopt_long)
AC_MSG_CHECKING([for optreset])
AC_CACHE_VAL(sudo_cv_optreset, [

View File

@@ -186,13 +186,6 @@
#undef ISSET
#define ISSET(t, f) ((t) & (f))
/*
* Some systems define this in <sys/param.h> but we don't include that anymore.
*/
#ifndef howmany
# define howmany(x, y) (((x) + ((y) - 1)) / (y))
#endif
/*
* Older systems may be missing stddef.h and/or offsetof macro
*/
@@ -413,9 +406,6 @@ char *mkdtemp(char *);
#ifndef HAVE_MKSTEMPS
int mkstemps(char *, int);
#endif
#ifndef HAVE_NANOSLEEP
int nanosleep(const struct timespec *, struct timespec *);
#endif
#ifndef HAVE_PW_DUP
struct passwd *pw_dup(const struct passwd *);
#endif

View File

@@ -20,9 +20,10 @@
#include "queue.h"
/* Event types */
#define SUDO_EV_READ 0x01 /* fire when readable */
#define SUDO_EV_WRITE 0x02 /* fire when writable */
#define SUDO_EV_PERSIST 0x04 /* persist until deleted */
#define SUDO_EV_TIMEOUT 0x01 /* fire after timeout */
#define SUDO_EV_READ 0x02 /* fire when readable */
#define SUDO_EV_WRITE 0x04 /* fire when writable */
#define SUDO_EV_PERSIST 0x08 /* persist until deleted */
/* Event flags (internal) */
#define SUDO_EV_ACTIVE 0x01 /* event is on the active queue */
@@ -46,6 +47,7 @@ typedef void (*sudo_ev_callback_t)(int fd, int what, void *closure);
struct sudo_event {
TAILQ_ENTRY(sudo_event) entries;
TAILQ_ENTRY(sudo_event) active_entries;
TAILQ_ENTRY(sudo_event) timeouts_entries;
struct sudo_event_base *base; /* base this event belongs to */
int fd; /* fd we are interested in */
short events; /* SUDO_EV_* flags (in) */
@@ -53,6 +55,7 @@ struct sudo_event {
short flags; /* internal event flags */
short pfd_idx; /* index into pfds array (XXX) */
sudo_ev_callback_t callback;/* user-provided callback */
struct timeval timeout; /* for SUDO_EV_TIMEOUT */
void *closure; /* user-provided data pointer */
};
@@ -61,6 +64,7 @@ TAILQ_HEAD(sudo_event_list, sudo_event);
struct sudo_event_base {
struct sudo_event_list events; /* tail queue of all events */
struct sudo_event_list active; /* tail queue of active events */
struct sudo_event_list timeouts; /* tail queue of timeout events */
struct sudo_event *cur; /* current active event being serviced */
struct sudo_event *pending; /* next active event to be serviced */
#ifdef HAVE_POLL
@@ -89,7 +93,7 @@ struct sudo_event *sudo_ev_alloc(int fd, short events, sudo_ev_callback_t callba
void sudo_ev_free(struct sudo_event *ev);
/* Add an event, returns 0 on success, -1 on error */
int sudo_ev_add(struct sudo_event_base *head, struct sudo_event *ev, bool tohead);
int sudo_ev_add(struct sudo_event_base *head, struct sudo_event *ev, struct timeval *timo, bool tohead);
/* Delete an event, returns 0 on success, -1 on error */
int sudo_ev_del(struct sudo_event_base *head, struct sudo_event *ev);
@@ -115,9 +119,16 @@ bool sudo_ev_got_break(struct sudo_event_base *base);
/* Return the fd associated with an event. */
#define sudo_ev_get_fd(_ev) ((_ev) ? (_ev)->fd : -1)
/* Return the (absolute) timeout associated with an event or NULL. */
#define sudo_ev_get_timeout(_ev) \
(((_ev) && timevalisset(&(_ev)->timeout)) ? &(_ev)->timeout : NULL)
/* Return the base an event is associated with or NULL. */
#define sudo_ev_get_base(_ev) ((_ev) ? (_ev)->base : NULL)
/* Magic pointer value to use self pointer as callback arg. */
#define sudo_ev_self_cbarg() ((void *)-1)
/*
* Backend implementation.
*/
@@ -125,6 +136,6 @@ int sudo_ev_base_alloc_impl(struct sudo_event_base *base);
void sudo_ev_base_free_impl(struct sudo_event_base *base);
int sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev);
int sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev);
int sudo_ev_loop_impl(struct sudo_event_base *base, int flags);
int sudo_ev_scan_impl(struct sudo_event_base *base, int flags);
#endif /* _SUDO_EVENT_H */

View File

@@ -70,7 +70,7 @@ sub mkdep {
$makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo sssd.lo:;
# XXX - fill in AUTH_OBJS from contents of the auth dir instead
$makefile =~ s:\@AUTH_OBJS\@:afs.lo aix_auth.lo bsdauth.lo dce.lo fwtk.lo getspwuid.lo kerb5.lo pam.lo passwd.lo rfc1938.lo secureware.lo securid5.lo sia.lo:;
$makefile =~ s:\@LTLIBOBJS\@:closefrom.lo dlopen.lo fnmatch.lo getcwd.lo getgrouplist.lo getline.lo getprogname.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo nanosleep.lo pw_dup.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo utimes.lo globtest.o fnm_test.o:;
$makefile =~ s:\@LTLIBOBJS\@:closefrom.lo dlopen.lo fnmatch.lo getcwd.lo getgrouplist.lo getline.lo getprogname.lo getopt_long.lo glob.lo isblank.lo memrchr.lo memset_s.lo mksiglist.lo mksigname.lo mktemp.lo pw_dup.lo sig2str.lo siglist.lo signame.lo snprintf.lo strlcat.lo strlcpy.lo strsignal.lo utimes.lo globtest.o fnm_test.o:;
# Parse OBJS lines
my %objs;

View File

@@ -840,7 +840,8 @@ sudoreplay.o: $(srcdir)/sudoreplay.c $(top_builddir)/config.h \
$(incdir)/missing.h $(incdir)/alloc.h $(incdir)/fatal.h \
$(incdir)/gettext.h $(srcdir)/logging.h $(srcdir)/iolog.h \
$(incdir)/queue.h $(incdir)/sudo_plugin.h $(incdir)/sudo_conf.h \
$(incdir)/queue.h $(incdir)/sudo_debug.h
$(incdir)/queue.h $(incdir)/sudo_debug.h $(incdir)/sudo_event.h \
$(incdir)/queue.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sudoreplay.c
testsudoers.o: $(srcdir)/testsudoers.c $(top_builddir)/config.h \
$(top_srcdir)/compat/fnmatch.h $(srcdir)/tsgetgrpw.h \

View File

@@ -18,16 +18,10 @@
#include <sys/types.h>
#include <sys/uio.h>
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h>
#endif
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
@@ -105,6 +99,7 @@
#include "sudo_plugin.h"
#include "sudo_conf.h"
#include "sudo_debug.h"
#include "sudo_event.h"
#ifndef LINE_MAX
# define LINE_MAX 2048
@@ -125,6 +120,14 @@ struct log_info {
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:
* ( user millert or user root ) and tty console and command /bin/sh
@@ -162,7 +165,9 @@ struct search_node {
static struct search_node_list search_expr = STAILQ_HEAD_INITIALIZER(search_expr);
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;
@@ -186,17 +191,16 @@ extern void get_ttysize(int *rowp, int *colp);
static int list_sessions(int, char **, const char *, const char *, const char *);
static int parse_expr(struct search_node_list *, char **, bool);
static void check_input(int, double *);
static void delay(double);
static void check_input(int fd, int what, void *v);
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 parse_timing(const char *buf, const char *decimal, int *idx, double *seconds, size_t *nbytes);
static struct log_info *parse_logfile(char *logfile);
static void free_log_info(struct log_info *li);
static ssize_t atomic_writev(int fd, struct iovec *iov, int iovcnt);
static void sudoreplay_handler(int);
static void sudoreplay_cleanup(void);
static void write_output(int fd, int what, void *v);
#ifdef HAVE_REGCOMP
# define REGEX_T regex_t
@@ -228,12 +232,16 @@ main(int argc, char *argv[])
bool def_filter = true;
const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL;
char path[PATH_MAX], buf[LINE_MAX], *cp, *ep;
double seconds, to_wait, speed = 1.0, max_wait = 0;
double seconds, to_wait, max_wait = 0;
struct sudo_event_base *evbase;
struct sudo_event *input_ev, *output_ev;
struct timeval timeout;
sigaction_t sa;
size_t len, nbytes, nread;
struct log_info *li;
struct iovec *iov = NULL;
int iovcnt = 0, iovmax = 0;
unsigned int i, iovcnt = 0, iovmax = 0;
struct write_closure wc;
debug_decl(main, SUDO_DEBUG_MAIN)
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
@@ -291,7 +299,7 @@ main(int argc, char *argv[])
break;
case 's':
errno = 0;
speed = strtod(optarg, &ep);
speed_factor = strtod(optarg, &ep);
if (*ep != '\0' || errno != 0)
fatalx(_("invalid speed factor: %s"), optarg);
break;
@@ -392,6 +400,18 @@ main(int argc, char *argv[])
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"
*/
@@ -405,14 +425,18 @@ main(int argc, char *argv[])
if (!parse_timing(buf, decimal, &idx, &seconds, &nbytes))
fatalx(_("invalid timing file line: %s"), buf);
if (interactive)
check_input(STDIN_FILENO, &speed);
/* 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)
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. */
if (io_log_files[idx].fd.v == NULL)
@@ -423,6 +447,7 @@ main(int argc, char *argv[])
need_nlcr = (idx == IOFD_STDOUT || idx == IOFD_STDERR);
/* All output is sent to stdout. */
/* XXX - assumes no wall clock time spent writing output. */
while (nbytes != 0) {
if (nbytes > sizeof(buf))
len = sizeof(buf);
@@ -485,8 +510,19 @@ main(int argc, char *argv[])
iov[0].iov_len = nread;
iovcnt = 1;
}
if (atomic_writev(STDOUT_FILENO, iov, iovcnt) == -1)
fatal(_("writing to standard output"));
/* Setup closure for write_output. */
memset(&wc, 0, sizeof(wc));
wc.wevent = output_ev;
wc.iov = iov;
wc.iovcnt = iovcnt;
for (i = 0; i < iovcnt; i++)
wc.nbytes += iov[i].iov_len;
/* Run event event loop to write output. */
/* XXX - should use a single event loop with a circular buffer. */
sudo_ev_add(evbase, output_ev, NULL, false);
sudo_ev_loop(evbase, 0);
}
}
term_restore(STDIN_FILENO, 1);
@@ -495,31 +531,6 @@ done:
exit(exitcode);
}
static void
delay(double secs)
{
struct timespec ts, rts;
int rval;
/*
* Typical max resolution is 1/HZ but we can't portably check that.
* If the interval is small enough, just ignore it.
*/
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);
}
}
static int
open_io_fd(char *path, int len, struct io_log_file *iol)
{
@@ -538,67 +549,55 @@ open_io_fd(char *path, int len, struct io_log_file *iol)
debug_return_int(iol->fd.v ? 0 : -1);
}
/*
* Call writev(), restarting as needed and handling EAGAIN since
* fd may be in non-blocking mode.
*/
static ssize_t
atomic_writev(int fd, struct iovec *iov, int iovcnt)
static void
write_output(int fd, int what, void *v)
{
ssize_t n, nwritten = 0;
size_t count, remainder, nbytes = 0;
int i;
debug_decl(atomic_writev, SUDO_DEBUG_UTIL)
struct write_closure *wc = v;
ssize_t nwritten;
size_t count, remainder;
unsigned int i;
debug_decl(write_output, SUDO_DEBUG_UTIL)
for (i = 0; i < iovcnt; i++)
nbytes += iov[i].iov_len;
for (;;) {
n = writev(STDOUT_FILENO, iov, iovcnt);
if (n > 0) {
nwritten += n;
remainder = nbytes - nwritten;
if (remainder == 0)
nwritten = writev(STDOUT_FILENO, wc->iov, wc->iovcnt);
switch (nwritten) {
case -1:
if (errno != EINTR && errno != EAGAIN)
fatal(_("unable to write to %s"), "stdout");
break;
/* short writev, adjust iov and do the rest. */
case 0:
break;
default:
remainder = wc->nbytes - nwritten;
if (remainder == 0) {
/* writev completed */
debug_return;
}
/* short writev, adjust iov so we can write the remainder. */
count = 0;
i = iovcnt;
i = wc->iovcnt;
while (i--) {
count += iov[i].iov_len;
count += wc->iov[i].iov_len;
if (count == remainder) {
iov += i;
iovcnt -= i;
wc->iov += i;
wc->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;
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;
}
}
continue;
}
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;
}
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;
}
/*
@@ -1051,44 +1050,58 @@ list_sessions(int argc, char **argv, const char *pattern, const char *user,
* pause, slow, fast
*/
static void
check_input(int ttyfd, double *speed)
check_input(int fd, int what, void *v)
{
fd_set *fdsr;
int nready, paused = 0;
struct timeval tv;
struct sudo_event *ev = v;
struct sudo_event_base *evbase = sudo_ev_get_base(ev);
struct timeval tv, *timeout = NULL;
static bool paused = 0;
char ch;
ssize_t n;
debug_decl(check_input, SUDO_DEBUG_UTIL)
fdsr = ecalloc(howmany(ttyfd + 1, NFDBITS), sizeof(fd_mask));
for (;;) {
FD_SET(ttyfd, fdsr);
tv.tv_sec = 0;
tv.tv_usec = 0;
nready = select(ttyfd + 1, fdsr, NULL, NULL, paused ? NULL : &tv);
if (nready != 1)
if (ISSET(what, SUDO_EV_READ)) {
switch (read(fd, &ch, 1)) {
case -1:
if (errno != EINTR && errno != EAGAIN)
fatal(_("unable to read %s"), "stdin");
break;
n = read(ttyfd, &ch, 1);
if (n == 1) {
case 0:
/* Ignore EOF. */
break;
case 1:
if (paused) {
paused = 0;
continue;
/* Any key will unpause, event is finished. */
/* XXX - pause time could be less than timeout */
paused = false;
debug_return;
}
switch (ch) {
case ' ':
paused = 1;
paused = true;
break;
case '<':
*speed /= 2;
speed_factor /= 2;
break;
case '>':
*speed *= 2;
speed_factor *= 2;
break;
}
break;
}
if (!paused) {
/* Determine remaining timeout, if any. */
timeout = sudo_ev_get_timeout(ev);
if (timeout != NULL) {
struct timeval now;
gettimeofday(&now, NULL);
tv = *timeout;
timevalsub(&tv, &now);
timeout = &tv;
}
}
free(fdsr);
/* Re-enable event. */
sudo_ev_add(evbase, ev, timeout, false);
}
debug_return;
}

View File

@@ -305,7 +305,7 @@ exec_event_setup(int backchannel, struct exec_closure *ec)
SUDO_EV_READ|SUDO_EV_PERSIST, signal_pipe_cb, ec);
if (signal_event == NULL)
fatal(NULL);
if (sudo_ev_add(evbase, signal_event, false) == -1)
if (sudo_ev_add(evbase, signal_event, NULL, false) == -1)
fatal(_("unable to add event to queue"));
/* Event for command status via backchannel. */
@@ -313,7 +313,7 @@ exec_event_setup(int backchannel, struct exec_closure *ec)
SUDO_EV_READ|SUDO_EV_PERSIST, backchannel_cb, ec);
if (backchannel_event == NULL)
fatal(NULL);
if (sudo_ev_add(evbase, backchannel_event, false) == -1)
if (sudo_ev_add(evbase, backchannel_event, NULL, false) == -1)
fatal(_("unable to add event to queue"));
/* The signal forwarding event gets added on demand. */
@@ -837,7 +837,7 @@ schedule_signal(struct sudo_event_base *evbase, int signo)
sigfwd->signo = signo;
TAILQ_INSERT_TAIL(&sigfwd_list, sigfwd, entries);
if (sudo_ev_add(evbase, sigfwd_event, true) == -1)
if (sudo_ev_add(evbase, sigfwd_event, NULL, true) == -1)
fatal(_("unable to add event to queue"));
debug_return;

View File

@@ -503,12 +503,12 @@ io_callback(int fd, int what, void *v)
/* Enable writer if not /dev/tty or we are foreground pgrp. */
if (iob->wevent != NULL &&
(foreground || !USERTTY_EVENT(iob->wevent))) {
if (sudo_ev_add(evbase, iob->wevent, false) == -1)
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
/* Re-enable reader if buffer is not full. */
if (iob->len != sizeof(iob->buf)) {
if (sudo_ev_add(evbase, iob->revent, false) == -1)
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
break;
@@ -566,14 +566,14 @@ io_callback(int fd, int what, void *v)
}
/* Re-enable writer if buffer is not empty. */
if (iob->len > iob->off) {
if (sudo_ev_add(evbase, iob->wevent, false) == -1)
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
/* Enable reader if buffer is not full. */
if (iob->revent != NULL &&
(ttymode == TERM_RAW || !USERTTY_EVENT(iob->revent))) {
if (iob->len != sizeof(iob->buf)) {
if (sudo_ev_add(evbase, iob->revent, false) == -1)
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
}
@@ -866,7 +866,7 @@ add_io_events(struct sudo_event_base *evbase)
sudo_debug_printf(SUDO_DEBUG_INFO,
"added I/O revent %p, fd %d, events %d",
iob->revent, iob->revent->fd, iob->revent->events);
if (sudo_ev_add(evbase, iob->revent, false) == -1)
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
}
@@ -876,7 +876,7 @@ add_io_events(struct sudo_event_base *evbase)
sudo_debug_printf(SUDO_DEBUG_INFO,
"added I/O wevent %p, fd %d, events %d",
iob->wevent, iob->wevent->fd, iob->wevent->events);
if (sudo_ev_add(evbase, iob->wevent, false) == -1)
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
}
@@ -921,14 +921,14 @@ del_io_events(void)
/* Don't read from /dev/tty while flushing. */
if (iob->revent != NULL && !USERTTY_EVENT(iob->revent)) {
if (iob->len != sizeof(iob->buf)) {
if (sudo_ev_add(evbase, iob->revent, false) == -1)
if (sudo_ev_add(evbase, iob->revent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
}
/* Flush any write buffers with data in them. */
if (iob->wevent != NULL) {
if (iob->len > iob->off) {
if (sudo_ev_add(evbase, iob->wevent, false) == -1)
if (sudo_ev_add(evbase, iob->wevent, NULL, false) == -1)
fatal(_("unable to add event to queue"));
}
}
@@ -1330,21 +1330,21 @@ exec_monitor(struct command_details *details, int backchannel)
SUDO_EV_READ|SUDO_EV_PERSIST, mon_signal_pipe_cb, &mc);
if (mc.signal_pipe_event == NULL)
fatal(NULL);
if (sudo_ev_add(evbase, mc.signal_pipe_event, false) == -1)
if (sudo_ev_add(evbase, mc.signal_pipe_event, NULL, false) == -1)
fatal(_("unable to add event to queue"));
mc.errpipe_event = sudo_ev_alloc(errpipe[0],
SUDO_EV_READ|SUDO_EV_PERSIST, mon_errpipe_cb, &mc);
if (mc.errpipe_event == NULL)
fatal(NULL);
if (sudo_ev_add(evbase, mc.errpipe_event, false) == -1)
if (sudo_ev_add(evbase, mc.errpipe_event, NULL, false) == -1)
fatal(_("unable to add event to queue"));
mc.backchannel_event = sudo_ev_alloc(backchannel,
SUDO_EV_READ|SUDO_EV_PERSIST, mon_backchannel_cb, &mc);
if (mc.backchannel_event == NULL)
fatal(NULL);
if (sudo_ev_add(evbase, mc.backchannel_event, false) == -1)
if (sudo_ev_add(evbase, mc.backchannel_event, NULL, false) == -1)
fatal(_("unable to add event to queue"));
/*