]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/time-util.c
tree-wide: "<n>bit" → "<n>-bit"
[thirdparty/systemd.git] / src / basic / time-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <ctype.h>
4 #include <errno.h>
5 #include <limits.h>
6 #include <stdlib.h>
7 #include <sys/mman.h>
8 #include <sys/time.h>
9 #include <sys/timerfd.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include "alloc-util.h"
14 #include "fd-util.h"
15 #include "fileio.h"
16 #include "fs-util.h"
17 #include "io-util.h"
18 #include "log.h"
19 #include "macro.h"
20 #include "missing_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"
28 #include "strv.h"
29 #include "time-util.h"
30
31 static clockid_t map_clock_id(clockid_t c) {
32
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. */
37
38 switch (c) {
39
40 case CLOCK_BOOTTIME_ALARM:
41 return CLOCK_BOOTTIME;
42
43 case CLOCK_REALTIME_ALARM:
44 return CLOCK_REALTIME;
45
46 default:
47 return c;
48 }
49 }
50
51 usec_t now(clockid_t clock_id) {
52 struct timespec ts;
53
54 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
55
56 return timespec_load(&ts);
57 }
58
59 nsec_t now_nsec(clockid_t clock_id) {
60 struct timespec ts;
61
62 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
63
64 return timespec_load_nsec(&ts);
65 }
66
67 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
68 assert(ts);
69
70 ts->realtime = now(CLOCK_REALTIME);
71 ts->monotonic = now(CLOCK_MONOTONIC);
72
73 return ts;
74 }
75
76 triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
77 assert(ts);
78
79 ts->realtime = now(CLOCK_REALTIME);
80 ts->monotonic = now(CLOCK_MONOTONIC);
81 ts->boottime = now(CLOCK_BOOTTIME);
82
83 return ts;
84 }
85
86 static usec_t map_clock_usec_internal(usec_t from, usec_t from_base, usec_t to_base) {
87
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:
90 *
91 * from - from_base + to_base
92 *
93 * But takes care of overflows/underflows and avoids signed operations. */
94
95 if (from >= from_base) { /* In the future */
96 usec_t delta = from - from_base;
97
98 if (to_base >= USEC_INFINITY - delta) /* overflow? */
99 return USEC_INFINITY;
100
101 return to_base + delta;
102
103 } else { /* In the past */
104 usec_t delta = from_base - from;
105
106 if (to_base <= delta) /* underflow? */
107 return 0;
108
109 return to_base - delta;
110 }
111 }
112
113 usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
114
115 /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
116 * onto itself */
117 if (map_clock_id(from_clock) == map_clock_id(to_clock))
118 return from;
119
120 /* Keep infinity as is */
121 if (from == USEC_INFINITY)
122 return from;
123
124 return map_clock_usec_internal(from, now(from_clock), now(to_clock));
125 }
126
127 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
128 assert(ts);
129
130 if (!timestamp_is_set(u)) {
131 ts->realtime = ts->monotonic = u;
132 return ts;
133 }
134
135 ts->realtime = u;
136 ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
137 return ts;
138 }
139
140 triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
141 usec_t nowr;
142
143 assert(ts);
144
145 if (!timestamp_is_set(u)) {
146 ts->realtime = ts->monotonic = ts->boottime = u;
147 return ts;
148 }
149
150 nowr = now(CLOCK_REALTIME);
151
152 ts->realtime = u;
153 ts->monotonic = map_clock_usec_internal(u, nowr, now(CLOCK_MONOTONIC));
154 ts->boottime = map_clock_usec_internal(u, nowr, now(CLOCK_BOOTTIME));
155
156 return ts;
157 }
158
159 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
160 assert(ts);
161
162 if (u == USEC_INFINITY) {
163 ts->realtime = ts->monotonic = USEC_INFINITY;
164 return ts;
165 }
166
167 ts->monotonic = u;
168 ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
169 return ts;
170 }
171
172 dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
173 usec_t nowm;
174
175 assert(ts);
176
177 if (u == USEC_INFINITY) {
178 ts->realtime = ts->monotonic = USEC_INFINITY;
179 return ts;
180 }
181
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));
185 return ts;
186 }
187
188 usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
189 assert(ts);
190
191 switch (clock) {
192
193 case CLOCK_REALTIME:
194 case CLOCK_REALTIME_ALARM:
195 return ts->realtime;
196
197 case CLOCK_MONOTONIC:
198 return ts->monotonic;
199
200 case CLOCK_BOOTTIME:
201 case CLOCK_BOOTTIME_ALARM:
202 return ts->boottime;
203
204 default:
205 return USEC_INFINITY;
206 }
207 }
208
209 usec_t timespec_load(const struct timespec *ts) {
210 assert(ts);
211
212 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
213 return USEC_INFINITY;
214
215 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
216 return USEC_INFINITY;
217
218 return
219 (usec_t) ts->tv_sec * USEC_PER_SEC +
220 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
221 }
222
223 nsec_t timespec_load_nsec(const struct timespec *ts) {
224 assert(ts);
225
226 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
227 return NSEC_INFINITY;
228
229 if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
230 return NSEC_INFINITY;
231
232 return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
233 }
234
235 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
236 assert(ts);
237
238 if (u == USEC_INFINITY ||
239 u / USEC_PER_SEC >= TIME_T_MAX) {
240 ts->tv_sec = (time_t) -1;
241 ts->tv_nsec = -1L;
242 return ts;
243 }
244
245 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
246 ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
247
248 return ts;
249 }
250
251 struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
252 assert(ts);
253
254 if (n == NSEC_INFINITY ||
255 n / NSEC_PER_SEC >= TIME_T_MAX) {
256 ts->tv_sec = (time_t) -1;
257 ts->tv_nsec = -1L;
258 return ts;
259 }
260
261 ts->tv_sec = (time_t) (n / NSEC_PER_SEC);
262 ts->tv_nsec = (long) (n % NSEC_PER_SEC);
263
264 return ts;
265 }
266
267 usec_t timeval_load(const struct timeval *tv) {
268 assert(tv);
269
270 if (tv->tv_sec < 0 || tv->tv_usec < 0)
271 return USEC_INFINITY;
272
273 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
274 return USEC_INFINITY;
275
276 return
277 (usec_t) tv->tv_sec * USEC_PER_SEC +
278 (usec_t) tv->tv_usec;
279 }
280
281 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
282 assert(tv);
283
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;
288 } else {
289 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
290 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
291 }
292
293 return tv;
294 }
295
296 char *format_timestamp_style(
297 char *buf,
298 size_t l,
299 usec_t t,
300 TimestampStyle style) {
301
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[] = {
305 [0] = "Sun",
306 [1] = "Mon",
307 [2] = "Tue",
308 [3] = "Wed",
309 [4] = "Thu",
310 [5] = "Fri",
311 [6] = "Sat",
312 };
313
314 struct tm tm;
315 bool utc, us;
316 time_t sec;
317 size_t n;
318
319 assert(buf);
320 assert(style >= 0);
321 assert(style < _TIMESTAMP_STYLE_MAX);
322
323 if (!timestamp_is_set(t))
324 return NULL; /* Timestamp is unset */
325
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 */
329
330 return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC); /* round down μs → s */
331 }
332
333 utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
334 us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC);
335
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 */
342 1))
343 return NULL; /* Not enough space even for the shortest form. */
344
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",
353 };
354
355 assert(l >= strlen(xxx[style]) + 1);
356 return strcpy(buf, xxx[style]);
357 }
358
359 sec = (time_t) (t / USEC_PER_SEC); /* Round down */
360
361 if (!localtime_or_gmtime_r(&sec, &tm, utc))
362 return NULL;
363
364 /* Start with the week day */
365 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
366 memcpy(buf, weekdays[tm.tm_wday], 4);
367
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 */
372
373 return buf;
374 }
375
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 */
379
380 /* Append the microseconds part, if that's requested */
381 if (us) {
382 n = strlen(buf);
383 if (n + 8 > l)
384 return NULL; /* Microseconds part doesn't fit. */
385
386 sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
387 }
388
389 /* Append the timezone */
390 n = strlen(buf);
391 if (utc) {
392 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
393 * normally uses the obsolete "GMT" instead. */
394 if (n + 5 > l)
395 return NULL; /* "UTC" doesn't fit. */
396
397 strcpy(buf + n, " UTC");
398
399 } else if (!isempty(tm.tm_zone)) {
400 size_t tn;
401
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. */
406
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. */
410
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(). */
416 } else {
417 buf[n++] = ' ';
418 strcpy(buf + n, tm.tm_zone);
419 }
420 }
421
422 return buf;
423 }
424
425 char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, clockid_t clock, bool implicit_left) {
426 const char *s;
427 usec_t n, d;
428
429 assert(buf);
430
431 if (!timestamp_is_set(t))
432 return NULL;
433
434 n = now(clock);
435 if (n > t) {
436 d = n - t;
437 s = " ago";
438 } else {
439 d = t - n;
440 s = implicit_left ? "" : " left";
441 }
442
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;
446
447 (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
448 years,
449 years == 1 ? "year" : "years",
450 months,
451 months == 1 ? "month" : "months",
452 s);
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;
456
457 (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
458 months,
459 months == 1 ? "month" : "months",
460 days,
461 days == 1 ? "day" : "days",
462 s);
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;
466
467 (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
468 weeks,
469 weeks == 1 ? "week" : "weeks",
470 days,
471 days == 1 ? "day" : "days",
472 s);
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",
483 d / USEC_PER_HOUR,
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",
490 d / USEC_PER_MINUTE,
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);
498 else if (d > 0)
499 (void) snprintf(buf, l, USEC_FMT"us%s",
500 d, s);
501 else
502 (void) snprintf(buf, l, "now");
503
504 buf[l-1] = 0;
505 return buf;
506 }
507
508 char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
509 static const struct {
510 const char *suffix;
511 usec_t usec;
512 } table[] = {
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 },
521 { "us", 1 },
522 };
523
524 char *p = ASSERT_PTR(buf);
525 bool something = false;
526
527 assert(l > 0);
528
529 if (t == USEC_INFINITY) {
530 strncpy(p, "infinity", l-1);
531 p[l-1] = 0;
532 return p;
533 }
534
535 if (t <= 0) {
536 strncpy(p, "0", l-1);
537 p[l-1] = 0;
538 return p;
539 }
540
541 /* The result of this function can be parsed with parse_sec */
542
543 for (size_t i = 0; i < ELEMENTSOF(table); i++) {
544 int k = 0;
545 size_t n;
546 bool done = false;
547 usec_t a, b;
548
549 if (t <= 0)
550 break;
551
552 if (t < accuracy && something)
553 break;
554
555 if (t < table[i].usec)
556 continue;
557
558 if (l <= 1)
559 break;
560
561 a = t / table[i].usec;
562 b = t % table[i].usec;
563
564 /* Let's see if we should shows this in dot notation */
565 if (t < USEC_PER_MINUTE && b > 0) {
566 signed char j = 0;
567
568 for (usec_t cc = table[i].usec; cc > 1; cc /= 10)
569 j++;
570
571 for (usec_t cc = accuracy; cc > 1; cc /= 10) {
572 b /= 10;
573 j--;
574 }
575
576 if (j > 0) {
577 k = snprintf(p, l,
578 "%s"USEC_FMT".%0*"PRI_USEC"%s",
579 p > buf ? " " : "",
580 a,
581 j,
582 b,
583 table[i].suffix);
584
585 t = 0;
586 done = true;
587 }
588 }
589
590 /* No? Then let's show it normally */
591 if (!done) {
592 k = snprintf(p, l,
593 "%s"USEC_FMT"%s",
594 p > buf ? " " : "",
595 a,
596 table[i].suffix);
597
598 t = b;
599 }
600
601 n = MIN((size_t) k, l-1);
602
603 l -= n;
604 p += n;
605
606 something = true;
607 }
608
609 *p = 0;
610
611 return buf;
612 }
613
614 static int parse_timestamp_impl(
615 const char *t,
616 size_t tz_offset,
617 bool utc,
618 int isdst,
619 long gmtoff,
620 usec_t *ret) {
621
622 static const struct {
623 const char *name;
624 const int nr;
625 } day_nr[] = {
626 { "Sunday", 0 },
627 { "Sun", 0 },
628 { "Monday", 1 },
629 { "Mon", 1 },
630 { "Tuesday", 2 },
631 { "Tue", 2 },
632 { "Wednesday", 3 },
633 { "Wed", 3 },
634 { "Thursday", 4 },
635 { "Thu", 4 },
636 { "Friday", 5 },
637 { "Fri", 5 },
638 { "Saturday", 6 },
639 { "Sat", 6 },
640 };
641
642 _cleanup_free_ char *t_alloc = NULL;
643 usec_t usec, plus = 0, minus = 0;
644 bool with_tz = false;
645 int r, weekday = -1;
646 unsigned fractional = 0;
647 const char *k;
648 struct tm tm, copy;
649 time_t sec;
650
651 /* Allowed syntaxes:
652 *
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)
658 * now
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)
662 * +5min
663 * -5days
664 * @2147483647 (seconds since epoch)
665 *
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".
668 */
669
670 assert(t);
671
672 if (tz_offset != SIZE_MAX) {
673 /* If the input string contains timezone, then cut it here. */
674
675 if (tz_offset <= 1) /* timezone must be after a space. */
676 return -EINVAL;
677
678 t_alloc = strndup(t, tz_offset - 1);
679 if (!t_alloc)
680 return -ENOMEM;
681
682 t = t_alloc;
683 with_tz = true;
684 }
685
686 if (utc) {
687 /* glibc accepts gmtoff more than 24 hours, but we refuse it. */
688 if ((usec_t) labs(gmtoff) * USEC_PER_SEC > USEC_PER_DAY)
689 return -EINVAL;
690 } else {
691 if (gmtoff != 0)
692 return -EINVAL;
693 }
694
695 if (t[0] == '@' && !with_tz)
696 return parse_sec(t + 1, ret);
697
698 usec = now(CLOCK_REALTIME);
699
700 if (!with_tz) {
701 if (streq(t, "now"))
702 goto finish;
703
704 if (t[0] == '+') {
705 r = parse_sec(t+1, &plus);
706 if (r < 0)
707 return r;
708
709 goto finish;
710 }
711
712 if (t[0] == '-') {
713 r = parse_sec(t+1, &minus);
714 if (r < 0)
715 return r;
716
717 goto finish;
718 }
719
720 if ((k = endswith(t, " ago"))) {
721 _cleanup_free_ char *buf = NULL;
722
723 buf = strndup(t, k - t);
724 if (!buf)
725 return -ENOMEM;
726
727 r = parse_sec(buf, &minus);
728 if (r < 0)
729 return r;
730
731 goto finish;
732 }
733
734 if ((k = endswith(t, " left"))) {
735 _cleanup_free_ char *buf = NULL;
736
737 buf = strndup(t, k - t);
738 if (!buf)
739 return -ENOMEM;
740
741 r = parse_sec(buf, &plus);
742 if (r < 0)
743 return r;
744
745 goto finish;
746 }
747 }
748
749 sec = (time_t) (usec / USEC_PER_SEC);
750
751 if (!localtime_or_gmtime_r(&sec, &tm, utc))
752 return -EINVAL;
753
754 tm.tm_isdst = isdst;
755
756 if (streq(t, "today")) {
757 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
758 goto from_tm;
759
760 } else if (streq(t, "yesterday")) {
761 tm.tm_mday--;
762 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
763 goto from_tm;
764
765 } else if (streq(t, "tomorrow")) {
766 tm.tm_mday++;
767 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
768 goto from_tm;
769 }
770
771 for (size_t i = 0; i < ELEMENTSOF(day_nr); i++) {
772 k = startswith_no_case(t, day_nr[i].name);
773 if (!k || *k != ' ')
774 continue;
775
776 weekday = day_nr[i].nr;
777 t = k + 1;
778 break;
779 }
780
781 copy = tm;
782 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
783 if (k) {
784 if (*k == '.')
785 goto parse_usec;
786 else if (*k == 0)
787 goto from_tm;
788 }
789
790 tm = copy;
791 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
792 if (k) {
793 if (*k == '.')
794 goto parse_usec;
795 else if (*k == 0)
796 goto from_tm;
797 }
798
799 /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
800 tm = copy;
801 k = strptime(t, "%b %d %H:%M:%S", &tm);
802 if (k) {
803 if (*k == '.')
804 goto parse_usec;
805 else if (*k == 0)
806 goto from_tm;
807 }
808
809 tm = copy;
810 k = strptime(t, "%y-%m-%d %H:%M", &tm);
811 if (k && *k == 0) {
812 tm.tm_sec = 0;
813 goto from_tm;
814 }
815
816 tm = copy;
817 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
818 if (k && *k == 0) {
819 tm.tm_sec = 0;
820 goto from_tm;
821 }
822
823 tm = copy;
824 k = strptime(t, "%y-%m-%d", &tm);
825 if (k && *k == 0) {
826 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
827 goto from_tm;
828 }
829
830 tm = copy;
831 k = strptime(t, "%Y-%m-%d", &tm);
832 if (k && *k == 0) {
833 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
834 goto from_tm;
835 }
836
837 tm = copy;
838 k = strptime(t, "%H:%M:%S", &tm);
839 if (k) {
840 if (*k == '.')
841 goto parse_usec;
842 else if (*k == 0)
843 goto from_tm;
844 }
845
846 tm = copy;
847 k = strptime(t, "%H:%M", &tm);
848 if (k && *k == 0) {
849 tm.tm_sec = 0;
850 goto from_tm;
851 }
852
853 return -EINVAL;
854
855 parse_usec:
856 k++;
857 r = parse_fractional_part_u(&k, 6, &fractional);
858 if (r < 0)
859 return -EINVAL;
860 if (*k != '\0')
861 return -EINVAL;
862
863 from_tm:
864 assert(plus == 0);
865 assert(minus == 0);
866
867 if (weekday >= 0 && tm.tm_wday != weekday)
868 return -EINVAL;
869
870 if (gmtoff < 0) {
871 plus = -gmtoff * USEC_PER_SEC;
872
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
877 * later. */
878 if (tm.tm_year == 69 && tm.tm_mon == 11 && tm.tm_mday == 31) {
879 /* Thu 1970-01-01-00:00:00 */
880 tm.tm_year = 70;
881 tm.tm_mon = 0;
882 tm.tm_mday = 1;
883 tm.tm_wday = 4;
884 tm.tm_yday = 0;
885 minus = USEC_PER_DAY;
886 }
887 } else
888 minus = gmtoff * USEC_PER_SEC;
889
890 sec = mktime_or_timegm(&tm, utc);
891 if (sec < 0)
892 return -EINVAL;
893
894 usec = usec_add(sec * USEC_PER_SEC, fractional);
895
896 finish:
897 usec = usec_add(usec, plus);
898
899 if (usec < minus)
900 return -EINVAL;
901
902 usec = usec_sub_unsigned(usec, minus);
903
904 if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
905 return -EINVAL;
906
907 if (ret)
908 *ret = usec;
909 return 0;
910 }
911
912 static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool valid_tz, usec_t *ret) {
913 assert(t);
914
915 tzset();
916
917 for (int j = 0; j <= 1; j++) {
918 if (isempty(tzname[j]))
919 continue;
920
921 if (!streq(t + tz_offset, tzname[j]))
922 continue;
923
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);
926 }
927
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);
933 }
934
935 typedef struct ParseTimestampResult {
936 usec_t usec;
937 int return_value;
938 } ParseTimestampResult;
939
940 int parse_timestamp(const char *t, usec_t *ret) {
941 ParseTimestampResult *shared, tmp;
942 const char *k, *tz, *current_tz;
943 size_t tz_offset;
944 struct tm tm;
945 int r;
946
947 assert(t);
948
949 tz = strrchr(t, ' ');
950 if (!tz)
951 return parse_timestamp_impl(t, /* tz_offset = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
952
953 tz++;
954 tz_offset = tz - t;
955
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);
959
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);
964 if (k && *k == '\0')
965 return parse_timestamp_impl(t, tz_offset, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
966
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);
971
972 /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
973 * the process. */
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);
977
978 /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
979 * the specified timezone in the child process. */
980
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();
984
985 r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
986 if (r < 0) {
987 (void) munmap(shared, sizeof *shared);
988 return r;
989 }
990 if (r == 0) {
991 const char *colon_tz;
992
993 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
994 colon_tz = strjoina(":", tz);
995
996 if (setenv("TZ", colon_tz, 1) != 0) {
997 shared->return_value = negative_errno();
998 _exit(EXIT_FAILURE);
999 }
1000
1001 shared->return_value = parse_timestamp_maybe_with_tz(t, tz_offset, /* valid_tz = */ true, &shared->usec);
1002
1003 _exit(EXIT_SUCCESS);
1004 }
1005
1006 tmp = *shared;
1007 if (munmap(shared, sizeof *shared) != 0)
1008 return negative_errno();
1009
1010 if (tmp.return_value == 0 && ret)
1011 *ret = tmp.usec;
1012
1013 return tmp.return_value;
1014 }
1015
1016 static const char* extract_multiplier(const char *p, usec_t *ret) {
1017 static const struct {
1018 const char *suffix;
1019 usec_t usec;
1020 } table[] = {
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 },
1047 { "usec", 1ULL },
1048 { "us", 1ULL },
1049 { "μs", 1ULL }, /* U+03bc (aka GREEK SMALL LETTER MU) */
1050 { "µs", 1ULL }, /* U+b5 (aka MICRO SIGN) */
1051 };
1052
1053 assert(p);
1054 assert(ret);
1055
1056 for (size_t i = 0; i < ELEMENTSOF(table); i++) {
1057 char *e;
1058
1059 e = startswith(p, table[i].suffix);
1060 if (e) {
1061 *ret = table[i].usec;
1062 return e;
1063 }
1064 }
1065
1066 return p;
1067 }
1068
1069 int parse_time(const char *t, usec_t *ret, usec_t default_unit) {
1070 const char *p, *s;
1071 usec_t usec = 0;
1072 bool something = false;
1073
1074 assert(t);
1075 assert(default_unit > 0);
1076
1077 p = t;
1078
1079 p += strspn(p, WHITESPACE);
1080 s = startswith(p, "infinity");
1081 if (s) {
1082 s += strspn(s, WHITESPACE);
1083 if (*s != 0)
1084 return -EINVAL;
1085
1086 if (ret)
1087 *ret = USEC_INFINITY;
1088 return 0;
1089 }
1090
1091 for (;;) {
1092 usec_t multiplier = default_unit, k;
1093 long long l;
1094 char *e;
1095
1096 p += strspn(p, WHITESPACE);
1097
1098 if (*p == 0) {
1099 if (!something)
1100 return -EINVAL;
1101
1102 break;
1103 }
1104
1105 if (*p == '-') /* Don't allow "-0" */
1106 return -ERANGE;
1107
1108 errno = 0;
1109 l = strtoll(p, &e, 10);
1110 if (errno > 0)
1111 return -errno;
1112 if (l < 0)
1113 return -ERANGE;
1114
1115 if (*e == '.') {
1116 p = e + 1;
1117 p += strspn(p, DIGITS);
1118 } else if (e == p)
1119 return -EINVAL;
1120 else
1121 p = e;
1122
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' */
1126 return -EINVAL;
1127
1128 p = s;
1129
1130 if ((usec_t) l >= USEC_INFINITY / multiplier)
1131 return -ERANGE;
1132
1133 k = (usec_t) l * multiplier;
1134 if (k >= USEC_INFINITY - usec)
1135 return -ERANGE;
1136
1137 usec += k;
1138
1139 something = true;
1140
1141 if (*e == '.') {
1142 usec_t m = multiplier / 10;
1143 const char *b;
1144
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)
1148 return -ERANGE;
1149
1150 usec += k;
1151 }
1152
1153 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1154 if (b == e + 1)
1155 return -EINVAL;
1156 }
1157 }
1158
1159 if (ret)
1160 *ret = usec;
1161 return 0;
1162 }
1163
1164 int parse_sec(const char *t, usec_t *ret) {
1165 return parse_time(t, ret, USEC_PER_SEC);
1166 }
1167
1168 int parse_sec_fix_0(const char *t, usec_t *ret) {
1169 usec_t k;
1170 int r;
1171
1172 assert(t);
1173 assert(ret);
1174
1175 r = parse_sec(t, &k);
1176 if (r < 0)
1177 return r;
1178
1179 *ret = k == 0 ? USEC_INFINITY : k;
1180 return r;
1181 }
1182
1183 int parse_sec_def_infinity(const char *t, usec_t *ret) {
1184 assert(t);
1185 assert(ret);
1186
1187 t += strspn(t, WHITESPACE);
1188 if (isempty(t)) {
1189 *ret = USEC_INFINITY;
1190 return 0;
1191 }
1192 return parse_sec(t, ret);
1193 }
1194
1195 static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
1196 static const struct {
1197 const char *suffix;
1198 nsec_t nsec;
1199 } table[] = {
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) */
1230 { "nsec", 1ULL },
1231 { "ns", 1ULL },
1232 { "", 1ULL }, /* default is nsec */
1233 };
1234 size_t i;
1235
1236 assert(p);
1237 assert(ret);
1238
1239 for (i = 0; i < ELEMENTSOF(table); i++) {
1240 char *e;
1241
1242 e = startswith(p, table[i].suffix);
1243 if (e) {
1244 *ret = table[i].nsec;
1245 return e;
1246 }
1247 }
1248
1249 return p;
1250 }
1251
1252 int parse_nsec(const char *t, nsec_t *ret) {
1253 const char *p, *s;
1254 nsec_t nsec = 0;
1255 bool something = false;
1256
1257 assert(t);
1258 assert(ret);
1259
1260 p = t;
1261
1262 p += strspn(p, WHITESPACE);
1263 s = startswith(p, "infinity");
1264 if (s) {
1265 s += strspn(s, WHITESPACE);
1266 if (*s != 0)
1267 return -EINVAL;
1268
1269 *ret = NSEC_INFINITY;
1270 return 0;
1271 }
1272
1273 for (;;) {
1274 nsec_t multiplier = 1, k;
1275 long long l;
1276 char *e;
1277
1278 p += strspn(p, WHITESPACE);
1279
1280 if (*p == 0) {
1281 if (!something)
1282 return -EINVAL;
1283
1284 break;
1285 }
1286
1287 if (*p == '-') /* Don't allow "-0" */
1288 return -ERANGE;
1289
1290 errno = 0;
1291 l = strtoll(p, &e, 10);
1292 if (errno > 0)
1293 return -errno;
1294 if (l < 0)
1295 return -ERANGE;
1296
1297 if (*e == '.') {
1298 p = e + 1;
1299 p += strspn(p, DIGITS);
1300 } else if (e == p)
1301 return -EINVAL;
1302 else
1303 p = e;
1304
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' */
1308 return -EINVAL;
1309
1310 p = s;
1311
1312 if ((nsec_t) l >= NSEC_INFINITY / multiplier)
1313 return -ERANGE;
1314
1315 k = (nsec_t) l * multiplier;
1316 if (k >= NSEC_INFINITY - nsec)
1317 return -ERANGE;
1318
1319 nsec += k;
1320
1321 something = true;
1322
1323 if (*e == '.') {
1324 nsec_t m = multiplier / 10;
1325 const char *b;
1326
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)
1330 return -ERANGE;
1331
1332 nsec += k;
1333 }
1334
1335 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1336 if (b == e + 1)
1337 return -EINVAL;
1338 }
1339 }
1340
1341 *ret = nsec;
1342
1343 return 0;
1344 }
1345
1346 static int get_timezones_from_zone1970_tab(char ***ret) {
1347 _cleanup_fclose_ FILE *f = NULL;
1348 _cleanup_strv_free_ char **zones = NULL;
1349 int r;
1350
1351 assert(ret);
1352
1353 f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
1354 if (!f)
1355 return -errno;
1356
1357 for (;;) {
1358 _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
1359
1360 r = read_line(f, LONG_LINE_MAX, &line);
1361 if (r < 0)
1362 return r;
1363 if (r == 0)
1364 break;
1365
1366 const char *p = line;
1367
1368 /* Line format is:
1369 * 'country codes' 'coordinates' 'timezone' 'comments' */
1370 r = extract_many_words(&p, NULL, 0, &cc, &co, &tz, NULL);
1371 if (r < 0)
1372 continue;
1373
1374 /* Lines that start with # are comments. */
1375 if (*cc == '#')
1376 continue;
1377
1378 r = strv_extend(&zones, tz);
1379 if (r < 0)
1380 return r;
1381 }
1382
1383 *ret = TAKE_PTR(zones);
1384 return 0;
1385 }
1386
1387 static int get_timezones_from_tzdata_zi(char ***ret) {
1388 _cleanup_fclose_ FILE *f = NULL;
1389 _cleanup_strv_free_ char **zones = NULL;
1390 int r;
1391
1392 assert(ret);
1393
1394 f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
1395 if (!f)
1396 return -errno;
1397
1398 for (;;) {
1399 _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
1400
1401 r = read_line(f, LONG_LINE_MAX, &line);
1402 if (r < 0)
1403 return r;
1404 if (r == 0)
1405 break;
1406
1407 const char *p = line;
1408
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);
1416 if (r < 0)
1417 continue;
1418
1419 char *tz;
1420 if (IN_SET(*type, 'Z', 'z'))
1421 /* Zone lines have timezone in field 1. */
1422 tz = f1;
1423 else if (IN_SET(*type, 'L', 'l'))
1424 /* Link lines have timezone in field 2. */
1425 tz = f2;
1426 else
1427 /* Not a line we care about. */
1428 continue;
1429
1430 r = strv_extend(&zones, tz);
1431 if (r < 0)
1432 return r;
1433 }
1434
1435 *ret = TAKE_PTR(zones);
1436 return 0;
1437 }
1438
1439 int get_timezones(char ***ret) {
1440 _cleanup_strv_free_ char **zones = NULL;
1441 int r;
1442
1443 assert(ret);
1444
1445 r = get_timezones_from_tzdata_zi(&zones);
1446 if (r == -ENOENT) {
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);
1449 if (r == -ENOENT)
1450 log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
1451 }
1452 if (r < 0 && r != -ENOENT)
1453 return r;
1454
1455 /* Always include UTC */
1456 r = strv_extend(&zones, "UTC");
1457 if (r < 0)
1458 return -ENOMEM;
1459
1460 strv_sort(zones);
1461 strv_uniq(zones);
1462
1463 *ret = TAKE_PTR(zones);
1464 return 0;
1465 }
1466
1467 int verify_timezone(const char *name, int log_level) {
1468 bool slash = false;
1469 const char *p, *t;
1470 _cleanup_close_ int fd = -EBADF;
1471 char buf[4];
1472 int r;
1473
1474 if (isempty(name))
1475 return -EINVAL;
1476
1477 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1478 if (streq(name, "UTC"))
1479 return 0;
1480
1481 if (name[0] == '/')
1482 return -EINVAL;
1483
1484 for (p = name; *p; p++) {
1485 if (!ascii_isdigit(*p) &&
1486 !ascii_isalpha(*p) &&
1487 !IN_SET(*p, '-', '_', '+', '/'))
1488 return -EINVAL;
1489
1490 if (*p == '/') {
1491
1492 if (slash)
1493 return -EINVAL;
1494
1495 slash = true;
1496 } else
1497 slash = false;
1498 }
1499
1500 if (slash)
1501 return -EINVAL;
1502
1503 if (p - name >= PATH_MAX)
1504 return -ENAMETOOLONG;
1505
1506 t = strjoina("/usr/share/zoneinfo/", name);
1507
1508 fd = open(t, O_RDONLY|O_CLOEXEC);
1509 if (fd < 0)
1510 return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
1511
1512 r = fd_verify_regular(fd);
1513 if (r < 0)
1514 return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
1515
1516 r = loop_read_exact(fd, buf, 4, false);
1517 if (r < 0)
1518 return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
1519
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);
1524
1525 return 0;
1526 }
1527
1528 bool clock_supported(clockid_t clock) {
1529 struct timespec ts;
1530
1531 switch (clock) {
1532
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 */
1537 return true;
1538
1539 default:
1540 /* For everything else, check properly */
1541 return clock_gettime(clock, &ts) >= 0;
1542 }
1543 }
1544
1545 int get_timezone(char **ret) {
1546 _cleanup_free_ char *t = NULL;
1547 const char *e;
1548 char *z;
1549 int r;
1550
1551 assert(ret);
1552
1553 r = readlink_malloc("/etc/localtime", &t);
1554 if (r == -ENOENT) {
1555 /* If the symlink does not exist, assume "UTC", like glibc does */
1556 z = strdup("UTC");
1557 if (!z)
1558 return -ENOMEM;
1559
1560 *ret = z;
1561 return 0;
1562 }
1563 if (r < 0)
1564 return r; /* returns EINVAL if not a symlink */
1565
1566 e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
1567 if (!e)
1568 return -EINVAL;
1569
1570 if (!timezone_is_valid(e, LOG_DEBUG))
1571 return -EINVAL;
1572
1573 z = strdup(e);
1574 if (!z)
1575 return -ENOMEM;
1576
1577 *ret = z;
1578 return 0;
1579 }
1580
1581 time_t mktime_or_timegm(struct tm *tm, bool utc) {
1582 assert(tm);
1583
1584 return utc ? timegm(tm) : mktime(tm);
1585 }
1586
1587 struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1588 assert(t);
1589 assert(tm);
1590
1591 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1592 }
1593
1594 static uint32_t sysconf_clock_ticks_cached(void) {
1595 static thread_local uint32_t hz = 0;
1596 long r;
1597
1598 if (hz == 0) {
1599 r = sysconf(_SC_CLK_TCK);
1600
1601 assert(r > 0);
1602 hz = r;
1603 }
1604
1605 return hz;
1606 }
1607
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);
1611 }
1612
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);
1616 }
1617
1618 usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1619 usec_t a, b;
1620
1621 if (x == USEC_INFINITY)
1622 return USEC_INFINITY;
1623 if (map_clock_id(from) == map_clock_id(to))
1624 return x;
1625
1626 a = now(from);
1627 b = now(to);
1628
1629 if (x > a)
1630 /* x lies in the future */
1631 return usec_add(b, usec_sub_unsigned(x, a));
1632 else
1633 /* x lies in the past */
1634 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1635 }
1636
1637 bool in_utc_timezone(void) {
1638 tzset();
1639
1640 return timezone == 0 && daylight == 0;
1641 }
1642
1643 int time_change_fd(void) {
1644
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,
1648 };
1649
1650 _cleanup_close_ int fd = -EBADF;
1651
1652 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
1653
1654 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1655 * CLOCK_MONOTONIC. */
1656
1657 fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1658 if (fd < 0)
1659 return -errno;
1660
1661 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
1662 return TAKE_FD(fd);
1663
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?
1668 *
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.
1671 *
1672 * See: https://github.com/systemd/systemd/issues/14362 */
1673
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,
1678 };
1679
1680 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0)
1681 return TAKE_FD(fd);
1682 }
1683 #endif
1684
1685 return -errno;
1686 }
1687
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",
1694 };
1695
1696 /* Use the macro for enum → string to allow for aliases */
1697 DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle);
1698
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) {
1701 TimestampStyle t;
1702
1703 t = (TimestampStyle) string_table_lookup(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
1704 if (t >= 0)
1705 return t;
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;
1710 return t;
1711 }