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