From b3227d3ed54f12b10147d06f8df3478bc6708886 Mon Sep 17 00:00:00 2001 From: "Todd C. Miller" Date: Sun, 19 Aug 2018 09:55:08 -0600 Subject: [PATCH] Add sudo_gettime_uptime() to measure time while not sleeping. --- aclocal.m4 | 114 +++++++++++++++++++++++++++++++++++++++++++ config.h.in | 7 ++- configure | 67 +++++++++++++++---------- configure.ac | 3 ++ include/sudo_util.h | 2 + lib/util/gettime.c | 88 ++++++++++++++++++++++++++++++--- lib/util/util.exp.in | 1 + 7 files changed, 245 insertions(+), 37 deletions(-) diff --git a/aclocal.m4 b/aclocal.m4 index 07cfad57d..4bc9f8ca5 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -12,6 +12,120 @@ # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +# longlong.m4 serial 17 +dnl Copyright (C) 1999-2007, 2009-2016 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. + +# Define HAVE_LONG_LONG_INT if 'long long int' works. +# This fixes a bug in Autoconf 2.61, and can be faster +# than what's in Autoconf 2.62 through 2.68. + +# Note: If the type 'long long int' exists but is only 32 bits large +# (as on some very old compilers), HAVE_LONG_LONG_INT will not be +# defined. In this case you can treat 'long long int' like 'long int'. + +AC_DEFUN([AC_TYPE_LONG_LONG_INT], +[ + AC_REQUIRE([AC_TYPE_UNSIGNED_LONG_LONG_INT]) + AC_CACHE_CHECK([for long long int], [ac_cv_type_long_long_int], + [ac_cv_type_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int + if test $ac_cv_type_long_long_int = yes; then + dnl Catch a bug in Tandem NonStop Kernel (OSS) cc -O circa 2004. + dnl If cross compiling, assume the bug is not important, since + dnl nobody cross compiles for this platform as far as we know. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [[@%:@include + @%:@ifndef LLONG_MAX + @%:@ define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + @%:@ define LLONG_MAX (HALF - 1 + HALF) + @%:@endif]], + [[long long int n = 1; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0;]])], + [], + [ac_cv_type_long_long_int=no], + [:]) + fi + fi]) + if test $ac_cv_type_long_long_int = yes; then + AC_DEFINE([HAVE_LONG_LONG_INT], [1], + [Define to 1 if the system has the type 'long long int'.]) + fi +]) + +# Define HAVE_UNSIGNED_LONG_LONG_INT if 'unsigned long long int' works. +# This fixes a bug in Autoconf 2.61, and can be faster +# than what's in Autoconf 2.62 through 2.68. + +# Note: If the type 'unsigned long long int' exists but is only 32 bits +# large (as on some very old compilers), AC_TYPE_UNSIGNED_LONG_LONG_INT +# will not be defined. In this case you can treat 'unsigned long long int' +# like 'unsigned long int'. + +AC_DEFUN([AC_TYPE_UNSIGNED_LONG_LONG_INT], +[ + AC_CACHE_CHECK([for unsigned long long int], + [ac_cv_type_unsigned_long_long_int], + [ac_cv_type_unsigned_long_long_int=yes + if test "x${ac_cv_prog_cc_c99-no}" = xno; then + AC_LINK_IFELSE( + [_AC_TYPE_LONG_LONG_SNIPPET], + [], + [ac_cv_type_unsigned_long_long_int=no]) + fi]) + if test $ac_cv_type_unsigned_long_long_int = yes; then + AC_DEFINE([HAVE_UNSIGNED_LONG_LONG_INT], [1], + [Define to 1 if the system has the type 'unsigned long long int'.]) + fi +]) + +# Expands to a C program that can be used to test for simultaneous support +# of 'long long' and 'unsigned long long'. We don't want to say that +# 'long long' is available if 'unsigned long long' is not, or vice versa, +# because too many programs rely on the symmetry between signed and unsigned +# integer types (excluding 'bool'). +AC_DEFUN([_AC_TYPE_LONG_LONG_SNIPPET], +[ + AC_LANG_PROGRAM( + [[/* For now, do not test the preprocessor; as of 2007 there are too many + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ + /* Test literals. */ + long long int ll = 9223372036854775807ll; + long long int nll = -9223372036854775807LL; + unsigned long long int ull = 18446744073709551615ULL; + /* Test constant expressions. */ + typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) + ? 1 : -1)]; + typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 + ? 1 : -1)]; + int i = 63;]], + [[/* Test availability of runtime routines for shift and division. */ + long long int llmax = 9223372036854775807ll; + unsigned long long int ullmax = 18446744073709551615ull; + return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull));]]) +]) + m4_include([m4/ax_append_flag.m4]) m4_include([m4/ax_check_compile_flag.m4]) m4_include([m4/ax_check_link_flag.m4]) diff --git a/config.h.in b/config.h.in index 69a2b647a..f8d09ceb0 100644 --- a/config.h.in +++ b/config.h.in @@ -470,7 +470,7 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LOGIN_CAP_H -/* Define to 1 if the system has the type `long long int'. */ +/* Define to 1 if the system has the type 'long long int'. */ #undef HAVE_LONG_LONG_INT /* Define to 1 if you have the `lrand48' function. */ @@ -479,6 +479,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MACHINE_ENDIAN_H +/* Define to 1 if you have the `mach_continuous_time' function. */ +#undef HAVE_MACH_CONTINUOUS_TIME + /* Define to 1 if you have the header file. */ #undef HAVE_MAILLOCK_H @@ -860,7 +863,7 @@ /* Define to 1 if you have the `unsetenv' function. */ #undef HAVE_UNSETENV -/* Define to 1 if the system has the type `unsigned long long int'. */ +/* Define to 1 if the system has the type 'unsigned long long int'. */ #undef HAVE_UNSIGNED_LONG_LONG_INT /* Define to 1 if you have the header file. */ diff --git a/configure b/configure index 6e2a79431..2391c9b17 100755 --- a/configure +++ b/configure @@ -16279,6 +16279,19 @@ done fi RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" + # Mach monotonic timer that runs while sleeping + for ac_func in mach_continuous_time +do : + ac_fn_c_check_func "$LINENO" "mach_continuous_time" "ac_cv_func_mach_continuous_time" +if test "x$ac_cv_func_mach_continuous_time" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_MACH_CONTINUOUS_TIME 1 +_ACEOF + +fi +done + + # Undocumented API that dynamically allocates the groups. for ac_func in getgrouplist_2 do : @@ -18232,18 +18245,18 @@ else /* end confdefs.h. */ /* For now, do not test the preprocessor; as of 2007 there are too many - implementations with broken preprocessors. Perhaps this can - be revisited in 2012. In the meantime, code should not expect - #if to work with literals wider than 32 bits. */ + implementations with broken preprocessors. Perhaps this can + be revisited in 2012. In the meantime, code should not expect + #if to work with literals wider than 32 bits. */ /* Test literals. */ long long int ll = 9223372036854775807ll; long long int nll = -9223372036854775807LL; unsigned long long int ull = 18446744073709551615ULL; /* Test constant expressions. */ typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll) - ? 1 : -1)]; + ? 1 : -1)]; typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1 - ? 1 : -1)]; + ? 1 : -1)]; int i = 63; int main () @@ -18252,9 +18265,9 @@ main () long long int llmax = 9223372036854775807ll; unsigned long long int ullmax = 18446744073709551615ull; return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i) - | (llmax / ll) | (llmax % ll) - | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) - | (ullmax / ull) | (ullmax % ull)); + | (llmax / ll) | (llmax % ll) + | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i) + | (ullmax / ull) | (ullmax % ull)); ; return 0; } @@ -18286,33 +18299,33 @@ if ${ac_cv_type_long_long_int+:} false; then : else ac_cv_type_long_long_int=yes if test "x${ac_cv_prog_cc_c99-no}" = xno; then - ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int - if test $ac_cv_type_long_long_int = yes; then - if test "$cross_compiling" = yes; then : + ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int + if test $ac_cv_type_long_long_int = yes; then + if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include - #ifndef LLONG_MAX - # define HALF \ - (1LL << (sizeof (long long int) * CHAR_BIT - 2)) - # define LLONG_MAX (HALF - 1 + HALF) - #endif + #ifndef LLONG_MAX + # define HALF \ + (1LL << (sizeof (long long int) * CHAR_BIT - 2)) + # define LLONG_MAX (HALF - 1 + HALF) + #endif int main () { long long int n = 1; - int i; - for (i = 0; ; i++) - { - long long int m = n << i; - if (m >> i != n) - return 1; - if (LLONG_MAX / 2 < m) - break; - } - return 0; + int i; + for (i = 0; ; i++) + { + long long int m = n << i; + if (m >> i != n) + return 1; + if (LLONG_MAX / 2 < m) + break; + } + return 0; ; return 0; } @@ -18326,7 +18339,7 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi - fi + fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5 diff --git a/configure.ac b/configure.ac index f95b7ba94..8ebff756e 100644 --- a/configure.ac +++ b/configure.ac @@ -2215,6 +2215,9 @@ case "$host" in fi RTLD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES" + # Mach monotonic timer that runs while sleeping + AC_CHECK_FUNCS([mach_continuous_time]) + # Undocumented API that dynamically allocates the groups. AC_CHECK_FUNCS([getgrouplist_2], [AC_CHECK_DECLS([getgrouplist_2])]) diff --git a/include/sudo_util.h b/include/sudo_util.h index 2d73107aa..41d859900 100644 --- a/include/sudo_util.h +++ b/include/sudo_util.h @@ -170,6 +170,8 @@ __dso_public char *sudo_gethostname_v1(void); #define sudo_gethostname() sudo_gethostname_v1() /* gettime.c */ +__dso_public int sudo_gettime_awake_v1(struct timespec *ts); +#define sudo_gettime_awake(_a) sudo_gettime_awake_v1((_a)) __dso_public int sudo_gettime_mono_v1(struct timespec *ts); #define sudo_gettime_mono(_a) sudo_gettime_mono_v1((_a)) __dso_public int sudo_gettime_real_v1(struct timespec *ts); diff --git a/lib/util/gettime.c b/lib/util/gettime.c index 054c7b695..63c9ea11d 100644 --- a/lib/util/gettime.c +++ b/lib/util/gettime.c @@ -34,13 +34,25 @@ #include "sudo_debug.h" #include "sudo_util.h" -/* On Linux, CLOCK_MONOTONIC does not run while suspended. */ +/* + * On Linux and FreeBSD, CLOCK_MONOTONIC does not run while sleeping. + * Linux provides CLOCK_BOOTTIME which runs while sleeping (FreeBSD does not). + * Some systems provide CLOCK_UPTIME which only runs while awake. + */ #if defined(CLOCK_BOOTTIME) -# define SUDO_CLOCK_MONOTONIC CLOCK_BOOTTIME +# define SUDO_CLOCK_BOOTTIME CLOCK_BOOTTIME +#elif defined(CLOCK_MONOTONIC) +# define SUDO_CLOCK_BOOTTIME CLOCK_MONOTONIC +#endif +#if defined(CLOCK_UPTIME) +# define SUDO_CLOCK_AWAKE CLOCK_UPTIME #elif defined(CLOCK_MONOTONIC) -# define SUDO_CLOCK_MONOTONIC CLOCK_MONOTONIC +# define SUDO_CLOCK_AWAKE CLOCK_MONOTONIC #endif +/* + * Wall clock time, may run backward. + */ #if defined(HAVE_CLOCK_GETTIME) int sudo_gettime_real_v1(struct timespec *ts) @@ -72,24 +84,28 @@ sudo_gettime_real_v1(struct timespec *ts) } #endif -#if defined(HAVE_CLOCK_GETTIME) && defined(SUDO_CLOCK_MONOTONIC) +/* + * Monotonic time, only runs forward. + * We use a timer that only increments while sleeping, if possible. + */ +#if defined(HAVE_CLOCK_GETTIME) && defined(SUDO_CLOCK_BOOTTIME) int sudo_gettime_mono_v1(struct timespec *ts) { static int has_monoclock = -1; debug_decl(sudo_gettime_mono, SUDO_DEBUG_UTIL) - /* Check whether the kernel/libc actually supports CLOCK_MONOTONIC. */ + /* Check whether the kernel/libc actually supports a monotonic clock. */ # ifdef _SC_MONOTONIC_CLOCK if (has_monoclock == -1) has_monoclock = sysconf(_SC_MONOTONIC_CLOCK) != -1; # endif if (!has_monoclock) debug_return_int(sudo_gettime_real(ts)); - if (clock_gettime(SUDO_CLOCK_MONOTONIC, ts) == -1) { + if (clock_gettime(SUDO_CLOCK_BOOTTIME, ts) == -1) { sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, "clock_gettime(%d) failed, using wall clock", - (int)SUDO_CLOCK_MONOTONIC); + (int)SUDO_CLOCK_BOOTTIME); has_monoclock = 0; debug_return_int(sudo_gettime_real(ts)); } @@ -105,7 +121,11 @@ sudo_gettime_mono_v1(struct timespec *ts) if (timebase_info.denom == 0) (void) mach_timebase_info(&timebase_info); - abstime = mach_absolute_time(); +#ifdef HAVE_MACH_CONTINUOUS_TIME + abstime = mach_continuous_time(); /* runs while asleep */ +#else + abstime = mach_absolute_time(); /* doesn't run while asleep */ +#endif nsec = abstime * timebase_info.numer / timebase_info.denom; ts->tv_sec = nsec / 1000000000; ts->tv_nsec = nsec % 1000000000; @@ -119,3 +139,55 @@ sudo_gettime_mono_v1(struct timespec *ts) return sudo_gettime_real(ts); } #endif + +/* + * Monotonic time, only runs forward. + * We use a timer that only increments while awake, if possible. + */ +#if defined(HAVE_CLOCK_GETTIME) && defined(SUDO_CLOCK_AWAKE) +int +sudo_gettime_awake_v1(struct timespec *ts) +{ + static int has_monoclock = -1; + debug_decl(sudo_gettime_awake, SUDO_DEBUG_UTIL) + + /* Check whether the kernel/libc actually supports a monotonic clock. */ +# ifdef _SC_MONOTONIC_CLOCK + if (has_monoclock == -1) + has_monoclock = sysconf(_SC_MONOTONIC_CLOCK) != -1; +# endif + if (!has_monoclock) + debug_return_int(sudo_gettime_real(ts)); + if (clock_gettime(SUDO_CLOCK_AWAKE, ts) == -1) { + sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, + "clock_gettime(%d) failed, using wall clock", + (int)SUDO_CLOCK_AWAKE); + has_monoclock = 0; + debug_return_int(sudo_gettime_real(ts)); + } + debug_return_int(0); +} +#elif defined(__MACH__) +int +sudo_gettime_awake_v1(struct timespec *ts) +{ + uint64_t abstime, nsec; + static mach_timebase_info_data_t timebase_info; + debug_decl(sudo_gettime_awake, SUDO_DEBUG_UTIL) + + if (timebase_info.denom == 0) + (void) mach_timebase_info(&timebase_info); + abstime = mach_absolute_time(); + nsec = abstime * timebase_info.numer / timebase_info.denom; + ts->tv_sec = nsec / 1000000000; + ts->tv_nsec = nsec % 1000000000; + debug_return_int(0); +} +#else +int +sudo_gettime_awake_v1(struct timespec *ts) +{ + /* No monotonic uptime clock available, use wall clock. */ + return sudo_gettime_real(ts); +} +#endif diff --git a/lib/util/util.exp.in b/lib/util/util.exp.in index 4ae914348..7cf84467d 100644 --- a/lib/util/util.exp.in +++ b/lib/util/util.exp.in @@ -71,6 +71,7 @@ sudo_fatalx_nodebug_v1 sudo_get_ttysize_v1 sudo_getgrouplist2_v1 sudo_gethostname_v1 +sudo_gettime_awake_v1 sudo_gettime_mono_v1 sudo_gettime_real_v1 sudo_lbuf_append_quoted_v1 -- 2.40.0