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_threads.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,
34 * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
35 * pendants (their only difference is when timers are set on them), let's just map them
36 * accordingly. This way, we can get the correct time even on those archs. */
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
= now(CLOCK_BOOTTIME
);
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 (!timestamp_is_set(u
)) {
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 (!timestamp_is_set(u
)) {
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
= map_clock_usec_internal(u
, nowr
, now(CLOCK_BOOTTIME
));
159 dual_timestamp
* dual_timestamp_from_monotonic(dual_timestamp
*ts
, usec_t u
) {
162 if (u
== USEC_INFINITY
) {
163 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
168 ts
->realtime
= map_clock_usec(u
, CLOCK_MONOTONIC
, CLOCK_REALTIME
);
172 dual_timestamp
* dual_timestamp_from_boottime(dual_timestamp
*ts
, usec_t u
) {
177 if (u
== USEC_INFINITY
) {
178 ts
->realtime
= ts
->monotonic
= USEC_INFINITY
;
182 nowm
= now(CLOCK_BOOTTIME
);
183 ts
->monotonic
= map_clock_usec_internal(u
, nowm
, now(CLOCK_MONOTONIC
));
184 ts
->realtime
= map_clock_usec_internal(u
, nowm
, now(CLOCK_REALTIME
));
188 usec_t
triple_timestamp_by_clock(triple_timestamp
*ts
, clockid_t clock
) {
194 case CLOCK_REALTIME_ALARM
:
197 case CLOCK_MONOTONIC
:
198 return ts
->monotonic
;
201 case CLOCK_BOOTTIME_ALARM
:
205 return USEC_INFINITY
;
209 usec_t
timespec_load(const struct timespec
*ts
) {
212 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
213 return USEC_INFINITY
;
215 if ((usec_t
) ts
->tv_sec
> (UINT64_MAX
- (ts
->tv_nsec
/ NSEC_PER_USEC
)) / USEC_PER_SEC
)
216 return USEC_INFINITY
;
219 (usec_t
) ts
->tv_sec
* USEC_PER_SEC
+
220 (usec_t
) ts
->tv_nsec
/ NSEC_PER_USEC
;
223 nsec_t
timespec_load_nsec(const struct timespec
*ts
) {
226 if (ts
->tv_sec
< 0 || ts
->tv_nsec
< 0)
227 return NSEC_INFINITY
;
229 if ((nsec_t
) ts
->tv_sec
>= (UINT64_MAX
- ts
->tv_nsec
) / NSEC_PER_SEC
)
230 return NSEC_INFINITY
;
232 return (nsec_t
) ts
->tv_sec
* NSEC_PER_SEC
+ (nsec_t
) ts
->tv_nsec
;
235 struct timespec
*timespec_store(struct timespec
*ts
, usec_t u
) {
238 if (u
== USEC_INFINITY
||
239 u
/ USEC_PER_SEC
>= TIME_T_MAX
) {
240 ts
->tv_sec
= (time_t) -1;
245 ts
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
246 ts
->tv_nsec
= (long) ((u
% USEC_PER_SEC
) * NSEC_PER_USEC
);
251 struct timespec
*timespec_store_nsec(struct timespec
*ts
, nsec_t n
) {
254 if (n
== NSEC_INFINITY
||
255 n
/ NSEC_PER_SEC
>= TIME_T_MAX
) {
256 ts
->tv_sec
= (time_t) -1;
261 ts
->tv_sec
= (time_t) (n
/ NSEC_PER_SEC
);
262 ts
->tv_nsec
= (long) (n
% NSEC_PER_SEC
);
267 usec_t
timeval_load(const struct timeval
*tv
) {
270 if (tv
->tv_sec
< 0 || tv
->tv_usec
< 0)
271 return USEC_INFINITY
;
273 if ((usec_t
) tv
->tv_sec
> (UINT64_MAX
- tv
->tv_usec
) / USEC_PER_SEC
)
274 return USEC_INFINITY
;
277 (usec_t
) tv
->tv_sec
* USEC_PER_SEC
+
278 (usec_t
) tv
->tv_usec
;
281 struct timeval
*timeval_store(struct timeval
*tv
, usec_t u
) {
284 if (u
== USEC_INFINITY
||
285 u
/ USEC_PER_SEC
> TIME_T_MAX
) {
286 tv
->tv_sec
= (time_t) -1;
287 tv
->tv_usec
= (suseconds_t
) -1;
289 tv
->tv_sec
= (time_t) (u
/ USEC_PER_SEC
);
290 tv
->tv_usec
= (suseconds_t
) (u
% USEC_PER_SEC
);
296 char *format_timestamp_style(
300 TimestampStyle style
) {
302 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
303 * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
304 static const char * const weekdays
[] = {
321 assert(style
< _TIMESTAMP_STYLE_MAX
);
323 if (!timestamp_is_set(t
))
324 return NULL
; /* Timestamp is unset */
326 if (style
== TIMESTAMP_UNIX
) {
327 if (l
< (size_t) (1 + 1 + 1))
328 return NULL
; /* not enough space for even the shortest of forms */
330 return snprintf_ok(buf
, l
, "@" USEC_FMT
, t
/ USEC_PER_SEC
); /* round down μs → s */
333 utc
= IN_SET(style
, TIMESTAMP_UTC
, TIMESTAMP_US_UTC
, TIMESTAMP_DATE
);
334 us
= IN_SET(style
, TIMESTAMP_US
, TIMESTAMP_US_UTC
);
336 if (l
< (size_t) (3 + /* week day */
337 1 + 10 + /* space and date */
338 style
== TIMESTAMP_DATE
? 0 :
339 (1 + 8 + /* space and time */
340 (us
? 1 + 6 : 0) + /* "." and microsecond part */
341 1 + (utc
? 3 : 1)) + /* space and shortest possible zone */
343 return NULL
; /* Not enough space even for the shortest form. */
345 /* Let's not format times with years > 9999 */
346 if (t
> USEC_TIMESTAMP_FORMATTABLE_MAX
) {
347 static const char* const xxx
[_TIMESTAMP_STYLE_MAX
] = {
348 [TIMESTAMP_PRETTY
] = "--- XXXX-XX-XX XX:XX:XX",
349 [TIMESTAMP_US
] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX",
350 [TIMESTAMP_UTC
] = "--- XXXX-XX-XX XX:XX:XX UTC",
351 [TIMESTAMP_US_UTC
] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC",
352 [TIMESTAMP_DATE
] = "--- XXXX-XX-XX",
355 assert(l
>= strlen(xxx
[style
]) + 1);
356 return strcpy(buf
, xxx
[style
]);
359 sec
= (time_t) (t
/ USEC_PER_SEC
); /* Round down */
361 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
364 /* Start with the week day */
365 assert((size_t) tm
.tm_wday
< ELEMENTSOF(weekdays
));
366 memcpy(buf
, weekdays
[tm
.tm_wday
], 4);
368 if (style
== TIMESTAMP_DATE
) {
369 /* Special format string if only date should be shown. */
370 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d", &tm
) <= 0)
371 return NULL
; /* Doesn't fit */
376 /* Add the main components */
377 if (strftime(buf
+ 3, l
- 3, " %Y-%m-%d %H:%M:%S", &tm
) <= 0)
378 return NULL
; /* Doesn't fit */
380 /* Append the microseconds part, if that's requested */
384 return NULL
; /* Microseconds part doesn't fit. */
386 sprintf(buf
+ n
, ".%06"PRI_USEC
, t
% USEC_PER_SEC
);
389 /* Append the timezone */
392 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
393 * normally uses the obsolete "GMT" instead. */
395 return NULL
; /* "UTC" doesn't fit. */
397 strcpy(buf
+ n
, " UTC");
399 } else if (!isempty(tm
.tm_zone
)) {
402 /* An explicit timezone is specified, let's use it, if it fits */
403 tn
= strlen(tm
.tm_zone
);
404 if (n
+ 1 + tn
+ 1 > l
) {
405 /* The full time zone does not fit in. Yuck. */
407 if (n
+ 1 + _POSIX_TZNAME_MAX
+ 1 > l
)
408 return NULL
; /* Not even enough space for the POSIX minimum (of 6)? In that
409 * case, complain that it doesn't fit. */
411 /* So the time zone doesn't fit in fully, but the caller passed enough space for the
412 * POSIX minimum time zone length. In this case suppress the timezone entirely, in
413 * order not to dump an overly long, hard to read string on the user. This should be
414 * safe, because the user will assume the local timezone anyway if none is shown. And
415 * so does parse_timestamp(). */
418 strcpy(buf
+ n
, tm
.tm_zone
);
425 char* format_timestamp_relative_full(char *buf
, size_t l
, usec_t t
, clockid_t clock
, bool implicit_left
) {
431 if (!timestamp_is_set(t
))
440 s
= implicit_left
? "" : " left";
443 if (d
>= USEC_PER_YEAR
) {
444 usec_t years
= d
/ USEC_PER_YEAR
;
445 usec_t months
= (d
% USEC_PER_YEAR
) / USEC_PER_MONTH
;
447 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s%s",
449 years
== 1 ? "year" : "years",
451 months
== 1 ? "month" : "months",
453 } else if (d
>= USEC_PER_MONTH
) {
454 usec_t months
= d
/ USEC_PER_MONTH
;
455 usec_t days
= (d
% USEC_PER_MONTH
) / USEC_PER_DAY
;
457 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s%s",
459 months
== 1 ? "month" : "months",
461 days
== 1 ? "day" : "days",
463 } else if (d
>= USEC_PER_WEEK
) {
464 usec_t weeks
= d
/ USEC_PER_WEEK
;
465 usec_t days
= (d
% USEC_PER_WEEK
) / USEC_PER_DAY
;
467 (void) snprintf(buf
, l
, USEC_FMT
" %s " USEC_FMT
" %s%s",
469 weeks
== 1 ? "week" : "weeks",
471 days
== 1 ? "day" : "days",
473 } else if (d
>= 2*USEC_PER_DAY
)
474 (void) snprintf(buf
, l
, USEC_FMT
" days%s", d
/ USEC_PER_DAY
,s
);
475 else if (d
>= 25*USEC_PER_HOUR
)
476 (void) snprintf(buf
, l
, "1 day " USEC_FMT
"h%s",
477 (d
- USEC_PER_DAY
) / USEC_PER_HOUR
, s
);
478 else if (d
>= 6*USEC_PER_HOUR
)
479 (void) snprintf(buf
, l
, USEC_FMT
"h%s",
480 d
/ USEC_PER_HOUR
, s
);
481 else if (d
>= USEC_PER_HOUR
)
482 (void) snprintf(buf
, l
, USEC_FMT
"h " USEC_FMT
"min%s",
484 (d
% USEC_PER_HOUR
) / USEC_PER_MINUTE
, s
);
485 else if (d
>= 5*USEC_PER_MINUTE
)
486 (void) snprintf(buf
, l
, USEC_FMT
"min%s",
487 d
/ USEC_PER_MINUTE
, s
);
488 else if (d
>= USEC_PER_MINUTE
)
489 (void) snprintf(buf
, l
, USEC_FMT
"min " USEC_FMT
"s%s",
491 (d
% USEC_PER_MINUTE
) / USEC_PER_SEC
, s
);
492 else if (d
>= USEC_PER_SEC
)
493 (void) snprintf(buf
, l
, USEC_FMT
"s%s",
494 d
/ USEC_PER_SEC
, s
);
495 else if (d
>= USEC_PER_MSEC
)
496 (void) snprintf(buf
, l
, USEC_FMT
"ms%s",
497 d
/ USEC_PER_MSEC
, s
);
499 (void) snprintf(buf
, l
, USEC_FMT
"us%s",
502 (void) snprintf(buf
, l
, "now");
508 char* format_timespan(char *buf
, size_t l
, usec_t t
, usec_t accuracy
) {
509 static const struct {
513 { "y", USEC_PER_YEAR
},
514 { "month", USEC_PER_MONTH
},
515 { "w", USEC_PER_WEEK
},
516 { "d", USEC_PER_DAY
},
517 { "h", USEC_PER_HOUR
},
518 { "min", USEC_PER_MINUTE
},
519 { "s", USEC_PER_SEC
},
520 { "ms", USEC_PER_MSEC
},
524 char *p
= ASSERT_PTR(buf
);
525 bool something
= false;
529 if (t
== USEC_INFINITY
) {
530 strncpy(p
, "infinity", l
-1);
536 strncpy(p
, "0", l
-1);
541 /* The result of this function can be parsed with parse_sec */
543 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
552 if (t
< accuracy
&& something
)
555 if (t
< table
[i
].usec
)
561 a
= t
/ table
[i
].usec
;
562 b
= t
% table
[i
].usec
;
564 /* Let's see if we should shows this in dot notation */
565 if (t
< USEC_PER_MINUTE
&& b
> 0) {
568 for (usec_t cc
= table
[i
].usec
; cc
> 1; cc
/= 10)
571 for (usec_t cc
= accuracy
; cc
> 1; cc
/= 10) {
578 "%s"USEC_FMT
".%0*"PRI_USEC
"%s",
590 /* No? Then let's show it normally */
601 n
= MIN((size_t) k
, l
-1);
614 static int parse_timestamp_impl(
622 static const struct {
642 _cleanup_free_
char *t_alloc
= NULL
;
643 usec_t usec
, plus
= 0, minus
= 0;
644 bool with_tz
= false;
646 unsigned fractional
= 0;
653 * 2012-09-22 16:34:22
654 * 2012-09-22 16:34 (seconds will be set to 0)
655 * 2012-09-22 (time will be set to 00:00:00)
656 * 16:34:22 (date will be set to today)
657 * 16:34 (date will be set to today, seconds to 0)
659 * yesterday (time is set to 00:00:00)
660 * today (time is set to 00:00:00)
661 * tomorrow (time is set to 00:00:00)
664 * @2147483647 (seconds since epoch)
666 * Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
667 * E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
672 if (tz_offset
!= SIZE_MAX
) {
673 /* If the input string contains timezone, then cut it here. */
675 if (tz_offset
<= 1) /* timezone must be after a space. */
678 t_alloc
= strndup(t
, tz_offset
- 1);
687 /* glibc accepts gmtoff more than 24 hours, but we refuse it. */
688 if ((usec_t
) labs(gmtoff
) * USEC_PER_SEC
> USEC_PER_DAY
)
695 if (t
[0] == '@' && !with_tz
)
696 return parse_sec(t
+ 1, ret
);
698 usec
= now(CLOCK_REALTIME
);
705 r
= parse_sec(t
+1, &plus
);
713 r
= parse_sec(t
+1, &minus
);
720 if ((k
= endswith(t
, " ago"))) {
721 _cleanup_free_
char *buf
= NULL
;
723 buf
= strndup(t
, k
- t
);
727 r
= parse_sec(buf
, &minus
);
734 if ((k
= endswith(t
, " left"))) {
735 _cleanup_free_
char *buf
= NULL
;
737 buf
= strndup(t
, k
- t
);
741 r
= parse_sec(buf
, &plus
);
749 sec
= (time_t) (usec
/ USEC_PER_SEC
);
751 if (!localtime_or_gmtime_r(&sec
, &tm
, utc
))
756 if (streq(t
, "today")) {
757 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
760 } else if (streq(t
, "yesterday")) {
762 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
765 } else if (streq(t
, "tomorrow")) {
767 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
771 for (size_t i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
772 k
= startswith_no_case(t
, day_nr
[i
].name
);
776 weekday
= day_nr
[i
].nr
;
782 k
= strptime(t
, "%y-%m-%d %H:%M:%S", &tm
);
791 k
= strptime(t
, "%Y-%m-%d %H:%M:%S", &tm
);
799 /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
801 k
= strptime(t
, "%b %d %H:%M:%S", &tm
);
810 k
= strptime(t
, "%y-%m-%d %H:%M", &tm
);
817 k
= strptime(t
, "%Y-%m-%d %H:%M", &tm
);
824 k
= strptime(t
, "%y-%m-%d", &tm
);
826 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
831 k
= strptime(t
, "%Y-%m-%d", &tm
);
833 tm
.tm_sec
= tm
.tm_min
= tm
.tm_hour
= 0;
838 k
= strptime(t
, "%H:%M:%S", &tm
);
847 k
= strptime(t
, "%H:%M", &tm
);
857 r
= parse_fractional_part_u(&k
, 6, &fractional
);
867 if (weekday
>= 0 && tm
.tm_wday
!= weekday
)
871 plus
= -gmtoff
* USEC_PER_SEC
;
873 /* If gmtoff is negative, the string may be too old to be parsed as UTC.
874 * E.g. 1969-12-31 23:00:00 -06 == 1970-01-01 05:00:00 UTC
875 * We assumed that gmtoff is in the range of -24:00…+24:00, hence the only date we need to
876 * handle here is 1969-12-31. So, let's shift the date with one day, then subtract the shift
878 if (tm
.tm_year
== 69 && tm
.tm_mon
== 11 && tm
.tm_mday
== 31) {
879 /* Thu 1970-01-01-00:00:00 */
885 minus
= USEC_PER_DAY
;
888 minus
= gmtoff
* USEC_PER_SEC
;
890 sec
= mktime_or_timegm(&tm
, utc
);
894 usec
= usec_add(sec
* USEC_PER_SEC
, fractional
);
897 usec
= usec_add(usec
, plus
);
902 usec
= usec_sub_unsigned(usec
, minus
);
904 if (usec
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
912 static int parse_timestamp_maybe_with_tz(const char *t
, size_t tz_offset
, bool valid_tz
, usec_t
*ret
) {
917 for (int j
= 0; j
<= 1; j
++) {
918 if (isempty(tzname
[j
]))
921 if (!streq(t
+ tz_offset
, tzname
[j
]))
924 /* The specified timezone matches tzname[] of the local timezone. */
925 return parse_timestamp_impl(t
, tz_offset
, /* utc = */ false, /* isdst = */ j
, /* gmtoff = */ 0, ret
);
928 /* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
929 * and parse the remaining string as a local time. If we know that the last word is not a timezone,
930 * then assume that it is a part of the time and try to parse the whole string as a local time. */
931 return parse_timestamp_impl(t
, valid_tz
? tz_offset
: SIZE_MAX
,
932 /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
935 typedef struct ParseTimestampResult
{
938 } ParseTimestampResult
;
940 int parse_timestamp(const char *t
, usec_t
*ret
) {
941 ParseTimestampResult
*shared
, tmp
;
942 const char *k
, *tz
, *current_tz
;
949 tz
= strrchr(t
, ' ');
951 return parse_timestamp_impl(t
, /* tz_offset = */ SIZE_MAX
, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
956 /* Shortcut, parse the string as UTC. */
957 if (streq(tz
, "UTC"))
958 return parse_timestamp_impl(t
, tz_offset
, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret
);
960 /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
961 * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
962 * tzname[] may be in the same format. */
963 k
= strptime(tz
, "%z", &tm
);
965 return parse_timestamp_impl(t
, tz_offset
, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm
.tm_gmtoff
, ret
);
967 /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
968 * tzname[] of the local timezone, e.g. JST or CEST. */
969 if (!timezone_is_valid(tz
, LOG_DEBUG
))
970 return parse_timestamp_maybe_with_tz(t
, tz_offset
, /* valid_tz = */ false, ret
);
972 /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
974 current_tz
= getenv("TZ");
975 if (current_tz
&& *current_tz
== ':' && streq(current_tz
+ 1, tz
))
976 return parse_timestamp_maybe_with_tz(t
, tz_offset
, /* valid_tz = */ true, ret
);
978 /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
979 * the specified timezone in the child process. */
981 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
982 if (shared
== MAP_FAILED
)
983 return negative_errno();
985 r
= safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_WAIT
, NULL
);
987 (void) munmap(shared
, sizeof *shared
);
991 const char *colon_tz
;
993 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
994 colon_tz
= strjoina(":", tz
);
996 if (setenv("TZ", colon_tz
, 1) != 0) {
997 shared
->return_value
= negative_errno();
1001 shared
->return_value
= parse_timestamp_maybe_with_tz(t
, tz_offset
, /* valid_tz = */ true, &shared
->usec
);
1003 _exit(EXIT_SUCCESS
);
1007 if (munmap(shared
, sizeof *shared
) != 0)
1008 return negative_errno();
1010 if (tmp
.return_value
== 0 && ret
)
1013 return tmp
.return_value
;
1016 static const char* extract_multiplier(const char *p
, usec_t
*ret
) {
1017 static const struct {
1021 { "seconds", USEC_PER_SEC
},
1022 { "second", USEC_PER_SEC
},
1023 { "sec", USEC_PER_SEC
},
1024 { "s", USEC_PER_SEC
},
1025 { "minutes", USEC_PER_MINUTE
},
1026 { "minute", USEC_PER_MINUTE
},
1027 { "min", USEC_PER_MINUTE
},
1028 { "months", USEC_PER_MONTH
},
1029 { "month", USEC_PER_MONTH
},
1030 { "M", USEC_PER_MONTH
},
1031 { "msec", USEC_PER_MSEC
},
1032 { "ms", USEC_PER_MSEC
},
1033 { "m", USEC_PER_MINUTE
},
1034 { "hours", USEC_PER_HOUR
},
1035 { "hour", USEC_PER_HOUR
},
1036 { "hr", USEC_PER_HOUR
},
1037 { "h", USEC_PER_HOUR
},
1038 { "days", USEC_PER_DAY
},
1039 { "day", USEC_PER_DAY
},
1040 { "d", USEC_PER_DAY
},
1041 { "weeks", USEC_PER_WEEK
},
1042 { "week", USEC_PER_WEEK
},
1043 { "w", USEC_PER_WEEK
},
1044 { "years", USEC_PER_YEAR
},
1045 { "year", USEC_PER_YEAR
},
1046 { "y", USEC_PER_YEAR
},
1049 { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
1050 { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
1056 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
1059 e
= startswith(p
, table
[i
].suffix
);
1061 *ret
= table
[i
].usec
;
1069 int parse_time(const char *t
, usec_t
*ret
, usec_t default_unit
) {
1072 bool something
= false;
1075 assert(default_unit
> 0);
1079 p
+= strspn(p
, WHITESPACE
);
1080 s
= startswith(p
, "infinity");
1082 s
+= strspn(s
, WHITESPACE
);
1087 *ret
= USEC_INFINITY
;
1092 usec_t multiplier
= default_unit
, k
;
1096 p
+= strspn(p
, WHITESPACE
);
1105 if (*p
== '-') /* Don't allow "-0" */
1109 l
= strtoll(p
, &e
, 10);
1117 p
+= strspn(p
, DIGITS
);
1123 s
= extract_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1124 if (s
== p
&& *s
!= '\0')
1125 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1130 if ((usec_t
) l
>= USEC_INFINITY
/ multiplier
)
1133 k
= (usec_t
) l
* multiplier
;
1134 if (k
>= USEC_INFINITY
- usec
)
1142 usec_t m
= multiplier
/ 10;
1145 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1146 k
= (usec_t
) (*b
- '0') * m
;
1147 if (k
>= USEC_INFINITY
- usec
)
1153 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1164 int parse_sec(const char *t
, usec_t
*ret
) {
1165 return parse_time(t
, ret
, USEC_PER_SEC
);
1168 int parse_sec_fix_0(const char *t
, usec_t
*ret
) {
1175 r
= parse_sec(t
, &k
);
1179 *ret
= k
== 0 ? USEC_INFINITY
: k
;
1183 int parse_sec_def_infinity(const char *t
, usec_t
*ret
) {
1187 t
+= strspn(t
, WHITESPACE
);
1189 *ret
= USEC_INFINITY
;
1192 return parse_sec(t
, ret
);
1195 static const char* extract_nsec_multiplier(const char *p
, nsec_t
*ret
) {
1196 static const struct {
1200 { "seconds", NSEC_PER_SEC
},
1201 { "second", NSEC_PER_SEC
},
1202 { "sec", NSEC_PER_SEC
},
1203 { "s", NSEC_PER_SEC
},
1204 { "minutes", NSEC_PER_MINUTE
},
1205 { "minute", NSEC_PER_MINUTE
},
1206 { "min", NSEC_PER_MINUTE
},
1207 { "months", NSEC_PER_MONTH
},
1208 { "month", NSEC_PER_MONTH
},
1209 { "M", NSEC_PER_MONTH
},
1210 { "msec", NSEC_PER_MSEC
},
1211 { "ms", NSEC_PER_MSEC
},
1212 { "m", NSEC_PER_MINUTE
},
1213 { "hours", NSEC_PER_HOUR
},
1214 { "hour", NSEC_PER_HOUR
},
1215 { "hr", NSEC_PER_HOUR
},
1216 { "h", NSEC_PER_HOUR
},
1217 { "days", NSEC_PER_DAY
},
1218 { "day", NSEC_PER_DAY
},
1219 { "d", NSEC_PER_DAY
},
1220 { "weeks", NSEC_PER_WEEK
},
1221 { "week", NSEC_PER_WEEK
},
1222 { "w", NSEC_PER_WEEK
},
1223 { "years", NSEC_PER_YEAR
},
1224 { "year", NSEC_PER_YEAR
},
1225 { "y", NSEC_PER_YEAR
},
1226 { "usec", NSEC_PER_USEC
},
1227 { "us", NSEC_PER_USEC
},
1228 { "μs", NSEC_PER_USEC
}, /* U+03bc (aka GREEK LETTER MU) */
1229 { "µs", NSEC_PER_USEC
}, /* U+b5 (aka MICRO SIGN) */
1232 { "", 1ULL }, /* default is nsec */
1239 for (i
= 0; i
< ELEMENTSOF(table
); i
++) {
1242 e
= startswith(p
, table
[i
].suffix
);
1244 *ret
= table
[i
].nsec
;
1252 int parse_nsec(const char *t
, nsec_t
*ret
) {
1255 bool something
= false;
1262 p
+= strspn(p
, WHITESPACE
);
1263 s
= startswith(p
, "infinity");
1265 s
+= strspn(s
, WHITESPACE
);
1269 *ret
= NSEC_INFINITY
;
1274 nsec_t multiplier
= 1, k
;
1278 p
+= strspn(p
, WHITESPACE
);
1287 if (*p
== '-') /* Don't allow "-0" */
1291 l
= strtoll(p
, &e
, 10);
1299 p
+= strspn(p
, DIGITS
);
1305 s
= extract_nsec_multiplier(p
+ strspn(p
, WHITESPACE
), &multiplier
);
1306 if (s
== p
&& *s
!= '\0')
1307 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1312 if ((nsec_t
) l
>= NSEC_INFINITY
/ multiplier
)
1315 k
= (nsec_t
) l
* multiplier
;
1316 if (k
>= NSEC_INFINITY
- nsec
)
1324 nsec_t m
= multiplier
/ 10;
1327 for (b
= e
+ 1; *b
>= '0' && *b
<= '9'; b
++, m
/= 10) {
1328 k
= (nsec_t
) (*b
- '0') * m
;
1329 if (k
>= NSEC_INFINITY
- nsec
)
1335 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1346 static int get_timezones_from_zone1970_tab(char ***ret
) {
1347 _cleanup_fclose_
FILE *f
= NULL
;
1348 _cleanup_strv_free_
char **zones
= NULL
;
1353 f
= fopen("/usr/share/zoneinfo/zone1970.tab", "re");
1358 _cleanup_free_
char *line
= NULL
, *cc
= NULL
, *co
= NULL
, *tz
= NULL
;
1360 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1366 const char *p
= line
;
1369 * 'country codes' 'coordinates' 'timezone' 'comments' */
1370 r
= extract_many_words(&p
, NULL
, 0, &cc
, &co
, &tz
, NULL
);
1374 /* Lines that start with # are comments. */
1378 r
= strv_extend(&zones
, tz
);
1383 *ret
= TAKE_PTR(zones
);
1387 static int get_timezones_from_tzdata_zi(char ***ret
) {
1388 _cleanup_fclose_
FILE *f
= NULL
;
1389 _cleanup_strv_free_
char **zones
= NULL
;
1394 f
= fopen("/usr/share/zoneinfo/tzdata.zi", "re");
1399 _cleanup_free_
char *line
= NULL
, *type
= NULL
, *f1
= NULL
, *f2
= NULL
;
1401 r
= read_line(f
, LONG_LINE_MAX
, &line
);
1407 const char *p
= line
;
1409 /* The only lines we care about are Zone and Link lines.
1410 * Zone line format is:
1411 * 'Zone' 'timezone' ...
1412 * Link line format is:
1413 * 'Link' 'target' 'alias'
1414 * See 'man zic' for more detail. */
1415 r
= extract_many_words(&p
, NULL
, 0, &type
, &f1
, &f2
, NULL
);
1420 if (IN_SET(*type
, 'Z', 'z'))
1421 /* Zone lines have timezone in field 1. */
1423 else if (IN_SET(*type
, 'L', 'l'))
1424 /* Link lines have timezone in field 2. */
1427 /* Not a line we care about. */
1430 r
= strv_extend(&zones
, tz
);
1435 *ret
= TAKE_PTR(zones
);
1439 int get_timezones(char ***ret
) {
1440 _cleanup_strv_free_
char **zones
= NULL
;
1445 r
= get_timezones_from_tzdata_zi(&zones
);
1447 log_debug_errno(r
, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
1448 r
= get_timezones_from_zone1970_tab(&zones
);
1450 log_debug_errno(r
, "Could not get timezone data from zone1970.tab, using UTC: %m");
1452 if (r
< 0 && r
!= -ENOENT
)
1455 /* Always include UTC */
1456 r
= strv_extend(&zones
, "UTC");
1463 *ret
= TAKE_PTR(zones
);
1467 int verify_timezone(const char *name
, int log_level
) {
1470 _cleanup_close_
int fd
= -EBADF
;
1477 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1478 if (streq(name
, "UTC"))
1484 for (p
= name
; *p
; p
++) {
1485 if (!ascii_isdigit(*p
) &&
1486 !ascii_isalpha(*p
) &&
1487 !IN_SET(*p
, '-', '_', '+', '/'))
1503 if (p
- name
>= PATH_MAX
)
1504 return -ENAMETOOLONG
;
1506 t
= strjoina("/usr/share/zoneinfo/", name
);
1508 fd
= open(t
, O_RDONLY
|O_CLOEXEC
);
1510 return log_full_errno(log_level
, errno
, "Failed to open timezone file '%s': %m", t
);
1512 r
= fd_verify_regular(fd
);
1514 return log_full_errno(log_level
, r
, "Timezone file '%s' is not a regular file: %m", t
);
1516 r
= loop_read_exact(fd
, buf
, 4, false);
1518 return log_full_errno(log_level
, r
, "Failed to read from timezone file '%s': %m", t
);
1520 /* Magic from tzfile(5) */
1521 if (memcmp(buf
, "TZif", 4) != 0)
1522 return log_full_errno(log_level
, SYNTHETIC_ERRNO(EBADMSG
),
1523 "Timezone file '%s' has wrong magic bytes", t
);
1528 bool clock_supported(clockid_t clock
) {
1533 case CLOCK_MONOTONIC
:
1534 case CLOCK_REALTIME
:
1535 case CLOCK_BOOTTIME
:
1536 /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
1540 /* For everything else, check properly */
1541 return clock_gettime(clock
, &ts
) >= 0;
1545 int get_timezone(char **ret
) {
1546 _cleanup_free_
char *t
= NULL
;
1553 r
= readlink_malloc("/etc/localtime", &t
);
1555 /* If the symlink does not exist, assume "UTC", like glibc does */
1564 return r
; /* returns EINVAL if not a symlink */
1566 e
= PATH_STARTSWITH_SET(t
, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
1570 if (!timezone_is_valid(e
, LOG_DEBUG
))
1581 time_t mktime_or_timegm(struct tm
*tm
, bool utc
) {
1584 return utc
? timegm(tm
) : mktime(tm
);
1587 struct tm
*localtime_or_gmtime_r(const time_t *t
, struct tm
*tm
, bool utc
) {
1591 return utc
? gmtime_r(t
, tm
) : localtime_r(t
, tm
);
1594 static uint32_t sysconf_clock_ticks_cached(void) {
1595 static thread_local
uint32_t hz
= 0;
1599 r
= sysconf(_SC_CLK_TCK
);
1608 uint32_t usec_to_jiffies(usec_t u
) {
1609 uint32_t hz
= sysconf_clock_ticks_cached();
1610 return DIV_ROUND_UP(u
, USEC_PER_SEC
/ hz
);
1613 usec_t
jiffies_to_usec(uint32_t j
) {
1614 uint32_t hz
= sysconf_clock_ticks_cached();
1615 return DIV_ROUND_UP(j
* USEC_PER_SEC
, hz
);
1618 usec_t
usec_shift_clock(usec_t x
, clockid_t from
, clockid_t to
) {
1621 if (x
== USEC_INFINITY
)
1622 return USEC_INFINITY
;
1623 if (map_clock_id(from
) == map_clock_id(to
))
1630 /* x lies in the future */
1631 return usec_add(b
, usec_sub_unsigned(x
, a
));
1633 /* x lies in the past */
1634 return usec_sub_unsigned(b
, usec_sub_unsigned(a
, x
));
1637 bool in_utc_timezone(void) {
1640 return timezone
== 0 && daylight
== 0;
1643 int time_change_fd(void) {
1645 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1646 static const struct itimerspec its
= {
1647 .it_value
.tv_sec
= TIME_T_MAX
,
1650 _cleanup_close_
int fd
= -EBADF
;
1652 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX
));
1654 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1655 * CLOCK_MONOTONIC. */
1657 fd
= timerfd_create(CLOCK_REALTIME
, TFD_NONBLOCK
|TFD_CLOEXEC
);
1661 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its
, NULL
) >= 0)
1664 /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
1665 * 64-bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
1666 * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1667 * of a black magic thing though, but what can we do?
1669 * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
1670 * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
1672 * See: https://github.com/systemd/systemd/issues/14362 */
1674 #if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
1675 if (ERRNO_IS_NOT_SUPPORTED(errno
) || errno
== EOVERFLOW
) {
1676 static const struct itimerspec its32
= {
1677 .it_value
.tv_sec
= INT32_MAX
,
1680 if (timerfd_settime(fd
, TFD_TIMER_ABSTIME
|TFD_TIMER_CANCEL_ON_SET
, &its32
, NULL
) >= 0)
1688 static const char* const timestamp_style_table
[_TIMESTAMP_STYLE_MAX
] = {
1689 [TIMESTAMP_PRETTY
] = "pretty",
1690 [TIMESTAMP_US
] = "us",
1691 [TIMESTAMP_UTC
] = "utc",
1692 [TIMESTAMP_US_UTC
] = "us+utc",
1693 [TIMESTAMP_UNIX
] = "unix",
1696 /* Use the macro for enum → string to allow for aliases */
1697 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style
, TimestampStyle
);
1699 /* For the string → enum mapping we use the generic implementation, but also support two aliases */
1700 TimestampStyle
timestamp_style_from_string(const char *s
) {
1703 t
= (TimestampStyle
) string_table_lookup(timestamp_style_table
, ELEMENTSOF(timestamp_style_table
), s
);
1706 if (STRPTR_IN_SET(s
, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
1707 return TIMESTAMP_US
;
1708 if (STRPTR_IN_SET(s
, "µs+utc", "μs+utc"))
1709 return TIMESTAMP_US_UTC
;