]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/time-util.c
time-util: also use 32bit hack on EOVERFLOW
[thirdparty/systemd.git] / src / basic / time-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
9a98c7a1 2
279f52a1 3#include <ctype.h>
11c3a366
TA
4#include <errno.h>
5#include <limits.h>
6#include <stdlib.h>
48d26c01 7#include <sys/mman.h>
11c3a366 8#include <sys/time.h>
77ff2de9 9#include <sys/timerfd.h>
07630cea 10#include <sys/timex.h>
11c3a366
TA
11#include <sys/types.h>
12#include <unistd.h>
9a98c7a1 13
b5efdb8a 14#include "alloc-util.h"
3ffd4af2 15#include "fd-util.h"
0d39fa9c 16#include "fileio.h"
f4f15635 17#include "fs-util.h"
a2932d51 18#include "io-util.h"
11c3a366
TA
19#include "log.h"
20#include "macro.h"
3c94e504 21#include "missing_timerfd.h"
93cc7779
TA
22#include "parse-util.h"
23#include "path-util.h"
dccca82b 24#include "process-util.h"
a2932d51 25#include "stat-util.h"
07630cea 26#include "string-util.h"
75683450 27#include "strv.h"
07630cea 28#include "time-util.h"
9a98c7a1 29
32c1f5a5
LP
30static clockid_t map_clock_id(clockid_t c) {
31
32 /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus, clock_gettime() will
33 * fail for them. Since they are essentially the same as their non-ALARM pendants (their only difference is
34 * when timers are set on them), let's just map them accordingly. This way, we can get the correct time even on
bdf19f8f 35 * those archs. */
32c1f5a5
LP
36
37 switch (c) {
38
39 case CLOCK_BOOTTIME_ALARM:
bdf19f8f 40 return CLOCK_BOOTTIME;
32c1f5a5
LP
41
42 case CLOCK_REALTIME_ALARM:
43 return CLOCK_REALTIME;
44
45 default:
46 return c;
47 }
48}
49
9a98c7a1
LP
50usec_t now(clockid_t clock_id) {
51 struct timespec ts;
52
32c1f5a5 53 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
9a98c7a1
LP
54
55 return timespec_load(&ts);
56}
57
45d7a8bb
LP
58nsec_t now_nsec(clockid_t clock_id) {
59 struct timespec ts;
60
32c1f5a5 61 assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
45d7a8bb
LP
62
63 return timespec_load_nsec(&ts);
64}
65
9a98c7a1
LP
66dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
67 assert(ts);
68
69 ts->realtime = now(CLOCK_REALTIME);
70 ts->monotonic = now(CLOCK_MONOTONIC);
71
72 return ts;
73}
74
fe624c4c
LP
75triple_timestamp* triple_timestamp_get(triple_timestamp *ts) {
76 assert(ts);
77
78 ts->realtime = now(CLOCK_REALTIME);
79 ts->monotonic = now(CLOCK_MONOTONIC);
80 ts->boottime = clock_boottime_supported() ? now(CLOCK_BOOTTIME) : USEC_INFINITY;
81
82 return ts;
83}
84
9a98c7a1
LP
85dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
86 int64_t delta;
87 assert(ts);
88
75a5f1d8
LP
89 if (u == USEC_INFINITY || u <= 0) {
90 ts->realtime = ts->monotonic = u;
cae0c5e0
LP
91 return ts;
92 }
93
9a98c7a1
LP
94 ts->realtime = u;
95
75a5f1d8 96 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
54d8ef14 97 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
9a98c7a1
LP
98
99 return ts;
100}
101
fe624c4c
LP
102triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
103 int64_t delta;
104
105 assert(ts);
106
107 if (u == USEC_INFINITY || u <= 0) {
108 ts->realtime = ts->monotonic = ts->boottime = u;
109 return ts;
110 }
111
112 ts->realtime = u;
113 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
54d8ef14
LP
114 ts->monotonic = usec_sub_signed(now(CLOCK_MONOTONIC), delta);
115 ts->boottime = clock_boottime_supported() ? usec_sub_signed(now(CLOCK_BOOTTIME), delta) : USEC_INFINITY;
fe624c4c
LP
116
117 return ts;
118}
119
cae0c5e0
LP
120dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
121 int64_t delta;
122 assert(ts);
123
3a43da28
KS
124 if (u == USEC_INFINITY) {
125 ts->realtime = ts->monotonic = USEC_INFINITY;
cae0c5e0
LP
126 return ts;
127 }
128
129 ts->monotonic = u;
130 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
54d8ef14 131 ts->realtime = usec_sub_signed(now(CLOCK_REALTIME), delta);
cae0c5e0
LP
132
133 return ts;
134}
135
fbe55073
LP
136dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
137 int64_t delta;
138
139 if (u == USEC_INFINITY) {
140 ts->realtime = ts->monotonic = USEC_INFINITY;
141 return ts;
142 }
fbe55073 143
0345d252 144 dual_timestamp_get(ts);
fbe55073 145 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
54d8ef14
LP
146 ts->realtime = usec_sub_signed(ts->realtime, delta);
147 ts->monotonic = usec_sub_signed(ts->monotonic, delta);
fbe55073
LP
148
149 return ts;
150}
151
fe624c4c
LP
152usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
153
154 switch (clock) {
155
156 case CLOCK_REALTIME:
157 case CLOCK_REALTIME_ALARM:
158 return ts->realtime;
159
160 case CLOCK_MONOTONIC:
161 return ts->monotonic;
162
163 case CLOCK_BOOTTIME:
164 case CLOCK_BOOTTIME_ALARM:
165 return ts->boottime;
166
167 default:
168 return USEC_INFINITY;
169 }
170}
171
9a98c7a1
LP
172usec_t timespec_load(const struct timespec *ts) {
173 assert(ts);
174
c477ff14 175 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
3a43da28 176 return USEC_INFINITY;
9a98c7a1
LP
177
178 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
3a43da28 179 return USEC_INFINITY;
9a98c7a1
LP
180
181 return
182 (usec_t) ts->tv_sec * USEC_PER_SEC +
183 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
184}
185
3a730176 186nsec_t timespec_load_nsec(const struct timespec *ts) {
45d7a8bb
LP
187 assert(ts);
188
c477ff14 189 if (ts->tv_sec < 0 || ts->tv_nsec < 0)
45d7a8bb
LP
190 return NSEC_INFINITY;
191
a2daa2f0
ZJS
192 if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
193 return NSEC_INFINITY;
194
195 return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
45d7a8bb
LP
196}
197
9a98c7a1
LP
198struct timespec *timespec_store(struct timespec *ts, usec_t u) {
199 assert(ts);
200
f977849c 201 if (u == USEC_INFINITY ||
d201d908 202 u / USEC_PER_SEC >= TIME_T_MAX) {
9a98c7a1
LP
203 ts->tv_sec = (time_t) -1;
204 ts->tv_nsec = (long) -1;
205 return ts;
206 }
207
208 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
209 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
210
211 return ts;
212}
213
214usec_t timeval_load(const struct timeval *tv) {
215 assert(tv);
216
c477ff14 217 if (tv->tv_sec < 0 || tv->tv_usec < 0)
3a43da28 218 return USEC_INFINITY;
9a98c7a1
LP
219
220 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
3a43da28 221 return USEC_INFINITY;
9a98c7a1
LP
222
223 return
224 (usec_t) tv->tv_sec * USEC_PER_SEC +
225 (usec_t) tv->tv_usec;
226}
227
228struct timeval *timeval_store(struct timeval *tv, usec_t u) {
229 assert(tv);
230
68bdd2d2 231 if (u == USEC_INFINITY ||
f977849c 232 u / USEC_PER_SEC > TIME_T_MAX) {
9a98c7a1
LP
233 tv->tv_sec = (time_t) -1;
234 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
235 } else {
236 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
237 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
238 }
239
9a98c7a1
LP
240 return tv;
241}
242
21b3a0fc
LP
243static char *format_timestamp_internal(
244 char *buf,
245 size_t l,
246 usec_t t,
247 bool utc,
248 bool us) {
249
250 /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that our
251 * generated timestamps may be parsed with parse_timestamp(), and always read the same. */
252 static const char * const weekdays[] = {
253 [0] = "Sun",
254 [1] = "Mon",
255 [2] = "Tue",
256 [3] = "Wed",
257 [4] = "Thu",
258 [5] = "Fri",
259 [6] = "Sat",
260 };
261
9a98c7a1
LP
262 struct tm tm;
263 time_t sec;
21b3a0fc 264 size_t n;
9a98c7a1
LP
265
266 assert(buf);
9a98c7a1 267
8164e306
YW
268 if (l < (size_t) (3 + /* week day */
269 1 + 10 + /* space and date */
270 1 + 8 + /* space and time */
271 (us ? 1 + 6 : 0) + /* "." and microsecond part */
272 1 + 1 + /* space and shortest possible zone */
273 1))
21b3a0fc 274 return NULL; /* Not enough space even for the shortest form. */
3a43da28 275 if (t <= 0 || t == USEC_INFINITY)
21b3a0fc
LP
276 return NULL; /* Timestamp is unset */
277
1bb4b028 278 /* Let's not format times with years > 9999 */
d3d28024 279 if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
490c5a37 280 assert(l >= STRLEN("--- XXXX-XX-XX XX:XX:XX") + 1);
d3d28024
ZJS
281 strcpy(buf, "--- XXXX-XX-XX XX:XX:XX");
282 return buf;
283 }
1bb4b028 284
21b3a0fc 285 sec = (time_t) (t / USEC_PER_SEC); /* Round down */
21b3a0fc
LP
286
287 if (!localtime_or_gmtime_r(&sec, &tm, utc))
9a98c7a1
LP
288 return NULL;
289
21b3a0fc
LP
290 /* Start with the week day */
291 assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
292 memcpy(buf, weekdays[tm.tm_wday], 4);
9a98c7a1 293
21b3a0fc
LP
294 /* Add the main components */
295 if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
296 return NULL; /* Doesn't fit */
0056086a 297
21b3a0fc 298 /* Append the microseconds part, if that's requested */
0056086a 299 if (us) {
21b3a0fc
LP
300 n = strlen(buf);
301 if (n + 8 > l)
302 return NULL; /* Microseconds part doesn't fit. */
303
70887c5f 304 sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
21b3a0fc
LP
305 }
306
307 /* Append the timezone */
308 n = strlen(buf);
309 if (utc) {
310 /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r() normally uses the
311 * obsolete "GMT" instead. */
312 if (n + 5 > l)
313 return NULL; /* "UTC" doesn't fit. */
314
315 strcpy(buf + n, " UTC");
316
317 } else if (!isempty(tm.tm_zone)) {
318 size_t tn;
319
320 /* An explicit timezone is specified, let's use it, if it fits */
321 tn = strlen(tm.tm_zone);
322 if (n + 1 + tn + 1 > l) {
323 /* The full time zone does not fit in. Yuck. */
324
325 if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
326 return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that case, complain that it doesn't fit */
327
328 /* So the time zone doesn't fit in fully, but the caller passed enough space for the POSIX
329 * minimum time zone length. In this case suppress the timezone entirely, in order not to dump
330 * an overly long, hard to read string on the user. This should be safe, because the user will
331 * assume the local timezone anyway if none is shown. And so does parse_timestamp(). */
332 } else {
333 buf[n++] = ' ';
334 strcpy(buf + n, tm.tm_zone);
335 }
0056086a 336 }
9a98c7a1
LP
337
338 return buf;
339}
340
a62e83b4 341char *format_timestamp(char *buf, size_t l, usec_t t) {
0056086a 342 return format_timestamp_internal(buf, l, t, false, false);
a62e83b4
JS
343}
344
5ab99e07 345char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
0056086a 346 return format_timestamp_internal(buf, l, t, true, false);
f02d8367
ZJS
347}
348
5ab99e07 349char *format_timestamp_us(char *buf, size_t l, usec_t t) {
0056086a 350 return format_timestamp_internal(buf, l, t, false, true);
5ab99e07
LP
351}
352
353char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
0056086a 354 return format_timestamp_internal(buf, l, t, true, true);
5ab99e07
LP
355}
356
bbb8486e 357char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 358 const char *s;
9a98c7a1
LP
359 usec_t n, d;
360
65de0395 361 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
362 return NULL;
363
65de0395 364 n = now(CLOCK_REALTIME);
1fcf71f5
LP
365 if (n > t) {
366 d = n - t;
367 s = "ago";
368 } else {
369 d = t - n;
370 s = "left";
371 }
9a98c7a1
LP
372
373 if (d >= USEC_PER_YEAR)
609e002e 374 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
de0671ee
ZJS
375 d / USEC_PER_YEAR,
376 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
9a98c7a1 377 else if (d >= USEC_PER_MONTH)
609e002e 378 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
de0671ee
ZJS
379 d / USEC_PER_MONTH,
380 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
9a98c7a1 381 else if (d >= USEC_PER_WEEK)
609e002e 382 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
de0671ee
ZJS
383 d / USEC_PER_WEEK,
384 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
9a98c7a1 385 else if (d >= 2*USEC_PER_DAY)
609e002e 386 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
9a98c7a1 387 else if (d >= 25*USEC_PER_HOUR)
609e002e 388 snprintf(buf, l, "1 day " USEC_FMT "h %s",
de0671ee 389 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 390 else if (d >= 6*USEC_PER_HOUR)
609e002e 391 snprintf(buf, l, USEC_FMT "h %s",
de0671ee 392 d / USEC_PER_HOUR, s);
9a98c7a1 393 else if (d >= USEC_PER_HOUR)
609e002e 394 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
de0671ee
ZJS
395 d / USEC_PER_HOUR,
396 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 397 else if (d >= 5*USEC_PER_MINUTE)
609e002e 398 snprintf(buf, l, USEC_FMT "min %s",
de0671ee 399 d / USEC_PER_MINUTE, s);
9a98c7a1 400 else if (d >= USEC_PER_MINUTE)
609e002e 401 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
de0671ee
ZJS
402 d / USEC_PER_MINUTE,
403 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 404 else if (d >= USEC_PER_SEC)
609e002e 405 snprintf(buf, l, USEC_FMT "s %s",
de0671ee 406 d / USEC_PER_SEC, s);
9a98c7a1 407 else if (d >= USEC_PER_MSEC)
609e002e 408 snprintf(buf, l, USEC_FMT "ms %s",
de0671ee 409 d / USEC_PER_MSEC, s);
9a98c7a1 410 else if (d > 0)
de0671ee
ZJS
411 snprintf(buf, l, USEC_FMT"us %s",
412 d, s);
9a98c7a1
LP
413 else
414 snprintf(buf, l, "now");
415
416 buf[l-1] = 0;
417 return buf;
418}
419
2fa4092c 420char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
421 static const struct {
422 const char *suffix;
423 usec_t usec;
424 } table[] = {
eb55ec9f
LP
425 { "y", USEC_PER_YEAR },
426 { "month", USEC_PER_MONTH },
427 { "w", USEC_PER_WEEK },
428 { "d", USEC_PER_DAY },
429 { "h", USEC_PER_HOUR },
430 { "min", USEC_PER_MINUTE },
431 { "s", USEC_PER_SEC },
432 { "ms", USEC_PER_MSEC },
433 { "us", 1 },
9a98c7a1
LP
434 };
435
da6053d0 436 size_t i;
9a98c7a1 437 char *p = buf;
2fa4092c 438 bool something = false;
9a98c7a1
LP
439
440 assert(buf);
441 assert(l > 0);
442
bb1fada8
LP
443 if (t == USEC_INFINITY) {
444 strncpy(p, "infinity", l-1);
445 p[l-1] = 0;
446 return p;
447 }
448
449 if (t <= 0) {
450 strncpy(p, "0", l-1);
7c537b2e
LP
451 p[l-1] = 0;
452 return p;
453 }
454
7f602784 455 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
456
457 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 458 int k = 0;
9a98c7a1 459 size_t n;
2fa4092c
LP
460 bool done = false;
461 usec_t a, b;
462
7c537b2e
LP
463 if (t <= 0)
464 break;
2fa4092c 465
7c537b2e 466 if (t < accuracy && something)
2fa4092c 467 break;
9a98c7a1
LP
468
469 if (t < table[i].usec)
470 continue;
471
472 if (l <= 1)
473 break;
474
2fa4092c
LP
475 a = t / table[i].usec;
476 b = t % table[i].usec;
477
478 /* Let's see if we should shows this in dot notation */
479 if (t < USEC_PER_MINUTE && b > 0) {
480 usec_t cc;
5fd8d5be 481 signed char j;
2fa4092c
LP
482
483 j = 0;
484 for (cc = table[i].usec; cc > 1; cc /= 10)
485 j++;
486
487 for (cc = accuracy; cc > 1; cc /= 10) {
488 b /= 10;
489 j--;
490 }
491
492 if (j > 0) {
493 k = snprintf(p, l,
70887c5f 494 "%s"USEC_FMT".%0*"PRI_USEC"%s",
2fa4092c 495 p > buf ? " " : "",
de0671ee 496 a,
2fa4092c 497 j,
70887c5f 498 b,
2fa4092c
LP
499 table[i].suffix);
500
501 t = 0;
502 done = true;
503 }
504 }
505
506 /* No? Then let's show it normally */
507 if (!done) {
508 k = snprintf(p, l,
de0671ee 509 "%s"USEC_FMT"%s",
2fa4092c 510 p > buf ? " " : "",
de0671ee 511 a,
2fa4092c
LP
512 table[i].suffix);
513
514 t = b;
515 }
516
9a98c7a1
LP
517 n = MIN((size_t) k, l);
518
519 l -= n;
520 p += n;
521
2fa4092c 522 something = true;
9a98c7a1
LP
523 }
524
525 *p = 0;
526
527 return buf;
528}
529
48d26c01 530static int parse_timestamp_impl(const char *t, usec_t *usec, bool with_tz) {
92134489
LP
531 static const struct {
532 const char *name;
533 const int nr;
534 } day_nr[] = {
535 { "Sunday", 0 },
536 { "Sun", 0 },
537 { "Monday", 1 },
538 { "Mon", 1 },
539 { "Tuesday", 2 },
540 { "Tue", 2 },
541 { "Wednesday", 3 },
542 { "Wed", 3 },
543 { "Thursday", 4 },
544 { "Thu", 4 },
545 { "Friday", 5 },
546 { "Fri", 5 },
547 { "Saturday", 6 },
548 { "Sat", 6 },
549 };
550
48d26c01 551 const char *k, *utc = NULL, *tzn = NULL;
9a98c7a1
LP
552 struct tm tm, copy;
553 time_t x;
e4eaf99a 554 usec_t x_usec, plus = 0, minus = 0, ret;
21b3a0fc 555 int r, weekday = -1, dst = -1;
da6053d0 556 size_t i;
9a98c7a1 557
947f9f01 558 /* Allowed syntaxes:
9a98c7a1
LP
559 *
560 * 2012-09-22 16:34:22
561 * 2012-09-22 16:34 (seconds will be set to 0)
562 * 2012-09-22 (time will be set to 00:00:00)
563 * 16:34:22 (date will be set to today)
564 * 16:34 (date will be set to today, seconds to 0)
565 * now
566 * yesterday (time is set to 00:00:00)
567 * today (time is set to 00:00:00)
568 * tomorrow (time is set to 00:00:00)
569 * +5min
570 * -5days
5ba6e094 571 * @2147483647 (seconds since epoch)
9a98c7a1
LP
572 */
573
574 assert(t);
9a98c7a1 575
48d26c01 576 if (t[0] == '@' && !with_tz)
e4eaf99a 577 return parse_sec(t + 1, usec);
9a98c7a1 578
e4eaf99a 579 ret = now(CLOCK_REALTIME);
9a98c7a1 580
48d26c01
IK
581 if (!with_tz) {
582 if (streq(t, "now"))
583 goto finish;
9a98c7a1 584
48d26c01
IK
585 else if (t[0] == '+') {
586 r = parse_sec(t+1, &plus);
587 if (r < 0)
588 return r;
9a98c7a1 589
48d26c01 590 goto finish;
9a98c7a1 591
48d26c01
IK
592 } else if (t[0] == '-') {
593 r = parse_sec(t+1, &minus);
594 if (r < 0)
595 return r;
9a98c7a1 596
48d26c01 597 goto finish;
decad910 598
48d26c01
IK
599 } else if ((k = endswith(t, " ago"))) {
600 t = strndupa(t, k - t);
decad910 601
48d26c01
IK
602 r = parse_sec(t, &minus);
603 if (r < 0)
604 return r;
decad910 605
48d26c01 606 goto finish;
1fcf71f5 607
48d26c01
IK
608 } else if ((k = endswith(t, " left"))) {
609 t = strndupa(t, k - t);
1fcf71f5 610
48d26c01
IK
611 r = parse_sec(t, &plus);
612 if (r < 0)
613 return r;
1fcf71f5 614
48d26c01
IK
615 goto finish;
616 }
9a98c7a1 617
48d26c01
IK
618 /* See if the timestamp is suffixed with UTC */
619 utc = endswith_no_case(t, " UTC");
620 if (utc)
621 t = strndupa(t, utc - t);
622 else {
623 const char *e = NULL;
624 int j;
21b3a0fc 625
48d26c01 626 tzset();
21b3a0fc 627
48d26c01 628 /* See if the timestamp is suffixed by either the DST or non-DST local timezone. Note that we only
947f9f01
YW
629 * support the local timezones here, nothing else. Not because we wouldn't want to, but simply because
630 * there are no nice APIs available to cover this. By accepting the local time zone strings, we make
631 * sure that all timestamps written by format_timestamp() can be parsed correctly, even though we don't
632 * support arbitrary timezone specifications. */
e4eaf99a 633
48d26c01 634 for (j = 0; j <= 1; j++) {
21b3a0fc 635
48d26c01
IK
636 if (isempty(tzname[j]))
637 continue;
21b3a0fc 638
48d26c01
IK
639 e = endswith_no_case(t, tzname[j]);
640 if (!e)
641 continue;
642 if (e == t)
643 continue;
644 if (e[-1] != ' ')
645 continue;
21b3a0fc 646
48d26c01
IK
647 break;
648 }
21b3a0fc 649
48d26c01
IK
650 if (IN_SET(j, 0, 1)) {
651 /* Found one of the two timezones specified. */
652 t = strndupa(t, e - t - 1);
653 dst = j;
654 tzn = tzname[j];
655 }
21b3a0fc
LP
656 }
657 }
658
659 x = (time_t) (ret / USEC_PER_SEC);
e4eaf99a
HV
660 x_usec = 0;
661
21b3a0fc
LP
662 if (!localtime_or_gmtime_r(&x, &tm, utc))
663 return -EINVAL;
664
2e72b794
IK
665 tm.tm_isdst = dst;
666 if (!with_tz && tzn)
667 tm.tm_zone = tzn;
e4eaf99a
HV
668
669 if (streq(t, "today")) {
670 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
671 goto from_tm;
672
673 } else if (streq(t, "yesterday")) {
313cefa1 674 tm.tm_mday--;
e4eaf99a
HV
675 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
676 goto from_tm;
677
678 } else if (streq(t, "tomorrow")) {
313cefa1 679 tm.tm_mday++;
e4eaf99a
HV
680 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
681 goto from_tm;
682 }
683
92134489
LP
684 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
685 size_t skip;
686
687 if (!startswith_no_case(t, day_nr[i].name))
688 continue;
689
690 skip = strlen(day_nr[i].name);
691 if (t[skip] != ' ')
692 continue;
693
694 weekday = day_nr[i].nr;
695 t += skip + 1;
696 break;
697 }
698
9a98c7a1
LP
699 copy = tm;
700 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
701 if (k) {
702 if (*k == '.')
703 goto parse_usec;
704 else if (*k == 0)
705 goto from_tm;
706 }
9a98c7a1
LP
707
708 tm = copy;
709 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
e4eaf99a
HV
710 if (k) {
711 if (*k == '.')
712 goto parse_usec;
713 else if (*k == 0)
714 goto from_tm;
715 }
9a98c7a1
LP
716
717 tm = copy;
718 k = strptime(t, "%y-%m-%d %H:%M", &tm);
719 if (k && *k == 0) {
720 tm.tm_sec = 0;
e4eaf99a 721 goto from_tm;
9a98c7a1
LP
722 }
723
724 tm = copy;
725 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
726 if (k && *k == 0) {
727 tm.tm_sec = 0;
e4eaf99a 728 goto from_tm;
9a98c7a1
LP
729 }
730
731 tm = copy;
732 k = strptime(t, "%y-%m-%d", &tm);
733 if (k && *k == 0) {
734 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 735 goto from_tm;
9a98c7a1
LP
736 }
737
738 tm = copy;
739 k = strptime(t, "%Y-%m-%d", &tm);
740 if (k && *k == 0) {
741 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
e4eaf99a 742 goto from_tm;
9a98c7a1
LP
743 }
744
745 tm = copy;
746 k = strptime(t, "%H:%M:%S", &tm);
e4eaf99a
HV
747 if (k) {
748 if (*k == '.')
749 goto parse_usec;
750 else if (*k == 0)
751 goto from_tm;
752 }
9a98c7a1
LP
753
754 tm = copy;
755 k = strptime(t, "%H:%M", &tm);
756 if (k && *k == 0) {
757 tm.tm_sec = 0;
e4eaf99a 758 goto from_tm;
9a98c7a1
LP
759 }
760
761 return -EINVAL;
762
e4eaf99a
HV
763parse_usec:
764 {
436dd70f 765 unsigned add;
e4eaf99a
HV
766
767 k++;
436dd70f
HV
768 r = parse_fractional_part_u(&k, 6, &add);
769 if (r < 0)
e4eaf99a
HV
770 return -EINVAL;
771
436dd70f 772 if (*k)
e4eaf99a
HV
773 return -EINVAL;
774
436dd70f 775 x_usec = add;
e4eaf99a
HV
776 }
777
778from_tm:
214cc95d 779 if (weekday >= 0 && tm.tm_wday != weekday)
68bdd2d2 780 return -EINVAL;
9a98c7a1 781
214cc95d
MH
782 x = mktime_or_timegm(&tm, utc);
783 if (x < 0)
92134489
LP
784 return -EINVAL;
785
68bdd2d2 786 ret = (usec_t) x * USEC_PER_SEC + x_usec;
1bb4b028
LP
787 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
788 return -EINVAL;
9a98c7a1 789
e4eaf99a 790finish:
315782db 791 if (ret + plus < ret) /* overflow? */
68bdd2d2 792 return -EINVAL;
9a98c7a1 793 ret += plus;
1bb4b028
LP
794 if (ret > USEC_TIMESTAMP_FORMATTABLE_MAX)
795 return -EINVAL;
796
68bdd2d2 797 if (ret >= minus)
9a98c7a1
LP
798 ret -= minus;
799 else
68bdd2d2 800 return -EINVAL;
9a98c7a1 801
a6a67f71
ZJS
802 if (usec)
803 *usec = ret;
9a98c7a1
LP
804 return 0;
805}
806
48d26c01
IK
807typedef struct ParseTimestampResult {
808 usec_t usec;
809 int return_value;
810} ParseTimestampResult;
811
812int parse_timestamp(const char *t, usec_t *usec) {
ff69484a 813 char *last_space, *tz = NULL;
48d26c01 814 ParseTimestampResult *shared, tmp;
4c253ed1 815 int r;
48d26c01
IK
816
817 last_space = strrchr(t, ' ');
089fb865 818 if (last_space != NULL && timezone_is_valid(last_space + 1, LOG_DEBUG))
ff69484a 819 tz = last_space + 1;
48d26c01 820
234519ae 821 if (!tz || endswith_no_case(t, " UTC"))
48d26c01
IK
822 return parse_timestamp_impl(t, usec, false);
823
48d26c01
IK
824 shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
825 if (shared == MAP_FAILED)
826 return negative_errno();
827
1f5d1e02 828 r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL);
4c253ed1 829 if (r < 0) {
48d26c01 830 (void) munmap(shared, sizeof *shared);
4c253ed1 831 return r;
48d26c01 832 }
4c253ed1 833 if (r == 0) {
3fd4929b 834 bool with_tz = true;
437f48a4 835 char *colon_tz;
3fd4929b 836
437f48a4
LP
837 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
838 colon_tz = strjoina(":", tz);
839
840 if (setenv("TZ", colon_tz, 1) != 0) {
48d26c01
IK
841 shared->return_value = negative_errno();
842 _exit(EXIT_FAILURE);
843 }
844
845 tzset();
846
3fd4929b 847 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
947f9f01 848 * Otherwise just cut it off. */
3fd4929b
MH
849 with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
850
f21f31b2 851 /* Cut off the timezone if we don't need it. */
3fd4929b
MH
852 if (with_tz)
853 t = strndupa(t, last_space - t);
854
855 shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
48d26c01
IK
856
857 _exit(EXIT_SUCCESS);
858 }
859
48d26c01
IK
860 tmp = *shared;
861 if (munmap(shared, sizeof *shared) != 0)
862 return negative_errno();
863
a6a67f71 864 if (tmp.return_value == 0 && usec)
48d26c01
IK
865 *usec = tmp.usec;
866
867 return tmp.return_value;
868}
869
ed2e7967 870static const char* extract_multiplier(const char *p, usec_t *multiplier) {
9a98c7a1
LP
871 static const struct {
872 const char *suffix;
873 usec_t usec;
874 } table[] = {
eb55ec9f
LP
875 { "seconds", USEC_PER_SEC },
876 { "second", USEC_PER_SEC },
877 { "sec", USEC_PER_SEC },
878 { "s", USEC_PER_SEC },
9a98c7a1 879 { "minutes", USEC_PER_MINUTE },
eb55ec9f
LP
880 { "minute", USEC_PER_MINUTE },
881 { "min", USEC_PER_MINUTE },
882 { "months", USEC_PER_MONTH },
883 { "month", USEC_PER_MONTH },
884 { "M", USEC_PER_MONTH },
885 { "msec", USEC_PER_MSEC },
886 { "ms", USEC_PER_MSEC },
887 { "m", USEC_PER_MINUTE },
888 { "hours", USEC_PER_HOUR },
889 { "hour", USEC_PER_HOUR },
890 { "hr", USEC_PER_HOUR },
891 { "h", USEC_PER_HOUR },
892 { "days", USEC_PER_DAY },
893 { "day", USEC_PER_DAY },
894 { "d", USEC_PER_DAY },
895 { "weeks", USEC_PER_WEEK },
896 { "week", USEC_PER_WEEK },
897 { "w", USEC_PER_WEEK },
898 { "years", USEC_PER_YEAR },
899 { "year", USEC_PER_YEAR },
900 { "y", USEC_PER_YEAR },
901 { "usec", 1ULL },
902 { "us", 1ULL },
5efdbf11 903 { "µs", 1ULL },
9a98c7a1 904 };
da6053d0 905 size_t i;
240a7ba9
ZJS
906
907 for (i = 0; i < ELEMENTSOF(table); i++) {
908 char *e;
909
910 e = startswith(p, table[i].suffix);
911 if (e) {
912 *multiplier = table[i].usec;
913 return e;
914 }
915 }
9a98c7a1 916
240a7ba9
ZJS
917 return p;
918}
919
920int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
b1d6dcf5 921 const char *p, *s;
9a98c7a1 922 usec_t r = 0;
cb0dac05 923 bool something = false;
9a98c7a1
LP
924
925 assert(t);
519cffec 926 assert(default_unit > 0);
9a98c7a1
LP
927
928 p = t;
b1d6dcf5
ZJS
929
930 p += strspn(p, WHITESPACE);
931 s = startswith(p, "infinity");
932 if (s) {
933 s += strspn(s, WHITESPACE);
934 if (*s != 0)
935 return -EINVAL;
936
a6a67f71
ZJS
937 if (usec)
938 *usec = USEC_INFINITY;
b1d6dcf5
ZJS
939 return 0;
940 }
941
cb0dac05 942 for (;;) {
5a9fb358 943 usec_t multiplier = default_unit, k;
ed2e7967 944 long long l;
5a9fb358 945 char *e;
cb0dac05
LP
946
947 p += strspn(p, WHITESPACE);
948
949 if (*p == 0) {
950 if (!something)
951 return -EINVAL;
952
953 break;
954 }
9a98c7a1 955
5a9fb358
LP
956 if (*p == '-') /* Don't allow "-0" */
957 return -ERANGE;
958
9a98c7a1
LP
959 errno = 0;
960 l = strtoll(p, &e, 10);
8333c77e 961 if (errno > 0)
9a98c7a1 962 return -errno;
9a98c7a1
LP
963 if (l < 0)
964 return -ERANGE;
965
cb0dac05 966 if (*e == '.') {
ed2e7967
YW
967 p = e + 1;
968 p += strspn(p, DIGITS);
cb0dac05 969 } else if (e == p)
9a98c7a1 970 return -EINVAL;
ed2e7967
YW
971 else
972 p = e;
9a98c7a1 973
ed2e7967
YW
974 s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
975 if (s == p && *s != '\0')
976 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
977 return -EINVAL;
9a98c7a1 978
ed2e7967 979 p = s;
519cffec 980
ed2e7967
YW
981 if ((usec_t) l >= USEC_INFINITY / multiplier)
982 return -ERANGE;
8079c903 983
ed2e7967
YW
984 k = (usec_t) l * multiplier;
985 if (k >= USEC_INFINITY - r)
8079c903
YW
986 return -ERANGE;
987
ed2e7967 988 r += k;
519cffec 989
ed2e7967 990 something = true;
519cffec 991
ed2e7967
YW
992 if (*e == '.') {
993 usec_t m = multiplier / 10;
994 const char *b;
8079c903 995
ed2e7967
YW
996 for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
997 k = (usec_t) (*b - '0') * m;
998 if (k >= USEC_INFINITY - r)
999 return -ERANGE;
1000
1001 r += k;
1002 }
1003
1004 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
1005 if (b == e + 1)
1006 return -EINVAL;
1007 }
cb0dac05 1008 }
9a98c7a1 1009
a6a67f71
ZJS
1010 if (usec)
1011 *usec = r;
9a98c7a1
LP
1012 return 0;
1013}
1014
519cffec
LP
1015int parse_sec(const char *t, usec_t *usec) {
1016 return parse_time(t, usec, USEC_PER_SEC);
1017}
1018
def34f63
LP
1019int parse_sec_fix_0(const char *t, usec_t *ret) {
1020 usec_t k;
1021 int r;
eae51da3 1022
def34f63
LP
1023 assert(t);
1024 assert(ret);
eae51da3 1025
def34f63
LP
1026 r = parse_sec(t, &k);
1027 if (r < 0)
1028 return r;
0004f698 1029
def34f63
LP
1030 *ret = k == 0 ? USEC_INFINITY : k;
1031 return r;
0004f698
ZJS
1032}
1033
7b61ce3c
FB
1034int parse_sec_def_infinity(const char *t, usec_t *ret) {
1035 t += strspn(t, WHITESPACE);
1036 if (isempty(t)) {
1037 *ret = USEC_INFINITY;
1038 return 0;
1039 }
1040 return parse_sec(t, ret);
1041}
1042
ed2e7967 1043static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
9a98c7a1
LP
1044 static const struct {
1045 const char *suffix;
1046 nsec_t nsec;
1047 } table[] = {
ed2e7967
YW
1048 { "seconds", NSEC_PER_SEC },
1049 { "second", NSEC_PER_SEC },
1050 { "sec", NSEC_PER_SEC },
1051 { "s", NSEC_PER_SEC },
9a98c7a1 1052 { "minutes", NSEC_PER_MINUTE },
ed2e7967
YW
1053 { "minute", NSEC_PER_MINUTE },
1054 { "min", NSEC_PER_MINUTE },
1055 { "months", NSEC_PER_MONTH },
1056 { "month", NSEC_PER_MONTH },
1057 { "M", NSEC_PER_MONTH },
1058 { "msec", NSEC_PER_MSEC },
1059 { "ms", NSEC_PER_MSEC },
1060 { "m", NSEC_PER_MINUTE },
1061 { "hours", NSEC_PER_HOUR },
1062 { "hour", NSEC_PER_HOUR },
1063 { "hr", NSEC_PER_HOUR },
1064 { "h", NSEC_PER_HOUR },
1065 { "days", NSEC_PER_DAY },
1066 { "day", NSEC_PER_DAY },
1067 { "d", NSEC_PER_DAY },
1068 { "weeks", NSEC_PER_WEEK },
1069 { "week", NSEC_PER_WEEK },
1070 { "w", NSEC_PER_WEEK },
1071 { "years", NSEC_PER_YEAR },
1072 { "year", NSEC_PER_YEAR },
1073 { "y", NSEC_PER_YEAR },
1074 { "usec", NSEC_PER_USEC },
1075 { "us", NSEC_PER_USEC },
1076 { "µs", NSEC_PER_USEC },
1077 { "nsec", 1ULL },
1078 { "ns", 1ULL },
1079 { "", 1ULL }, /* default is nsec */
9a98c7a1 1080 };
ed2e7967
YW
1081 size_t i;
1082
1083 for (i = 0; i < ELEMENTSOF(table); i++) {
1084 char *e;
1085
1086 e = startswith(p, table[i].suffix);
1087 if (e) {
1088 *multiplier = table[i].nsec;
1089 return e;
1090 }
1091 }
1092
1093 return p;
1094}
9a98c7a1 1095
ed2e7967 1096int parse_nsec(const char *t, nsec_t *nsec) {
e73c78c2 1097 const char *p, *s;
9a98c7a1 1098 nsec_t r = 0;
cb0dac05 1099 bool something = false;
9a98c7a1
LP
1100
1101 assert(t);
1102 assert(nsec);
1103
1104 p = t;
e73c78c2
LP
1105
1106 p += strspn(p, WHITESPACE);
1107 s = startswith(p, "infinity");
1108 if (s) {
1109 s += strspn(s, WHITESPACE);
8e8933ca 1110 if (*s != 0)
e73c78c2
LP
1111 return -EINVAL;
1112
1113 *nsec = NSEC_INFINITY;
1114 return 0;
1115 }
1116
cb0dac05 1117 for (;;) {
ed2e7967
YW
1118 nsec_t multiplier = 1, k;
1119 long long l;
9a98c7a1 1120 char *e;
cb0dac05
LP
1121
1122 p += strspn(p, WHITESPACE);
1123
1124 if (*p == 0) {
1125 if (!something)
1126 return -EINVAL;
1127
1128 break;
1129 }
9a98c7a1 1130
ed2e7967 1131 if (*p == '-') /* Don't allow "-0" */
5a9fb358
LP
1132 return -ERANGE;
1133
9a98c7a1
LP
1134 errno = 0;
1135 l = strtoll(p, &e, 10);
8333c77e 1136 if (errno > 0)
9a98c7a1 1137 return -errno;
9a98c7a1
LP
1138 if (l < 0)
1139 return -ERANGE;
1140
cb0dac05 1141 if (*e == '.') {
ed2e7967
YW
1142 p = e + 1;
1143 p += strspn(p, DIGITS);
cb0dac05 1144 } else if (e == p)
9a98c7a1 1145 return -EINVAL;
ed2e7967
YW
1146 else
1147 p = e;
9a98c7a1 1148
ed2e7967
YW
1149 s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
1150 if (s == p && *s != '\0')
1151 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
1152 return -EINVAL;
9a98c7a1 1153
ed2e7967 1154 p = s;
f6a178e9 1155
ed2e7967
YW
1156 if ((nsec_t) l >= NSEC_INFINITY / multiplier)
1157 return -ERANGE;
1158
1159 k = (nsec_t) l * multiplier;
1160 if (k >= NSEC_INFINITY - r)
1161 return -ERANGE;
f6a178e9 1162
ed2e7967
YW
1163 r += k;
1164
1165 something = true;
cb0dac05 1166
ed2e7967
YW
1167 if (*e == '.') {
1168 nsec_t m = multiplier / 10;
1169 const char *b;
cb0dac05 1170
ed2e7967
YW
1171 for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
1172 k = (nsec_t) (*b - '0') * m;
1173 if (k >= NSEC_INFINITY - r)
f6a178e9
YW
1174 return -ERANGE;
1175
1176 r += k;
9a98c7a1
LP
1177 }
1178
ed2e7967
YW
1179 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
1180 if (b == e + 1)
1181 return -EINVAL;
1182 }
cb0dac05 1183 }
9a98c7a1
LP
1184
1185 *nsec = r;
1186
1187 return 0;
1188}
03cc26dd
LP
1189
1190bool ntp_synced(void) {
1191 struct timex txc = {};
1192
1193 if (adjtimex(&txc) < 0)
1194 return false;
1195
bca5a0ea
ML
1196 /* Consider the system clock synchronized if the reported maximum error is smaller than the maximum
1197 * value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from
1198 * touching the RTC. */
1199 if (txc.maxerror >= 16000000)
03cc26dd
LP
1200 return false;
1201
1202 return true;
1203}
75683450
LP
1204
1205int get_timezones(char ***ret) {
1206 _cleanup_fclose_ FILE *f = NULL;
1207 _cleanup_strv_free_ char **zones = NULL;
1208 size_t n_zones = 0, n_allocated = 0;
8d2b9d14 1209 int r;
75683450
LP
1210
1211 assert(ret);
1212
bea1a013 1213 zones = strv_new("UTC");
75683450
LP
1214 if (!zones)
1215 return -ENOMEM;
1216
1217 n_allocated = 2;
1218 n_zones = 1;
1219
ba32084f 1220 f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
75683450 1221 if (f) {
8d2b9d14
LP
1222 for (;;) {
1223 _cleanup_free_ char *line = NULL;
75683450
LP
1224 char *p, *w;
1225 size_t k;
1226
8d2b9d14
LP
1227 r = read_line(f, LONG_LINE_MAX, &line);
1228 if (r < 0)
1229 return r;
1230 if (r == 0)
1231 break;
1232
1233 p = strstrip(line);
75683450
LP
1234
1235 if (isempty(p) || *p == '#')
1236 continue;
1237
1238 /* Skip over country code */
1239 p += strcspn(p, WHITESPACE);
1240 p += strspn(p, WHITESPACE);
1241
1242 /* Skip over coordinates */
1243 p += strcspn(p, WHITESPACE);
1244 p += strspn(p, WHITESPACE);
1245
1246 /* Found timezone name */
1247 k = strcspn(p, WHITESPACE);
1248 if (k <= 0)
1249 continue;
1250
1251 w = strndup(p, k);
1252 if (!w)
1253 return -ENOMEM;
1254
1255 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1256 free(w);
1257 return -ENOMEM;
1258 }
1259
1260 zones[n_zones++] = w;
1261 zones[n_zones] = NULL;
1262 }
1263
1264 strv_sort(zones);
55fd6dca 1265 strv_uniq(zones);
75683450
LP
1266
1267 } else if (errno != ENOENT)
1268 return -errno;
1269
1cc6c93a 1270 *ret = TAKE_PTR(zones);
75683450
LP
1271
1272 return 0;
1273}
1274
089fb865 1275bool timezone_is_valid(const char *name, int log_level) {
75683450
LP
1276 bool slash = false;
1277 const char *p, *t;
a2932d51
MG
1278 _cleanup_close_ int fd = -1;
1279 char buf[4];
1280 int r;
75683450 1281
5c904ba5
LP
1282 if (isempty(name))
1283 return false;
1284
e8b9e9c4
LP
1285 /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1286 if (streq(name, "UTC"))
1287 return true;
1288
5c904ba5 1289 if (name[0] == '/')
75683450
LP
1290 return false;
1291
1292 for (p = name; *p; p++) {
1293 if (!(*p >= '0' && *p <= '9') &&
1294 !(*p >= 'a' && *p <= 'z') &&
1295 !(*p >= 'A' && *p <= 'Z') &&
4c701096 1296 !IN_SET(*p, '-', '_', '+', '/'))
75683450
LP
1297 return false;
1298
1299 if (*p == '/') {
1300
1301 if (slash)
1302 return false;
1303
1304 slash = true;
1305 } else
1306 slash = false;
1307 }
1308
1309 if (slash)
1310 return false;
1311
1c73b60b
YW
1312 if (p - name >= PATH_MAX)
1313 return false;
1314
63c372cb 1315 t = strjoina("/usr/share/zoneinfo/", name);
a2932d51
MG
1316
1317 fd = open(t, O_RDONLY|O_CLOEXEC);
1318 if (fd < 0) {
089fb865 1319 log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
a2932d51
MG
1320 return false;
1321 }
1322
1323 r = fd_verify_regular(fd);
1324 if (r < 0) {
089fb865 1325 log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
75683450 1326 return false;
a2932d51
MG
1327 }
1328
1329 r = loop_read_exact(fd, buf, 4, false);
1330 if (r < 0) {
089fb865 1331 log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
a2932d51
MG
1332 return false;
1333 }
75683450 1334
a2932d51
MG
1335 /* Magic from tzfile(5) */
1336 if (memcmp(buf, "TZif", 4) != 0) {
089fb865 1337 log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
75683450 1338 return false;
a2932d51 1339 }
75683450
LP
1340
1341 return true;
1342}
77ff2de9 1343
3411372e
LP
1344bool clock_boottime_supported(void) {
1345 static int supported = -1;
1346
1347 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1348
1349 if (supported < 0) {
1350 int fd;
1351
1352 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1353 if (fd < 0)
1354 supported = false;
1355 else {
1356 safe_close(fd);
1357 supported = true;
1358 }
77ff2de9
TG
1359 }
1360
3411372e
LP
1361 return supported;
1362}
1363
1364clockid_t clock_boottime_or_monotonic(void) {
1365 if (clock_boottime_supported())
1366 return CLOCK_BOOTTIME;
1367 else
1368 return CLOCK_MONOTONIC;
77ff2de9 1369}
5c904ba5 1370
fe624c4c
LP
1371bool clock_supported(clockid_t clock) {
1372 struct timespec ts;
1373
1374 switch (clock) {
1375
1376 case CLOCK_MONOTONIC:
1377 case CLOCK_REALTIME:
1378 return true;
1379
1380 case CLOCK_BOOTTIME:
1381 return clock_boottime_supported();
1382
1383 case CLOCK_BOOTTIME_ALARM:
1384 if (!clock_boottime_supported())
1385 return false;
1386
4831981d 1387 _fallthrough_;
fe624c4c
LP
1388 default:
1389 /* For everything else, check properly */
1390 return clock_gettime(clock, &ts) >= 0;
1391 }
1392}
1393
bc9ecd48 1394int get_timezone(char **ret) {
5c904ba5
LP
1395 _cleanup_free_ char *t = NULL;
1396 const char *e;
1397 char *z;
1398 int r;
1399
1400 r = readlink_malloc("/etc/localtime", &t);
bc9ecd48
LP
1401 if (r == -ENOENT) {
1402 /* If the symlink does not exist, assume "UTC", like glibc does*/
1403 z = strdup("UTC");
1404 if (!z)
1405 return -ENOMEM;
1406
1407 *ret = z;
1408 return 0;
1409 }
5c904ba5
LP
1410 if (r < 0)
1411 return r; /* returns EINVAL if not a symlink */
1412
da9fc98d 1413 e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
5c904ba5
LP
1414 if (!e)
1415 return -EINVAL;
1416
089fb865 1417 if (!timezone_is_valid(e, LOG_DEBUG))
5c904ba5
LP
1418 return -EINVAL;
1419
1420 z = strdup(e);
1421 if (!z)
1422 return -ENOMEM;
1423
bc9ecd48 1424 *ret = z;
5c904ba5
LP
1425 return 0;
1426}
7c67c79c
HV
1427
1428time_t mktime_or_timegm(struct tm *tm, bool utc) {
1429 return utc ? timegm(tm) : mktime(tm);
1430}
1431
1432struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1433 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1434}
87b8ce69 1435
77372afb
YW
1436static uint32_t sysconf_clock_ticks_cached(void) {
1437 static thread_local uint32_t hz = 0;
87b8ce69
SS
1438 long r;
1439
1440 if (hz == 0) {
1441 r = sysconf(_SC_CLK_TCK);
1442
1443 assert(r > 0);
70887c5f 1444 hz = r;
87b8ce69
SS
1445 }
1446
77372afb
YW
1447 return hz;
1448}
1449
1450uint32_t usec_to_jiffies(usec_t u) {
1451 uint32_t hz = sysconf_clock_ticks_cached();
1452 return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
1453}
1454
1455usec_t jiffies_to_usec(uint32_t j) {
1456 uint32_t hz = sysconf_clock_ticks_cached();
1457 return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
87b8ce69 1458}
1007ec60
LP
1459
1460usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1461 usec_t a, b;
1462
1463 if (x == USEC_INFINITY)
1464 return USEC_INFINITY;
1465 if (map_clock_id(from) == map_clock_id(to))
1466 return x;
1467
1468 a = now(from);
1469 b = now(to);
1470
1471 if (x > a)
1472 /* x lies in the future */
1473 return usec_add(b, usec_sub_unsigned(x, a));
1474 else
1475 /* x lies in the past */
1476 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1477}
9a9a4f10
LP
1478
1479bool in_utc_timezone(void) {
1480 tzset();
1481
1482 return timezone == 0 && daylight == 0;
1483}
4f811d27
LP
1484
1485int time_change_fd(void) {
1486
1487 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1488 static const struct itimerspec its = {
1489 .it_value.tv_sec = TIME_T_MAX,
1490 };
1491
1492 _cleanup_close_ int fd;
1493
1494 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
1495
1496 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1497 * CLOCK_MONOTONIC. */
1498
1499 fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1500 if (fd < 0)
1501 return -errno;
1502
601f91be
LP
1503 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
1504 return TAKE_FD(fd);
1505
1506 /* So apparently there are systems where time_t is 64bit, but the kernel actually doesn't support
1507 * 64bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
1508 * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1509 * of a black magic thing though, but what can we do?
1510 *
1511 * We don't want this code on x86-64, hence let's conditionalize this for systems with 64bit time_t
1512 * but where "long" is shorter than 64bit, i.e. 32bit archs.
1513 *
1514 * See: https://github.com/systemd/systemd/issues/14362 */
1515
1516#if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
9e7c8f64 1517 if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EOVERFLOW) {
601f91be
LP
1518 static const struct itimerspec its32 = {
1519 .it_value.tv_sec = INT32_MAX,
1520 };
1521
1522 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0)
1523 return TAKE_FD(fd);
1524 }
1525#endif
4f811d27 1526
601f91be 1527 return -errno;
4f811d27 1528}