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,
33 * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
34 * pendants (their only difference is when timers are set on them), let's just map them
35 * accordingly. This way, we can get the correct time even on those archs. */
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
= now(CLOCK_BOOTTIME
);
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 (!timestamp_is_set(u
)) {
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 (!timestamp_is_set(u
)) {
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
= map_clock_usec_internal(u
, nowr
, now(CLOCK_BOOTTIME
));
158 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
161 if (u
== USEC_INFINITY
) {
162 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
167 ts
->realtime
= map_clock_usec(u
, CLOCK_MONOTONIC
, CLOCK_REALTIME
);
171 dual_timestamp
* dual_timestamp_from_boottime(dual_timestamp
*ts
, usec_t u
) {
174 if (u
== USEC_INFINITY
) {
175 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
179 nowm
= now(CLOCK_BOOTTIME
);
180 ts
->monotonic
= map_clock_usec_internal(u
, nowm
, now(CLOCK_MONOTONIC
));
181 ts
->realtime
= map_clock_usec_internal(u
, nowm
, now(CLOCK_REALTIME
));
185 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
190 case CLOCK_REALTIME_ALARM
:
193 case CLOCK_MONOTONIC
:
194 return ts
->monotonic
;
197 case CLOCK_BOOTTIME_ALARM
:
201 return USEC_INFINITY
;
205 usec_t
timespec_load(const struct timespec
*ts
) {
208 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
209 return USEC_INFINITY
;
211 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
212 return USEC_INFINITY
;
215 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
216 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
219 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
222 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
223 return NSEC_INFINITY
;
225 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
226 return NSEC_INFINITY
;
228 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
231 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
234 if (u
== USEC_INFINITY
||
235 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
236 ts
->tv_sec
= (time_t) -1;
241 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
242 ts
->tv_nsec
= (long) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
247 struct timespec
*timespec_store_nsec(struct timespec
*ts
, nsec_t n
) {
250 if (n
== NSEC_INFINITY
||
251 n
/ NSEC_PER_SEC
>= TIME_T_MAX
) {
252 ts
->tv_sec
= (time_t) -1;
257 ts
->tv_sec
= (time_t) (n
/ NSEC_PER_SEC
);
258 ts
->tv_nsec
= (long) (n
% NSEC_PER_SEC
);
263 usec_t
timeval_load(const struct timeval
*tv
) {
266 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
267 return USEC_INFINITY
;
269 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
270 return USEC_INFINITY
;
273 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
274 (usec_t
) tv
->tv_usec
;
277 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
280 if (u
== USEC_INFINITY
||
281 u
/ USEC_PER_SEC
> TIME_T_MAX
) {
282 tv
->tv_sec
= (time_t) -1;
283 tv
->tv_usec
= (suseconds_t
) -1;
285 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
286 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
292 char *format_timestamp_style(
296 TimestampStyle style
) {
298 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
299 * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
300 static const char * const weekdays
[] = {
313 bool utc
= false, us
= false;
319 case TIMESTAMP_PRETTY
:
328 case TIMESTAMP_US_UTC
:
336 if (l
< (size_t) (3 + /* week day */
337 1 + 10 + /* space and date */
338 1 + 8 + /* space and time */
339 (us
? 1 + 6 : 0) + /* "." and microsecond part */
340 1 + 1 + /* space and shortest possible zone */
342 return NULL
; /* Not enough space even for the shortest form. */
343 if (!timestamp_is_set(t
))
344 return NULL
; /* Timestamp is unset */
346 if (style
== TIMESTAMP_UNIX
) {
347 r
= snprintf(buf
, l
, "@" USEC_FMT
, t
/ USEC_PER_SEC
); /* round down µs → s */
348 if (r
< 0 || (size_t) r
>= l
)
349 return NULL
; /* Doesn't fit */
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()
387 * normally uses the 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
403 * case, complain that it doesn't fit. */
405 /* So the time zone doesn't fit in fully, but the caller passed enough space for the
406 * POSIX minimum time zone length. In this case suppress the timezone entirely, in
407 * order not to dump an overly long, hard to read string on the user. This should be
408 * safe, because the user will assume the local timezone anyway if none is shown. And
409 * so does parse_timestamp(). */
412 strcpy(buf
+ n
, tm
.tm_zone
);
419 char *format_timestamp_relative(char *buf
, size_t l
, usec_t t
) {
423 if (!timestamp_is_set(t
))
426 n
= now(CLOCK_REALTIME
);
435 if (d
>= USEC_PER_YEAR
) {
436 usec_t years
= d
/ USEC_PER_YEAR
;
437 usec_t months
= (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
;
439 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s %s",
441 years
== 1 ? "year" : "years",
443 months
== 1 ? "month" : "months",
445 } else if (d
>= USEC_PER_MONTH
) {
446 usec_t months
= d
/ USEC_PER_MONTH
;
447 usec_t days
= (d
% USEC_PER_MONTH
) / USEC_PER_DAY
;
449 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s %s",
451 months
== 1 ? "month" : "months",
453 days
== 1 ? "day" : "days",
455 } else if (d
>= USEC_PER_WEEK
) {
456 usec_t weeks
= d
/ USEC_PER_WEEK
;
457 usec_t days
= (d
% USEC_PER_WEEK
) / USEC_PER_DAY
;
459 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s %s",
461 weeks
== 1 ? "week" : "weeks",
463 days
== 1 ? "day" : "days",
465 } else if (d
>= 2*USEC_PER_DAY
)
466 (void) snprintf(buf
, l
, USEC_FMT
" days %s", d
/ USEC_PER_DAY
, s
);
467 else if (d
>= 25*USEC_PER_HOUR
)
468 (void) snprintf(buf
, l
, "1 day " USEC_FMT
"h %s",
469 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
470 else if (d
>= 6*USEC_PER_HOUR
)
471 (void) snprintf(buf
, l
, USEC_FMT
"h %s",
472 d
/ USEC_PER_HOUR
, s
);
473 else if (d
>= USEC_PER_HOUR
)
474 (void) snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min %s",
476 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
477 else if (d
>= 5*USEC_PER_MINUTE
)
478 (void) snprintf(buf
, l
, USEC_FMT
"min %s",
479 d
/ USEC_PER_MINUTE
, s
);
480 else if (d
>= USEC_PER_MINUTE
)
481 (void) snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s %s",
483 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
484 else if (d
>= USEC_PER_SEC
)
485 (void) snprintf(buf
, l
, USEC_FMT
"s %s",
486 d
/ USEC_PER_SEC
, s
);
487 else if (d
>= USEC_PER_MSEC
)
488 (void) snprintf(buf
, l
, USEC_FMT
"ms %s",
489 d
/ USEC_PER_MSEC
, s
);
491 (void) snprintf(buf
, l
, USEC_FMT
"us %s",
494 (void) snprintf(buf
, l
, "now");
500 char *format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
501 static const struct {
505 { "y", USEC_PER_YEAR
},
506 { "month", USEC_PER_MONTH
},
507 { "w", USEC_PER_WEEK
},
508 { "d", USEC_PER_DAY
},
509 { "h", USEC_PER_HOUR
},
510 { "min", USEC_PER_MINUTE
},
511 { "s", USEC_PER_SEC
},
512 { "ms", USEC_PER_MSEC
},
516 char *p
= ASSERT_PTR(buf
);
517 bool something
= false;
521 if (t
== USEC_INFINITY
) {
522 strncpy(p
, "infinity", l
-1);
528 strncpy(p
, "0", l
-1);
533 /* The result of this function can be parsed with parse_sec */
535 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
544 if (t
< accuracy
&& something
)
547 if (t
< table
[i
].usec
)
553 a
= t
/ table
[i
].usec
;
554 b
= t
% table
[i
].usec
;
556 /* Let's see if we should shows this in dot notation */
557 if (t
< USEC_PER_MINUTE
&& b
> 0) {
560 for (usec_t cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
563 for (usec_t cc
= accuracy
; cc
> 1; cc
/= 10) {
570 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
582 /* No? Then let's show it normally */
593 n
= MIN((size_t) k
, l
-1);
606 static int parse_timestamp_impl(const char *t
, usec_t
*usec
, bool with_tz
) {
607 static const struct {
627 const char *k
, *utc
= NULL
, *tzn
= NULL
;
630 usec_t x_usec
, plus
= 0, minus
= 0, ret
;
631 int r
, weekday
= -1, dst
= -1;
636 * 2012-09-22 16:34:22
637 * 2012-09-22 16:34 (seconds will be set to 0)
638 * 2012-09-22 (time will be set to 00:00:00)
639 * 16:34:22 (date will be set to today)
640 * 16:34 (date will be set to today, seconds to 0)
642 * yesterday (time is set to 00:00:00)
643 * today (time is set to 00:00:00)
644 * tomorrow (time is set to 00:00:00)
647 * @2147483647 (seconds since epoch)
652 if (t
[0] == '@' && !with_tz
)
653 return parse_sec(t
+ 1, usec
);
655 ret
= now(CLOCK_REALTIME
);
661 else if (t
[0] == '+') {
662 r
= parse_sec(t
+1, &plus
);
668 } else if (t
[0] == '-') {
669 r
= parse_sec(t
+1, &minus
);
675 } else if ((k
= endswith(t
, " ago"))) {
676 t
= strndupa_safe(t
, k
- t
);
678 r
= parse_sec(t
, &minus
);
684 } else if ((k
= endswith(t
, " left"))) {
685 t
= strndupa_safe(t
, k
- t
);
687 r
= parse_sec(t
, &plus
);
694 /* See if the timestamp is suffixed with UTC */
695 utc
= endswith_no_case(t
, " UTC");
697 t
= strndupa_safe(t
, utc
- t
);
699 const char *e
= NULL
;
704 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note
705 * that we only support the local timezones here, nothing else. Not because we
706 * wouldn't want to, but simply because there are no nice APIs available to cover
707 * this. By accepting the local time zone strings, we make sure that all timestamps
708 * written by format_timestamp() can be parsed correctly, even though we don't
709 * support arbitrary timezone specifications. */
711 for (j
= 0; j
<= 1; j
++) {
713 if (isempty(tzname
[j
]))
716 e
= endswith_no_case(t
, tzname
[j
]);
727 if (IN_SET(j
, 0, 1)) {
728 /* Found one of the two timezones specified. */
729 t
= strndupa_safe(t
, e
- t
- 1);
736 x
= (time_t) (ret
/ USEC_PER_SEC
);
739 if (!localtime_or_gmtime_r(&x
, &tm
, utc
))
746 if (streq(t
, "today")) {
747 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
750 } else if (streq(t
, "yesterday")) {
752 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
755 } else if (streq(t
, "tomorrow")) {
757 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
761 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
764 if (!startswith_no_case(t
, day_nr
[i
].name
))
767 skip
= strlen(day_nr
[i
].name
);
771 weekday
= day_nr
[i
].nr
;
777 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
786 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
794 /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
796 k
= strptime(t
, "%b %d %H:%M:%S", &tm
);
805 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
812 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
819 k
= strptime(t
, "%y-%m-%d", &tm
);
821 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
826 k
= strptime(t
, "%Y-%m-%d", &tm
);
828 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
833 k
= strptime(t
, "%H:%M:%S", &tm
);
842 k
= strptime(t
, "%H:%M", &tm
);
855 r
= parse_fractional_part_u(&k
, 6, &add
);
866 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
869 x
= mktime_or_timegm(&tm
, utc
);
873 ret
= (usec_t
) x
* USEC_PER_SEC
+ x_usec
;
874 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
878 if (ret
+ plus
< ret
) /* overflow? */
881 if (ret
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
894 typedef struct ParseTimestampResult
{
897 } ParseTimestampResult
;
899 int parse_timestamp(const char *t
, usec_t
*usec
) {
900 char *last_space
, *tz
= NULL
;
901 ParseTimestampResult
*shared
, tmp
;
904 last_space
= strrchr(t
, ' ');
905 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1, LOG_DEBUG
))
908 if (!tz
|| endswith_no_case(t
, " UTC"))
909 return parse_timestamp_impl(t
, usec
, false);
911 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
912 if (shared
== MAP_FAILED
)
913 return negative_errno();
915 r
= safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_WAIT
, NULL
);
917 (void) munmap(shared
, sizeof *shared
);
924 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
925 colon_tz
= strjoina(":", tz
);
927 if (setenv("TZ", colon_tz
, 1) != 0) {
928 shared
->return_value
= negative_errno();
934 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
935 * Otherwise just cut it off. */
936 with_tz
= !STR_IN_SET(tz
, tzname
[0], tzname
[1]);
938 /* Cut off the timezone if we don't need it. */
940 t
= strndupa_safe(t
, last_space
- t
);
942 shared
->return_value
= parse_timestamp_impl(t
, &shared
->usec
, with_tz
);
948 if (munmap(shared
, sizeof *shared
) != 0)
949 return negative_errno();
951 if (tmp
.return_value
== 0 && usec
)
954 return tmp
.return_value
;
957 static const char* extract_multiplier(const char *p
, usec_t
*multiplier
) {
958 static const struct {
962 { "seconds", USEC_PER_SEC
},
963 { "second", USEC_PER_SEC
},
964 { "sec", USEC_PER_SEC
},
965 { "s", USEC_PER_SEC
},
966 { "minutes", USEC_PER_MINUTE
},
967 { "minute", USEC_PER_MINUTE
},
968 { "min", USEC_PER_MINUTE
},
969 { "months", USEC_PER_MONTH
},
970 { "month", USEC_PER_MONTH
},
971 { "M", USEC_PER_MONTH
},
972 { "msec", USEC_PER_MSEC
},
973 { "ms", USEC_PER_MSEC
},
974 { "m", USEC_PER_MINUTE
},
975 { "hours", USEC_PER_HOUR
},
976 { "hour", USEC_PER_HOUR
},
977 { "hr", USEC_PER_HOUR
},
978 { "h", USEC_PER_HOUR
},
979 { "days", USEC_PER_DAY
},
980 { "day", USEC_PER_DAY
},
981 { "d", USEC_PER_DAY
},
982 { "weeks", USEC_PER_WEEK
},
983 { "week", USEC_PER_WEEK
},
984 { "w", USEC_PER_WEEK
},
985 { "years", USEC_PER_YEAR
},
986 { "year", USEC_PER_YEAR
},
987 { "y", USEC_PER_YEAR
},
993 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
996 e
= startswith(p
, table
[i
].suffix
);
998 *multiplier
= table
[i
].usec
;
1006 int parse_time(const char *t
, usec_t
*usec
, usec_t default_unit
) {
1009 bool something
= false;
1012 assert(default_unit
> 0);
1016 p
+= strspn(p
, WHITESPACE
);
1017 s
= startswith(p
, "infinity");
1019 s
+= strspn(s
, WHITESPACE
);
1024 *usec
= USEC_INFINITY
;
1029 usec_t multiplier
= default_unit
, k
;
1033 p
+= strspn(p
, WHITESPACE
);
1042 if (*p
== '-') /* Don't allow "-0" */
1046 l
= strtoll(p
, &e
, 10);
1054 p
+= strspn(p
, DIGITS
);
1060 s
= extract_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1061 if (s
== p
&& *s
!= '\0')
1062 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1067 if ((usec_t
) l
>= USEC_INFINITY
/ multiplier
)
1070 k
= (usec_t
) l
* multiplier
;
1071 if (k
>= USEC_INFINITY
- r
)
1079 usec_t m
= multiplier
/ 10;
1082 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1083 k
= (usec_t
) (*b
- '0') * m
;
1084 if (k
>= USEC_INFINITY
- r
)
1090 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1101 int parse_sec(const char *t
, usec_t
*usec
) {
1102 return parse_time(t
, usec
, USEC_PER_SEC
);
1105 int parse_sec_fix_0(const char *t
, usec_t
*ret
) {
1112 r
= parse_sec(t
, &k
);
1116 *ret
= k
== 0 ? USEC_INFINITY
: k
;
1120 int parse_sec_def_infinity(const char *t
, usec_t
*ret
) {
1121 t
+= strspn(t
, WHITESPACE
);
1123 *ret
= USEC_INFINITY
;
1126 return parse_sec(t
, ret
);
1129 static const char* extract_nsec_multiplier(const char *p
, nsec_t
*multiplier
) {
1130 static const struct {
1134 { "seconds", NSEC_PER_SEC
},
1135 { "second", NSEC_PER_SEC
},
1136 { "sec", NSEC_PER_SEC
},
1137 { "s", NSEC_PER_SEC
},
1138 { "minutes", NSEC_PER_MINUTE
},
1139 { "minute", NSEC_PER_MINUTE
},
1140 { "min", NSEC_PER_MINUTE
},
1141 { "months", NSEC_PER_MONTH
},
1142 { "month", NSEC_PER_MONTH
},
1143 { "M", NSEC_PER_MONTH
},
1144 { "msec", NSEC_PER_MSEC
},
1145 { "ms", NSEC_PER_MSEC
},
1146 { "m", NSEC_PER_MINUTE
},
1147 { "hours", NSEC_PER_HOUR
},
1148 { "hour", NSEC_PER_HOUR
},
1149 { "hr", NSEC_PER_HOUR
},
1150 { "h", NSEC_PER_HOUR
},
1151 { "days", NSEC_PER_DAY
},
1152 { "day", NSEC_PER_DAY
},
1153 { "d", NSEC_PER_DAY
},
1154 { "weeks", NSEC_PER_WEEK
},
1155 { "week", NSEC_PER_WEEK
},
1156 { "w", NSEC_PER_WEEK
},
1157 { "years", NSEC_PER_YEAR
},
1158 { "year", NSEC_PER_YEAR
},
1159 { "y", NSEC_PER_YEAR
},
1160 { "usec", NSEC_PER_USEC
},
1161 { "us", NSEC_PER_USEC
},
1162 { "µs", NSEC_PER_USEC
},
1165 { "", 1ULL }, /* default is nsec */
1169 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
1172 e
= startswith(p
, table
[i
].suffix
);
1174 *multiplier
= table
[i
].nsec
;
1182 int parse_nsec(const char *t
, nsec_t
*nsec
) {
1185 bool something
= false;
1192 p
+= strspn(p
, WHITESPACE
);
1193 s
= startswith(p
, "infinity");
1195 s
+= strspn(s
, WHITESPACE
);
1199 *nsec
= NSEC_INFINITY
;
1204 nsec_t multiplier
= 1, k
;
1208 p
+= strspn(p
, WHITESPACE
);
1217 if (*p
== '-') /* Don't allow "-0" */
1221 l
= strtoll(p
, &e
, 10);
1229 p
+= strspn(p
, DIGITS
);
1235 s
= extract_nsec_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1236 if (s
== p
&& *s
!= '\0')
1237 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1242 if ((nsec_t
) l
>= NSEC_INFINITY
/ multiplier
)
1245 k
= (nsec_t
) l
* multiplier
;
1246 if (k
>= NSEC_INFINITY
- r
)
1254 nsec_t m
= multiplier
/ 10;
1257 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1258 k
= (nsec_t
) (*b
- '0') * m
;
1259 if (k
>= NSEC_INFINITY
- r
)
1265 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1276 static int get_timezones_from_zone1970_tab(char ***ret
) {
1277 _cleanup_fclose_
FILE *f
= NULL
;
1278 _cleanup_strv_free_
char **zones
= NULL
;
1283 f
= fopen("/usr/share/zoneinfo/zone1970.tab", "re");
1288 _cleanup_free_
char *line
= NULL
, *cc
= NULL
, *co
= NULL
, *tz
= NULL
;
1290 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1296 const char *p
= line
;
1299 * 'country codes' 'coordinates' 'timezone' 'comments' */
1300 r
= extract_many_words(&p
, NULL
, 0, &cc
, &co
, &tz
, NULL
);
1304 /* Lines that start with # are comments. */
1308 r
= strv_extend(&zones
, tz
);
1313 *ret
= TAKE_PTR(zones
);
1317 static int get_timezones_from_tzdata_zi(char ***ret
) {
1318 _cleanup_fclose_
FILE *f
= NULL
;
1319 _cleanup_strv_free_
char **zones
= NULL
;
1322 f
= fopen("/usr/share/zoneinfo/tzdata.zi", "re");
1327 _cleanup_free_
char *line
= NULL
, *type
= NULL
, *f1
= NULL
, *f2
= NULL
;
1329 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1335 const char *p
= line
;
1337 /* The only lines we care about are Zone and Link lines.
1338 * Zone line format is:
1339 * 'Zone' 'timezone' ...
1340 * Link line format is:
1341 * 'Link' 'target' 'alias'
1342 * See 'man zic' for more detail. */
1343 r
= extract_many_words(&p
, NULL
, 0, &type
, &f1
, &f2
, NULL
);
1348 if (IN_SET(*type
, 'Z', 'z'))
1349 /* Zone lines have timezone in field 1. */
1351 else if (IN_SET(*type
, 'L', 'l'))
1352 /* Link lines have timezone in field 2. */
1355 /* Not a line we care about. */
1358 r
= strv_extend(&zones
, tz
);
1363 *ret
= TAKE_PTR(zones
);
1367 int get_timezones(char ***ret
) {
1368 _cleanup_strv_free_
char **zones
= NULL
;
1373 r
= get_timezones_from_tzdata_zi(&zones
);
1375 log_debug_errno(r
, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
1376 r
= get_timezones_from_zone1970_tab(&zones
);
1378 log_debug_errno(r
, "Could not get timezone data from zone1970.tab, using UTC: %m");
1380 if (r
< 0 && r
!= -ENOENT
)
1383 /* Always include UTC */
1384 r
= strv_extend(&zones
, "UTC");
1391 *ret
= TAKE_PTR(zones
);
1395 int verify_timezone(const char *name
, int log_level
) {
1398 _cleanup_close_
int fd
= -1;
1405 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1406 if (streq(name
, "UTC"))
1412 for (p
= name
; *p
; p
++) {
1413 if (!ascii_isdigit(*p
) &&
1414 !ascii_isalpha(*p
) &&
1415 !IN_SET(*p
, '-', '_', '+', '/'))
1431 if (p
- name
>= PATH_MAX
)
1432 return -ENAMETOOLONG
;
1434 t
= strjoina("/usr/share/zoneinfo/", name
);
1436 fd
= open(t
, O_RDONLY
|O_CLOEXEC
);
1438 return log_full_errno(log_level
, errno
, "Failed to open timezone file '%s': %m", t
);
1440 r
= fd_verify_regular(fd
);
1442 return log_full_errno(log_level
, r
, "Timezone file '%s' is not a regular file: %m", t
);
1444 r
= loop_read_exact(fd
, buf
, 4, false);
1446 return log_full_errno(log_level
, r
, "Failed to read from timezone file '%s': %m", t
);
1448 /* Magic from tzfile(5) */
1449 if (memcmp(buf
, "TZif", 4) != 0)
1450 return log_full_errno(log_level
, SYNTHETIC_ERRNO(EBADMSG
),
1451 "Timezone file '%s' has wrong magic bytes", t
);
1456 bool clock_supported(clockid_t clock
) {
1461 case CLOCK_MONOTONIC
:
1462 case CLOCK_REALTIME
:
1463 case CLOCK_BOOTTIME
:
1464 /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
1468 /* For everything else, check properly */
1469 return clock_gettime(clock
, &ts
) >= 0;
1473 int get_timezone(char **ret
) {
1474 _cleanup_free_
char *t
= NULL
;
1479 r
= readlink_malloc("/etc/localtime", &t
);
1481 /* If the symlink does not exist, assume "UTC", like glibc does */
1490 return r
; /* returns EINVAL if not a symlink */
1492 e
= PATH_STARTSWITH_SET(t
, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
1496 if (!timezone_is_valid(e
, LOG_DEBUG
))
1507 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1508 return utc
? timegm(tm
) : mktime(tm
);
1511 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1512 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1515 static uint32_t sysconf_clock_ticks_cached(void) {
1516 static thread_local
uint32_t hz
= 0;
1520 r
= sysconf(_SC_CLK_TCK
);
1529 uint32_t usec_to_jiffies(usec_t u
) {
1530 uint32_t hz
= sysconf_clock_ticks_cached();
1531 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);
1534 usec_t
jiffies_to_usec(uint32_t j
) {
1535 uint32_t hz
= sysconf_clock_ticks_cached();
1536 return DIV_ROUND_UP(j
* USEC_PER_SEC
, hz
);
1539 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1542 if (x
== USEC_INFINITY
)
1543 return USEC_INFINITY
;
1544 if (map_clock_id(from
) == map_clock_id(to
))
1551 /* x lies in the future */
1552 return usec_add(b
, usec_sub_unsigned(x
, a
));
1554 /* x lies in the past */
1555 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));
1558 bool in_utc_timezone(void) {
1561 return timezone
== 0 && daylight
== 0;
1564 int time_change_fd(void) {
1566 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1567 static const struct itimerspec its
= {
1568 .it_value
.tv_sec
= TIME_T_MAX
,
1571 _cleanup_close_
int fd
= -1;
1573 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX
));
1575 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1576 * CLOCK_MONOTONIC. */
1578 fd
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1582 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
) >= 0)
1585 /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support
1586 * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
1587 * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1588 * of a black magic thing though, but what can we do?
1590 * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t
1591 * but where "long" is shorter than 64bit, i.e. 32bit archs.
1593 * See: https://github.com/systemd/systemd/issues/14362 */
1595 #if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
1596 if (ERRNO_IS_NOT_SUPPORTED(errno
) || errno
== EOVERFLOW
) {
1597 static const struct itimerspec its32
= {
1598 .it_value
.tv_sec
= INT32_MAX
,
1601 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its32
, NULL
) >= 0)
1609 static const char* const timestamp_style_table
[_TIMESTAMP_STYLE_MAX
] = {
1610 [TIMESTAMP_PRETTY
] = "pretty",
1611 [TIMESTAMP_US
] = "us",
1612 [TIMESTAMP_UTC
] = "utc",
1613 [TIMESTAMP_US_UTC
] = "us+utc",
1614 [TIMESTAMP_UNIX
] = "unix",
1617 /* Use the macro for enum → string to allow for aliases */
1618 _DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style
, TimestampStyle
,);
1620 /* For the string → enum mapping we use the generic implementation, but also support two aliases */
1621 TimestampStyle
timestamp_style_from_string(const char *s
) {
1624 t
= (TimestampStyle
) string_table_lookup(timestamp_style_table
, ELEMENTSOF(timestamp_style_table
), s
);
1627 if (streq_ptr(s
, "µs"))
1628 return TIMESTAMP_US
;
1629 if (streq_ptr(s
, "µs+utc"))
1630 return TIMESTAMP_US_UTC
;