/src/sudo/lib/util/gettime.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 2014-2018 Todd C. Miller <Todd.Miller@sudo.ws> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <config.h> |
20 | | |
21 | | #include <sys/time.h> |
22 | | #include <time.h> |
23 | | |
24 | | #if defined(__MACH__) && !defined(HAVE_CLOCK_GETTIME) |
25 | | # include <mach/mach.h> |
26 | | # include <mach/mach_time.h> |
27 | | # include <mach/clock.h> |
28 | | #endif |
29 | | |
30 | | #include <sudo_compat.h> |
31 | | #include <sudo_debug.h> |
32 | | #include <sudo_util.h> |
33 | | |
34 | | /* |
35 | | * On Linux and FreeBSD, CLOCK_MONOTONIC does not run while sleeping. |
36 | | * Linux provides CLOCK_BOOTTIME which runs while sleeping (FreeBSD does not). |
37 | | * Some systems provide CLOCK_UPTIME which only runs while awake. |
38 | | */ |
39 | | #if defined(CLOCK_BOOTTIME) |
40 | 0 | # define SUDO_CLOCK_BOOTTIME CLOCK_BOOTTIME |
41 | | #elif defined(CLOCK_MONOTONIC_RAW) |
42 | | # define SUDO_CLOCK_BOOTTIME CLOCK_MONOTONIC_RAW |
43 | | #elif defined(CLOCK_MONOTONIC) |
44 | | # define SUDO_CLOCK_BOOTTIME CLOCK_MONOTONIC |
45 | | #endif |
46 | | #if defined(CLOCK_UPTIME_RAW) |
47 | | # define SUDO_CLOCK_UPTIME CLOCK_UPTIME_RAW |
48 | | #elif defined(CLOCK_UPTIME) |
49 | | # define SUDO_CLOCK_UPTIME CLOCK_UPTIME |
50 | | #elif defined(CLOCK_MONOTONIC) |
51 | 5.51k | # define SUDO_CLOCK_UPTIME CLOCK_MONOTONIC |
52 | | #endif |
53 | | |
54 | | /* |
55 | | * Wall clock time, may run backward. |
56 | | */ |
57 | | #if defined(HAVE_CLOCK_GETTIME) |
58 | | int |
59 | | sudo_gettime_real_v1(struct timespec *ts) |
60 | 0 | { |
61 | 0 | debug_decl(sudo_gettime_real, SUDO_DEBUG_UTIL); |
62 | |
|
63 | 0 | if (clock_gettime(CLOCK_REALTIME, ts) == -1) { |
64 | 0 | struct timeval tv; |
65 | |
|
66 | 0 | sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, |
67 | 0 | "clock_gettime(CLOCK_REALTIME) failed, trying gettimeofday()"); |
68 | 0 | if (gettimeofday(&tv, NULL) == -1) |
69 | 0 | debug_return_int(-1); |
70 | 0 | TIMEVAL_TO_TIMESPEC(&tv, ts); |
71 | 0 | } |
72 | 0 | debug_return_int(0); |
73 | 0 | } |
74 | | #else |
75 | | int |
76 | | sudo_gettime_real_v1(struct timespec *ts) |
77 | | { |
78 | | struct timeval tv; |
79 | | debug_decl(sudo_gettime_real, SUDO_DEBUG_UTIL); |
80 | | |
81 | | if (gettimeofday(&tv, NULL) == -1) |
82 | | debug_return_int(-1); |
83 | | TIMEVAL_TO_TIMESPEC(&tv, ts); |
84 | | debug_return_int(0); |
85 | | } |
86 | | #endif |
87 | | |
88 | | /* |
89 | | * Monotonic time, only runs forward. |
90 | | * We use a timer that only increments while sleeping, if possible. |
91 | | */ |
92 | | #if defined(HAVE_CLOCK_GETTIME) && defined(SUDO_CLOCK_BOOTTIME) |
93 | | int |
94 | | sudo_gettime_mono_v1(struct timespec *ts) |
95 | 0 | { |
96 | 0 | static int has_monoclock = -1; |
97 | 0 | debug_decl(sudo_gettime_mono, SUDO_DEBUG_UTIL); |
98 | | |
99 | | /* Check whether the kernel/libc actually supports a monotonic clock. */ |
100 | 0 | # ifdef _SC_MONOTONIC_CLOCK |
101 | 0 | if (has_monoclock == -1) |
102 | 0 | has_monoclock = sysconf(_SC_MONOTONIC_CLOCK) != -1; |
103 | 0 | # endif |
104 | 0 | if (!has_monoclock) |
105 | 0 | debug_return_int(sudo_gettime_real(ts)); |
106 | 0 | if (clock_gettime(SUDO_CLOCK_BOOTTIME, ts) == -1) { |
107 | 0 | sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, |
108 | 0 | "clock_gettime(%d) failed, using wall clock", |
109 | 0 | (int)SUDO_CLOCK_BOOTTIME); |
110 | 0 | has_monoclock = 0; |
111 | 0 | debug_return_int(sudo_gettime_real(ts)); |
112 | 0 | } |
113 | 0 | debug_return_int(0); |
114 | 0 | } |
115 | | #elif defined(HAVE_GETHRTIME) |
116 | | int |
117 | | sudo_gettime_mono_v1(struct timespec *ts) |
118 | | { |
119 | | hrtime_t nsec; |
120 | | debug_decl(sudo_gettime_mono, SUDO_DEBUG_UTIL); |
121 | | |
122 | | nsec = gethrtime(); |
123 | | ts->tv_sec = nsec / 1000000000; |
124 | | ts->tv_nsec = nsec % 1000000000; |
125 | | debug_return_int(0); |
126 | | } |
127 | | #elif defined(__MACH__) |
128 | | int |
129 | | sudo_gettime_mono_v1(struct timespec *ts) |
130 | | { |
131 | | uint64_t abstime, nsec; |
132 | | static mach_timebase_info_data_t timebase_info; |
133 | | debug_decl(sudo_gettime_mono, SUDO_DEBUG_UTIL); |
134 | | |
135 | | if (timebase_info.denom == 0) |
136 | | (void) mach_timebase_info(&timebase_info); |
137 | | #ifdef HAVE_MACH_CONTINUOUS_TIME |
138 | | abstime = mach_continuous_time(); /* runs while asleep */ |
139 | | #else |
140 | | abstime = mach_absolute_time(); /* doesn't run while asleep */ |
141 | | #endif |
142 | | nsec = abstime * timebase_info.numer / timebase_info.denom; |
143 | | ts->tv_sec = nsec / 1000000000; |
144 | | ts->tv_nsec = nsec % 1000000000; |
145 | | debug_return_int(0); |
146 | | } |
147 | | #else |
148 | | int |
149 | | sudo_gettime_mono_v1(struct timespec *ts) |
150 | | { |
151 | | /* No monotonic clock available, use wall clock. */ |
152 | | return sudo_gettime_real(ts); |
153 | | } |
154 | | #endif |
155 | | |
156 | | /* |
157 | | * Monotonic time, only runs forward. |
158 | | * We use a timer that only increments while awake, if possible. |
159 | | */ |
160 | | #if defined(HAVE_CLOCK_GETTIME) && defined(SUDO_CLOCK_UPTIME) |
161 | | int |
162 | | sudo_gettime_awake_v1(struct timespec *ts) |
163 | 5.51k | { |
164 | 5.51k | static int has_monoclock = -1; |
165 | 5.51k | debug_decl(sudo_gettime_awake, SUDO_DEBUG_UTIL); |
166 | | |
167 | | /* Check whether the kernel/libc actually supports a monotonic clock. */ |
168 | 5.51k | # ifdef _SC_MONOTONIC_CLOCK |
169 | 5.51k | if (has_monoclock == -1) |
170 | 1 | has_monoclock = sysconf(_SC_MONOTONIC_CLOCK) != -1; |
171 | 5.51k | # endif |
172 | 5.51k | if (!has_monoclock) |
173 | 0 | debug_return_int(sudo_gettime_real(ts)); |
174 | 5.51k | if (clock_gettime(SUDO_CLOCK_UPTIME, ts) == -1) { |
175 | 0 | sudo_debug_printf(SUDO_DEBUG_WARN|SUDO_DEBUG_ERRNO|SUDO_DEBUG_LINENO, |
176 | 0 | "clock_gettime(%d) failed, using wall clock", |
177 | 0 | (int)SUDO_CLOCK_UPTIME); |
178 | 0 | has_monoclock = 0; |
179 | 0 | debug_return_int(sudo_gettime_real(ts)); |
180 | 0 | } |
181 | 5.51k | debug_return_int(0); |
182 | 5.51k | } |
183 | | #elif defined(HAVE_GETHRTIME) |
184 | | int |
185 | | sudo_gettime_awake_v1(struct timespec *ts) |
186 | | { |
187 | | hrtime_t nsec; |
188 | | debug_decl(sudo_gettime_awake, SUDO_DEBUG_UTIL); |
189 | | |
190 | | /* Currently the same as sudo_gettime_mono() */ |
191 | | nsec = gethrtime(); |
192 | | ts->tv_sec = nsec / 1000000000; |
193 | | ts->tv_nsec = nsec % 1000000000; |
194 | | debug_return_int(0); |
195 | | } |
196 | | #elif defined(__MACH__) |
197 | | int |
198 | | sudo_gettime_awake_v1(struct timespec *ts) |
199 | | { |
200 | | uint64_t abstime, nsec; |
201 | | static mach_timebase_info_data_t timebase_info; |
202 | | debug_decl(sudo_gettime_awake, SUDO_DEBUG_UTIL); |
203 | | |
204 | | if (timebase_info.denom == 0) |
205 | | (void) mach_timebase_info(&timebase_info); |
206 | | abstime = mach_absolute_time(); |
207 | | nsec = abstime * timebase_info.numer / timebase_info.denom; |
208 | | ts->tv_sec = nsec / 1000000000; |
209 | | ts->tv_nsec = nsec % 1000000000; |
210 | | debug_return_int(0); |
211 | | } |
212 | | #else |
213 | | int |
214 | | sudo_gettime_awake_v1(struct timespec *ts) |
215 | | { |
216 | | /* No monotonic uptime clock available, use wall clock. */ |
217 | | return sudo_gettime_real(ts); |
218 | | } |
219 | | #endif |