]> granicus.if.org Git - sudo/commitdiff
Use struct timespec, not struct timeval in the event subsystem.
authorTodd C. Miller <Todd.Miller@sudo.ws>
Sun, 26 Aug 2018 03:02:05 +0000 (21:02 -0600)
committerTodd C. Miller <Todd.Miller@sudo.ws>
Sun, 26 Aug 2018 03:02:05 +0000 (21:02 -0600)
Use ppoll() or pselect() if avaialble which use timespec.

config.h.in
configure
configure.ac
include/sudo_event.h
lib/util/event.c
lib/util/event_poll.c
lib/util/event_select.c
plugins/sudoers/iolog_util.h
plugins/sudoers/sudoers.h
plugins/sudoers/sudoreplay.c

index 710af3ac487aae623a6302b022e77e023f3963aa..920e8411857edb7d4caab6f062fb57b72cfe5364 100644 (file)
 /* 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
 
 /* Define to 1 if you have the <project.h> 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
 
index af2560f0681ca403a1f84658208aed1ec77b7c48..e78bf5f53b82d885876204a0b36b9eeb1631252e 100755 (executable)
--- 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
 
index 04242579e9ad3dc69b414c5495aeaaa129961964..0b303fcccb7720ef922de598349686d0f7506c40 100644 (file)
@@ -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
 
index c60c7d1045991d4feb9d5f26469f089260028e0b..20df2be48c242540ca3e92db9c33ef2c69570a06 100644 (file)
@@ -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. */
index 30e6e319e1846032d39adc004d66d88550aab6f6..568232f6167c6de7120349c6874f6164995d50e3 100644 (file)
@@ -17,7 +17,6 @@
 #include <config.h>
 
 #include <sys/types.h>
-#include <sys/time.h>
 #include <stdio.h>
 #include <stdlib.h>
 #ifdef HAVE_STDBOOL_H
@@ -33,6 +32,7 @@
 #endif /* HAVE_STRINGS_H */
 #include <errno.h>
 #include <fcntl.h>
+#include <time.h>
 #include <unistd.h>
 
 #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);
 }
index b2b82c07332f26cf74df00211346dd751f051538..484f949eb39aba6d6a0468bc3a1fbdf5c911f113 100644 (file)
@@ -17,7 +17,6 @@
 #include <config.h>
 
 #include <sys/types.h>
-#include <sys/time.h>
 #include <stdio.h>
 #include <stdlib.h>
 #ifdef HAVE_STDBOOL_H
 #ifdef HAVE_STRINGS_H
 # include <strings.h>
 #endif /* HAVE_STRINGS_H */
+#include <time.h>
 #include <unistd.h>
 #include <errno.h>
 #include <poll.h>
 
 #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:
index 85cd2942e52972415da9a1f7af07b7fd820f42b5..dd0f0fa16ae5a86d478fe4ac441c436c0d4de7d7 100644 (file)
 #ifdef HAVE_STRINGS_H
 # include <strings.h>
 #endif /* HAVE_STRINGS_H */
+#include <time.h>
 #include <unistd.h>
 #include <errno.h>
 
 #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:
index f4d00bd435e2795714ed563ea9b33036148c5adf..dd2908f17aabbb7b1b56c162bd1c243c995a08ea 100644 (file)
@@ -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 {
index 81b8594c9c22b1b77acd2f7228fa244e944ad896..28dbbb36c04344a9f0dff8dafa29c1c1bdffcf00 100644 (file)
@@ -234,7 +234,7 @@ struct sudo_user {
 struct sudo_lbuf;
 struct passwd;
 struct stat;
-struct timeval;
+struct timespec;
 
 /*
  * Function prototypes
index c6f78ff69133247e280de4f9e7f522c4b5a54d3a..979b13140023f76455e22227ab63c45793238570 100644 (file)
@@ -19,7 +19,6 @@
 #include <sys/types.h>
 #include <sys/uio.h>
 #include <sys/stat.h>
-#include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/ioctl.h>
 #include <stdio.h>
@@ -49,6 +48,7 @@
 #endif /* HAVE_STDBOOL_H */
 #include <regex.h>
 #include <signal.h>
+#include <time.h>
 #ifdef HAVE_ZLIB_H
 # include <zlib.h>
 #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");
                }