From: Todd C. Miller Date: Sun, 26 Aug 2018 03:02:05 +0000 (-0600) Subject: Use struct timespec, not struct timeval in the event subsystem. X-Git-Tag: SUDO_1_8_25^2~26 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=04d1f56d904e9b5cab153807557fd973d5b70708;p=sudo Use struct timespec, not struct timeval in the event subsystem. Use ppoll() or pselect() if avaialble which use timespec. --- diff --git a/config.h.in b/config.h.in index 710af3ac4..920e84118 100644 --- a/config.h.in +++ b/config.h.in @@ -575,6 +575,9 @@ /* Define to 1 if you have the `posix_spawnp' function. */ #undef HAVE_POSIX_SPAWNP +/* Define to 1 if you have the `ppoll' function. */ +#undef HAVE_PPOLL + /* Define to 1 if you have the `pread' function. */ #undef HAVE_PREAD @@ -590,6 +593,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_PROJECT_H +/* Define to 1 if you have the `pselect' function. */ +#undef HAVE_PSELECT + /* Define to 1 if you have the `pstat_getproc' function. */ #undef HAVE_PSTAT_GETPROC diff --git a/configure b/configure index af2560f06..e78bf5f53 100755 --- a/configure +++ b/configure @@ -24602,26 +24602,50 @@ done fi if test X"$enable_poll" = X""; then - for ac_func in poll + for ac_func in ppoll poll do : - ac_fn_c_check_func "$LINENO" "poll" "ac_cv_func_poll" -if test "x$ac_cv_func_poll" = xyes; then : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_POLL 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF - enable_poll=yes + enable_poll=yes; break else enable_poll=no fi done elif test X"$enable_poll" = X"yes"; then - $as_echo "#define HAVE_POLL 1" >>confdefs.h + for ac_func in ppoll +do : + ac_fn_c_check_func "$LINENO" "ppoll" "ac_cv_func_ppoll" +if test "x$ac_cv_func_ppoll" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PPOLL 1 +_ACEOF + +else + $as_echo "#define HAVE_POLL 1" >>confdefs.h + +fi +done fi if test "$enable_poll" = "yes"; then COMMON_OBJS="${COMMON_OBJS} event_poll.lo" else + for ac_func in pselect +do : + ac_fn_c_check_func "$LINENO" "pselect" "ac_cv_func_pselect" +if test "x$ac_cv_func_pselect" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_PSELECT 1 +_ACEOF + +fi +done + COMMON_OBJS="${COMMON_OBJS} event_select.lo" fi diff --git a/configure.ac b/configure.ac index 04242579e..0b303fccc 100644 --- a/configure.ac +++ b/configure.ac @@ -3778,13 +3778,14 @@ dnl dnl Choose event subsystem backend: poll or select dnl if test X"$enable_poll" = X""; then - AC_CHECK_FUNCS([poll], [enable_poll=yes], [enable_poll=no]) + AC_CHECK_FUNCS([ppoll poll], [enable_poll=yes; break], [enable_poll=no]) elif test X"$enable_poll" = X"yes"; then - AC_DEFINE(HAVE_POLL) + AC_CHECK_FUNCS([ppoll], [], AC_DEFINE(HAVE_POLL)) fi if test "$enable_poll" = "yes"; then COMMON_OBJS="${COMMON_OBJS} event_poll.lo" else + AC_CHECK_FUNCS([pselect]) COMMON_OBJS="${COMMON_OBJS} event_select.lo" fi diff --git a/include/sudo_event.h b/include/sudo_event.h index c60c7d104..20df2be48 100644 --- a/include/sudo_event.h +++ b/include/sudo_event.h @@ -70,7 +70,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 */ + struct timespec timeout; /* for SUDO_EV_TIMEOUT */ void *closure; /* user-provided data pointer */ }; TAILQ_HEAD(sudo_event_list, sudo_event); @@ -87,7 +87,7 @@ struct sudo_event_base { sig_atomic_t signal_caught; /* at least one signal caught */ int num_handlers; /* number of installed handlers */ int signal_pipe[2]; /* so we can wake up on singal */ -#ifdef HAVE_POLL +#if defined(HAVE_POLL) || defined(HAVE_PPOLL) struct pollfd *pfds; /* array of struct pollfd */ int pfd_max; /* size of the pfds array */ int pfd_high; /* highest slot used */ @@ -124,7 +124,7 @@ __dso_public void sudo_ev_free_v1(struct sudo_event *ev); #define sudo_ev_free(_a) sudo_ev_free_v1((_a)) /* Add an event, returns 0 on success, -1 on error */ -__dso_public int sudo_ev_add_v1(struct sudo_event_base *head, struct sudo_event *ev, struct timeval *timo, bool tohead); +__dso_public int sudo_ev_add_v1(struct sudo_event_base *head, struct sudo_event *ev, struct timespec *timo, bool tohead); #define sudo_ev_add(_a, _b, _c, _d) sudo_ev_add_v1((_a), (_b), (_c), (_d)) /* Delete an event, returns 0 on success, -1 on error */ @@ -140,7 +140,7 @@ __dso_public int sudo_ev_loop_v1(struct sudo_event_base *head, int flags); #define sudo_ev_loop(_a, _b) sudo_ev_loop_v1((_a), (_b)) /* Return the remaining timeout associated with an event. */ -__dso_public int sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timeval *tv); +__dso_public int sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timespec *tv); #define sudo_ev_get_timeleft(_a, _b) sudo_ev_get_timeleft_v1((_a), (_b)) /* Cause the event loop to exit after one run through. */ diff --git a/lib/util/event.c b/lib/util/event.c index 30e6e319e..568232f61 100644 --- a/lib/util/event.c +++ b/lib/util/event.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #ifdef HAVE_STDBOOL_H @@ -33,6 +32,7 @@ #endif /* HAVE_STRINGS_H */ #include #include +#include #include #include "sudo_compat.h" @@ -392,11 +392,11 @@ sudo_ev_add_signal(struct sudo_event_base *base, struct sudo_event *ev, /* Install signal handler as needed, saving the original value. */ if (TAILQ_EMPTY(&base->signals[signo])) { struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sigfillset(&sa.sa_mask); - sa.sa_flags = SA_RESTART|SA_SIGINFO; - sa.sa_sigaction = sudo_ev_handler; - if (sigaction(signo, &sa, base->orig_handlers[signo]) != 0) { + memset(&sa, 0, sizeof(sa)); + sigfillset(&sa.sa_mask); + sa.sa_flags = SA_RESTART|SA_SIGINFO; + sa.sa_sigaction = sudo_ev_handler; + if (sigaction(signo, &sa, base->orig_handlers[signo]) != 0) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "%s: unable to install handler for signo %d", __func__, signo); debug_return_int(-1); @@ -429,7 +429,7 @@ sudo_ev_add_signal(struct sudo_event_base *base, struct sudo_event *ev, int sudo_ev_add_v1(struct sudo_event_base *base, struct sudo_event *ev, - struct timeval *timo, bool tohead) + struct timespec *timo, bool tohead) { debug_decl(sudo_ev_add, SUDO_DEBUG_EVENT) @@ -484,11 +484,11 @@ sudo_ev_add_v1(struct sudo_event_base *base, struct sudo_event *ev, 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; + /* XXX - use monotime */ + sudo_gettime_real(&ev->timeout); + sudo_timespecadd(&ev->timeout, timo, &ev->timeout); TAILQ_FOREACH(evtmp, &base->timeouts, timeouts_entries) { - if (sudo_timevalcmp(timo, &evtmp->timeout, <)) + if (sudo_timespeccmp(timo, &evtmp->timeout, <)) break; } if (evtmp != NULL) { @@ -596,7 +596,7 @@ sudo_ev_dispatch_v1(struct sudo_event_base *base) int sudo_ev_loop_v1(struct sudo_event_base *base, int flags) { - struct timeval now; + struct timespec now; struct sudo_event *ev; int nready, rc = 0; debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT) @@ -637,9 +637,9 @@ rescan: goto done; case 0: /* Timed out, activate timeout events. */ - gettimeofday(&now, NULL); + sudo_gettime_real(&now); while ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) { - if (sudo_timevalcmp(&ev->timeout, &now, >)) + if (sudo_timespeccmp(&ev->timeout, &now, >)) break; /* Remove from timeouts list. */ CLR(ev->flags, SUDO_EVQ_TIMEOUTS); @@ -749,19 +749,19 @@ sudo_ev_got_break_v1(struct sudo_event_base *base) } int -sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timeval *tv) +sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timespec *ts) { - struct timeval now; + struct timespec now; debug_decl(sudo_ev_get_timeleft, SUDO_DEBUG_EVENT) if (!ISSET(ev->flags, SUDO_EVQ_TIMEOUTS)) { - sudo_timevalclear(tv); + sudo_timespecclear(ts); debug_return_int(-1); } - gettimeofday(&now, NULL); - sudo_timevalsub(&ev->timeout, &now, tv); - if (tv->tv_sec < 0 || (tv->tv_sec == 0 && tv->tv_usec < 0)) - sudo_timevalclear(tv); + sudo_gettime_real(&now); + sudo_timespecsub(&ev->timeout, &now, ts); + if (ts->tv_sec < 0 || (ts->tv_sec == 0 && ts->tv_nsec < 0)) + sudo_timespecclear(ts); debug_return_int(0); } diff --git a/lib/util/event_poll.c b/lib/util/event_poll.c index b2b82c073..484f949eb 100644 --- a/lib/util/event_poll.c +++ b/lib/util/event_poll.c @@ -17,7 +17,6 @@ #include #include -#include #include #include #ifdef HAVE_STDBOOL_H @@ -31,11 +30,13 @@ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ +#include #include #include #include #include "sudo_compat.h" +#include "sudo_util.h" #include "sudo_fatal.h" #include "sudo_debug.h" #include "sudo_event.h" @@ -133,26 +134,46 @@ sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev) debug_return_int(0); } +#ifdef HAVE_PPOLL +static int +sudo_ev_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timo) +{ + return ppoll(fds, nfds, timo, NULL); +} +#else +static int +sudo_ev_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timo) +{ + const int timeout = (timo->tv_sec * 1000) + (timo->tv_nsec / 1000000); + + return poll(fds, nfds, timeout); +} +#endif /* HAVE_PPOLL */ + int sudo_ev_scan_impl(struct sudo_event_base *base, int flags) { + struct timespec now, ts, *timeout; struct sudo_event *ev; - int nready, timeout; - struct timeval now; + int nready; debug_decl(sudo_ev_scan_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) - timeout = 0; + sudo_gettime_real(&now); + sudo_timespecsub(&ev->timeout, &now, &ts); + if (ts.tv_sec < 0 || (ts.tv_sec == 0 && ts.tv_nsec < 0)) + sudo_timespecclear(&ts); + timeout = &ts; } else { - timeout = (flags & SUDO_EVLOOP_NONBLOCK) ? 0 : -1; + if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) { + sudo_timespecclear(&ts); + timeout = &ts; + } else { + timeout = NULL; + } } - nready = poll(base->pfds, base->pfd_high + 1, timeout); + nready = sudo_ev_poll(base->pfds, base->pfd_high + 1, timeout); sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready); switch (nready) { case -1: diff --git a/lib/util/event_select.c b/lib/util/event_select.c index 85cd2942e..dd0f0fa16 100644 --- a/lib/util/event_select.c +++ b/lib/util/event_select.c @@ -37,14 +37,15 @@ #ifdef HAVE_STRINGS_H # include #endif /* HAVE_STRINGS_H */ +#include #include #include #include "sudo_compat.h" +#include "sudo_util.h" #include "sudo_fatal.h" #include "sudo_debug.h" #include "sudo_event.h" -#include "sudo_util.h" int sudo_ev_base_alloc_impl(struct sudo_event_base *base) @@ -167,25 +168,47 @@ sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev) debug_return_int(0); } +#ifdef HAVE_PSELECT +static int +sudo_ev_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, const struct timespec *timeout) +{ + return pselect(nfds, readfds, writefds, exceptfds, timeout, NULL); +} +#else +static int +sudo_ev_select(int nfds, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds, const struct timespec *timeout) +{ + struct timeval tvbuf, *tv = NULL; + + if (timeout != NULL) { + TIMESPEC_TO_TIMEVAL(&tvbuf, timeout); + tv = &tvbuf; + } + return select(nfds, readfds, writefds, exceptfds, tv); +} +#endif /* HAVE_PSELECT */ + int sudo_ev_scan_impl(struct sudo_event_base *base, int flags) { - struct timeval now, tv, *timeout; + struct timespec now, ts, *timeout; struct sudo_event *ev; size_t setsize; int nready; debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT) if ((ev = TAILQ_FIRST(&base->timeouts)) != NULL) { - gettimeofday(&now, NULL); - sudo_timevalsub(&ev->timeout, &now, &tv); - if (tv.tv_sec < 0 || (tv.tv_sec == 0 && tv.tv_usec < 0)) - sudo_timevalclear(&tv); - timeout = &tv; + sudo_gettime_real(&now); + sudo_timespecsub(&ev->timeout, &now, &ts); + if (ts.tv_sec < 0 || (ts.tv_sec == 0 && ts.tv_nsec < 0)) + sudo_timespecclear(&ts); + timeout = &ts; } else { if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) { - sudo_timevalclear(&tv); - timeout = &tv; + sudo_timespecclear(&ts); + timeout = &ts; } else { timeout = NULL; } @@ -198,8 +221,8 @@ sudo_ev_scan_impl(struct sudo_event_base *base, int flags) sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: select high fd %d", __func__, base->highfd); - nready = select(base->highfd + 1, base->readfds_out, base->writefds_out, - NULL, timeout); + nready = sudo_ev_select(base->highfd + 1, base->readfds_out, + base->writefds_out, NULL, timeout); sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready", __func__, nready); switch (nready) { case -1: diff --git a/plugins/sudoers/iolog_util.h b/plugins/sudoers/iolog_util.h index f4d00bd43..dd2908f17 100644 --- a/plugins/sudoers/iolog_util.h +++ b/plugins/sudoers/iolog_util.h @@ -34,7 +34,7 @@ struct log_info { struct timing_closure { const char *decimal; - struct timeval *max_delay; + struct timespec *max_delay; int idx; union { struct { diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h index 81b8594c9..28dbbb36c 100644 --- a/plugins/sudoers/sudoers.h +++ b/plugins/sudoers/sudoers.h @@ -234,7 +234,7 @@ struct sudo_user { struct sudo_lbuf; struct passwd; struct stat; -struct timeval; +struct timespec; /* * Function prototypes diff --git a/plugins/sudoers/sudoreplay.c b/plugins/sudoers/sudoreplay.c index c6f78ff69..979b13140 100644 --- a/plugins/sudoers/sudoreplay.c +++ b/plugins/sudoers/sudoreplay.c @@ -19,7 +19,6 @@ #include #include #include -#include #include #include #include @@ -49,6 +48,7 @@ #endif /* HAVE_STDBOOL_H */ #include #include +#include #ifdef HAVE_ZLIB_H # include #endif @@ -163,7 +163,7 @@ 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 void read_keyboard(int fd, int what, void *v); static void help(void) __attribute__((__noreturn__)); -static int replay_session(struct timeval *max_wait, const char *decimal, bool interactive); +static int replay_session(struct timespec *max_wait, const char *decimal, bool interactive); static void sudoreplay_cleanup(void); static void usage(int); static void write_output(int fd, int what, void *v); @@ -195,7 +195,7 @@ main(int argc, char *argv[]) const char *decimal, *id, *user = NULL, *pattern = NULL, *tty = NULL; char *cp, *ep, path[PATH_MAX]; struct log_info *li; - struct timeval max_delay_storage, *max_delay = NULL; + struct timespec max_delay_storage, *max_delay = NULL; double dval; debug_decl(main, SUDO_DEBUG_MAIN) @@ -256,11 +256,11 @@ main(int argc, char *argv[]) if (*ep != '\0' || errno != 0) sudo_fatalx(U_("invalid max wait: %s"), optarg); if (dval <= 0.0) { - sudo_timevalclear(&max_delay_storage); + sudo_timespecclear(&max_delay_storage); } else { max_delay_storage.tv_sec = dval; - max_delay_storage.tv_usec = - (dval - max_delay_storage.tv_sec) * 1000000.0; + max_delay_storage.tv_nsec = + (dval - max_delay_storage.tv_sec) * 1000000000.0; } max_delay = &max_delay_storage; break; @@ -435,7 +435,7 @@ struct getsize_closure { int state; const char *cp; struct sudo_event *ev; - struct timeval timeout; + struct timespec timeout; }; /* getsize states */ @@ -564,7 +564,7 @@ xterm_get_size(int *new_rows, int *new_cols) gc.nums_maxdepth = 1; gc.cp = getsize_response; gc.timeout.tv_sec = 10; - gc.timeout.tv_usec = 0; + gc.timeout.tv_nsec = 0; /* Setup an event for reading the terminal size */ evbase = sudo_ev_base_alloc(); @@ -743,7 +743,7 @@ restore_terminal_size(void) static int read_timing_record(struct replay_closure *closure) { - struct timeval timeout; + struct timespec timeout; char buf[LINE_MAX]; double delay; debug_decl(read_timing_record, SUDO_DEBUG_UTIL) @@ -771,13 +771,13 @@ read_timing_record(struct replay_closure *closure) /* Adjust delay using speed factor. */ delay /= speed_factor; - /* Convert delay to a timeval. */ + /* Convert delay to a timespec. */ timeout.tv_sec = delay; - timeout.tv_usec = (delay - timeout.tv_sec) * 1000000.0; + timeout.tv_nsec = (delay - timeout.tv_sec) * 1000000000.0; /* Clamp timeout to max delay. */ if (closure->timing.max_delay != NULL) { - if (sudo_timevalcmp(&timeout, closure->timing.max_delay, >)) + if (sudo_timespeccmp(&timeout, closure->timing.max_delay, >)) timeout = *closure->timing.max_delay; } @@ -923,7 +923,7 @@ signal_cb(int signo, int what, void *v) } static struct replay_closure * -replay_closure_alloc(struct timeval *max_delay, const char *decimal, bool interactive) +replay_closure_alloc(struct timespec *max_delay, const char *decimal, bool interactive) { struct replay_closure *closure; debug_decl(replay_closure_alloc, SUDO_DEBUG_UTIL) @@ -1004,7 +1004,7 @@ bad: } static int -replay_session(struct timeval *max_delay, const char *decimal, bool interactive) +replay_session(struct timespec *max_delay, const char *decimal, bool interactive) { struct replay_closure *closure; int ret = 0; @@ -1500,7 +1500,7 @@ read_keyboard(int fd, int what, void *v) { struct replay_closure *closure = v; static bool paused = false; - struct timeval tv; + struct timespec ts; ssize_t nread; char ch; debug_decl(read_keyboard, SUDO_DEBUG_UTIL) @@ -1529,16 +1529,16 @@ read_keyboard(int fd, int what, void *v) break; case '<': speed_factor /= 2; - sudo_ev_get_timeleft(closure->delay_ev, &tv); - if (sudo_timevalisset(&tv)) { + sudo_ev_get_timeleft(closure->delay_ev, &ts); + if (sudo_timespecisset(&ts)) { /* Double remaining timeout. */ - tv.tv_sec *= 2; - tv.tv_usec *= 2; - if (tv.tv_usec >= 1000000) { - tv.tv_sec++; - tv.tv_usec -= 1000000; + ts.tv_sec *= 2; + ts.tv_nsec *= 2; + if (ts.tv_nsec >= 1000000000) { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; } - if (sudo_ev_add(NULL, closure->delay_ev, &tv, false) == -1) { + if (sudo_ev_add(NULL, closure->delay_ev, &ts, false) == -1) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "failed to double remaining delay timeout"); } @@ -1546,14 +1546,14 @@ read_keyboard(int fd, int what, void *v) break; case '>': speed_factor *= 2; - sudo_ev_get_timeleft(closure->delay_ev, &tv); - if (sudo_timevalisset(&tv)) { + sudo_ev_get_timeleft(closure->delay_ev, &ts); + if (sudo_timespecisset(&ts)) { /* Halve remaining timeout. */ - if (tv.tv_sec & 1) - tv.tv_usec += 500000; - tv.tv_sec /= 2; - tv.tv_usec /= 2; - if (sudo_ev_add(NULL, closure->delay_ev, &tv, false) == -1) { + if (ts.tv_sec & 1) + ts.tv_nsec += 500000000; + ts.tv_sec /= 2; + ts.tv_nsec /= 2; + if (sudo_ev_add(NULL, closure->delay_ev, &ts, false) == -1) { sudo_debug_printf(SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO, "failed to halve remaining delay timeout"); }