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