/src/wireshark/wsutil/time_util.c
Line | Count | Source |
1 | | /* time_util.c |
2 | | * |
3 | | * Wireshark - Network traffic analyzer |
4 | | * By Gerald Combs <gerald@wireshark.org> |
5 | | * Copyright 1998 Gerald Combs |
6 | | * |
7 | | * SPDX-License-Identifier: GPL-2.0-or-later |
8 | | */ |
9 | | |
10 | | #include "config.h" |
11 | 0 | #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL |
12 | | #include "time_util.h" |
13 | | |
14 | | #include <errno.h> |
15 | | |
16 | | #include <wsutil/epochs.h> |
17 | | |
18 | | #ifndef _WIN32 |
19 | | #include <sys/time.h> |
20 | | #include <sys/resource.h> |
21 | | #else |
22 | | #include <windows.h> |
23 | | #endif |
24 | | |
25 | | /* Test if the given year is a leap year */ |
26 | 0 | #define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0)) |
27 | | |
28 | | /* converts a broken down date representation, relative to UTC, |
29 | | * to a timestamp; it uses timegm() if it's available. |
30 | | * |
31 | | * Returns -1 and sets errno to EINVAL on error; returns the timestamp |
32 | | * and sets errno to 0 on success. |
33 | | */ |
34 | | time_t |
35 | | mktime_utc(struct tm *tm) |
36 | 91 | { |
37 | 91 | time_t retval; |
38 | | #ifndef HAVE_TIMEGM |
39 | | /* |
40 | | * We don't have timegm(), so use code copied from Glib source |
41 | | * gtimer.c. |
42 | | */ |
43 | | static const int days_before[] = |
44 | | { |
45 | | 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 |
46 | | }; |
47 | | |
48 | | int yr; |
49 | | |
50 | | if (tm->tm_mon < 0 || tm->tm_mon > 11) { |
51 | | errno = EINVAL; |
52 | | return (time_t) -1; |
53 | | } |
54 | | |
55 | | retval = (tm->tm_year - 70) * 365; |
56 | | |
57 | | /* count number of leap years */ |
58 | | yr = tm->tm_year + 1900; |
59 | | if (tm->tm_mon + 1 < 3 && isleap(yr)) |
60 | | yr--; |
61 | | retval += (((yr / 4) - (yr / 100) + (yr / 400)) - 477); /* 477 = ((1970 / 4) - (1970 / 100) + (1970 / 400)) */ |
62 | | |
63 | | retval += days_before[tm->tm_mon] + tm->tm_mday - 1; |
64 | | |
65 | | retval = ((((retval * 24) + tm->tm_hour) * 60) + tm->tm_min) * 60 + tm->tm_sec; |
66 | | |
67 | | /* |
68 | | * Just in case somebody asked for 1969-12-31 23:59:59 UTC, |
69 | | * which is one second before the Unix epoch. |
70 | | */ |
71 | | errno = 0; |
72 | | return retval; |
73 | | #else |
74 | 91 | retval = timegm(tm); |
75 | | /* |
76 | | * If passed a struct tm for 2013-03-01 00:00:00, both |
77 | | * macOS and FreeBSD timegm() return the epoch time |
78 | | * value for 2013-03-01 00:00:00 UTC, but also set |
79 | | * errno to EOVERFLOW. This may be true of other |
80 | | * implementations based on the tzcode reference |
81 | | * implementation of timegm(). |
82 | | * |
83 | | * The macOS and FreeBSD documentation for timegm() neither |
84 | | * commit to leaving errno alone nor commit to setting it |
85 | | * to a particular value. |
86 | | * |
87 | | * Force errno to 0, and check for an error and set it to |
88 | | * EINVAL iff we got an error. |
89 | | */ |
90 | 91 | errno = 0; |
91 | 91 | if (retval == (time_t)-1) { |
92 | | /* |
93 | | * Did somebody ask for 1969-12-31 23:59:59 UTC, |
94 | | * which is one second before the Unix epoch? |
95 | | * |
96 | | * If so, timegm() happened to return the correct |
97 | | * timestamp (whether because it calculated it or |
98 | | * because it failed in some fashion). |
99 | | * |
100 | | * If not, set errno to EINVAL. |
101 | | */ |
102 | 0 | if (tm->tm_year != (1969 - 1900) || |
103 | 0 | tm->tm_mon != (12 - 1) || |
104 | 0 | tm->tm_mday != 31 || |
105 | 0 | tm->tm_hour != 23 || |
106 | 0 | tm->tm_min != 59 || |
107 | 0 | tm->tm_sec != 59) |
108 | 0 | errno = EINVAL; |
109 | 0 | } |
110 | 91 | return retval; |
111 | 91 | #endif /* !HAVE_TIMEGM */ |
112 | 91 | } |
113 | | |
114 | | /* Validate the values in a time_t |
115 | | * Currently checks tm_year, tm_mon, tm_mday, tm_hour, tm_min, and tm_sec; |
116 | | * disregards tm_wday, tm_yday, and tm_isdst. |
117 | | * Use this in situations where you wish to return an error rather than |
118 | | * normalizing invalid dates; otherwise you could specify, for example, |
119 | | * 2020-10-40 (to quote the macOS and probably *BSD manual |
120 | | * page for ctime()/localtime()/mktime()/etc., "October 40 |
121 | | * is changed into November 9"). |
122 | | */ |
123 | | bool |
124 | | tm_is_valid(struct tm *tm) |
125 | 13 | { |
126 | 13 | static const int8_t days_in_month[12] = { |
127 | 13 | 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 |
128 | 13 | }; |
129 | | |
130 | 13 | if (tm->tm_mon < 0 || tm->tm_mon > 11) { |
131 | 0 | return false; |
132 | 0 | } |
133 | 13 | if (tm->tm_mday < 0 || tm->tm_mday > |
134 | 11 | ((tm->tm_mon == 1 && isleap(tm->tm_year)) ? 29 : days_in_month[tm->tm_mon])) { |
135 | 2 | return false; |
136 | 2 | } |
137 | 11 | if (tm->tm_hour < 0 || tm->tm_hour > 23) { |
138 | 0 | return false; |
139 | 0 | } |
140 | | /* XXX: ISO 8601 and others allow 24:00:00 for end of day, perhaps that |
141 | | * one case should be allowed? |
142 | | */ |
143 | 11 | if (tm->tm_min < 0 || tm->tm_min > 59) { |
144 | 0 | return false; |
145 | 0 | } |
146 | 11 | if (tm->tm_sec < 0 || tm->tm_sec > 60) { |
147 | | /* 60, not 59, to account for leap seconds */ |
148 | 0 | return false; |
149 | 0 | } |
150 | 11 | return true; |
151 | 11 | } |
152 | | |
153 | 0 | void get_resource_usage(double *user_time, double *sys_time) { |
154 | 0 | #ifndef _WIN32 |
155 | 0 | struct rusage ru; |
156 | |
|
157 | 0 | getrusage(RUSAGE_SELF, &ru); |
158 | |
|
159 | 0 | *user_time = ru.ru_utime.tv_sec + (ru.ru_utime.tv_usec / 1000000.0); |
160 | 0 | *sys_time = ru.ru_stime.tv_sec + (ru.ru_stime.tv_usec / 1000000.0); |
161 | | #else /* _WIN32 */ |
162 | | HANDLE h_proc = GetCurrentProcess(); |
163 | | FILETIME cft, eft, kft, uft; |
164 | | ULARGE_INTEGER uli_time; |
165 | | |
166 | | GetProcessTimes(h_proc, &cft, &eft, &kft, &uft); |
167 | | |
168 | | uli_time.LowPart = uft.dwLowDateTime; |
169 | | uli_time.HighPart = uft.dwHighDateTime; |
170 | | *user_time = uli_time.QuadPart / 10000000.0; |
171 | | uli_time.LowPart = kft.dwLowDateTime; |
172 | | uli_time.HighPart = kft.dwHighDateTime; |
173 | | *sys_time = uli_time.QuadPart / 1000000000.0; |
174 | | #endif /* _WIN32 */ |
175 | 0 | } |
176 | | |
177 | | static double last_user_time = 0.0; |
178 | | static double last_sys_time = 0.0; |
179 | | |
180 | 0 | void log_resource_usage(bool reset_delta, const char *format, ...) { |
181 | 0 | va_list ap; |
182 | 0 | GString *log_str = g_string_new(""); |
183 | 0 | double user_time; |
184 | 0 | double sys_time; |
185 | |
|
186 | 0 | get_resource_usage(&user_time, &sys_time); |
187 | |
|
188 | 0 | if (reset_delta || last_user_time == 0.0) { |
189 | 0 | last_user_time = user_time; |
190 | 0 | last_sys_time = sys_time; |
191 | 0 | } |
192 | |
|
193 | 0 | g_string_append_printf(log_str, "user %.3f +%.3f sys %.3f +%.3f ", |
194 | 0 | user_time, user_time - last_user_time, |
195 | 0 | sys_time, sys_time - last_sys_time); |
196 | |
|
197 | 0 | va_start(ap, format); |
198 | 0 | g_string_append_vprintf(log_str, format, ap); |
199 | 0 | va_end(ap); |
200 | |
|
201 | 0 | ws_warning("%s", log_str->str); |
202 | 0 | g_string_free(log_str, TRUE); |
203 | |
|
204 | 0 | } |
205 | | |
206 | | /* Copied from pcapio.c pcapng_write_interface_statistics_block()*/ |
207 | | uint64_t |
208 | 0 | create_timestamp(void) { |
209 | 0 | uint64_t timestamp; |
210 | | #ifdef _WIN32 |
211 | | FILETIME now; |
212 | | #else |
213 | 0 | struct timeval now; |
214 | 0 | #endif |
215 | |
|
216 | | #ifdef _WIN32 |
217 | | /* |
218 | | * Current time, represented as 100-nanosecond intervals since |
219 | | * January 1, 1601, 00:00:00 UTC. |
220 | | * |
221 | | * I think DWORD might be signed, so cast both parts of "now" |
222 | | * to uint32_t so that the sign bit doesn't get treated specially. |
223 | | * |
224 | | * Windows 8 provides GetSystemTimePreciseAsFileTime which we |
225 | | * might want to use instead. |
226 | | */ |
227 | | GetSystemTimeAsFileTime(&now); |
228 | | timestamp = (((uint64_t)(uint32_t)now.dwHighDateTime) << 32) + |
229 | | (uint32_t)now.dwLowDateTime; |
230 | | |
231 | | /* |
232 | | * Convert to same thing but as 1-microsecond, i.e. 1000-nanosecond, |
233 | | * intervals. |
234 | | */ |
235 | | timestamp /= 10; |
236 | | |
237 | | /* |
238 | | * Subtract difference, in microseconds, between January 1, 1601 |
239 | | * 00:00:00 UTC and January 1, 1970, 00:00:00 UTC. |
240 | | */ |
241 | | timestamp -= EPOCH_DELTA_1601_01_01_00_00_00_UTC*1000000; |
242 | | #else |
243 | | /* |
244 | | * Current time, represented as seconds and microseconds since |
245 | | * January 1, 1970, 00:00:00 UTC. |
246 | | */ |
247 | 0 | gettimeofday(&now, NULL); |
248 | | |
249 | | /* |
250 | | * Convert to delta in microseconds. |
251 | | */ |
252 | 0 | timestamp = (uint64_t)(now.tv_sec) * 1000000 + (uint64_t)(now.tv_usec); |
253 | 0 | #endif |
254 | 0 | return timestamp; |
255 | 0 | } |
256 | | |
257 | | struct timespec * |
258 | | ws_clock_get_realtime(struct timespec *ts) |
259 | 0 | { |
260 | 0 | #if defined(HAVE_CLOCK_GETTIME) |
261 | 0 | if (clock_gettime(CLOCK_REALTIME, ts) == 0) |
262 | 0 | return ts; |
263 | | #elif defined(HAVE_TIMESPEC_GET) |
264 | | if (timespec_get(ts, TIME_UTC) == TIME_UTC) |
265 | | return ts; |
266 | | #endif |
267 | | |
268 | 0 | #ifndef _WIN32 |
269 | | /* Fall back on gettimeofday(). */ |
270 | 0 | struct timeval usectimenow; |
271 | 0 | gettimeofday(&usectimenow, NULL); |
272 | 0 | ts->tv_sec = usectimenow.tv_sec; |
273 | 0 | ts->tv_nsec = usectimenow.tv_usec*1000; |
274 | 0 | return ts; |
275 | | #else |
276 | | /* Fall back on time(). */ |
277 | | ts->tv_sec = time(NULL); |
278 | | ts->tv_nsec = 0; |
279 | | return ts; |
280 | | #endif |
281 | 0 | } |
282 | | |
283 | | struct tm * |
284 | | ws_localtime_r(const time_t *timep, struct tm *result) |
285 | 489 | { |
286 | 489 | #if defined(HAVE_LOCALTIME_R) |
287 | 489 | return localtime_r(timep, result); |
288 | | #elif defined(_MSC_VER) |
289 | | errno_t err = localtime_s(result, timep); |
290 | | if (err == 0) |
291 | | return result; |
292 | | return NULL; |
293 | | #else |
294 | | struct tm *aux = localtime(timep); |
295 | | if (aux == NULL) |
296 | | return NULL; |
297 | | *result = *aux; |
298 | | return result; |
299 | | #endif |
300 | 489 | } |
301 | | |
302 | | void ws_tzset(void) |
303 | 14 | { |
304 | 14 | #ifdef HAVE_TZSET |
305 | 14 | tzset(); |
306 | 14 | #endif |
307 | 14 | } |
308 | | |
309 | | struct tm * |
310 | | ws_gmtime_r(const time_t *timep, struct tm *result) |
311 | 0 | { |
312 | 0 | #if defined(HAVE_GMTIME_R) |
313 | 0 | return gmtime_r(timep, result); |
314 | | #elif defined(_MSC_VER) |
315 | | errno_t err = gmtime_s(result, timep); |
316 | | if (err == 0) |
317 | | return result; |
318 | | return NULL; |
319 | | #else |
320 | | struct tm *aux = gmtime(timep); |
321 | | if (aux == NULL) |
322 | | return NULL; |
323 | | *result = *aux; |
324 | | return result; |
325 | | #endif |
326 | 0 | } |
327 | | |
328 | | /* |
329 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
330 | | * |
331 | | * Local variables: |
332 | | * c-basic-offset: 8 |
333 | | * tab-width: 8 |
334 | | * indent-tabs-mode: t |
335 | | * End: |
336 | | * |
337 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
338 | | * :indentSize=8:tabSize=8:noTabs=false: |
339 | | */ |