File: | build-scan/../src/basic/time-util.c |
Warning: | line 751, column 29 The left operand of '!=' is a garbage value due to array index out of bounds |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ | ||||||||
2 | |||||||||
3 | #include <errno(*__errno_location ()).h> | ||||||||
4 | #include <limits.h> | ||||||||
5 | #include <stdlib.h> | ||||||||
6 | #include <string.h> | ||||||||
7 | #include <sys/mman.h> | ||||||||
8 | #include <sys/stat.h> | ||||||||
9 | #include <sys/time.h> | ||||||||
10 | #include <sys/timerfd.h> | ||||||||
11 | #include <sys/timex.h> | ||||||||
12 | #include <sys/types.h> | ||||||||
13 | #include <unistd.h> | ||||||||
14 | |||||||||
15 | #include "alloc-util.h" | ||||||||
16 | #include "fd-util.h" | ||||||||
17 | #include "fileio.h" | ||||||||
18 | #include "fs-util.h" | ||||||||
19 | #include "io-util.h" | ||||||||
20 | #include "log.h" | ||||||||
21 | #include "macro.h" | ||||||||
22 | #include "parse-util.h" | ||||||||
23 | #include "path-util.h" | ||||||||
24 | #include "process-util.h" | ||||||||
25 | #include "stat-util.h" | ||||||||
26 | #include "string-util.h" | ||||||||
27 | #include "strv.h" | ||||||||
28 | #include "time-util.h" | ||||||||
29 | |||||||||
30 | static clockid_t map_clock_id(clockid_t c) { | ||||||||
31 | |||||||||
32 | /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will | ||||||||
33 | * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is | ||||||||
34 | * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on | ||||||||
35 | * those archs. */ | ||||||||
36 | |||||||||
37 | switch (c) { | ||||||||
38 | |||||||||
39 | case CLOCK_BOOTTIME_ALARM9: | ||||||||
40 | return CLOCK_BOOTTIME7; | ||||||||
41 | |||||||||
42 | case CLOCK_REALTIME_ALARM8: | ||||||||
43 | return CLOCK_REALTIME0; | ||||||||
44 | |||||||||
45 | default: | ||||||||
46 | return c; | ||||||||
47 | } | ||||||||
48 | } | ||||||||
49 | |||||||||
50 | usec_t now(clockid_t clock_id) { | ||||||||
51 | struct timespec ts; | ||||||||
52 | |||||||||
53 | assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0)do { if ((__builtin_expect(!!(!(clock_gettime(map_clock_id(clock_id ), &ts) == 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("clock_gettime(map_clock_id(clock_id), &ts) == 0"), "../src/basic/time-util.c" , 53, __PRETTY_FUNCTION__); } while (0); | ||||||||
54 | |||||||||
55 | return timespec_load(&ts); | ||||||||
56 | } | ||||||||
57 | |||||||||
58 | nsec_t now_nsec(clockid_t clock_id) { | ||||||||
59 | struct timespec ts; | ||||||||
60 | |||||||||
61 | assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0)do { if ((__builtin_expect(!!(!(clock_gettime(map_clock_id(clock_id ), &ts) == 0)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("clock_gettime(map_clock_id(clock_id), &ts) == 0"), "../src/basic/time-util.c" , 61, __PRETTY_FUNCTION__); } while (0); | ||||||||
62 | |||||||||
63 | return timespec_load_nsec(&ts); | ||||||||
64 | } | ||||||||
65 | |||||||||
66 | dual_timestamp* dual_timestamp_get(dual_timestamp *ts) { | ||||||||
67 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 67, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
68 | |||||||||
69 | ts->realtime = now(CLOCK_REALTIME0); | ||||||||
70 | ts->monotonic = now(CLOCK_MONOTONIC1); | ||||||||
71 | |||||||||
72 | return ts; | ||||||||
73 | } | ||||||||
74 | |||||||||
75 | triple_timestamp* triple_timestamp_get(triple_timestamp *ts) { | ||||||||
76 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 76, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
77 | |||||||||
78 | ts->realtime = now(CLOCK_REALTIME0); | ||||||||
79 | ts->monotonic = now(CLOCK_MONOTONIC1); | ||||||||
80 | ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME7) : USEC_INFINITY((usec_t) -1); | ||||||||
81 | |||||||||
82 | return ts; | ||||||||
83 | } | ||||||||
84 | |||||||||
85 | dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) { | ||||||||
86 | int64_t delta; | ||||||||
87 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 87, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
88 | |||||||||
89 | if (u == USEC_INFINITY((usec_t) -1) || u <= 0) { | ||||||||
90 | ts->realtime = ts->monotonic = u; | ||||||||
91 | return ts; | ||||||||
92 | } | ||||||||
93 | |||||||||
94 | ts->realtime = u; | ||||||||
95 | |||||||||
96 | delta = (int64_t) now(CLOCK_REALTIME0) - (int64_t) u; | ||||||||
97 | ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC1), delta); | ||||||||
98 | |||||||||
99 | return ts; | ||||||||
100 | } | ||||||||
101 | |||||||||
102 | triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) { | ||||||||
103 | int64_t delta; | ||||||||
104 | |||||||||
105 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 105, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
106 | |||||||||
107 | if (u == USEC_INFINITY((usec_t) -1) || u <= 0) { | ||||||||
108 | ts->realtime = ts->monotonic = ts->boottime = u; | ||||||||
109 | return ts; | ||||||||
110 | } | ||||||||
111 | |||||||||
112 | ts->realtime = u; | ||||||||
113 | delta = (int64_t) now(CLOCK_REALTIME0) - (int64_t) u; | ||||||||
114 | ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC1), delta); | ||||||||
115 | ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME7), delta) : USEC_INFINITY((usec_t) -1); | ||||||||
116 | |||||||||
117 | return ts; | ||||||||
118 | } | ||||||||
119 | |||||||||
120 | dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) { | ||||||||
121 | int64_t delta; | ||||||||
122 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 122, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
123 | |||||||||
124 | if (u == USEC_INFINITY((usec_t) -1)) { | ||||||||
125 | ts->realtime = ts->monotonic = USEC_INFINITY((usec_t) -1); | ||||||||
126 | return ts; | ||||||||
127 | } | ||||||||
128 | |||||||||
129 | ts->monotonic = u; | ||||||||
130 | delta = (int64_t) now(CLOCK_MONOTONIC1) - (int64_t) u; | ||||||||
131 | ts->realtime = usec_sub_signed(now(CLOCK_REALTIME0), delta); | ||||||||
132 | |||||||||
133 | return ts; | ||||||||
134 | } | ||||||||
135 | |||||||||
136 | dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) { | ||||||||
137 | int64_t delta; | ||||||||
138 | |||||||||
139 | if (u == USEC_INFINITY((usec_t) -1)) { | ||||||||
140 | ts->realtime = ts->monotonic = USEC_INFINITY((usec_t) -1); | ||||||||
141 | return ts; | ||||||||
142 | } | ||||||||
143 | |||||||||
144 | dual_timestamp_get(ts); | ||||||||
145 | delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u; | ||||||||
146 | ts->realtime = usec_sub_signed(ts->realtime, delta); | ||||||||
147 | ts->monotonic = usec_sub_signed(ts->monotonic, delta); | ||||||||
148 | |||||||||
149 | return ts; | ||||||||
150 | } | ||||||||
151 | |||||||||
152 | usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) { | ||||||||
153 | |||||||||
154 | switch (clock) { | ||||||||
155 | |||||||||
156 | case CLOCK_REALTIME0: | ||||||||
157 | case CLOCK_REALTIME_ALARM8: | ||||||||
158 | return ts->realtime; | ||||||||
159 | |||||||||
160 | case CLOCK_MONOTONIC1: | ||||||||
161 | return ts->monotonic; | ||||||||
162 | |||||||||
163 | case CLOCK_BOOTTIME7: | ||||||||
164 | case CLOCK_BOOTTIME_ALARM9: | ||||||||
165 | return ts->boottime; | ||||||||
166 | |||||||||
167 | default: | ||||||||
168 | return USEC_INFINITY((usec_t) -1); | ||||||||
169 | } | ||||||||
170 | } | ||||||||
171 | |||||||||
172 | usec_t timespec_load(const struct timespec *ts) { | ||||||||
173 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 173, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
174 | |||||||||
175 | if (ts->tv_sec < 0 || ts->tv_nsec < 0) | ||||||||
176 | return USEC_INFINITY((usec_t) -1); | ||||||||
177 | |||||||||
178 | if ((usec_t) ts->tv_sec > (UINT64_MAX(18446744073709551615UL) - (ts->tv_nsec / NSEC_PER_USEC((nsec_t) 1000ULL))) / USEC_PER_SEC((usec_t) 1000000ULL)) | ||||||||
179 | return USEC_INFINITY((usec_t) -1); | ||||||||
180 | |||||||||
181 | return | ||||||||
182 | (usec_t) ts->tv_sec * USEC_PER_SEC((usec_t) 1000000ULL) + | ||||||||
183 | (usec_t) ts->tv_nsec / NSEC_PER_USEC((nsec_t) 1000ULL); | ||||||||
184 | } | ||||||||
185 | |||||||||
186 | nsec_t timespec_load_nsec(const struct timespec *ts) { | ||||||||
187 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 187, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
188 | |||||||||
189 | if (ts->tv_sec < 0 || ts->tv_nsec < 0) | ||||||||
190 | return NSEC_INFINITY((nsec_t) -1); | ||||||||
191 | |||||||||
192 | if ((nsec_t) ts->tv_sec >= (UINT64_MAX(18446744073709551615UL) - ts->tv_nsec) / NSEC_PER_SEC((nsec_t) 1000000000ULL)) | ||||||||
193 | return NSEC_INFINITY((nsec_t) -1); | ||||||||
194 | |||||||||
195 | return (nsec_t) ts->tv_sec * NSEC_PER_SEC((nsec_t) 1000000000ULL) + (nsec_t) ts->tv_nsec; | ||||||||
196 | } | ||||||||
197 | |||||||||
198 | struct timespec *timespec_store(struct timespec *ts, usec_t u) { | ||||||||
199 | assert(ts)do { if ((__builtin_expect(!!(!(ts)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ts"), "../src/basic/time-util.c", 199, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
200 | |||||||||
201 | if (u == USEC_INFINITY((usec_t) -1) || | ||||||||
202 | u / USEC_PER_SEC((usec_t) 1000000ULL) >= TIME_T_MAX(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 )) { | ||||||||
203 | ts->tv_sec = (time_t) -1; | ||||||||
204 | ts->tv_nsec = (long) -1; | ||||||||
205 | return ts; | ||||||||
206 | } | ||||||||
207 | |||||||||
208 | ts->tv_sec = (time_t) (u / USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
209 | ts->tv_nsec = (long int) ((u % USEC_PER_SEC((usec_t) 1000000ULL)) * NSEC_PER_USEC((nsec_t) 1000ULL)); | ||||||||
210 | |||||||||
211 | return ts; | ||||||||
212 | } | ||||||||
213 | |||||||||
214 | usec_t timeval_load(const struct timeval *tv) { | ||||||||
215 | assert(tv)do { if ((__builtin_expect(!!(!(tv)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("tv"), "../src/basic/time-util.c", 215, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
216 | |||||||||
217 | if (tv->tv_sec < 0 || tv->tv_usec < 0) | ||||||||
218 | return USEC_INFINITY((usec_t) -1); | ||||||||
219 | |||||||||
220 | if ((usec_t) tv->tv_sec > (UINT64_MAX(18446744073709551615UL) - tv->tv_usec) / USEC_PER_SEC((usec_t) 1000000ULL)) | ||||||||
221 | return USEC_INFINITY((usec_t) -1); | ||||||||
222 | |||||||||
223 | return | ||||||||
224 | (usec_t) tv->tv_sec * USEC_PER_SEC((usec_t) 1000000ULL) + | ||||||||
225 | (usec_t) tv->tv_usec; | ||||||||
226 | } | ||||||||
227 | |||||||||
228 | struct timeval *timeval_store(struct timeval *tv, usec_t u) { | ||||||||
229 | assert(tv)do { if ((__builtin_expect(!!(!(tv)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("tv"), "../src/basic/time-util.c", 229, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
230 | |||||||||
231 | if (u == USEC_INFINITY((usec_t) -1) || | ||||||||
232 | u / USEC_PER_SEC((usec_t) 1000000ULL) > TIME_T_MAX(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 )) { | ||||||||
233 | tv->tv_sec = (time_t) -1; | ||||||||
234 | tv->tv_usec = (suseconds_t) -1; | ||||||||
235 | } else { | ||||||||
236 | tv->tv_sec = (time_t) (u / USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
237 | tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
238 | } | ||||||||
239 | |||||||||
240 | return tv; | ||||||||
241 | } | ||||||||
242 | |||||||||
243 | static char *format_timestamp_internal( | ||||||||
244 | char *buf, | ||||||||
245 | size_t l, | ||||||||
246 | usec_t t, | ||||||||
247 | bool_Bool utc, | ||||||||
248 | bool_Bool us) { | ||||||||
249 | |||||||||
250 | /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our | ||||||||
251 | * generated timestamps may be parsed with parse_timestamp(), and always read the same. */ | ||||||||
252 | static const char * const weekdays[] = { | ||||||||
253 | [0] = "Sun", | ||||||||
254 | [1] = "Mon", | ||||||||
255 | [2] = "Tue", | ||||||||
256 | [3] = "Wed", | ||||||||
257 | [4] = "Thu", | ||||||||
258 | [5] = "Fri", | ||||||||
259 | [6] = "Sat", | ||||||||
260 | }; | ||||||||
261 | |||||||||
262 | struct tm tm; | ||||||||
263 | time_t sec; | ||||||||
264 | size_t n; | ||||||||
265 | |||||||||
266 | assert(buf)do { if ((__builtin_expect(!!(!(buf)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buf"), "../src/basic/time-util.c", 266, __PRETTY_FUNCTION__); } while (0); | ||||||||
267 | |||||||||
268 | if (l < | ||||||||
269 | 3 + /* week day */ | ||||||||
270 | 1 + 10 + /* space and date */ | ||||||||
271 | 1 + 8 + /* space and time */ | ||||||||
272 | (us ? 1 + 6 : 0) + /* "." and microsecond part */ | ||||||||
273 | 1 + 1 + /* space and shortest possible zone */ | ||||||||
274 | 1) | ||||||||
275 | return NULL((void*)0); /* Not enough space even for the shortest form. */ | ||||||||
276 | if (t <= 0 || t == USEC_INFINITY((usec_t) -1)) | ||||||||
277 | return NULL((void*)0); /* Timestamp is unset */ | ||||||||
278 | |||||||||
279 | /* Let's not format times with years > 9999 */ | ||||||||
280 | if (t > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000)) { | ||||||||
281 | assert(l >= strlen("--- XXXX-XX-XX XX:XX:XX") + 1)do { if ((__builtin_expect(!!(!(l >= strlen("--- XXXX-XX-XX XX:XX:XX" ) + 1)),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD, ("l >= strlen(\"--- XXXX-XX-XX XX:XX:XX\") + 1" ), "../src/basic/time-util.c", 281, __PRETTY_FUNCTION__); } while (0); | ||||||||
282 | strcpy(buf, "--- XXXX-XX-XX XX:XX:XX"); | ||||||||
283 | return buf; | ||||||||
284 | } | ||||||||
285 | |||||||||
286 | sec = (time_t) (t / USEC_PER_SEC((usec_t) 1000000ULL)); /* Round down */ | ||||||||
287 | |||||||||
288 | if (!localtime_or_gmtime_r(&sec, &tm, utc)) | ||||||||
289 | return NULL((void*)0); | ||||||||
290 | |||||||||
291 | /* Start with the week day */ | ||||||||
292 | assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays))do { if ((__builtin_expect(!!(!((size_t) tm.tm_wday < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p(typeof (weekdays), typeof(&*(weekdays))), sizeof(weekdays)/sizeof ((weekdays)[0]), ((void)0))))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD , ("(size_t) tm.tm_wday < ELEMENTSOF(weekdays)"), "../src/basic/time-util.c" , 292, __PRETTY_FUNCTION__); } while (0); | ||||||||
293 | memcpy(buf, weekdays[tm.tm_wday], 4); | ||||||||
294 | |||||||||
295 | /* Add the main components */ | ||||||||
296 | if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0) | ||||||||
297 | return NULL((void*)0); /* Doesn't fit */ | ||||||||
298 | |||||||||
299 | /* Append the microseconds part, if that's requested */ | ||||||||
300 | if (us) { | ||||||||
301 | n = strlen(buf); | ||||||||
302 | if (n + 8 > l) | ||||||||
303 | return NULL((void*)0); /* Microseconds part doesn't fit. */ | ||||||||
304 | |||||||||
305 | sprintf(buf + n, ".%06"PRI_USEC"l" "u", t % USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
306 | } | ||||||||
307 | |||||||||
308 | /* Append the timezone */ | ||||||||
309 | n = strlen(buf); | ||||||||
310 | if (utc) { | ||||||||
311 | /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the | ||||||||
312 | * obsolete "GMT" instead. */ | ||||||||
313 | if (n + 5 > l) | ||||||||
314 | return NULL((void*)0); /* "UTC" doesn't fit. */ | ||||||||
315 | |||||||||
316 | strcpy(buf + n, " UTC"); | ||||||||
317 | |||||||||
318 | } else if (!isempty(tm.tm_zone)) { | ||||||||
319 | size_t tn; | ||||||||
320 | |||||||||
321 | /* An explicit timezone is specified, let's use it, if it fits */ | ||||||||
322 | tn = strlen(tm.tm_zone); | ||||||||
323 | if (n + 1 + tn + 1 > l) { | ||||||||
324 | /* The full time zone does not fit in. Yuck. */ | ||||||||
325 | |||||||||
326 | if (n + 1 + _POSIX_TZNAME_MAX6 + 1 > l) | ||||||||
327 | return NULL((void*)0); /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */ | ||||||||
328 | |||||||||
329 | /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX | ||||||||
330 | * minimum time zone length. In this case suppress the timezone entirely, in order not to dump | ||||||||
331 | * an overly long, hard to read string on the user. This should be safe, because the user will | ||||||||
332 | * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */ | ||||||||
333 | } else { | ||||||||
334 | buf[n++] = ' '; | ||||||||
335 | strcpy(buf + n, tm.tm_zone); | ||||||||
336 | } | ||||||||
337 | } | ||||||||
338 | |||||||||
339 | return buf; | ||||||||
340 | } | ||||||||
341 | |||||||||
342 | char *format_timestamp(char *buf, size_t l, usec_t t) { | ||||||||
343 | return format_timestamp_internal(buf, l, t, false0, false0); | ||||||||
344 | } | ||||||||
345 | |||||||||
346 | char *format_timestamp_utc(char *buf, size_t l, usec_t t) { | ||||||||
347 | return format_timestamp_internal(buf, l, t, true1, false0); | ||||||||
348 | } | ||||||||
349 | |||||||||
350 | char *format_timestamp_us(char *buf, size_t l, usec_t t) { | ||||||||
351 | return format_timestamp_internal(buf, l, t, false0, true1); | ||||||||
352 | } | ||||||||
353 | |||||||||
354 | char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) { | ||||||||
355 | return format_timestamp_internal(buf, l, t, true1, true1); | ||||||||
356 | } | ||||||||
357 | |||||||||
358 | char *format_timestamp_relative(char *buf, size_t l, usec_t t) { | ||||||||
359 | const char *s; | ||||||||
360 | usec_t n, d; | ||||||||
361 | |||||||||
362 | if (t <= 0 || t == USEC_INFINITY((usec_t) -1)) | ||||||||
363 | return NULL((void*)0); | ||||||||
364 | |||||||||
365 | n = now(CLOCK_REALTIME0); | ||||||||
366 | if (n > t) { | ||||||||
367 | d = n - t; | ||||||||
368 | s = "ago"; | ||||||||
369 | } else { | ||||||||
370 | d = t - n; | ||||||||
371 | s = "left"; | ||||||||
372 | } | ||||||||
373 | |||||||||
374 | if (d >= USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL)))) | ||||||||
375 | snprintf(buf, l, USEC_FMT"%" "l" "u" " years " USEC_FMT"%" "l" "u" " months %s", | ||||||||
376 | d / USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))), | ||||||||
377 | (d % USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL)))) / USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))), s); | ||||||||
378 | else if (d >= USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL)))) | ||||||||
379 | snprintf(buf, l, USEC_FMT"%" "l" "u" " months " USEC_FMT"%" "l" "u" " days %s", | ||||||||
380 | d / USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))), | ||||||||
381 | (d % USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL)))) / USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))), s); | ||||||||
382 | else if (d >= USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL)))))))))) | ||||||||
383 | snprintf(buf, l, USEC_FMT"%" "l" "u" " weeks " USEC_FMT"%" "l" "u" " days %s", | ||||||||
384 | d / USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))), | ||||||||
385 | (d % USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL)))))))))) / USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))), s); | ||||||||
386 | else if (d >= 2*USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL )))))))) | ||||||||
387 | snprintf(buf, l, USEC_FMT"%" "l" "u" " days %s", d / USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))), s); | ||||||||
388 | else if (d >= 25*USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) | ||||||||
389 | snprintf(buf, l, "1 day " USEC_FMT"%" "l" "u" "h %s", | ||||||||
390 | (d - USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL )))))))) / USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))), s); | ||||||||
391 | else if (d >= 6*USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) | ||||||||
392 | snprintf(buf, l, USEC_FMT"%" "l" "u" "h %s", | ||||||||
393 | d / USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))), s); | ||||||||
394 | else if (d >= USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) | ||||||||
395 | snprintf(buf, l, USEC_FMT"%" "l" "u" "h " USEC_FMT"%" "l" "u" "min %s", | ||||||||
396 | d / USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))), | ||||||||
397 | (d % USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL)))))) / USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))), s); | ||||||||
398 | else if (d >= 5*USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL)))) | ||||||||
399 | snprintf(buf, l, USEC_FMT"%" "l" "u" "min %s", | ||||||||
400 | d / USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))), s); | ||||||||
401 | else if (d >= USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL)))) | ||||||||
402 | snprintf(buf, l, USEC_FMT"%" "l" "u" "min " USEC_FMT"%" "l" "u" "s %s", | ||||||||
403 | d / USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))), | ||||||||
404 | (d % USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL)))) / USEC_PER_SEC((usec_t) 1000000ULL), s); | ||||||||
405 | else if (d >= USEC_PER_SEC((usec_t) 1000000ULL)) | ||||||||
406 | snprintf(buf, l, USEC_FMT"%" "l" "u" "s %s", | ||||||||
407 | d / USEC_PER_SEC((usec_t) 1000000ULL), s); | ||||||||
408 | else if (d >= USEC_PER_MSEC((usec_t) 1000ULL)) | ||||||||
409 | snprintf(buf, l, USEC_FMT"%" "l" "u" "ms %s", | ||||||||
410 | d / USEC_PER_MSEC((usec_t) 1000ULL), s); | ||||||||
411 | else if (d > 0) | ||||||||
412 | snprintf(buf, l, USEC_FMT"%" "l" "u""us %s", | ||||||||
413 | d, s); | ||||||||
414 | else | ||||||||
415 | snprintf(buf, l, "now"); | ||||||||
416 | |||||||||
417 | buf[l-1] = 0; | ||||||||
418 | return buf; | ||||||||
419 | } | ||||||||
420 | |||||||||
421 | char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) { | ||||||||
422 | static const struct { | ||||||||
423 | const char *suffix; | ||||||||
424 | usec_t usec; | ||||||||
425 | } table[] = { | ||||||||
426 | { "y", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
427 | { "month", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
428 | { "w", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
429 | { "d", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
430 | { "h", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
431 | { "min", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
432 | { "s", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
433 | { "ms", USEC_PER_MSEC((usec_t) 1000ULL) }, | ||||||||
434 | { "us", 1 }, | ||||||||
435 | }; | ||||||||
436 | |||||||||
437 | size_t i; | ||||||||
438 | char *p = buf; | ||||||||
439 | bool_Bool something = false0; | ||||||||
440 | |||||||||
441 | assert(buf)do { if ((__builtin_expect(!!(!(buf)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("buf"), "../src/basic/time-util.c", 441, __PRETTY_FUNCTION__); } while (0); | ||||||||
442 | assert(l > 0)do { if ((__builtin_expect(!!(!(l > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("l > 0"), "../src/basic/time-util.c", 442, __PRETTY_FUNCTION__); } while (0); | ||||||||
443 | |||||||||
444 | if (t == USEC_INFINITY((usec_t) -1)) { | ||||||||
445 | strncpy(p, "infinity", l-1); | ||||||||
446 | p[l-1] = 0; | ||||||||
447 | return p; | ||||||||
448 | } | ||||||||
449 | |||||||||
450 | if (t <= 0) { | ||||||||
451 | strncpy(p, "0", l-1); | ||||||||
452 | p[l-1] = 0; | ||||||||
453 | return p; | ||||||||
454 | } | ||||||||
455 | |||||||||
456 | /* The result of this function can be parsed with parse_sec */ | ||||||||
457 | |||||||||
458 | for (i = 0; i < ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0))); i++) { | ||||||||
459 | int k = 0; | ||||||||
460 | size_t n; | ||||||||
461 | bool_Bool done = false0; | ||||||||
462 | usec_t a, b; | ||||||||
463 | |||||||||
464 | if (t <= 0) | ||||||||
465 | break; | ||||||||
466 | |||||||||
467 | if (t < accuracy && something) | ||||||||
468 | break; | ||||||||
469 | |||||||||
470 | if (t < table[i].usec) | ||||||||
471 | continue; | ||||||||
472 | |||||||||
473 | if (l <= 1) | ||||||||
474 | break; | ||||||||
475 | |||||||||
476 | a = t / table[i].usec; | ||||||||
477 | b = t % table[i].usec; | ||||||||
478 | |||||||||
479 | /* Let's see if we should shows this in dot notation */ | ||||||||
480 | if (t < USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) && b > 0) { | ||||||||
481 | usec_t cc; | ||||||||
482 | signed char j; | ||||||||
483 | |||||||||
484 | j = 0; | ||||||||
485 | for (cc = table[i].usec; cc > 1; cc /= 10) | ||||||||
486 | j++; | ||||||||
487 | |||||||||
488 | for (cc = accuracy; cc > 1; cc /= 10) { | ||||||||
489 | b /= 10; | ||||||||
490 | j--; | ||||||||
491 | } | ||||||||
492 | |||||||||
493 | if (j > 0) { | ||||||||
494 | k = snprintf(p, l, | ||||||||
495 | "%s"USEC_FMT"%" "l" "u"".%0*"PRI_USEC"l" "u""%s", | ||||||||
496 | p > buf ? " " : "", | ||||||||
497 | a, | ||||||||
498 | j, | ||||||||
499 | b, | ||||||||
500 | table[i].suffix); | ||||||||
501 | |||||||||
502 | t = 0; | ||||||||
503 | done = true1; | ||||||||
504 | } | ||||||||
505 | } | ||||||||
506 | |||||||||
507 | /* No? Then let's show it normally */ | ||||||||
508 | if (!done) { | ||||||||
509 | k = snprintf(p, l, | ||||||||
510 | "%s"USEC_FMT"%" "l" "u""%s", | ||||||||
511 | p > buf ? " " : "", | ||||||||
512 | a, | ||||||||
513 | table[i].suffix); | ||||||||
514 | |||||||||
515 | t = b; | ||||||||
516 | } | ||||||||
517 | |||||||||
518 | n = MIN((size_t) k, l)__extension__ ({ const typeof(((size_t) k)) __unique_prefix_A16 = (((size_t) k)); const typeof((l)) __unique_prefix_B17 = (( l)); __unique_prefix_A16 < __unique_prefix_B17 ? __unique_prefix_A16 : __unique_prefix_B17; }); | ||||||||
519 | |||||||||
520 | l -= n; | ||||||||
521 | p += n; | ||||||||
522 | |||||||||
523 | something = true1; | ||||||||
524 | } | ||||||||
525 | |||||||||
526 | *p = 0; | ||||||||
527 | |||||||||
528 | return buf; | ||||||||
529 | } | ||||||||
530 | |||||||||
531 | void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) { | ||||||||
532 | |||||||||
533 | assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("f"), "../src/basic/time-util.c", 533, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
534 | assert(name)do { if ((__builtin_expect(!!(!(name)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("name"), "../src/basic/time-util.c", 534 , __PRETTY_FUNCTION__); } while (0); | ||||||||
535 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 535, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
536 | |||||||||
537 | if (!dual_timestamp_is_set(t)) | ||||||||
538 | return; | ||||||||
539 | |||||||||
540 | fprintf(f, "%s="USEC_FMT"%" "l" "u"" "USEC_FMT"%" "l" "u""\n", | ||||||||
541 | name, | ||||||||
542 | t->realtime, | ||||||||
543 | t->monotonic); | ||||||||
544 | } | ||||||||
545 | |||||||||
546 | int dual_timestamp_deserialize(const char *value, dual_timestamp *t) { | ||||||||
547 | uint64_t a, b; | ||||||||
548 | int r, pos; | ||||||||
549 | |||||||||
550 | assert(value)do { if ((__builtin_expect(!!(!(value)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("value"), "../src/basic/time-util.c", 550 , __PRETTY_FUNCTION__); } while (0); | ||||||||
551 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 551, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
552 | |||||||||
553 | pos = strspn(value, WHITESPACE" \t\n\r"); | ||||||||
554 | if (value[pos] == '-') | ||||||||
555 | return -EINVAL22; | ||||||||
556 | pos += strspn(value + pos, DIGITS"0123456789"); | ||||||||
557 | pos += strspn(value + pos, WHITESPACE" \t\n\r"); | ||||||||
558 | if (value[pos] == '-') | ||||||||
559 | return -EINVAL22; | ||||||||
560 | |||||||||
561 | r = sscanf(value, "%" PRIu64"l" "u" "%" PRIu64"l" "u" "%n", &a, &b, &pos); | ||||||||
562 | if (r != 2) { | ||||||||
563 | log_debug("Failed to parse dual timestamp value \"%s\".", value)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 563, __func__, "Failed to parse dual timestamp value \"%s\"." , value) : -abs(_e); }); | ||||||||
564 | return -EINVAL22; | ||||||||
565 | } | ||||||||
566 | |||||||||
567 | if (value[pos] != '\0') | ||||||||
568 | /* trailing garbage */ | ||||||||
569 | return -EINVAL22; | ||||||||
570 | |||||||||
571 | t->realtime = a; | ||||||||
572 | t->monotonic = b; | ||||||||
573 | |||||||||
574 | return 0; | ||||||||
575 | } | ||||||||
576 | |||||||||
577 | int timestamp_deserialize(const char *value, usec_t *timestamp) { | ||||||||
578 | int r; | ||||||||
579 | |||||||||
580 | assert(value)do { if ((__builtin_expect(!!(!(value)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("value"), "../src/basic/time-util.c", 580 , __PRETTY_FUNCTION__); } while (0); | ||||||||
581 | |||||||||
582 | r = safe_atou64(value, timestamp); | ||||||||
583 | if (r < 0) | ||||||||
584 | return log_debug_errno(r, "Failed to parse timestamp value \"%s\": %m", value)({ int _level = ((7)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 584, __func__, "Failed to parse timestamp value \"%s\": %m" , value) : -abs(_e); }); | ||||||||
585 | |||||||||
586 | return r; | ||||||||
587 | } | ||||||||
588 | |||||||||
589 | static int parse_timestamp_impl(const char *t, usec_t *usec, bool_Bool with_tz) { | ||||||||
590 | static const struct { | ||||||||
591 | const char *name; | ||||||||
592 | const int nr; | ||||||||
593 | } day_nr[] = { | ||||||||
594 | { "Sunday", 0 }, | ||||||||
595 | { "Sun", 0 }, | ||||||||
596 | { "Monday", 1 }, | ||||||||
597 | { "Mon", 1 }, | ||||||||
598 | { "Tuesday", 2 }, | ||||||||
599 | { "Tue", 2 }, | ||||||||
600 | { "Wednesday", 3 }, | ||||||||
601 | { "Wed", 3 }, | ||||||||
602 | { "Thursday", 4 }, | ||||||||
603 | { "Thu", 4 }, | ||||||||
604 | { "Friday", 5 }, | ||||||||
605 | { "Fri", 5 }, | ||||||||
606 | { "Saturday", 6 }, | ||||||||
607 | { "Sat", 6 }, | ||||||||
608 | }; | ||||||||
609 | |||||||||
610 | const char *k, *utc = NULL((void*)0), *tzn = NULL((void*)0); | ||||||||
611 | struct tm tm, copy; | ||||||||
612 | time_t x; | ||||||||
613 | usec_t x_usec, plus = 0, minus = 0, ret; | ||||||||
614 | int r, weekday = -1, dst = -1; | ||||||||
615 | size_t i; | ||||||||
616 | |||||||||
617 | /* Allowed syntaxes: | ||||||||
618 | * | ||||||||
619 | * 2012-09-22 16:34:22 | ||||||||
620 | * 2012-09-22 16:34 (seconds will be set to 0) | ||||||||
621 | * 2012-09-22 (time will be set to 00:00:00) | ||||||||
622 | * 16:34:22 (date will be set to today) | ||||||||
623 | * 16:34 (date will be set to today, seconds to 0) | ||||||||
624 | * now | ||||||||
625 | * yesterday (time is set to 00:00:00) | ||||||||
626 | * today (time is set to 00:00:00) | ||||||||
627 | * tomorrow (time is set to 00:00:00) | ||||||||
628 | * +5min | ||||||||
629 | * -5days | ||||||||
630 | * @2147483647 (seconds since epoch) | ||||||||
631 | */ | ||||||||
632 | |||||||||
633 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 633, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
| |||||||||
634 | assert(usec)do { if ((__builtin_expect(!!(!(usec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("usec"), "../src/basic/time-util.c", 634 , __PRETTY_FUNCTION__); } while (0); | ||||||||
635 | |||||||||
636 | if (t[0] == '@' && !with_tz) | ||||||||
637 | return parse_sec(t + 1, usec); | ||||||||
638 | |||||||||
639 | ret = now(CLOCK_REALTIME0); | ||||||||
640 | |||||||||
641 | if (!with_tz) { | ||||||||
642 | if (streq(t, "now")(strcmp((t),("now")) == 0)) | ||||||||
643 | goto finish; | ||||||||
644 | |||||||||
645 | else if (t[0] == '+') { | ||||||||
646 | r = parse_sec(t+1, &plus); | ||||||||
647 | if (r < 0) | ||||||||
648 | return r; | ||||||||
649 | |||||||||
650 | goto finish; | ||||||||
651 | |||||||||
652 | } else if (t[0] == '-') { | ||||||||
653 | r = parse_sec(t+1, &minus); | ||||||||
654 | if (r < 0) | ||||||||
655 | return r; | ||||||||
656 | |||||||||
657 | goto finish; | ||||||||
658 | |||||||||
659 | } else if ((k = endswith(t, " ago"))) { | ||||||||
660 | t = strndupa(t, k - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (k - t)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })); | ||||||||
661 | |||||||||
662 | r = parse_sec(t, &minus); | ||||||||
663 | if (r < 0) | ||||||||
664 | return r; | ||||||||
665 | |||||||||
666 | goto finish; | ||||||||
667 | |||||||||
668 | } else if ((k = endswith(t, " left"))) { | ||||||||
669 | t = strndupa(t, k - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (k - t)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old, __len ); })); | ||||||||
670 | |||||||||
671 | r = parse_sec(t, &plus); | ||||||||
672 | if (r < 0) | ||||||||
673 | return r; | ||||||||
674 | |||||||||
675 | goto finish; | ||||||||
676 | } | ||||||||
677 | |||||||||
678 | /* See if the timestamp is suffixed with UTC */ | ||||||||
679 | utc = endswith_no_case(t, " UTC"); | ||||||||
680 | if (utc) | ||||||||
681 | t = strndupa(t, utc - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (utc - t)); char *__new = (char *) __builtin_alloca ( __len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | ||||||||
682 | else { | ||||||||
683 | const char *e = NULL((void*)0); | ||||||||
684 | int j; | ||||||||
685 | |||||||||
686 | tzset(); | ||||||||
687 | |||||||||
688 | /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only | ||||||||
689 | * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because | ||||||||
690 | * there are no nice APIs available to cover this. By accepting the local time zone strings, we make | ||||||||
691 | * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't | ||||||||
692 | * support arbitrary timezone specifications. */ | ||||||||
693 | |||||||||
694 | for (j = 0; j <= 1; j++) { | ||||||||
695 | |||||||||
696 | if (isempty(tzname[j])) | ||||||||
697 | continue; | ||||||||
698 | |||||||||
699 | e = endswith_no_case(t, tzname[j]); | ||||||||
700 | if (!e) | ||||||||
701 | continue; | ||||||||
702 | if (e == t) | ||||||||
703 | continue; | ||||||||
704 | if (e[-1] != ' ') | ||||||||
705 | continue; | ||||||||
706 | |||||||||
707 | break; | ||||||||
708 | } | ||||||||
709 | |||||||||
710 | if (IN_SET(j, 0, 1)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){0, 1})/sizeof(int)]; switch(j) { case 0: case 1: _found = 1; break; default: break; } _found; })) { | ||||||||
711 | /* Found one of the two timezones specified. */ | ||||||||
712 | t = strndupa(t, e - t - 1)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (e - t - 1)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | ||||||||
713 | dst = j; | ||||||||
714 | tzn = tzname[j]; | ||||||||
715 | } | ||||||||
716 | } | ||||||||
717 | } | ||||||||
718 | |||||||||
719 | x = (time_t) (ret / USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
720 | x_usec = 0; | ||||||||
721 | |||||||||
722 | if (!localtime_or_gmtime_r(&x, &tm, utc)) | ||||||||
723 | return -EINVAL22; | ||||||||
724 | |||||||||
725 | tm.tm_isdst = dst; | ||||||||
726 | if (!with_tz
| ||||||||
727 | tm.tm_zone = tzn; | ||||||||
728 | |||||||||
729 | if (streq(t, "today")(strcmp((t),("today")) == 0)) { | ||||||||
730 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
731 | goto from_tm; | ||||||||
732 | |||||||||
733 | } else if (streq(t, "yesterday")(strcmp((t),("yesterday")) == 0)) { | ||||||||
734 | tm.tm_mday--; | ||||||||
735 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
736 | goto from_tm; | ||||||||
737 | |||||||||
738 | } else if (streq(t, "tomorrow")(strcmp((t),("tomorrow")) == 0)) { | ||||||||
739 | tm.tm_mday++; | ||||||||
740 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
741 | goto from_tm; | ||||||||
742 | } | ||||||||
743 | |||||||||
744 | for (i = 0; i < ELEMENTSOF(day_nr)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(day_nr), typeof(&*(day_nr))), sizeof(day_nr)/sizeof ((day_nr)[0]), ((void)0))); i++) { | ||||||||
745 | size_t skip; | ||||||||
746 | |||||||||
747 | if (!startswith_no_case(t, day_nr[i].name)) | ||||||||
748 | continue; | ||||||||
749 | |||||||||
750 | skip = strlen(day_nr[i].name); | ||||||||
751 | if (t[skip] != ' ') | ||||||||
| |||||||||
752 | continue; | ||||||||
753 | |||||||||
754 | weekday = day_nr[i].nr; | ||||||||
755 | t += skip + 1; | ||||||||
756 | break; | ||||||||
757 | } | ||||||||
758 | |||||||||
759 | copy = tm; | ||||||||
760 | k = strptime(t, "%y-%m-%d %H:%M:%S", &tm); | ||||||||
761 | if (k) { | ||||||||
762 | if (*k == '.') | ||||||||
763 | goto parse_usec; | ||||||||
764 | else if (*k == 0) | ||||||||
765 | goto from_tm; | ||||||||
766 | } | ||||||||
767 | |||||||||
768 | tm = copy; | ||||||||
769 | k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm); | ||||||||
770 | if (k) { | ||||||||
771 | if (*k == '.') | ||||||||
772 | goto parse_usec; | ||||||||
773 | else if (*k == 0) | ||||||||
774 | goto from_tm; | ||||||||
775 | } | ||||||||
776 | |||||||||
777 | tm = copy; | ||||||||
778 | k = strptime(t, "%y-%m-%d %H:%M", &tm); | ||||||||
779 | if (k && *k == 0) { | ||||||||
780 | tm.tm_sec = 0; | ||||||||
781 | goto from_tm; | ||||||||
782 | } | ||||||||
783 | |||||||||
784 | tm = copy; | ||||||||
785 | k = strptime(t, "%Y-%m-%d %H:%M", &tm); | ||||||||
786 | if (k && *k == 0) { | ||||||||
787 | tm.tm_sec = 0; | ||||||||
788 | goto from_tm; | ||||||||
789 | } | ||||||||
790 | |||||||||
791 | tm = copy; | ||||||||
792 | k = strptime(t, "%y-%m-%d", &tm); | ||||||||
793 | if (k && *k == 0) { | ||||||||
794 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
795 | goto from_tm; | ||||||||
796 | } | ||||||||
797 | |||||||||
798 | tm = copy; | ||||||||
799 | k = strptime(t, "%Y-%m-%d", &tm); | ||||||||
800 | if (k && *k == 0) { | ||||||||
801 | tm.tm_sec = tm.tm_min = tm.tm_hour = 0; | ||||||||
802 | goto from_tm; | ||||||||
803 | } | ||||||||
804 | |||||||||
805 | tm = copy; | ||||||||
806 | k = strptime(t, "%H:%M:%S", &tm); | ||||||||
807 | if (k) { | ||||||||
808 | if (*k == '.') | ||||||||
809 | goto parse_usec; | ||||||||
810 | else if (*k == 0) | ||||||||
811 | goto from_tm; | ||||||||
812 | } | ||||||||
813 | |||||||||
814 | tm = copy; | ||||||||
815 | k = strptime(t, "%H:%M", &tm); | ||||||||
816 | if (k && *k == 0) { | ||||||||
817 | tm.tm_sec = 0; | ||||||||
818 | goto from_tm; | ||||||||
819 | } | ||||||||
820 | |||||||||
821 | return -EINVAL22; | ||||||||
822 | |||||||||
823 | parse_usec: | ||||||||
824 | { | ||||||||
825 | unsigned add; | ||||||||
826 | |||||||||
827 | k++; | ||||||||
828 | r = parse_fractional_part_u(&k, 6, &add); | ||||||||
829 | if (r < 0) | ||||||||
830 | return -EINVAL22; | ||||||||
831 | |||||||||
832 | if (*k) | ||||||||
833 | return -EINVAL22; | ||||||||
834 | |||||||||
835 | x_usec = add; | ||||||||
836 | } | ||||||||
837 | |||||||||
838 | from_tm: | ||||||||
839 | if (weekday >= 0 && tm.tm_wday != weekday) | ||||||||
840 | return -EINVAL22; | ||||||||
841 | |||||||||
842 | x = mktime_or_timegm(&tm, utc); | ||||||||
843 | if (x < 0) | ||||||||
844 | return -EINVAL22; | ||||||||
845 | |||||||||
846 | ret = (usec_t) x * USEC_PER_SEC((usec_t) 1000000ULL) + x_usec; | ||||||||
847 | if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000)) | ||||||||
848 | return -EINVAL22; | ||||||||
849 | |||||||||
850 | finish: | ||||||||
851 | if (ret + plus < ret) /* overflow? */ | ||||||||
852 | return -EINVAL22; | ||||||||
853 | ret += plus; | ||||||||
854 | if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000)) | ||||||||
855 | return -EINVAL22; | ||||||||
856 | |||||||||
857 | if (ret >= minus) | ||||||||
858 | ret -= minus; | ||||||||
859 | else | ||||||||
860 | return -EINVAL22; | ||||||||
861 | |||||||||
862 | *usec = ret; | ||||||||
863 | |||||||||
864 | return 0; | ||||||||
865 | } | ||||||||
866 | |||||||||
867 | typedef struct ParseTimestampResult { | ||||||||
868 | usec_t usec; | ||||||||
869 | int return_value; | ||||||||
870 | } ParseTimestampResult; | ||||||||
871 | |||||||||
872 | int parse_timestamp(const char *t, usec_t *usec) { | ||||||||
873 | char *last_space, *tz = NULL((void*)0); | ||||||||
874 | ParseTimestampResult *shared, tmp; | ||||||||
875 | int r; | ||||||||
876 | |||||||||
877 | last_space = strrchr(t, ' '); | ||||||||
878 | if (last_space != NULL((void*)0) && timezone_is_valid(last_space + 1, LOG_DEBUG7)) | ||||||||
879 | tz = last_space + 1; | ||||||||
880 | |||||||||
881 | if (!tz || endswith_no_case(t, " UTC")) | ||||||||
882 | return parse_timestamp_impl(t, usec, false0); | ||||||||
883 | |||||||||
884 | shared = mmap(NULL((void*)0), sizeof *shared, PROT_READ0x1|PROT_WRITE0x2, MAP_SHARED0x01|MAP_ANONYMOUS0x20, -1, 0); | ||||||||
885 | if (shared == MAP_FAILED((void *) -1)) | ||||||||
886 | return negative_errno(); | ||||||||
887 | |||||||||
888 | r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL((void*)0)); | ||||||||
889 | if (r < 0) { | ||||||||
890 | (void) munmap(shared, sizeof *shared); | ||||||||
891 | return r; | ||||||||
892 | } | ||||||||
893 | if (r == 0) { | ||||||||
894 | bool_Bool with_tz = true1; | ||||||||
895 | |||||||||
896 | if (setenv("TZ", tz, 1) != 0) { | ||||||||
897 | shared->return_value = negative_errno(); | ||||||||
898 | _exit(EXIT_FAILURE1); | ||||||||
899 | } | ||||||||
900 | |||||||||
901 | tzset(); | ||||||||
902 | |||||||||
903 | /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation. | ||||||||
904 | * Otherwise just cut it off. */ | ||||||||
905 | with_tz = !STR_IN_SET(tz, tzname[0], tzname[1])(!!strv_find((((char**) ((const char*[]) { tzname[0], tzname[ 1], ((void*)0) }))), (tz))); | ||||||||
906 | |||||||||
907 | /* Cut off the timezone if we dont need it. */ | ||||||||
908 | if (with_tz) | ||||||||
909 | t = strndupa(t, last_space - t)(__extension__ ({ const char *__old = (t); size_t __len = strnlen (__old, (last_space - t)); char *__new = (char *) __builtin_alloca (__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old , __len); })); | ||||||||
910 | |||||||||
911 | shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz); | ||||||||
912 | |||||||||
913 | _exit(EXIT_SUCCESS0); | ||||||||
914 | } | ||||||||
915 | |||||||||
916 | tmp = *shared; | ||||||||
917 | if (munmap(shared, sizeof *shared) != 0) | ||||||||
918 | return negative_errno(); | ||||||||
919 | |||||||||
920 | if (tmp.return_value == 0) | ||||||||
921 | *usec = tmp.usec; | ||||||||
922 | |||||||||
923 | return tmp.return_value; | ||||||||
924 | } | ||||||||
925 | |||||||||
926 | static char* extract_multiplier(char *p, usec_t *multiplier) { | ||||||||
927 | static const struct { | ||||||||
928 | const char *suffix; | ||||||||
929 | usec_t usec; | ||||||||
930 | } table[] = { | ||||||||
931 | { "seconds", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
932 | { "second", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
933 | { "sec", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
934 | { "s", USEC_PER_SEC((usec_t) 1000000ULL) }, | ||||||||
935 | { "minutes", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
936 | { "minute", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
937 | { "min", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
938 | { "months", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
939 | { "month", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
940 | { "M", USEC_PER_MONTH((usec_t) (2629800ULL*((usec_t) 1000000ULL))) }, | ||||||||
941 | { "msec", USEC_PER_MSEC((usec_t) 1000ULL) }, | ||||||||
942 | { "ms", USEC_PER_MSEC((usec_t) 1000ULL) }, | ||||||||
943 | { "m", USEC_PER_MINUTE((usec_t) (60ULL*((usec_t) 1000000ULL))) }, | ||||||||
944 | { "hours", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
945 | { "hour", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
946 | { "hr", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
947 | { "h", USEC_PER_HOUR((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL))))) }, | ||||||||
948 | { "days", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
949 | { "day", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
950 | { "d", USEC_PER_DAY((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) (60ULL*((usec_t) 1000000ULL ))))))) }, | ||||||||
951 | { "weeks", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
952 | { "week", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
953 | { "w", USEC_PER_WEEK((usec_t) (7ULL*((usec_t) (24ULL*((usec_t) (60ULL*((usec_t) ( 60ULL*((usec_t) 1000000ULL))))))))) }, | ||||||||
954 | { "years", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
955 | { "year", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
956 | { "y", USEC_PER_YEAR((usec_t) (31557600ULL*((usec_t) 1000000ULL))) }, | ||||||||
957 | { "usec", 1ULL }, | ||||||||
958 | { "us", 1ULL }, | ||||||||
959 | { "µs", 1ULL }, | ||||||||
960 | }; | ||||||||
961 | size_t i; | ||||||||
962 | |||||||||
963 | for (i = 0; i < ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0))); i++) { | ||||||||
964 | char *e; | ||||||||
965 | |||||||||
966 | e = startswith(p, table[i].suffix); | ||||||||
967 | if (e) { | ||||||||
968 | *multiplier = table[i].usec; | ||||||||
969 | return e; | ||||||||
970 | } | ||||||||
971 | } | ||||||||
972 | |||||||||
973 | return p; | ||||||||
974 | } | ||||||||
975 | |||||||||
976 | int parse_time(const char *t, usec_t *usec, usec_t default_unit) { | ||||||||
977 | const char *p, *s; | ||||||||
978 | usec_t r = 0; | ||||||||
979 | bool_Bool something = false0; | ||||||||
980 | |||||||||
981 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 981, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
982 | assert(usec)do { if ((__builtin_expect(!!(!(usec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("usec"), "../src/basic/time-util.c", 982 , __PRETTY_FUNCTION__); } while (0); | ||||||||
983 | assert(default_unit > 0)do { if ((__builtin_expect(!!(!(default_unit > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("default_unit > 0"), "../src/basic/time-util.c" , 983, __PRETTY_FUNCTION__); } while (0); | ||||||||
984 | |||||||||
985 | p = t; | ||||||||
986 | |||||||||
987 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
988 | s = startswith(p, "infinity"); | ||||||||
989 | if (s) { | ||||||||
990 | s += strspn(s, WHITESPACE" \t\n\r"); | ||||||||
991 | if (*s != 0) | ||||||||
992 | return -EINVAL22; | ||||||||
993 | |||||||||
994 | *usec = USEC_INFINITY((usec_t) -1); | ||||||||
995 | return 0; | ||||||||
996 | } | ||||||||
997 | |||||||||
998 | for (;;) { | ||||||||
999 | long long l, z = 0; | ||||||||
1000 | char *e; | ||||||||
1001 | unsigned n = 0; | ||||||||
1002 | usec_t multiplier = default_unit, k; | ||||||||
1003 | |||||||||
1004 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
1005 | |||||||||
1006 | if (*p == 0) { | ||||||||
1007 | if (!something) | ||||||||
1008 | return -EINVAL22; | ||||||||
1009 | |||||||||
1010 | break; | ||||||||
1011 | } | ||||||||
1012 | |||||||||
1013 | errno(*__errno_location ()) = 0; | ||||||||
1014 | l = strtoll(p, &e, 10); | ||||||||
1015 | if (errno(*__errno_location ()) > 0) | ||||||||
1016 | return -errno(*__errno_location ()); | ||||||||
1017 | if (l < 0) | ||||||||
1018 | return -ERANGE34; | ||||||||
1019 | |||||||||
1020 | if (*e == '.') { | ||||||||
1021 | char *b = e + 1; | ||||||||
1022 | |||||||||
1023 | errno(*__errno_location ()) = 0; | ||||||||
1024 | z = strtoll(b, &e, 10); | ||||||||
1025 | if (errno(*__errno_location ()) > 0) | ||||||||
1026 | return -errno(*__errno_location ()); | ||||||||
1027 | |||||||||
1028 | if (z < 0) | ||||||||
1029 | return -ERANGE34; | ||||||||
1030 | |||||||||
1031 | if (e == b) | ||||||||
1032 | return -EINVAL22; | ||||||||
1033 | |||||||||
1034 | n = e - b; | ||||||||
1035 | |||||||||
1036 | } else if (e == p) | ||||||||
1037 | return -EINVAL22; | ||||||||
1038 | |||||||||
1039 | e += strspn(e, WHITESPACE" \t\n\r"); | ||||||||
1040 | p = extract_multiplier(e, &multiplier); | ||||||||
1041 | |||||||||
1042 | something = true1; | ||||||||
1043 | |||||||||
1044 | k = (usec_t) z * multiplier; | ||||||||
1045 | |||||||||
1046 | for (; n > 0; n--) | ||||||||
1047 | k /= 10; | ||||||||
1048 | |||||||||
1049 | r += (usec_t) l * multiplier + k; | ||||||||
1050 | } | ||||||||
1051 | |||||||||
1052 | *usec = r; | ||||||||
1053 | |||||||||
1054 | return 0; | ||||||||
1055 | } | ||||||||
1056 | |||||||||
1057 | int parse_sec(const char *t, usec_t *usec) { | ||||||||
1058 | return parse_time(t, usec, USEC_PER_SEC((usec_t) 1000000ULL)); | ||||||||
1059 | } | ||||||||
1060 | |||||||||
1061 | int parse_sec_fix_0(const char *t, usec_t *usec) { | ||||||||
1062 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 1062, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
1063 | assert(usec)do { if ((__builtin_expect(!!(!(usec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("usec"), "../src/basic/time-util.c", 1063 , __PRETTY_FUNCTION__); } while (0); | ||||||||
1064 | |||||||||
1065 | t += strspn(t, WHITESPACE" \t\n\r"); | ||||||||
1066 | |||||||||
1067 | if (streq(t, "0")(strcmp((t),("0")) == 0)) { | ||||||||
1068 | *usec = USEC_INFINITY((usec_t) -1); | ||||||||
1069 | return 0; | ||||||||
1070 | } | ||||||||
1071 | |||||||||
1072 | return parse_sec(t, usec); | ||||||||
1073 | } | ||||||||
1074 | |||||||||
1075 | int parse_sec_def_infinity(const char *t, usec_t *ret) { | ||||||||
1076 | t += strspn(t, WHITESPACE" \t\n\r"); | ||||||||
1077 | if (isempty(t)) { | ||||||||
1078 | *ret = USEC_INFINITY((usec_t) -1); | ||||||||
1079 | return 0; | ||||||||
1080 | } | ||||||||
1081 | return parse_sec(t, ret); | ||||||||
1082 | } | ||||||||
1083 | |||||||||
1084 | int parse_nsec(const char *t, nsec_t *nsec) { | ||||||||
1085 | static const struct { | ||||||||
1086 | const char *suffix; | ||||||||
1087 | nsec_t nsec; | ||||||||
1088 | } table[] = { | ||||||||
1089 | { "seconds", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
1090 | { "second", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
1091 | { "sec", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
1092 | { "s", NSEC_PER_SEC((nsec_t) 1000000000ULL) }, | ||||||||
1093 | { "minutes", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1094 | { "minute", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1095 | { "min", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1096 | { "months", NSEC_PER_MONTH((nsec_t) (2629800ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1097 | { "month", NSEC_PER_MONTH((nsec_t) (2629800ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1098 | { "msec", NSEC_PER_MSEC((nsec_t) 1000000ULL) }, | ||||||||
1099 | { "ms", NSEC_PER_MSEC((nsec_t) 1000000ULL) }, | ||||||||
1100 | { "m", NSEC_PER_MINUTE((nsec_t) (60ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1101 | { "hours", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
1102 | { "hour", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
1103 | { "hr", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
1104 | { "h", NSEC_PER_HOUR((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL)))) ) }, | ||||||||
1105 | { "days", NSEC_PER_DAY((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL ))))))) }, | ||||||||
1106 | { "day", NSEC_PER_DAY((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL ))))))) }, | ||||||||
1107 | { "d", NSEC_PER_DAY((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) (60ULL*((nsec_t) 1000000000ULL ))))))) }, | ||||||||
1108 | { "weeks", NSEC_PER_WEEK((nsec_t) (7ULL*((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) ( 60ULL*((nsec_t) 1000000000ULL))))))))) }, | ||||||||
1109 | { "week", NSEC_PER_WEEK((nsec_t) (7ULL*((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) ( 60ULL*((nsec_t) 1000000000ULL))))))))) }, | ||||||||
1110 | { "w", NSEC_PER_WEEK((nsec_t) (7ULL*((nsec_t) (24ULL*((nsec_t) (60ULL*((nsec_t) ( 60ULL*((nsec_t) 1000000000ULL))))))))) }, | ||||||||
1111 | { "years", NSEC_PER_YEAR((nsec_t) (31557600ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1112 | { "year", NSEC_PER_YEAR((nsec_t) (31557600ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1113 | { "y", NSEC_PER_YEAR((nsec_t) (31557600ULL*((nsec_t) 1000000000ULL))) }, | ||||||||
1114 | { "usec", NSEC_PER_USEC((nsec_t) 1000ULL) }, | ||||||||
1115 | { "us", NSEC_PER_USEC((nsec_t) 1000ULL) }, | ||||||||
1116 | { "µs", NSEC_PER_USEC((nsec_t) 1000ULL) }, | ||||||||
1117 | { "nsec", 1ULL }, | ||||||||
1118 | { "ns", 1ULL }, | ||||||||
1119 | { "", 1ULL }, /* default is nsec */ | ||||||||
1120 | }; | ||||||||
1121 | |||||||||
1122 | const char *p, *s; | ||||||||
1123 | nsec_t r = 0; | ||||||||
1124 | bool_Bool something = false0; | ||||||||
1125 | |||||||||
1126 | assert(t)do { if ((__builtin_expect(!!(!(t)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("t"), "../src/basic/time-util.c", 1126, __PRETTY_FUNCTION__ ); } while (0); | ||||||||
1127 | assert(nsec)do { if ((__builtin_expect(!!(!(nsec)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("nsec"), "../src/basic/time-util.c", 1127 , __PRETTY_FUNCTION__); } while (0); | ||||||||
1128 | |||||||||
1129 | p = t; | ||||||||
1130 | |||||||||
1131 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
1132 | s = startswith(p, "infinity"); | ||||||||
1133 | if (s) { | ||||||||
1134 | s += strspn(s, WHITESPACE" \t\n\r"); | ||||||||
1135 | if (*s != 0) | ||||||||
1136 | return -EINVAL22; | ||||||||
1137 | |||||||||
1138 | *nsec = NSEC_INFINITY((nsec_t) -1); | ||||||||
1139 | return 0; | ||||||||
1140 | } | ||||||||
1141 | |||||||||
1142 | for (;;) { | ||||||||
1143 | long long l, z = 0; | ||||||||
1144 | size_t n = 0, i; | ||||||||
1145 | char *e; | ||||||||
1146 | |||||||||
1147 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
1148 | |||||||||
1149 | if (*p == 0) { | ||||||||
1150 | if (!something) | ||||||||
1151 | return -EINVAL22; | ||||||||
1152 | |||||||||
1153 | break; | ||||||||
1154 | } | ||||||||
1155 | |||||||||
1156 | errno(*__errno_location ()) = 0; | ||||||||
1157 | l = strtoll(p, &e, 10); | ||||||||
1158 | |||||||||
1159 | if (errno(*__errno_location ()) > 0) | ||||||||
1160 | return -errno(*__errno_location ()); | ||||||||
1161 | |||||||||
1162 | if (l < 0) | ||||||||
1163 | return -ERANGE34; | ||||||||
1164 | |||||||||
1165 | if (*e == '.') { | ||||||||
1166 | char *b = e + 1; | ||||||||
1167 | |||||||||
1168 | errno(*__errno_location ()) = 0; | ||||||||
1169 | z = strtoll(b, &e, 10); | ||||||||
1170 | if (errno(*__errno_location ()) > 0) | ||||||||
1171 | return -errno(*__errno_location ()); | ||||||||
1172 | |||||||||
1173 | if (z < 0) | ||||||||
1174 | return -ERANGE34; | ||||||||
1175 | |||||||||
1176 | if (e == b) | ||||||||
1177 | return -EINVAL22; | ||||||||
1178 | |||||||||
1179 | n = e - b; | ||||||||
1180 | |||||||||
1181 | } else if (e == p) | ||||||||
1182 | return -EINVAL22; | ||||||||
1183 | |||||||||
1184 | e += strspn(e, WHITESPACE" \t\n\r"); | ||||||||
1185 | |||||||||
1186 | for (i = 0; i < ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0))); i++) | ||||||||
1187 | if (startswith(e, table[i].suffix)) { | ||||||||
1188 | nsec_t k = (nsec_t) z * table[i].nsec; | ||||||||
1189 | |||||||||
1190 | for (; n > 0; n--) | ||||||||
1191 | k /= 10; | ||||||||
1192 | |||||||||
1193 | r += (nsec_t) l * table[i].nsec + k; | ||||||||
1194 | p = e + strlen(table[i].suffix); | ||||||||
1195 | |||||||||
1196 | something = true1; | ||||||||
1197 | break; | ||||||||
1198 | } | ||||||||
1199 | |||||||||
1200 | if (i >= ELEMENTSOF(table)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(table), typeof(&*(table))), sizeof(table)/sizeof( (table)[0]), ((void)0)))) | ||||||||
1201 | return -EINVAL22; | ||||||||
1202 | |||||||||
1203 | } | ||||||||
1204 | |||||||||
1205 | *nsec = r; | ||||||||
1206 | |||||||||
1207 | return 0; | ||||||||
1208 | } | ||||||||
1209 | |||||||||
1210 | bool_Bool ntp_synced(void) { | ||||||||
1211 | struct timex txc = {}; | ||||||||
1212 | |||||||||
1213 | if (adjtimex(&txc) < 0) | ||||||||
1214 | return false0; | ||||||||
1215 | |||||||||
1216 | if (txc.status & STA_UNSYNC0x0040) | ||||||||
1217 | return false0; | ||||||||
1218 | |||||||||
1219 | return true1; | ||||||||
1220 | } | ||||||||
1221 | |||||||||
1222 | int get_timezones(char ***ret) { | ||||||||
1223 | _cleanup_fclose___attribute__((cleanup(fclosep))) FILE *f = NULL((void*)0); | ||||||||
1224 | _cleanup_strv_free___attribute__((cleanup(strv_freep))) char **zones = NULL((void*)0); | ||||||||
1225 | size_t n_zones = 0, n_allocated = 0; | ||||||||
1226 | |||||||||
1227 | assert(ret)do { if ((__builtin_expect(!!(!(ret)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("ret"), "../src/basic/time-util.c", 1227 , __PRETTY_FUNCTION__); } while (0); | ||||||||
1228 | |||||||||
1229 | zones = strv_new("UTC", NULL((void*)0)); | ||||||||
1230 | if (!zones) | ||||||||
1231 | return -ENOMEM12; | ||||||||
1232 | |||||||||
1233 | n_allocated = 2; | ||||||||
1234 | n_zones = 1; | ||||||||
1235 | |||||||||
1236 | f = fopen("/usr/share/zoneinfo/zone.tab", "re"); | ||||||||
1237 | if (f) { | ||||||||
1238 | char l[LINE_MAX2048]; | ||||||||
1239 | |||||||||
1240 | FOREACH_LINE(l, f, return -errno)for (;;) if (!fgets(l, sizeof(l), f)) { if (ferror(f)) { return -(*__errno_location ()); } break; } else { | ||||||||
1241 | char *p, *w; | ||||||||
1242 | size_t k; | ||||||||
1243 | |||||||||
1244 | p = strstrip(l); | ||||||||
1245 | |||||||||
1246 | if (isempty(p) || *p == '#') | ||||||||
1247 | continue; | ||||||||
1248 | |||||||||
1249 | /* Skip over country code */ | ||||||||
1250 | p += strcspn(p, WHITESPACE" \t\n\r"); | ||||||||
1251 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
1252 | |||||||||
1253 | /* Skip over coordinates */ | ||||||||
1254 | p += strcspn(p, WHITESPACE" \t\n\r"); | ||||||||
1255 | p += strspn(p, WHITESPACE" \t\n\r"); | ||||||||
1256 | |||||||||
1257 | /* Found timezone name */ | ||||||||
1258 | k = strcspn(p, WHITESPACE" \t\n\r"); | ||||||||
1259 | if (k <= 0) | ||||||||
1260 | continue; | ||||||||
1261 | |||||||||
1262 | w = strndup(p, k); | ||||||||
1263 | if (!w) | ||||||||
1264 | return -ENOMEM12; | ||||||||
1265 | |||||||||
1266 | if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)greedy_realloc((void**) &(zones), &(n_allocated), (n_zones + 2), sizeof((zones)[0]))) { | ||||||||
1267 | free(w); | ||||||||
1268 | return -ENOMEM12; | ||||||||
1269 | } | ||||||||
1270 | |||||||||
1271 | zones[n_zones++] = w; | ||||||||
1272 | zones[n_zones] = NULL((void*)0); | ||||||||
1273 | } | ||||||||
1274 | |||||||||
1275 | strv_sort(zones); | ||||||||
1276 | |||||||||
1277 | } else if (errno(*__errno_location ()) != ENOENT2) | ||||||||
1278 | return -errno(*__errno_location ()); | ||||||||
1279 | |||||||||
1280 | *ret = TAKE_PTR(zones)({ typeof(zones) _ptr_ = (zones); (zones) = ((void*)0); _ptr_ ; }); | ||||||||
1281 | |||||||||
1282 | return 0; | ||||||||
1283 | } | ||||||||
1284 | |||||||||
1285 | bool_Bool timezone_is_valid(const char *name, int log_level) { | ||||||||
1286 | bool_Bool slash = false0; | ||||||||
1287 | const char *p, *t; | ||||||||
1288 | _cleanup_close___attribute__((cleanup(closep))) int fd = -1; | ||||||||
1289 | char buf[4]; | ||||||||
1290 | int r; | ||||||||
1291 | |||||||||
1292 | if (isempty(name)) | ||||||||
1293 | return false0; | ||||||||
1294 | |||||||||
1295 | if (name[0] == '/') | ||||||||
1296 | return false0; | ||||||||
1297 | |||||||||
1298 | for (p = name; *p; p++) { | ||||||||
1299 | if (!(*p >= '0' && *p <= '9') && | ||||||||
1300 | !(*p >= 'a' && *p <= 'z') && | ||||||||
1301 | !(*p >= 'A' && *p <= 'Z') && | ||||||||
1302 | !IN_SET(*p, '-', '_', '+', '/')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended [20 - sizeof((int[]){'-', '_', '+', '/'})/sizeof(int)]; switch (*p) { case '-': case '_': case '+': case '/': _found = 1; break ; default: break; } _found; })) | ||||||||
1303 | return false0; | ||||||||
1304 | |||||||||
1305 | if (*p == '/') { | ||||||||
1306 | |||||||||
1307 | if (slash) | ||||||||
1308 | return false0; | ||||||||
1309 | |||||||||
1310 | slash = true1; | ||||||||
1311 | } else | ||||||||
1312 | slash = false0; | ||||||||
1313 | } | ||||||||
1314 | |||||||||
1315 | if (slash) | ||||||||
1316 | return false0; | ||||||||
1317 | |||||||||
1318 | if (p - name >= PATH_MAX4096) | ||||||||
1319 | return false0; | ||||||||
1320 | |||||||||
1321 | t = strjoina("/usr/share/zoneinfo/", name)({ const char *_appendees_[] = { "/usr/share/zoneinfo/", name }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_ [_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }); | ||||||||
1322 | |||||||||
1323 | fd = open(t, O_RDONLY00|O_CLOEXEC02000000); | ||||||||
1324 | if (fd < 0) { | ||||||||
1325 | log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t)({ int _level = ((log_level)), _e = (((*__errno_location ())) ), _realm = (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm ) >= ((_level) & 0x07)) ? log_internal_realm(((_realm) << 10 | (_level)), _e, "../src/basic/time-util.c", 1325 , __func__, "Failed to open timezone file '%s': %m", t) : -abs (_e); }); | ||||||||
1326 | return false0; | ||||||||
1327 | } | ||||||||
1328 | |||||||||
1329 | r = fd_verify_regular(fd); | ||||||||
1330 | if (r < 0) { | ||||||||
1331 | log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t)({ int _level = ((log_level)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 1331, __func__, "Timezone file '%s' is not a regular file: %m" , t) : -abs(_e); }); | ||||||||
1332 | return false0; | ||||||||
1333 | } | ||||||||
1334 | |||||||||
1335 | r = loop_read_exact(fd, buf, 4, false0); | ||||||||
1336 | if (r < 0) { | ||||||||
1337 | log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t)({ int _level = ((log_level)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 1337, __func__, "Failed to read from timezone file '%s': %m" , t) : -abs(_e); }); | ||||||||
1338 | return false0; | ||||||||
1339 | } | ||||||||
1340 | |||||||||
1341 | /* Magic from tzfile(5) */ | ||||||||
1342 | if (memcmp(buf, "TZif", 4) != 0) { | ||||||||
1343 | log_full(log_level, "Timezone file '%s' has wrong magic bytes", t)({ int _level = (((log_level))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD ); (log_get_max_level_realm(_realm) >= ((_level) & 0x07 )) ? log_internal_realm(((_realm) << 10 | (_level)), _e , "../src/basic/time-util.c", 1343, __func__, "Timezone file '%s' has wrong magic bytes" , t) : -abs(_e); }); | ||||||||
1344 | return false0; | ||||||||
1345 | } | ||||||||
1346 | |||||||||
1347 | return true1; | ||||||||
1348 | } | ||||||||
1349 | |||||||||
1350 | bool_Bool clock_boottime_supported(void) { | ||||||||
1351 | static int supported = -1; | ||||||||
1352 | |||||||||
1353 | /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */ | ||||||||
1354 | |||||||||
1355 | if (supported < 0) { | ||||||||
1356 | int fd; | ||||||||
1357 | |||||||||
1358 | fd = timerfd_create(CLOCK_BOOTTIME7, TFD_NONBLOCKTFD_NONBLOCK|TFD_CLOEXECTFD_CLOEXEC); | ||||||||
1359 | if (fd < 0) | ||||||||
1360 | supported = false0; | ||||||||
1361 | else { | ||||||||
1362 | safe_close(fd); | ||||||||
1363 | supported = true1; | ||||||||
1364 | } | ||||||||
1365 | } | ||||||||
1366 | |||||||||
1367 | return supported; | ||||||||
1368 | } | ||||||||
1369 | |||||||||
1370 | clockid_t clock_boottime_or_monotonic(void) { | ||||||||
1371 | if (clock_boottime_supported()) | ||||||||
1372 | return CLOCK_BOOTTIME7; | ||||||||
1373 | else | ||||||||
1374 | return CLOCK_MONOTONIC1; | ||||||||
1375 | } | ||||||||
1376 | |||||||||
1377 | bool_Bool clock_supported(clockid_t clock) { | ||||||||
1378 | struct timespec ts; | ||||||||
1379 | |||||||||
1380 | switch (clock) { | ||||||||
1381 | |||||||||
1382 | case CLOCK_MONOTONIC1: | ||||||||
1383 | case CLOCK_REALTIME0: | ||||||||
1384 | return true1; | ||||||||
1385 | |||||||||
1386 | case CLOCK_BOOTTIME7: | ||||||||
1387 | return clock_boottime_supported(); | ||||||||
1388 | |||||||||
1389 | case CLOCK_BOOTTIME_ALARM9: | ||||||||
1390 | if (!clock_boottime_supported()) | ||||||||
1391 | return false0; | ||||||||
1392 | |||||||||
1393 | _fallthrough_; | ||||||||
1394 | default: | ||||||||
1395 | /* For everything else, check properly */ | ||||||||
1396 | return clock_gettime(clock, &ts) >= 0; | ||||||||
1397 | } | ||||||||
1398 | } | ||||||||
1399 | |||||||||
1400 | int get_timezone(char **tz) { | ||||||||
1401 | _cleanup_free___attribute__((cleanup(freep))) char *t = NULL((void*)0); | ||||||||
1402 | const char *e; | ||||||||
1403 | char *z; | ||||||||
1404 | int r; | ||||||||
1405 | |||||||||
1406 | r = readlink_malloc("/etc/localtime", &t); | ||||||||
1407 | if (r < 0) | ||||||||
1408 | return r; /* returns EINVAL if not a symlink */ | ||||||||
1409 | |||||||||
1410 | e = path_startswith(t, "/usr/share/zoneinfo/"); | ||||||||
1411 | if (!e) | ||||||||
1412 | e = path_startswith(t, "../usr/share/zoneinfo/"); | ||||||||
1413 | if (!e) | ||||||||
1414 | return -EINVAL22; | ||||||||
1415 | |||||||||
1416 | if (!timezone_is_valid(e, LOG_DEBUG7)) | ||||||||
1417 | return -EINVAL22; | ||||||||
1418 | |||||||||
1419 | z = strdup(e); | ||||||||
1420 | if (!z) | ||||||||
1421 | return -ENOMEM12; | ||||||||
1422 | |||||||||
1423 | *tz = z; | ||||||||
1424 | return 0; | ||||||||
1425 | } | ||||||||
1426 | |||||||||
1427 | time_t mktime_or_timegm(struct tm *tm, bool_Bool utc) { | ||||||||
1428 | return utc ? timegm(tm) : mktime(tm); | ||||||||
1429 | } | ||||||||
1430 | |||||||||
1431 | struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool_Bool utc) { | ||||||||
1432 | return utc
| ||||||||
1433 | } | ||||||||
1434 | |||||||||
1435 | unsigned long usec_to_jiffies(usec_t u) { | ||||||||
1436 | static thread_local__thread unsigned long hz = 0; | ||||||||
1437 | long r; | ||||||||
1438 | |||||||||
1439 | if (hz == 0) { | ||||||||
1440 | r = sysconf(_SC_CLK_TCK_SC_CLK_TCK); | ||||||||
1441 | |||||||||
1442 | assert(r > 0)do { if ((__builtin_expect(!!(!(r > 0)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("r > 0"), "../src/basic/time-util.c", 1442, __PRETTY_FUNCTION__); } while (0); | ||||||||
1443 | hz = r; | ||||||||
1444 | } | ||||||||
1445 | |||||||||
1446 | return DIV_ROUND_UP(u , USEC_PER_SEC / hz)({ const typeof((u)) __unique_prefix_X18 = ((u)); const typeof ((((usec_t) 1000000ULL) / hz)) __unique_prefix_Y19 = ((((usec_t ) 1000000ULL) / hz)); (__unique_prefix_X18 / __unique_prefix_Y19 + !!(__unique_prefix_X18 % __unique_prefix_Y19)); }); | ||||||||
1447 | } | ||||||||
1448 | |||||||||
1449 | usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) { | ||||||||
1450 | usec_t a, b; | ||||||||
1451 | |||||||||
1452 | if (x == USEC_INFINITY((usec_t) -1)) | ||||||||
1453 | return USEC_INFINITY((usec_t) -1); | ||||||||
1454 | if (map_clock_id(from) == map_clock_id(to)) | ||||||||
1455 | return x; | ||||||||
1456 | |||||||||
1457 | a = now(from); | ||||||||
1458 | b = now(to); | ||||||||
1459 | |||||||||
1460 | if (x > a) | ||||||||
1461 | /* x lies in the future */ | ||||||||
1462 | return usec_add(b, usec_sub_unsigned(x, a)); | ||||||||
1463 | else | ||||||||
1464 | /* x lies in the past */ | ||||||||
1465 | return usec_sub_unsigned(b, usec_sub_unsigned(a, x)); | ||||||||
1466 | } | ||||||||
1467 | |||||||||
1468 | bool_Bool in_utc_timezone(void) { | ||||||||
1469 | tzset(); | ||||||||
1470 | |||||||||
1471 | return timezone == 0 && daylight == 0; | ||||||||
1472 | } | ||||||||
1473 | |||||||||
1474 | int time_change_fd(void) { | ||||||||
1475 | |||||||||
1476 | /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */ | ||||||||
1477 | static const struct itimerspec its = { | ||||||||
1478 | .it_value.tv_sec = TIME_T_MAX(time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 ), | ||||||||
1479 | }; | ||||||||
1480 | |||||||||
1481 | _cleanup_close___attribute__((cleanup(closep))) int fd; | ||||||||
1482 | |||||||||
1483 | assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX))GCC diagnostic push
; GCC diagnostic ignored "-Wdeclaration-after-statement" ; struct _assert_struct_20 { char x[(sizeof(time_t) == sizeof ((time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1 ))) ? 0 : -1]; }; GCC diagnostic pop ; | ||||||||
1484 | |||||||||
1485 | /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to | ||||||||
1486 | * CLOCK_MONOTONIC. */ | ||||||||
1487 | |||||||||
1488 | fd = timerfd_create(CLOCK_REALTIME0, TFD_NONBLOCKTFD_NONBLOCK|TFD_CLOEXECTFD_CLOEXEC); | ||||||||
1489 | if (fd < 0) | ||||||||
1490 | return -errno(*__errno_location ()); | ||||||||
1491 | |||||||||
1492 | if (timerfd_settime(fd, TFD_TIMER_ABSTIMETFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SETTFD_TIMER_CANCEL_ON_SET, &its, NULL((void*)0)) < 0) | ||||||||
1493 | return -errno(*__errno_location ()); | ||||||||
1494 | |||||||||
1495 | return TAKE_FD(fd)({ int _fd_ = (fd); (fd) = -1; _fd_; }); | ||||||||
1496 | } |
1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
2 | #pragma once |
3 | |
4 | #include <alloca.h> |
5 | #include <stdbool.h> |
6 | #include <stddef.h> |
7 | #include <string.h> |
8 | |
9 | #include "macro.h" |
10 | |
11 | /* What is interpreted as whitespace? */ |
12 | #define WHITESPACE" \t\n\r" " \t\n\r" |
13 | #define NEWLINE"\n\r" "\n\r" |
14 | #define QUOTES"\"\'" "\"\'" |
15 | #define COMMENTS"#;" "#;" |
16 | #define GLOB_CHARS"*?[" "*?[" |
17 | #define DIGITS"0123456789" "0123456789" |
18 | #define LOWERCASE_LETTERS"abcdefghijklmnopqrstuvwxyz" "abcdefghijklmnopqrstuvwxyz" |
19 | #define UPPERCASE_LETTERS"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
20 | #define LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" LOWERCASE_LETTERS"abcdefghijklmnopqrstuvwxyz" UPPERCASE_LETTERS"ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
21 | #define ALPHANUMERICAL"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789" LETTERS"abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" DIGITS"0123456789" |
22 | #define HEXDIGITS"0123456789" "abcdefABCDEF" DIGITS"0123456789" "abcdefABCDEF" |
23 | |
24 | #define streq(a,b)(strcmp((a),(b)) == 0) (strcmp((a),(b)) == 0) |
25 | #define strneq(a, b, n)(strncmp((a), (b), (n)) == 0) (strncmp((a), (b), (n)) == 0) |
26 | #define strcaseeq(a,b)(strcasecmp((a),(b)) == 0) (strcasecmp((a),(b)) == 0) |
27 | #define strncaseeq(a, b, n)(strncasecmp((a), (b), (n)) == 0) (strncasecmp((a), (b), (n)) == 0) |
28 | |
29 | int strcmp_ptr(const char *a, const char *b) _pure___attribute__ ((pure)); |
30 | |
31 | static inline bool_Bool streq_ptr(const char *a, const char *b) { |
32 | return strcmp_ptr(a, b) == 0; |
33 | } |
34 | |
35 | static inline const char* strempty(const char *s) { |
36 | return s ?: ""; |
37 | } |
38 | |
39 | static inline const char* strnull(const char *s) { |
40 | return s ?: "(null)"; |
41 | } |
42 | |
43 | static inline const char *strna(const char *s) { |
44 | return s ?: "n/a"; |
45 | } |
46 | |
47 | static inline bool_Bool isempty(const char *p) { |
48 | return !p || !p[0]; |
49 | } |
50 | |
51 | static inline const char *empty_to_null(const char *p) { |
52 | return isempty(p) ? NULL((void*)0) : p; |
53 | } |
54 | |
55 | static inline const char *empty_to_dash(const char *str) { |
56 | return isempty(str) ? "-" : str; |
57 | } |
58 | |
59 | static inline char *startswith(const char *s, const char *prefix) { |
60 | size_t l; |
61 | |
62 | l = strlen(prefix); |
63 | if (strncmp(s, prefix, l) == 0) |
64 | return (char*) s + l; |
65 | |
66 | return NULL((void*)0); |
67 | } |
68 | |
69 | static inline char *startswith_no_case(const char *s, const char *prefix) { |
70 | size_t l; |
71 | |
72 | l = strlen(prefix); |
73 | if (strncasecmp(s, prefix, l) == 0) |
74 | return (char*) s + l; |
75 | |
76 | return NULL((void*)0); |
77 | } |
78 | |
79 | char *endswith(const char *s, const char *postfix) _pure___attribute__ ((pure)); |
80 | char *endswith_no_case(const char *s, const char *postfix) _pure___attribute__ ((pure)); |
81 | |
82 | char *first_word(const char *s, const char *word) _pure___attribute__ ((pure)); |
83 | |
84 | const char* split(const char **state, size_t *l, const char *separator, bool_Bool quoted); |
85 | |
86 | #define FOREACH_WORD(word, length, s, state)for ((state) = (s), (word) = split(&(state), &(length ), (" \t\n\r"), (0)); (word); (word) = split(&(state), & (length), (" \t\n\r"), (0))) \ |
87 | _FOREACH_WORD(word, length, s, WHITESPACE, false, state)for ((state) = (s), (word) = split(&(state), &(length ), (" \t\n\r"), (0)); (word); (word) = split(&(state), & (length), (" \t\n\r"), (0))) |
88 | |
89 | #define FOREACH_WORD_SEPARATOR(word, length, s, separator, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (0)); (word); (word) = split(&(state), & (length), (separator), (0))) \ |
90 | _FOREACH_WORD(word, length, s, separator, false, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (0)); (word); (word) = split(&(state), & (length), (separator), (0))) |
91 | |
92 | #define _FOREACH_WORD(word, length, s, separator, quoted, state)for ((state) = (s), (word) = split(&(state), &(length ), (separator), (quoted)); (word); (word) = split(&(state ), &(length), (separator), (quoted))) \ |
93 | for ((state) = (s), (word) = split(&(state), &(length), (separator), (quoted)); (word); (word) = split(&(state), &(length), (separator), (quoted))) |
94 | |
95 | char *strappend(const char *s, const char *suffix); |
96 | char *strnappend(const char *s, const char *suffix, size_t length); |
97 | |
98 | char *strjoin_real(const char *x, ...) _sentinel___attribute__ ((sentinel)); |
99 | #define strjoin(a, ...)strjoin_real((a), ..., ((void*)0)) strjoin_real((a), __VA_ARGS__, NULL((void*)0)) |
100 | |
101 | #define strjoina(a, ...)({ const char *_appendees_[] = { a, ... }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__ ( __builtin_choose_expr( !__builtin_types_compatible_p(typeof(_appendees_ ), typeof(&*(_appendees_))), sizeof(_appendees_)/sizeof(( _appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_ ++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca (_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr ( !__builtin_types_compatible_p(typeof(_appendees_), typeof(& *(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0] ), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy (_p_, _appendees_[_i_]); *_p_ = 0; _d_; }) \ |
102 | ({ \ |
103 | const char *_appendees_[] = { a, __VA_ARGS__ }; \ |
104 | char *_d_, *_p_; \ |
105 | size_t _len_ = 0; \ |
106 | size_t _i_; \ |
107 | for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_++) \ |
108 | _len_ += strlen(_appendees_[_i_]); \ |
109 | _p_ = _d_ = alloca(_len_ + 1)__builtin_alloca (_len_ + 1); \ |
110 | for (_i_ = 0; _i_ < ELEMENTSOF(_appendees_)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p (typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_ )/sizeof((_appendees_)[0]), ((void)0))) && _appendees_[_i_]; _i_++) \ |
111 | _p_ = stpcpy(_p_, _appendees_[_i_]); \ |
112 | *_p_ = 0; \ |
113 | _d_; \ |
114 | }) |
115 | |
116 | char *strstrip(char *s); |
117 | char *delete_chars(char *s, const char *bad); |
118 | char *delete_trailing_chars(char *s, const char *bad); |
119 | char *truncate_nl(char *s); |
120 | |
121 | static inline char *skip_leading_chars(const char *s, const char *bad) { |
122 | |
123 | if (!s) |
124 | return NULL((void*)0); |
125 | |
126 | if (!bad) |
127 | bad = WHITESPACE" \t\n\r"; |
128 | |
129 | return (char*) s + strspn(s, bad); |
130 | } |
131 | |
132 | char ascii_tolower(char x); |
133 | char *ascii_strlower(char *s); |
134 | char *ascii_strlower_n(char *s, size_t n); |
135 | |
136 | char ascii_toupper(char x); |
137 | char *ascii_strupper(char *s); |
138 | |
139 | int ascii_strcasecmp_n(const char *a, const char *b, size_t n); |
140 | int ascii_strcasecmp_nn(const char *a, size_t n, const char *b, size_t m); |
141 | |
142 | bool_Bool chars_intersect(const char *a, const char *b) _pure___attribute__ ((pure)); |
143 | |
144 | static inline bool_Bool _pure___attribute__ ((pure)) in_charset(const char *s, const char* charset) { |
145 | assert(s)do { if ((__builtin_expect(!!(!(s)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("s"), "../src/basic/string-util.h", 145, __PRETTY_FUNCTION__); } while (0); |
146 | assert(charset)do { if ((__builtin_expect(!!(!(charset)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("charset"), "../src/basic/string-util.h" , 146, __PRETTY_FUNCTION__); } while (0); |
147 | return s[strspn(s, charset)] == '\0'; |
148 | } |
149 | |
150 | bool_Bool string_has_cc(const char *p, const char *ok) _pure___attribute__ ((pure)); |
151 | |
152 | char *ellipsize_mem(const char *s, size_t old_length_bytes, size_t new_length_columns, unsigned percent); |
153 | static inline char *ellipsize(const char *s, size_t length, unsigned percent) { |
154 | return ellipsize_mem(s, strlen(s), length, percent); |
155 | } |
156 | |
157 | char *cellescape(char *buf, size_t len, const char *s); |
158 | |
159 | /* This limit is arbitrary, enough to give some idea what the string contains */ |
160 | #define CELLESCAPE_DEFAULT_LENGTH64 64 |
161 | |
162 | bool_Bool nulstr_contains(const char *nulstr, const char *needle); |
163 | |
164 | char* strshorten(char *s, size_t l); |
165 | |
166 | char *strreplace(const char *text, const char *old_string, const char *new_string); |
167 | |
168 | char *strip_tab_ansi(char **ibuf, size_t *_isz, size_t highlight[2]); |
169 | |
170 | char *strextend_with_separator(char **x, const char *separator, ...) _sentinel___attribute__ ((sentinel)); |
171 | |
172 | #define strextend(x, ...)strextend_with_separator(x, ((void*)0), ...) strextend_with_separator(x, NULL((void*)0), __VA_ARGS__) |
173 | |
174 | char *strrep(const char *s, unsigned n); |
175 | |
176 | int split_pair(const char *s, const char *sep, char **l, char **r); |
177 | |
178 | int free_and_strdup(char **p, const char *s); |
179 | int free_and_strndup(char **p, const char *s, size_t l); |
180 | |
181 | /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */ |
182 | static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) { |
183 | |
184 | if (needlelen <= 0) |
185 | return (void*) haystack; |
186 | |
187 | if (haystacklen < needlelen) |
188 | return NULL((void*)0); |
189 | |
190 | assert(haystack)do { if ((__builtin_expect(!!(!(haystack)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("haystack"), "../src/basic/string-util.h" , 190, __PRETTY_FUNCTION__); } while (0); |
191 | assert(needle)do { if ((__builtin_expect(!!(!(needle)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("needle"), "../src/basic/string-util.h", 191, __PRETTY_FUNCTION__); } while (0); |
192 | |
193 | return memmem(haystack, haystacklen, needle, needlelen); |
194 | } |
195 | |
196 | #if !HAVE_EXPLICIT_BZERO1 |
197 | void explicit_bzero(void *p, size_t l); |
198 | #endif |
199 | |
200 | char *string_erase(char *x); |
201 | |
202 | char *string_free_erase(char *s); |
203 | DEFINE_TRIVIAL_CLEANUP_FUNC(char *, string_free_erase)static inline void string_free_erasep(char * *p) { if (*p) string_free_erase (*p); }; |
204 | #define _cleanup_string_free_erase___attribute__((cleanup(string_free_erasep))) _cleanup_(string_free_erasep)__attribute__((cleanup(string_free_erasep))) |
205 | |
206 | bool_Bool string_is_safe(const char *p) _pure___attribute__ ((pure)); |
207 | |
208 | static inline size_t strlen_ptr(const char *s) { |
209 | if (!s) |
210 | return 0; |
211 | |
212 | return strlen(s); |
213 | } |
214 | |
215 | /* Like startswith(), but operates on arbitrary memory blocks */ |
216 | static inline void *memory_startswith(const void *p, size_t sz, const char *token) { |
217 | size_t n; |
218 | |
219 | assert(token)do { if ((__builtin_expect(!!(!(token)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("token"), "../src/basic/string-util.h", 219 , __PRETTY_FUNCTION__); } while (0); |
220 | |
221 | n = strlen(token); |
222 | if (sz < n) |
223 | return NULL((void*)0); |
224 | |
225 | assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm (LOG_REALM_SYSTEMD, ("p"), "../src/basic/string-util.h", 225, __PRETTY_FUNCTION__); } while (0); |
226 | |
227 | if (memcmp(p, token, n) != 0) |
228 | return NULL((void*)0); |
229 | |
230 | return (uint8_t*) p + n; |
231 | } |