1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
9 #include <sys/timerfd.h>
10 #include <sys/timex.h>
11 #include <sys/types.h>
14 #include "alloc-util.h"
21 #include "missing_timerfd.h"
22 #include "parse-util.h"
23 #include "path-util.h"
24 #include "process-util.h"
25 #include "stat-util.h"
26 #include "string-table.h"
27 #include "string-util.h"
29 #include "time-util.h"
31 static clockid_t
map_clock_id(clockid_t c
) {
33 /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
34 * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
35 * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
40 case CLOCK_BOOTTIME_ALARM
:
41 return CLOCK_BOOTTIME
;
43 case CLOCK_REALTIME_ALARM
:
44 return CLOCK_REALTIME
;
51 usec_t
now(clockid_t clock_id
) {
54 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
56 return timespec_load(&ts
);
59 nsec_t
now_nsec(clockid_t clock_id
) {
62 assert_se(clock_gettime(map_clock_id(clock_id
), &ts
) == 0);
64 return timespec_load_nsec(&ts
);
67 dual_timestamp
* dual_timestamp_get(dual_timestamp
*ts
) {
70 ts
->realtime
= now(CLOCK_REALTIME
);
71 ts
->monotonic
= now(CLOCK_MONOTONIC
);
76 triple_timestamp
* triple_timestamp_get(triple_timestamp
*ts
) {
79 ts
->realtime
= now(CLOCK_REALTIME
);
80 ts
->monotonic
= now(CLOCK_MONOTONIC
);
81 ts
->boottime
= clock_boottime_supported() ? now(CLOCK_BOOTTIME
) : USEC_INFINITY
;
86 static usec_t
map_clock_usec_internal(usec_t from
, usec_t from_base
, usec_t to_base
) {
88 /* Maps the time 'from' between two clocks, based on a common reference point where the first clock
89 * is at 'from_base' and the second clock at 'to_base'. Basically calculates:
91 * from - from_base + to_base
93 * But takes care of overflows/underflows and avoids signed operations. */
95 if (from
>= from_base
) { /* In the future */
96 usec_t delta
= from
- from_base
;
98 if (to_base
>= USEC_INFINITY
- delta
) /* overflow? */
101 return to_base
+ delta
;
103 } else { /* In the past */
104 usec_t delta
= from_base
- from
;
106 if (to_base
<= delta
) /* underflow? */
109 return to_base
- delta
;
113 usec_t
map_clock_usec(usec_t from
, clockid_t from_clock
, clockid_t to_clock
) {
115 /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
117 if (map_clock_id(from_clock
) == map_clock_id(to_clock
))
120 /* Keep infinity as is */
121 if (from
== USEC_INFINITY
)
124 return map_clock_usec_internal(from
, now(from_clock
), now(to_clock
));
127 dual_timestamp
* dual_timestamp_from_realtime(dual_timestamp
*ts
, usec_t u
) {
130 if (u
== USEC_INFINITY
|| u
== 0) {
131 ts
->realtime
= ts
->monotonic
= u
;
136 ts
->monotonic
= map_clock_usec(u
, CLOCK_REALTIME
, CLOCK_MONOTONIC
);
140 triple_timestamp
* triple_timestamp_from_realtime(triple_timestamp
*ts
, usec_t u
) {
145 if (u
== USEC_INFINITY
|| u
== 0) {
146 ts
->realtime
= ts
->monotonic
= ts
->boottime
= u
;
150 nowr
= now(CLOCK_REALTIME
);
153 ts
->monotonic
= map_clock_usec_internal(u
, nowr
, now(CLOCK_MONOTONIC
));
154 ts
->boottime
= clock_boottime_supported() ?
155 map_clock_usec_internal(u
, nowr
, now(CLOCK_BOOTTIME
)) :
161 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
164 if (u
== USEC_INFINITY
) {
165 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
170 ts
->realtime
= map_clock_usec(u
, CLOCK_MONOTONIC
, CLOCK_REALTIME
);
174 dual_timestamp
* dual_timestamp_from_boottime_or_monotonic(dual_timestamp
*ts
, usec_t u
) {
178 if (u
== USEC_INFINITY
) {
179 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
183 cid
= clock_boottime_or_monotonic();
186 if (cid
== CLOCK_MONOTONIC
)
189 ts
->monotonic
= map_clock_usec_internal(u
, nowm
, now(CLOCK_MONOTONIC
));
191 ts
->realtime
= map_clock_usec_internal(u
, nowm
, now(CLOCK_REALTIME
));
195 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
200 case CLOCK_REALTIME_ALARM
:
203 case CLOCK_MONOTONIC
:
204 return ts
->monotonic
;
207 case CLOCK_BOOTTIME_ALARM
:
211 return USEC_INFINITY
;
215 usec_t
timespec_load(const struct timespec
*ts
) {
218 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
219 return USEC_INFINITY
;
221 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
222 return USEC_INFINITY
;
225 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
226 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
229 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
232 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
233 return NSEC_INFINITY
;
235 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
236 return NSEC_INFINITY
;
238 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
241 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
244 if (u
== USEC_INFINITY
||
245 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
246 ts
->tv_sec
= (time_t) -1;
251 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
252 ts
->tv_nsec
= (long) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
257 struct timespec
*timespec_store_nsec(struct timespec
*ts
, nsec_t n
) {
260 if (n
== NSEC_INFINITY
||
261 n
/ NSEC_PER_SEC
>= TIME_T_MAX
) {
262 ts
->tv_sec
= (time_t) -1;
267 ts
->tv_sec
= (time_t) (n
/ NSEC_PER_SEC
);
268 ts
->tv_nsec
= (long) (n
% NSEC_PER_SEC
);
273 usec_t
timeval_load(const struct timeval
*tv
) {
276 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
277 return USEC_INFINITY
;
279 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
280 return USEC_INFINITY
;
283 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
284 (usec_t
) tv
->tv_usec
;
287 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
290 if (u
== USEC_INFINITY
||
291 u
/ USEC_PER_SEC
> TIME_T_MAX
) {
292 tv
->tv_sec
= (time_t) -1;
293 tv
->tv_usec
= (suseconds_t
) -1;
295 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
296 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
302 char *format_timestamp_style(
306 TimestampStyle style
) {
308 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
309 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
310 static const char * const weekdays
[] = {
323 bool utc
= false, us
= false;
328 case TIMESTAMP_PRETTY
:
336 case TIMESTAMP_US_UTC
:
344 if (l
< (size_t) (3 + /* week day */
345 1 + 10 + /* space and date */
346 1 + 8 + /* space and time */
347 (us
? 1 + 6 : 0) + /* "." and microsecond part */
348 1 + 1 + /* space and shortest possible zone */
350 return NULL
; /* Not enough space even for the shortest form. */
351 if (t
<= 0 || t
== USEC_INFINITY
)
352 return NULL
; /* Timestamp is unset */
354 /* Let's not format times with years > 9999 */
355 if (t
> USEC_TIMESTAMP_FORMATTABLE_MAX
) {
356 assert(l
>= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1);
357 strcpy(buf
, "--- XXXX-XX-XX XX:XX:XX");
361 sec
= (time_t) (t
/ USEC_PER_SEC
); /* Round down */
363 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
366 /* Start with the week day */
367 assert((size_t) tm
.tm_wday
< ELEMENTSOF(weekdays
));
368 memcpy(buf
, weekdays
[tm
.tm_wday
], 4);
370 /* Add the main components */
371 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d %H:%M:%S", &tm
) <= 0)
372 return NULL
; /* Doesn't fit */
374 /* Append the microseconds part, if that's requested */
378 return NULL
; /* Microseconds part doesn't fit. */
380 sprintf(buf
+ n
, ".%06"PRI_USEC
, t
% USEC_PER_SEC
);
383 /* Append the timezone */
386 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
387 * obsolete "GMT" instead. */
389 return NULL
; /* "UTC" doesn't fit. */
391 strcpy(buf
+ n
, " UTC");
393 } else if (!isempty(tm
.tm_zone
)) {
396 /* An explicit timezone is specified, let's use it, if it fits */
397 tn
= strlen(tm
.tm_zone
);
398 if (n
+ 1 + tn
+ 1 > l
) {
399 /* The full time zone does not fit in. Yuck. */
401 if (n
+ 1 + _POSIX_TZNAME_MAX
+ 1 > l
)
402 return NULL
; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
404 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
405 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
406 * an overly long, hard to read string on the user. This should be safe, because the user will
407 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
410 strcpy(buf
+ n
, tm
.tm_zone
);
417 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
421 if (t
<= 0 || t
== USEC_INFINITY
)
424 n
= now(CLOCK_REALTIME
);
433 if (d
>= USEC_PER_YEAR
)
434 snprintf(buf
, l
, USEC_FMT
" years " USEC_FMT
" months %s",
436 (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
, s
);
437 else if (d
>= USEC_PER_MONTH
)
438 snprintf(buf
, l
, USEC_FMT
" months " USEC_FMT
" days %s",
440 (d
% USEC_PER_MONTH
) / USEC_PER_DAY
, s
);
441 else if (d
>= USEC_PER_WEEK
)
442 snprintf(buf
, l
, USEC_FMT
" weeks " USEC_FMT
" days %s",
444 (d
% USEC_PER_WEEK
) / USEC_PER_DAY
, s
);
445 else if (d
>= 2*USEC_PER_DAY
)
446 snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
447 else if (d
>= 25*USEC_PER_HOUR
)
448 snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
449 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
450 else if (d
>= 6*USEC_PER_HOUR
)
451 snprintf(buf
, l
, USEC_FMT
"h %s",
452 d
/ USEC_PER_HOUR
, s
);
453 else if (d
>= USEC_PER_HOUR
)
454 snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
456 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
457 else if (d
>= 5*USEC_PER_MINUTE
)
458 snprintf(buf
, l
, USEC_FMT
"min %s",
459 d
/ USEC_PER_MINUTE
, s
);
460 else if (d
>= USEC_PER_MINUTE
)
461 snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
463 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
464 else if (d
>= USEC_PER_SEC
)
465 snprintf(buf
, l
, USEC_FMT
"s %s",
466 d
/ USEC_PER_SEC
, s
);
467 else if (d
>= USEC_PER_MSEC
)
468 snprintf(buf
, l
, USEC_FMT
"ms %s",
469 d
/ USEC_PER_MSEC
, s
);
471 snprintf(buf
, l
, USEC_FMT
"us %s",
474 snprintf(buf
, l
, "now");
480 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
481 static const struct {
485 { "y", USEC_PER_YEAR
},
486 { "month", USEC_PER_MONTH
},
487 { "w", USEC_PER_WEEK
},
488 { "d", USEC_PER_DAY
},
489 { "h", USEC_PER_HOUR
},
490 { "min", USEC_PER_MINUTE
},
491 { "s", USEC_PER_SEC
},
492 { "ms", USEC_PER_MSEC
},
498 bool something
= false;
503 if (t
== USEC_INFINITY
) {
504 strncpy(p
, "infinity", l
-1);
510 strncpy(p
, "0", l
-1);
515 /* The result of this function can be parsed with parse_sec */
517 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
526 if (t
< accuracy
&& something
)
529 if (t
< table
[i
].usec
)
535 a
= t
/ table
[i
].usec
;
536 b
= t
% table
[i
].usec
;
538 /* Let's see if we should shows this in dot notation */
539 if (t
< USEC_PER_MINUTE
&& b
> 0) {
544 for (cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
547 for (cc
= accuracy
; cc
> 1; cc
/= 10) {
554 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
566 /* No? Then let's show it normally */
577 n
= MIN((size_t) k
, l
);
590 static int parse_timestamp_impl(const char *t
, usec_t
*usec
, bool with_tz
) {
591 static const struct {
611 const char *k
, *utc
= NULL
, *tzn
= NULL
;
614 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
615 int r
, weekday
= -1, dst
= -1;
620 * 2012-09-22 16:34:22
621 * 2012-09-22 16:34 (seconds will be set to 0)
622 * 2012-09-22 (time will be set to 00:00:00)
623 * 16:34:22 (date will be set to today)
624 * 16:34 (date will be set to today, seconds to 0)
626 * yesterday (time is set to 00:00:00)
627 * today (time is set to 00:00:00)
628 * tomorrow (time is set to 00:00:00)
631 * @2147483647 (seconds since epoch)
636 if (t
[0] == '@' && !with_tz
)
637 return parse_sec(t
+ 1, usec
);
639 ret
= now(CLOCK_REALTIME
);
645 else if (t
[0] == '+') {
646 r
= parse_sec(t
+1, &plus
);
652 } else if (t
[0] == '-') {
653 r
= parse_sec(t
+1, &minus
);
659 } else if ((k
= endswith(t
, " ago"))) {
660 t
= strndupa(t
, k
- t
);
662 r
= parse_sec(t
, &minus
);
668 } else if ((k
= endswith(t
, " left"))) {
669 t
= strndupa(t
, k
- t
);
671 r
= parse_sec(t
, &plus
);
678 /* See if the timestamp is suffixed with UTC */
679 utc
= endswith_no_case(t
, " UTC");
681 t
= strndupa(t
, utc
- t
);
683 const char *e
= NULL
;
688 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
689 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
690 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
691 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
692 * support arbitrary timezone specifications. */
694 for (j
= 0; j
<= 1; j
++) {
696 if (isempty(tzname
[j
]))
699 e
= endswith_no_case(t
, tzname
[j
]);
710 if (IN_SET(j
, 0, 1)) {
711 /* Found one of the two timezones specified. */
712 t
= strndupa(t
, e
- t
- 1);
719 x
= (time_t) (ret
/ USEC_PER_SEC
);
722 if (!localtime_or_gmtime_r(&x
, &tm
, utc
))
729 if (streq(t
, "today")) {
730 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
733 } else if (streq(t
, "yesterday")) {
735 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
738 } else if (streq(t
, "tomorrow")) {
740 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
744 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
747 if (!startswith_no_case(t
, day_nr
[i
].name
))
750 skip
= strlen(day_nr
[i
].name
);
754 weekday
= day_nr
[i
].nr
;
760 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
769 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
778 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
785 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
792 k
= strptime(t
, "%y-%m-%d", &tm
);
794 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
799 k
= strptime(t
, "%Y-%m-%d", &tm
);
801 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
806 k
= strptime(t
, "%H:%M:%S", &tm
);
815 k
= strptime(t
, "%H:%M", &tm
);
828 r
= parse_fractional_part_u(&k
, 6, &add
);
839 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
842 x
= mktime_or_timegm(&tm
, utc
);
846 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
847 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
851 if (ret
+ plus
< ret
) /* overflow? */
854 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
867 typedef struct ParseTimestampResult
{
870 } ParseTimestampResult
;
872 int parse_timestamp(const char *t
, usec_t
*usec
) {
873 char *last_space
, *tz
= NULL
;
874 ParseTimestampResult
*shared
, tmp
;
877 last_space
= strrchr(t
, ' ');
878 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1, LOG_DEBUG
))
881 if (!tz
|| endswith_no_case(t
, " UTC"))
882 return parse_timestamp_impl(t
, usec
, false);
884 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
885 if (shared
== MAP_FAILED
)
886 return negative_errno();
888 r
= safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_WAIT
, NULL
);
890 (void) munmap(shared
, sizeof *shared
);
897 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
898 colon_tz
= strjoina(":", tz
);
900 if (setenv("TZ", colon_tz
, 1) != 0) {
901 shared
->return_value
= negative_errno();
907 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
908 * Otherwise just cut it off. */
909 with_tz
= !STR_IN_SET(tz
, tzname
[0], tzname
[1]);
911 /* Cut off the timezone if we don't need it. */
913 t
= strndupa(t
, last_space
- t
);
915 shared
->return_value
= parse_timestamp_impl(t
, &shared
->usec
, with_tz
);
921 if (munmap(shared
, sizeof *shared
) != 0)
922 return negative_errno();
924 if (tmp
.return_value
== 0 && usec
)
927 return tmp
.return_value
;
930 static const char* extract_multiplier(const char *p
, usec_t
*multiplier
) {
931 static const struct {
935 { "seconds", USEC_PER_SEC
},
936 { "second", USEC_PER_SEC
},
937 { "sec", USEC_PER_SEC
},
938 { "s", USEC_PER_SEC
},
939 { "minutes", USEC_PER_MINUTE
},
940 { "minute", USEC_PER_MINUTE
},
941 { "min", USEC_PER_MINUTE
},
942 { "months", USEC_PER_MONTH
},
943 { "month", USEC_PER_MONTH
},
944 { "M", USEC_PER_MONTH
},
945 { "msec", USEC_PER_MSEC
},
946 { "ms", USEC_PER_MSEC
},
947 { "m", USEC_PER_MINUTE
},
948 { "hours", USEC_PER_HOUR
},
949 { "hour", USEC_PER_HOUR
},
950 { "hr", USEC_PER_HOUR
},
951 { "h", USEC_PER_HOUR
},
952 { "days", USEC_PER_DAY
},
953 { "day", USEC_PER_DAY
},
954 { "d", USEC_PER_DAY
},
955 { "weeks", USEC_PER_WEEK
},
956 { "week", USEC_PER_WEEK
},
957 { "w", USEC_PER_WEEK
},
958 { "years", USEC_PER_YEAR
},
959 { "year", USEC_PER_YEAR
},
960 { "y", USEC_PER_YEAR
},
967 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
970 e
= startswith(p
, table
[i
].suffix
);
972 *multiplier
= table
[i
].usec
;
980 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
983 bool something
= false;
986 assert(default_unit
> 0);
990 p
+= strspn(p
, WHITESPACE
);
991 s
= startswith(p
, "infinity");
993 s
+= strspn(s
, WHITESPACE
);
998 *usec
= USEC_INFINITY
;
1003 usec_t multiplier
= default_unit
, k
;
1007 p
+= strspn(p
, WHITESPACE
);
1016 if (*p
== '-') /* Don't allow "-0" */
1020 l
= strtoll(p
, &e
, 10);
1028 p
+= strspn(p
, DIGITS
);
1034 s
= extract_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1035 if (s
== p
&& *s
!= '\0')
1036 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
1041 if ((usec_t
) l
>= USEC_INFINITY
/ multiplier
)
1044 k
= (usec_t
) l
* multiplier
;
1045 if (k
>= USEC_INFINITY
- r
)
1053 usec_t m
= multiplier
/ 10;
1056 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1057 k
= (usec_t
) (*b
- '0') * m
;
1058 if (k
>= USEC_INFINITY
- r
)
1064 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
1075 int parse_sec(const char *t
, usec_t
*usec
) {
1076 return parse_time(t
, usec
, USEC_PER_SEC
);
1079 int parse_sec_fix_0(const char *t
, usec_t
*ret
) {
1086 r
= parse_sec(t
, &k
);
1090 *ret
= k
== 0 ? USEC_INFINITY
: k
;
1094 int parse_sec_def_infinity(const char *t
, usec_t
*ret
) {
1095 t
+= strspn(t
, WHITESPACE
);
1097 *ret
= USEC_INFINITY
;
1100 return parse_sec(t
, ret
);
1103 static const char* extract_nsec_multiplier(const char *p
, nsec_t
*multiplier
) {
1104 static const struct {
1108 { "seconds", NSEC_PER_SEC
},
1109 { "second", NSEC_PER_SEC
},
1110 { "sec", NSEC_PER_SEC
},
1111 { "s", NSEC_PER_SEC
},
1112 { "minutes", NSEC_PER_MINUTE
},
1113 { "minute", NSEC_PER_MINUTE
},
1114 { "min", NSEC_PER_MINUTE
},
1115 { "months", NSEC_PER_MONTH
},
1116 { "month", NSEC_PER_MONTH
},
1117 { "M", NSEC_PER_MONTH
},
1118 { "msec", NSEC_PER_MSEC
},
1119 { "ms", NSEC_PER_MSEC
},
1120 { "m", NSEC_PER_MINUTE
},
1121 { "hours", NSEC_PER_HOUR
},
1122 { "hour", NSEC_PER_HOUR
},
1123 { "hr", NSEC_PER_HOUR
},
1124 { "h", NSEC_PER_HOUR
},
1125 { "days", NSEC_PER_DAY
},
1126 { "day", NSEC_PER_DAY
},
1127 { "d", NSEC_PER_DAY
},
1128 { "weeks", NSEC_PER_WEEK
},
1129 { "week", NSEC_PER_WEEK
},
1130 { "w", NSEC_PER_WEEK
},
1131 { "years", NSEC_PER_YEAR
},
1132 { "year", NSEC_PER_YEAR
},
1133 { "y", NSEC_PER_YEAR
},
1134 { "usec", NSEC_PER_USEC
},
1135 { "us", NSEC_PER_USEC
},
1136 { "µs", NSEC_PER_USEC
},
1139 { "", 1ULL }, /* default is nsec */
1143 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
1146 e
= startswith(p
, table
[i
].suffix
);
1148 *multiplier
= table
[i
].nsec
;
1156 int parse_nsec(const char *t
, nsec_t
*nsec
) {
1159 bool something
= false;
1166 p
+= strspn(p
, WHITESPACE
);
1167 s
= startswith(p
, "infinity");
1169 s
+= strspn(s
, WHITESPACE
);
1173 *nsec
= NSEC_INFINITY
;
1178 nsec_t multiplier
= 1, k
;
1182 p
+= strspn(p
, WHITESPACE
);
1191 if (*p
== '-') /* Don't allow "-0" */
1195 l
= strtoll(p
, &e
, 10);
1203 p
+= strspn(p
, DIGITS
);
1209 s
= extract_nsec_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1210 if (s
== p
&& *s
!= '\0')
1211 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
1216 if ((nsec_t
) l
>= NSEC_INFINITY
/ multiplier
)
1219 k
= (nsec_t
) l
* multiplier
;
1220 if (k
>= NSEC_INFINITY
- r
)
1228 nsec_t m
= multiplier
/ 10;
1231 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1232 k
= (nsec_t
) (*b
- '0') * m
;
1233 if (k
>= NSEC_INFINITY
- r
)
1239 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
1250 bool ntp_synced(void) {
1251 struct timex txc
= {};
1253 if (adjtimex(&txc
) < 0)
1256 /* Consider the system clock synchronized if the reported maximum error is smaller than the maximum
1257 * value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from
1258 * touching the RTC. */
1259 if (txc
.maxerror
>= 16000000)
1265 int get_timezones(char ***ret
) {
1266 _cleanup_fclose_
FILE *f
= NULL
;
1267 _cleanup_strv_free_
char **zones
= NULL
;
1268 size_t n_zones
= 0, n_allocated
= 0;
1273 zones
= strv_new("UTC");
1280 f
= fopen("/usr/share/zoneinfo/zone1970.tab", "re");
1283 _cleanup_free_
char *line
= NULL
;
1287 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1295 if (isempty(p
) || *p
== '#')
1298 /* Skip over country code */
1299 p
+= strcspn(p
, WHITESPACE
);
1300 p
+= strspn(p
, WHITESPACE
);
1302 /* Skip over coordinates */
1303 p
+= strcspn(p
, WHITESPACE
);
1304 p
+= strspn(p
, WHITESPACE
);
1306 /* Found timezone name */
1307 k
= strcspn(p
, WHITESPACE
);
1315 if (!GREEDY_REALLOC(zones
, n_allocated
, n_zones
+ 2)) {
1320 zones
[n_zones
++] = w
;
1321 zones
[n_zones
] = NULL
;
1327 } else if (errno
!= ENOENT
)
1330 *ret
= TAKE_PTR(zones
);
1335 bool timezone_is_valid(const char *name
, int log_level
) {
1338 _cleanup_close_
int fd
= -1;
1345 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1346 if (streq(name
, "UTC"))
1352 for (p
= name
; *p
; p
++) {
1353 if (!(*p
>= '0' && *p
<= '9') &&
1354 !(*p
>= 'a' && *p
<= 'z') &&
1355 !(*p
>= 'A' && *p
<= 'Z') &&
1356 !IN_SET(*p
, '-', '_', '+', '/'))
1372 if (p
- name
>= PATH_MAX
)
1375 t
= strjoina("/usr/share/zoneinfo/", name
);
1377 fd
= open(t
, O_RDONLY
|O_CLOEXEC
);
1379 log_full_errno(log_level
, errno
, "Failed to open timezone file '%s': %m", t
);
1383 r
= fd_verify_regular(fd
);
1385 log_full_errno(log_level
, r
, "Timezone file '%s' is not a regular file: %m", t
);
1389 r
= loop_read_exact(fd
, buf
, 4, false);
1391 log_full_errno(log_level
, r
, "Failed to read from timezone file '%s': %m", t
);
1395 /* Magic from tzfile(5) */
1396 if (memcmp(buf
, "TZif", 4) != 0) {
1397 log_full(log_level
, "Timezone file '%s' has wrong magic bytes", t
);
1404 bool clock_boottime_supported(void) {
1405 static int supported
= -1;
1407 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1409 if (supported
< 0) {
1412 fd
= timerfd_create(CLOCK_BOOTTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1424 clockid_t
clock_boottime_or_monotonic(void) {
1425 if (clock_boottime_supported())
1426 return CLOCK_BOOTTIME
;
1428 return CLOCK_MONOTONIC
;
1431 bool clock_supported(clockid_t clock
) {
1436 case CLOCK_MONOTONIC
:
1437 case CLOCK_REALTIME
:
1440 case CLOCK_BOOTTIME
:
1441 return clock_boottime_supported();
1443 case CLOCK_BOOTTIME_ALARM
:
1444 if (!clock_boottime_supported())
1449 /* For everything else, check properly */
1450 return clock_gettime(clock
, &ts
) >= 0;
1454 int get_timezone(char **ret
) {
1455 _cleanup_free_
char *t
= NULL
;
1460 r
= readlink_malloc("/etc/localtime", &t
);
1462 /* If the symlink does not exist, assume "UTC", like glibc does*/
1471 return r
; /* returns EINVAL if not a symlink */
1473 e
= PATH_STARTSWITH_SET(t
, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
1477 if (!timezone_is_valid(e
, LOG_DEBUG
))
1488 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1489 return utc
? timegm(tm
) : mktime(tm
);
1492 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1493 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1496 static uint32_t sysconf_clock_ticks_cached(void) {
1497 static thread_local
uint32_t hz
= 0;
1501 r
= sysconf(_SC_CLK_TCK
);
1510 uint32_t usec_to_jiffies(usec_t u
) {
1511 uint32_t hz
= sysconf_clock_ticks_cached();
1512 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);
1515 usec_t
jiffies_to_usec(uint32_t j
) {
1516 uint32_t hz
= sysconf_clock_ticks_cached();
1517 return DIV_ROUND_UP(j
* USEC_PER_SEC
, hz
);
1520 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1523 if (x
== USEC_INFINITY
)
1524 return USEC_INFINITY
;
1525 if (map_clock_id(from
) == map_clock_id(to
))
1532 /* x lies in the future */
1533 return usec_add(b
, usec_sub_unsigned(x
, a
));
1535 /* x lies in the past */
1536 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));
1539 bool in_utc_timezone(void) {
1542 return timezone
== 0 && daylight
== 0;
1545 int time_change_fd(void) {
1547 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1548 static const struct itimerspec its
= {
1549 .it_value
.tv_sec
= TIME_T_MAX
,
1552 _cleanup_close_
int fd
;
1554 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX
));
1556 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1557 * CLOCK_MONOTONIC. */
1559 fd
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1563 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
) >= 0)
1566 /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support
1567 * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
1568 * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1569 * of a black magic thing though, but what can we do?
1571 * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t
1572 * but where "long" is shorter than 64bit, i.e. 32bit archs.
1574 * See: https://github.com/systemd/systemd/issues/14362 */
1576 #if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
1577 if (ERRNO_IS_NOT_SUPPORTED(errno
) || errno
== EOVERFLOW
) {
1578 static const struct itimerspec its32
= {
1579 .it_value
.tv_sec
= INT32_MAX
,
1582 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its32
, NULL
) >= 0)
1590 static const char* const timestamp_style_table
[_TIMESTAMP_STYLE_MAX
] = {
1591 [TIMESTAMP_PRETTY
] = "pretty",
1592 [TIMESTAMP_US
] = "us",
1593 [TIMESTAMP_UTC
] = "utc",
1594 [TIMESTAMP_US_UTC
] = "us+utc",
1597 /* Use the macro for enum → string to allow for aliases */
1598 _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style
, TimestampStyle
,);
1600 /* For the string → enum mapping we use the generic implementation, but also support two aliases */
1601 TimestampStyle
timestamp_style_from_string(const char *s
) {
1604 t
= (TimestampStyle
) string_table_lookup(timestamp_style_table
, ELEMENTSOF(timestamp_style_table
), s
);
1607 if (streq_ptr(s
, "µs"))
1608 return TIMESTAMP_US
;
1609 if (streq_ptr(s
, "µs+uts"))
1610 return TIMESTAMP_US_UTC
;