1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include <sys/timerfd.h>
10 #include <sys/types.h>
13 #include "alloc-util.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"
28 #include "time-util.h"
30 static clockid_t
map_clock_id(clockid_t c
) {
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
39 case CLOCK_BOOTTIME_ALARM
:
40 return CLOCK_BOOTTIME
;
42 case CLOCK_REALTIME_ALARM
:
43 return CLOCK_REALTIME
;
50 usec_t
now(clockid_t clock_id
) {
53 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
55 return timespec_load(&ts
);
58 nsec_t
now_nsec(clockid_t clock_id
) {
61 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
63 return timespec_load_nsec(&ts
);
66 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
69 ts
->realtime
= now(CLOCK_REALTIME
);
70 ts
->monotonic
= now(CLOCK_MONOTONIC
);
75 triple_timestamp
* triple_timestamp_get(triple_timestamp
*ts
) {
78 ts
->realtime
= now(CLOCK_REALTIME
);
79 ts
->monotonic
= now(CLOCK_MONOTONIC
);
80 ts
->boottime
= clock_boottime_supported() ? now(CLOCK_BOOTTIME
) : USEC_INFINITY
;
85 static usec_t
map_clock_usec_internal(usec_t from
, usec_t from_base
, usec_t to_base
) {
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:
90 * from - from_base + to_base
92 * But takes care of overflows/underflows and avoids signed operations. */
94 if (from
>= from_base
) { /* In the future */
95 usec_t delta
= from
- from_base
;
97 if (to_base
>= USEC_INFINITY
- delta
) /* overflow? */
100 return to_base
+ delta
;
102 } else { /* In the past */
103 usec_t delta
= from_base
- from
;
105 if (to_base
<= delta
) /* underflow? */
108 return to_base
- delta
;
112 usec_t
map_clock_usec(usec_t from
, clockid_t from_clock
, clockid_t to_clock
) {
114 /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
116 if (map_clock_id(from_clock
) == map_clock_id(to_clock
))
119 /* Keep infinity as is */
120 if (from
== USEC_INFINITY
)
123 return map_clock_usec_internal(from
, now(from_clock
), now(to_clock
));
126 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
129 if (u
== USEC_INFINITY
|| u
== 0) {
130 ts
->realtime
= ts
->monotonic
= u
;
135 ts
->monotonic
= map_clock_usec(u
, CLOCK_REALTIME
, CLOCK_MONOTONIC
);
139 triple_timestamp
* triple_timestamp_from_realtime(triple_timestamp
*ts
, usec_t u
) {
144 if (u
== USEC_INFINITY
|| u
== 0) {
145 ts
->realtime
= ts
->monotonic
= ts
->boottime
= u
;
149 nowr
= now(CLOCK_REALTIME
);
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
)) :
160 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
163 if (u
== USEC_INFINITY
) {
164 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
169 ts
->realtime
= map_clock_usec(u
, CLOCK_MONOTONIC
, CLOCK_REALTIME
);
173 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
177 if (u
== USEC_INFINITY
) {
178 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
182 cid
= clock_boottime_or_monotonic();
185 if (cid
== CLOCK_MONOTONIC
)
188 ts
->monotonic
= map_clock_usec_internal(u
, nowm
, now(CLOCK_MONOTONIC
));
190 ts
->realtime
= map_clock_usec_internal(u
, nowm
, now(CLOCK_REALTIME
));
194 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
199 case CLOCK_REALTIME_ALARM
:
202 case CLOCK_MONOTONIC
:
203 return ts
->monotonic
;
206 case CLOCK_BOOTTIME_ALARM
:
210 return USEC_INFINITY
;
214 usec_t
timespec_load(const struct timespec
*ts
) {
217 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
218 return USEC_INFINITY
;
220 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
221 return USEC_INFINITY
;
224 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
225 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
228 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
231 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
232 return NSEC_INFINITY
;
234 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
235 return NSEC_INFINITY
;
237 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
240 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
243 if (u
== USEC_INFINITY
||
244 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
245 ts
->tv_sec
= (time_t) -1;
250 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
251 ts
->tv_nsec
= (long) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
256 struct timespec
*timespec_store_nsec(struct timespec
*ts
, nsec_t n
) {
259 if (n
== NSEC_INFINITY
||
260 n
/ NSEC_PER_SEC
>= TIME_T_MAX
) {
261 ts
->tv_sec
= (time_t) -1;
266 ts
->tv_sec
= (time_t) (n
/ NSEC_PER_SEC
);
267 ts
->tv_nsec
= (long) (n
% NSEC_PER_SEC
);
272 usec_t
timeval_load(const struct timeval
*tv
) {
275 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
276 return USEC_INFINITY
;
278 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
279 return USEC_INFINITY
;
282 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
283 (usec_t
) tv
->tv_usec
;
286 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
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;
294 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
295 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
301 char *format_timestamp_style(
305 TimestampStyle style
) {
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
[] = {
322 bool utc
= false, us
= false;
327 case TIMESTAMP_PRETTY
:
335 case TIMESTAMP_US_UTC
:
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 */
349 return NULL
; /* Not enough space even for the shortest form. */
350 if (t
<= 0 || t
== USEC_INFINITY
)
351 return NULL
; /* Timestamp is unset */
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");
360 sec
= (time_t) (t
/ USEC_PER_SEC
); /* Round down */
362 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
365 /* Start with the week day */
366 assert((size_t) tm
.tm_wday
< ELEMENTSOF(weekdays
));
367 memcpy(buf
, weekdays
[tm
.tm_wday
], 4);
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 */
373 /* Append the microseconds part, if that's requested */
377 return NULL
; /* Microseconds part doesn't fit. */
379 sprintf(buf
+ n
, ".%06"PRI_USEC
, t
% USEC_PER_SEC
);
382 /* Append the timezone */
385 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
386 * obsolete "GMT" instead. */
388 return NULL
; /* "UTC" doesn't fit. */
390 strcpy(buf
+ n
, " UTC");
392 } else if (!isempty(tm
.tm_zone
)) {
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. */
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 */
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(). */
409 strcpy(buf
+ n
, tm
.tm_zone
);
416 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
420 if (t
<= 0 || t
== USEC_INFINITY
)
423 n
= now(CLOCK_REALTIME
);
432 if (d
>= USEC_PER_YEAR
)
433 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
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",
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",
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",
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",
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
);
470 snprintf(buf
, l
, USEC_FMT
"us %s",
473 snprintf(buf
, l
, "now");
479 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
480 static const struct {
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
},
496 bool something
= false;
501 if (t
== USEC_INFINITY
) {
502 strncpy(p
, "infinity", l
-1);
508 strncpy(p
, "0", l
-1);
513 /* The result of this function can be parsed with parse_sec */
515 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
524 if (t
< accuracy
&& something
)
527 if (t
< table
[i
].usec
)
533 a
= t
/ table
[i
].usec
;
534 b
= t
% table
[i
].usec
;
536 /* Let's see if we should shows this in dot notation */
537 if (t
< USEC_PER_MINUTE
&& b
> 0) {
542 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
545 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
552 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
564 /* No? Then let's show it normally */
575 n
= MIN((size_t) k
, l
);
588 static int parse_timestamp_impl(const char *t
, usec_t
*usec
, bool with_tz
) {
589 static const struct {
609 const char *k
, *utc
= NULL
, *tzn
= NULL
;
612 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
613 int r
, weekday
= -1, dst
= -1;
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)
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)
629 * @2147483647 (seconds since epoch)
634 if (t
[0] == '@' && !with_tz
)
635 return parse_sec(t
+ 1, usec
);
637 ret
= now(CLOCK_REALTIME
);
643 else if (t
[0] == '+') {
644 r
= parse_sec(t
+1, &plus
);
650 } else if (t
[0] == '-') {
651 r
= parse_sec(t
+1, &minus
);
657 } else if ((k
= endswith(t
, " ago"))) {
658 t
= strndupa(t
, k
- t
);
660 r
= parse_sec(t
, &minus
);
666 } else if ((k
= endswith(t
, " left"))) {
667 t
= strndupa(t
, k
- t
);
669 r
= parse_sec(t
, &plus
);
676 /* See if the timestamp is suffixed with UTC */
677 utc
= endswith_no_case(t
, " UTC");
679 t
= strndupa(t
, utc
- t
);
681 const char *e
= NULL
;
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. */
692 for (j
= 0; j
<= 1; j
++) {
694 if (isempty(tzname
[j
]))
697 e
= endswith_no_case(t
, tzname
[j
]);
708 if (IN_SET(j
, 0, 1)) {
709 /* Found one of the two timezones specified. */
710 t
= strndupa(t
, e
- t
- 1);
717 x
= (time_t) (ret
/ USEC_PER_SEC
);
720 if (!localtime_or_gmtime_r(&x
, &tm
, utc
))
727 if (streq(t
, "today")) {
728 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
731 } else if (streq(t
, "yesterday")) {
733 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
736 } else if (streq(t
, "tomorrow")) {
738 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
742 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
745 if (!startswith_no_case(t
, day_nr
[i
].name
))
748 skip
= strlen(day_nr
[i
].name
);
752 weekday
= day_nr
[i
].nr
;
758 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
767 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
776 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
783 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
790 k
= strptime(t
, "%y-%m-%d", &tm
);
792 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
797 k
= strptime(t
, "%Y-%m-%d", &tm
);
799 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
804 k
= strptime(t
, "%H:%M:%S", &tm
);
813 k
= strptime(t
, "%H:%M", &tm
);
826 r
= parse_fractional_part_u(&k
, 6, &add
);
837 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
840 x
= mktime_or_timegm(&tm
, utc
);
844 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
845 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
849 if (ret
+ plus
< ret
) /* overflow? */
852 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
865 typedef struct ParseTimestampResult
{
868 } ParseTimestampResult
;
870 int parse_timestamp(const char *t
, usec_t
*usec
) {
871 char *last_space
, *tz
= NULL
;
872 ParseTimestampResult
*shared
, tmp
;
875 last_space
= strrchr(t
, ' ');
876 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1, LOG_DEBUG
))
879 if (!tz
|| endswith_no_case(t
, " UTC"))
880 return parse_timestamp_impl(t
, usec
, false);
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();
886 r
= safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_WAIT
, NULL
);
888 (void) munmap(shared
, sizeof *shared
);
895 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
896 colon_tz
= strjoina(":", tz
);
898 if (setenv("TZ", colon_tz
, 1) != 0) {
899 shared
->return_value
= negative_errno();
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]);
909 /* Cut off the timezone if we don't need it. */
911 t
= strndupa(t
, last_space
- t
);
913 shared
->return_value
= parse_timestamp_impl(t
, &shared
->usec
, with_tz
);
919 if (munmap(shared
, sizeof *shared
) != 0)
920 return negative_errno();
922 if (tmp
.return_value
== 0 && usec
)
925 return tmp
.return_value
;
928 static const char* extract_multiplier(const char *p
, usec_t
*multiplier
) {
929 static const struct {
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
},
964 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
967 e
= startswith(p
, table
[i
].suffix
);
969 *multiplier
= table
[i
].usec
;
977 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
980 bool something
= false;
983 assert(default_unit
> 0);
987 p
+= strspn(p
, WHITESPACE
);
988 s
= startswith(p
, "infinity");
990 s
+= strspn(s
, WHITESPACE
);
995 *usec
= USEC_INFINITY
;
1000 usec_t multiplier
= default_unit
, k
;
1004 p
+= strspn(p
, WHITESPACE
);
1013 if (*p
== '-') /* Don't allow "-0" */
1017 l
= strtoll(p
, &e
, 10);
1025 p
+= strspn(p
, DIGITS
);
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' */
1038 if ((usec_t
) l
>= USEC_INFINITY
/ multiplier
)
1041 k
= (usec_t
) l
* multiplier
;
1042 if (k
>= USEC_INFINITY
- r
)
1050 usec_t m
= multiplier
/ 10;
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
)
1061 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1072 int parse_sec(const char *t
, usec_t
*usec
) {
1073 return parse_time(t
, usec
, USEC_PER_SEC
);
1076 int parse_sec_fix_0(const char *t
, usec_t
*ret
) {
1083 r
= parse_sec(t
, &k
);
1087 *ret
= k
== 0 ? USEC_INFINITY
: k
;
1091 int parse_sec_def_infinity(const char *t
, usec_t
*ret
) {
1092 t
+= strspn(t
, WHITESPACE
);
1094 *ret
= USEC_INFINITY
;
1097 return parse_sec(t
, ret
);
1100 static const char* extract_nsec_multiplier(const char *p
, nsec_t
*multiplier
) {
1101 static const struct {
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
},
1136 { "", 1ULL }, /* default is nsec */
1140 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
1143 e
= startswith(p
, table
[i
].suffix
);
1145 *multiplier
= table
[i
].nsec
;
1153 int parse_nsec(const char *t
, nsec_t
*nsec
) {
1156 bool something
= false;
1163 p
+= strspn(p
, WHITESPACE
);
1164 s
= startswith(p
, "infinity");
1166 s
+= strspn(s
, WHITESPACE
);
1170 *nsec
= NSEC_INFINITY
;
1175 nsec_t multiplier
= 1, k
;
1179 p
+= strspn(p
, WHITESPACE
);
1188 if (*p
== '-') /* Don't allow "-0" */
1192 l
= strtoll(p
, &e
, 10);
1200 p
+= strspn(p
, DIGITS
);
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' */
1213 if ((nsec_t
) l
>= NSEC_INFINITY
/ multiplier
)
1216 k
= (nsec_t
) l
* multiplier
;
1217 if (k
>= NSEC_INFINITY
- r
)
1225 nsec_t m
= multiplier
/ 10;
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
)
1236 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1247 int get_timezones(char ***ret
) {
1248 _cleanup_fclose_
FILE *f
= NULL
;
1249 _cleanup_strv_free_
char **zones
= NULL
;
1255 zones
= strv_new("UTC");
1261 f
= fopen("/usr/share/zoneinfo/zone1970.tab", "re");
1264 _cleanup_free_
char *line
= NULL
, *w
= NULL
;
1268 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1276 if (isempty(p
) || *p
== '#')
1279 /* Skip over country code */
1280 p
+= strcspn(p
, WHITESPACE
);
1281 p
+= strspn(p
, WHITESPACE
);
1283 /* Skip over coordinates */
1284 p
+= strcspn(p
, WHITESPACE
);
1285 p
+= strspn(p
, WHITESPACE
);
1287 /* Found timezone name */
1288 k
= strcspn(p
, WHITESPACE
);
1296 if (!GREEDY_REALLOC(zones
, n_zones
+ 2))
1299 zones
[n_zones
++] = TAKE_PTR(w
);
1300 zones
[n_zones
] = NULL
;
1306 } else if (errno
!= ENOENT
)
1309 *ret
= TAKE_PTR(zones
);
1314 bool timezone_is_valid(const char *name
, int log_level
) {
1317 _cleanup_close_
int fd
= -1;
1324 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1325 if (streq(name
, "UTC"))
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
, '-', '_', '+', '/'))
1351 if (p
- name
>= PATH_MAX
)
1354 t
= strjoina("/usr/share/zoneinfo/", name
);
1356 fd
= open(t
, O_RDONLY
|O_CLOEXEC
);
1358 log_full_errno(log_level
, errno
, "Failed to open timezone file '%s': %m", t
);
1362 r
= fd_verify_regular(fd
);
1364 log_full_errno(log_level
, r
, "Timezone file '%s' is not a regular file: %m", t
);
1368 r
= loop_read_exact(fd
, buf
, 4, false);
1370 log_full_errno(log_level
, r
, "Failed to read from timezone file '%s': %m", t
);
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
);
1383 bool clock_boottime_supported(void) {
1384 static int supported
= -1;
1386 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1388 if (supported
< 0) {
1391 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1403 clockid_t
clock_boottime_or_monotonic(void) {
1404 if (clock_boottime_supported())
1405 return CLOCK_BOOTTIME
;
1407 return CLOCK_MONOTONIC
;
1410 bool clock_supported(clockid_t clock
) {
1415 case CLOCK_MONOTONIC
:
1416 case CLOCK_REALTIME
:
1419 case CLOCK_BOOTTIME
:
1420 return clock_boottime_supported();
1422 case CLOCK_BOOTTIME_ALARM
:
1423 if (!clock_boottime_supported())
1428 /* For everything else, check properly */
1429 return clock_gettime(clock
, &ts
) >= 0;
1433 int get_timezone(char **ret
) {
1434 _cleanup_free_
char *t
= NULL
;
1439 r
= readlink_malloc("/etc/localtime", &t
);
1441 /* If the symlink does not exist, assume "UTC", like glibc does */
1450 return r
; /* returns EINVAL if not a symlink */
1452 e
= PATH_STARTSWITH_SET(t
, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
1456 if (!timezone_is_valid(e
, LOG_DEBUG
))
1467 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1468 return utc
? timegm(tm
) : mktime(tm
);
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
);
1475 static uint32_t sysconf_clock_ticks_cached(void) {
1476 static thread_local
uint32_t hz
= 0;
1480 r
= sysconf(_SC_CLK_TCK
);
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
);
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
);
1499 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1502 if (x
== USEC_INFINITY
)
1503 return USEC_INFINITY
;
1504 if (map_clock_id(from
) == map_clock_id(to
))
1511 /* x lies in the future */
1512 return usec_add(b
, usec_sub_unsigned(x
, a
));
1514 /* x lies in the past */
1515 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));
1518 bool in_utc_timezone(void) {
1521 return timezone
== 0 && daylight
== 0;
1524 int time_change_fd(void) {
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
,
1531 _cleanup_close_
int fd
= -1;
1533 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX
));
1535 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1536 * CLOCK_MONOTONIC. */
1538 fd
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1542 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
) >= 0)
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?
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.
1553 * See: https://github.com/systemd/systemd/issues/14362 */
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
,
1561 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its32
, NULL
) >= 0)
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",
1576 /* Use the macro for enum → string to allow for aliases */
1577 _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style
, TimestampStyle
,);
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
) {
1583 t
= (TimestampStyle
) string_table_lookup(timestamp_style_table
, ELEMENTSOF(timestamp_style_table
), s
);
1586 if (streq_ptr(s
, "µs"))
1587 return TIMESTAMP_US
;
1588 if (streq_ptr(s
, "µs+utc"))
1589 return TIMESTAMP_US_UTC
;