]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/time-util.c
tree-wide: drop stat.h or statfs.h when stat-util.h is included
[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
MH
834 bool with_tz = true;
835
ff69484a 836 if (setenv("TZ", tz, 1) != 0) {
48d26c01
IK
837 shared->return_value = negative_errno();
838 _exit(EXIT_FAILURE);
839 }
840
841 tzset();
842
3fd4929b 843 /* If there is a timezone that matches the tzname fields, leave the parsing to the implementation.
947f9f01 844 * Otherwise just cut it off. */
3fd4929b
MH
845 with_tz = !STR_IN_SET(tz, tzname[0], tzname[1]);
846
f21f31b2 847 /* Cut off the timezone if we don't need it. */
3fd4929b
MH
848 if (with_tz)
849 t = strndupa(t, last_space - t);
850
851 shared->return_value = parse_timestamp_impl(t, &shared->usec, with_tz);
48d26c01
IK
852
853 _exit(EXIT_SUCCESS);
854 }
855
48d26c01
IK
856 tmp = *shared;
857 if (munmap(shared, sizeof *shared) != 0)
858 return negative_errno();
859
a6a67f71 860 if (tmp.return_value == 0 && usec)
48d26c01
IK
861 *usec = tmp.usec;
862
863 return tmp.return_value;
864}
865
ed2e7967 866static const char* extract_multiplier(const char *p, usec_t *multiplier) {
9a98c7a1
LP
867 static const struct {
868 const char *suffix;
869 usec_t usec;
870 } table[] = {
eb55ec9f
LP
871 { "seconds", USEC_PER_SEC },
872 { "second", USEC_PER_SEC },
873 { "sec", USEC_PER_SEC },
874 { "s", USEC_PER_SEC },
9a98c7a1 875 { "minutes", USEC_PER_MINUTE },
eb55ec9f
LP
876 { "minute", USEC_PER_MINUTE },
877 { "min", USEC_PER_MINUTE },
878 { "months", USEC_PER_MONTH },
879 { "month", USEC_PER_MONTH },
880 { "M", USEC_PER_MONTH },
881 { "msec", USEC_PER_MSEC },
882 { "ms", USEC_PER_MSEC },
883 { "m", USEC_PER_MINUTE },
884 { "hours", USEC_PER_HOUR },
885 { "hour", USEC_PER_HOUR },
886 { "hr", USEC_PER_HOUR },
887 { "h", USEC_PER_HOUR },
888 { "days", USEC_PER_DAY },
889 { "day", USEC_PER_DAY },
890 { "d", USEC_PER_DAY },
891 { "weeks", USEC_PER_WEEK },
892 { "week", USEC_PER_WEEK },
893 { "w", USEC_PER_WEEK },
894 { "years", USEC_PER_YEAR },
895 { "year", USEC_PER_YEAR },
896 { "y", USEC_PER_YEAR },
897 { "usec", 1ULL },
898 { "us", 1ULL },
5efdbf11 899 { "µs", 1ULL },
9a98c7a1 900 };
da6053d0 901 size_t i;
240a7ba9
ZJS
902
903 for (i = 0; i < ELEMENTSOF(table); i++) {
904 char *e;
905
906 e = startswith(p, table[i].suffix);
907 if (e) {
908 *multiplier = table[i].usec;
909 return e;
910 }
911 }
9a98c7a1 912
240a7ba9
ZJS
913 return p;
914}
915
916int parse_time(const char *t, usec_t *usec, usec_t default_unit) {
b1d6dcf5 917 const char *p, *s;
9a98c7a1 918 usec_t r = 0;
cb0dac05 919 bool something = false;
9a98c7a1
LP
920
921 assert(t);
519cffec 922 assert(default_unit > 0);
9a98c7a1
LP
923
924 p = t;
b1d6dcf5
ZJS
925
926 p += strspn(p, WHITESPACE);
927 s = startswith(p, "infinity");
928 if (s) {
929 s += strspn(s, WHITESPACE);
930 if (*s != 0)
931 return -EINVAL;
932
a6a67f71
ZJS
933 if (usec)
934 *usec = USEC_INFINITY;
b1d6dcf5
ZJS
935 return 0;
936 }
937
cb0dac05 938 for (;;) {
5a9fb358 939 usec_t multiplier = default_unit, k;
ed2e7967 940 long long l;
5a9fb358 941 char *e;
cb0dac05
LP
942
943 p += strspn(p, WHITESPACE);
944
945 if (*p == 0) {
946 if (!something)
947 return -EINVAL;
948
949 break;
950 }
9a98c7a1 951
5a9fb358
LP
952 if (*p == '-') /* Don't allow "-0" */
953 return -ERANGE;
954
9a98c7a1
LP
955 errno = 0;
956 l = strtoll(p, &e, 10);
8333c77e 957 if (errno > 0)
9a98c7a1 958 return -errno;
9a98c7a1
LP
959 if (l < 0)
960 return -ERANGE;
961
cb0dac05 962 if (*e == '.') {
ed2e7967
YW
963 p = e + 1;
964 p += strspn(p, DIGITS);
cb0dac05 965 } else if (e == p)
9a98c7a1 966 return -EINVAL;
ed2e7967
YW
967 else
968 p = e;
9a98c7a1 969
ed2e7967
YW
970 s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
971 if (s == p && *s != '\0')
972 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
973 return -EINVAL;
9a98c7a1 974
ed2e7967 975 p = s;
519cffec 976
ed2e7967
YW
977 if ((usec_t) l >= USEC_INFINITY / multiplier)
978 return -ERANGE;
8079c903 979
ed2e7967
YW
980 k = (usec_t) l * multiplier;
981 if (k >= USEC_INFINITY - r)
8079c903
YW
982 return -ERANGE;
983
ed2e7967 984 r += k;
519cffec 985
ed2e7967 986 something = true;
519cffec 987
ed2e7967
YW
988 if (*e == '.') {
989 usec_t m = multiplier / 10;
990 const char *b;
8079c903 991
ed2e7967
YW
992 for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
993 k = (usec_t) (*b - '0') * m;
994 if (k >= USEC_INFINITY - r)
995 return -ERANGE;
996
997 r += k;
998 }
999
1000 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
1001 if (b == e + 1)
1002 return -EINVAL;
1003 }
cb0dac05 1004 }
9a98c7a1 1005
a6a67f71
ZJS
1006 if (usec)
1007 *usec = r;
9a98c7a1
LP
1008 return 0;
1009}
1010
519cffec
LP
1011int parse_sec(const char *t, usec_t *usec) {
1012 return parse_time(t, usec, USEC_PER_SEC);
1013}
1014
def34f63
LP
1015int parse_sec_fix_0(const char *t, usec_t *ret) {
1016 usec_t k;
1017 int r;
eae51da3 1018
def34f63
LP
1019 assert(t);
1020 assert(ret);
eae51da3 1021
def34f63
LP
1022 r = parse_sec(t, &k);
1023 if (r < 0)
1024 return r;
0004f698 1025
def34f63
LP
1026 *ret = k == 0 ? USEC_INFINITY : k;
1027 return r;
0004f698
ZJS
1028}
1029
7b61ce3c
FB
1030int parse_sec_def_infinity(const char *t, usec_t *ret) {
1031 t += strspn(t, WHITESPACE);
1032 if (isempty(t)) {
1033 *ret = USEC_INFINITY;
1034 return 0;
1035 }
1036 return parse_sec(t, ret);
1037}
1038
ed2e7967 1039static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) {
9a98c7a1
LP
1040 static const struct {
1041 const char *suffix;
1042 nsec_t nsec;
1043 } table[] = {
ed2e7967
YW
1044 { "seconds", NSEC_PER_SEC },
1045 { "second", NSEC_PER_SEC },
1046 { "sec", NSEC_PER_SEC },
1047 { "s", NSEC_PER_SEC },
9a98c7a1 1048 { "minutes", NSEC_PER_MINUTE },
ed2e7967
YW
1049 { "minute", NSEC_PER_MINUTE },
1050 { "min", NSEC_PER_MINUTE },
1051 { "months", NSEC_PER_MONTH },
1052 { "month", NSEC_PER_MONTH },
1053 { "M", NSEC_PER_MONTH },
1054 { "msec", NSEC_PER_MSEC },
1055 { "ms", NSEC_PER_MSEC },
1056 { "m", NSEC_PER_MINUTE },
1057 { "hours", NSEC_PER_HOUR },
1058 { "hour", NSEC_PER_HOUR },
1059 { "hr", NSEC_PER_HOUR },
1060 { "h", NSEC_PER_HOUR },
1061 { "days", NSEC_PER_DAY },
1062 { "day", NSEC_PER_DAY },
1063 { "d", NSEC_PER_DAY },
1064 { "weeks", NSEC_PER_WEEK },
1065 { "week", NSEC_PER_WEEK },
1066 { "w", NSEC_PER_WEEK },
1067 { "years", NSEC_PER_YEAR },
1068 { "year", NSEC_PER_YEAR },
1069 { "y", NSEC_PER_YEAR },
1070 { "usec", NSEC_PER_USEC },
1071 { "us", NSEC_PER_USEC },
1072 { "µs", NSEC_PER_USEC },
1073 { "nsec", 1ULL },
1074 { "ns", 1ULL },
1075 { "", 1ULL }, /* default is nsec */
9a98c7a1 1076 };
ed2e7967
YW
1077 size_t i;
1078
1079 for (i = 0; i < ELEMENTSOF(table); i++) {
1080 char *e;
1081
1082 e = startswith(p, table[i].suffix);
1083 if (e) {
1084 *multiplier = table[i].nsec;
1085 return e;
1086 }
1087 }
1088
1089 return p;
1090}
9a98c7a1 1091
ed2e7967 1092int parse_nsec(const char *t, nsec_t *nsec) {
e73c78c2 1093 const char *p, *s;
9a98c7a1 1094 nsec_t r = 0;
cb0dac05 1095 bool something = false;
9a98c7a1
LP
1096
1097 assert(t);
1098 assert(nsec);
1099
1100 p = t;
e73c78c2
LP
1101
1102 p += strspn(p, WHITESPACE);
1103 s = startswith(p, "infinity");
1104 if (s) {
1105 s += strspn(s, WHITESPACE);
8e8933ca 1106 if (*s != 0)
e73c78c2
LP
1107 return -EINVAL;
1108
1109 *nsec = NSEC_INFINITY;
1110 return 0;
1111 }
1112
cb0dac05 1113 for (;;) {
ed2e7967
YW
1114 nsec_t multiplier = 1, k;
1115 long long l;
9a98c7a1 1116 char *e;
cb0dac05
LP
1117
1118 p += strspn(p, WHITESPACE);
1119
1120 if (*p == 0) {
1121 if (!something)
1122 return -EINVAL;
1123
1124 break;
1125 }
9a98c7a1 1126
ed2e7967 1127 if (*p == '-') /* Don't allow "-0" */
5a9fb358
LP
1128 return -ERANGE;
1129
9a98c7a1
LP
1130 errno = 0;
1131 l = strtoll(p, &e, 10);
8333c77e 1132 if (errno > 0)
9a98c7a1 1133 return -errno;
9a98c7a1
LP
1134 if (l < 0)
1135 return -ERANGE;
1136
cb0dac05 1137 if (*e == '.') {
ed2e7967
YW
1138 p = e + 1;
1139 p += strspn(p, DIGITS);
cb0dac05 1140 } else if (e == p)
9a98c7a1 1141 return -EINVAL;
ed2e7967
YW
1142 else
1143 p = e;
9a98c7a1 1144
ed2e7967
YW
1145 s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
1146 if (s == p && *s != '\0')
1147 /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56'*/
1148 return -EINVAL;
9a98c7a1 1149
ed2e7967 1150 p = s;
f6a178e9 1151
ed2e7967
YW
1152 if ((nsec_t) l >= NSEC_INFINITY / multiplier)
1153 return -ERANGE;
1154
1155 k = (nsec_t) l * multiplier;
1156 if (k >= NSEC_INFINITY - r)
1157 return -ERANGE;
f6a178e9 1158
ed2e7967
YW
1159 r += k;
1160
1161 something = true;
cb0dac05 1162
ed2e7967
YW
1163 if (*e == '.') {
1164 nsec_t m = multiplier / 10;
1165 const char *b;
cb0dac05 1166
ed2e7967
YW
1167 for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
1168 k = (nsec_t) (*b - '0') * m;
1169 if (k >= NSEC_INFINITY - r)
f6a178e9
YW
1170 return -ERANGE;
1171
1172 r += k;
9a98c7a1
LP
1173 }
1174
ed2e7967
YW
1175 /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge"*/
1176 if (b == e + 1)
1177 return -EINVAL;
1178 }
cb0dac05 1179 }
9a98c7a1
LP
1180
1181 *nsec = r;
1182
1183 return 0;
1184}
03cc26dd
LP
1185
1186bool ntp_synced(void) {
1187 struct timex txc = {};
1188
1189 if (adjtimex(&txc) < 0)
1190 return false;
1191
bca5a0ea
ML
1192 /* Consider the system clock synchronized if the reported maximum error is smaller than the maximum
1193 * value (16 seconds). Ignore the STA_UNSYNC flag as it may have been set to prevent the kernel from
1194 * touching the RTC. */
1195 if (txc.maxerror >= 16000000)
03cc26dd
LP
1196 return false;
1197
1198 return true;
1199}
75683450
LP
1200
1201int get_timezones(char ***ret) {
1202 _cleanup_fclose_ FILE *f = NULL;
1203 _cleanup_strv_free_ char **zones = NULL;
1204 size_t n_zones = 0, n_allocated = 0;
8d2b9d14 1205 int r;
75683450
LP
1206
1207 assert(ret);
1208
bea1a013 1209 zones = strv_new("UTC");
75683450
LP
1210 if (!zones)
1211 return -ENOMEM;
1212
1213 n_allocated = 2;
1214 n_zones = 1;
1215
ba32084f 1216 f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
75683450 1217 if (f) {
8d2b9d14
LP
1218 for (;;) {
1219 _cleanup_free_ char *line = NULL;
75683450
LP
1220 char *p, *w;
1221 size_t k;
1222
8d2b9d14
LP
1223 r = read_line(f, LONG_LINE_MAX, &line);
1224 if (r < 0)
1225 return r;
1226 if (r == 0)
1227 break;
1228
1229 p = strstrip(line);
75683450
LP
1230
1231 if (isempty(p) || *p == '#')
1232 continue;
1233
1234 /* Skip over country code */
1235 p += strcspn(p, WHITESPACE);
1236 p += strspn(p, WHITESPACE);
1237
1238 /* Skip over coordinates */
1239 p += strcspn(p, WHITESPACE);
1240 p += strspn(p, WHITESPACE);
1241
1242 /* Found timezone name */
1243 k = strcspn(p, WHITESPACE);
1244 if (k <= 0)
1245 continue;
1246
1247 w = strndup(p, k);
1248 if (!w)
1249 return -ENOMEM;
1250
1251 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1252 free(w);
1253 return -ENOMEM;
1254 }
1255
1256 zones[n_zones++] = w;
1257 zones[n_zones] = NULL;
1258 }
1259
1260 strv_sort(zones);
1261
1262 } else if (errno != ENOENT)
1263 return -errno;
1264
1cc6c93a 1265 *ret = TAKE_PTR(zones);
75683450
LP
1266
1267 return 0;
1268}
1269
089fb865 1270bool timezone_is_valid(const char *name, int log_level) {
75683450
LP
1271 bool slash = false;
1272 const char *p, *t;
a2932d51
MG
1273 _cleanup_close_ int fd = -1;
1274 char buf[4];
1275 int r;
75683450 1276
5c904ba5
LP
1277 if (isempty(name))
1278 return false;
1279
1280 if (name[0] == '/')
75683450
LP
1281 return false;
1282
1283 for (p = name; *p; p++) {
1284 if (!(*p >= '0' && *p <= '9') &&
1285 !(*p >= 'a' && *p <= 'z') &&
1286 !(*p >= 'A' && *p <= 'Z') &&
4c701096 1287 !IN_SET(*p, '-', '_', '+', '/'))
75683450
LP
1288 return false;
1289
1290 if (*p == '/') {
1291
1292 if (slash)
1293 return false;
1294
1295 slash = true;
1296 } else
1297 slash = false;
1298 }
1299
1300 if (slash)
1301 return false;
1302
1c73b60b
YW
1303 if (p - name >= PATH_MAX)
1304 return false;
1305
63c372cb 1306 t = strjoina("/usr/share/zoneinfo/", name);
a2932d51
MG
1307
1308 fd = open(t, O_RDONLY|O_CLOEXEC);
1309 if (fd < 0) {
089fb865 1310 log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
a2932d51
MG
1311 return false;
1312 }
1313
1314 r = fd_verify_regular(fd);
1315 if (r < 0) {
089fb865 1316 log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
75683450 1317 return false;
a2932d51
MG
1318 }
1319
1320 r = loop_read_exact(fd, buf, 4, false);
1321 if (r < 0) {
089fb865 1322 log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
a2932d51
MG
1323 return false;
1324 }
75683450 1325
a2932d51
MG
1326 /* Magic from tzfile(5) */
1327 if (memcmp(buf, "TZif", 4) != 0) {
089fb865 1328 log_full(log_level, "Timezone file '%s' has wrong magic bytes", t);
75683450 1329 return false;
a2932d51 1330 }
75683450
LP
1331
1332 return true;
1333}
77ff2de9 1334
3411372e
LP
1335bool clock_boottime_supported(void) {
1336 static int supported = -1;
1337
1338 /* Note that this checks whether CLOCK_BOOTTIME is available in general as well as available for timerfds()! */
1339
1340 if (supported < 0) {
1341 int fd;
1342
1343 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1344 if (fd < 0)
1345 supported = false;
1346 else {
1347 safe_close(fd);
1348 supported = true;
1349 }
77ff2de9
TG
1350 }
1351
3411372e
LP
1352 return supported;
1353}
1354
1355clockid_t clock_boottime_or_monotonic(void) {
1356 if (clock_boottime_supported())
1357 return CLOCK_BOOTTIME;
1358 else
1359 return CLOCK_MONOTONIC;
77ff2de9 1360}
5c904ba5 1361
fe624c4c
LP
1362bool clock_supported(clockid_t clock) {
1363 struct timespec ts;
1364
1365 switch (clock) {
1366
1367 case CLOCK_MONOTONIC:
1368 case CLOCK_REALTIME:
1369 return true;
1370
1371 case CLOCK_BOOTTIME:
1372 return clock_boottime_supported();
1373
1374 case CLOCK_BOOTTIME_ALARM:
1375 if (!clock_boottime_supported())
1376 return false;
1377
4831981d 1378 _fallthrough_;
fe624c4c
LP
1379 default:
1380 /* For everything else, check properly */
1381 return clock_gettime(clock, &ts) >= 0;
1382 }
1383}
1384
64d6c229 1385int get_timezone(char **tz) {
5c904ba5
LP
1386 _cleanup_free_ char *t = NULL;
1387 const char *e;
1388 char *z;
1389 int r;
1390
1391 r = readlink_malloc("/etc/localtime", &t);
1392 if (r < 0)
1393 return r; /* returns EINVAL if not a symlink */
1394
da9fc98d 1395 e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
5c904ba5
LP
1396 if (!e)
1397 return -EINVAL;
1398
089fb865 1399 if (!timezone_is_valid(e, LOG_DEBUG))
5c904ba5
LP
1400 return -EINVAL;
1401
1402 z = strdup(e);
1403 if (!z)
1404 return -ENOMEM;
1405
64d6c229 1406 *tz = z;
5c904ba5
LP
1407 return 0;
1408}
7c67c79c
HV
1409
1410time_t mktime_or_timegm(struct tm *tm, bool utc) {
1411 return utc ? timegm(tm) : mktime(tm);
1412}
1413
1414struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1415 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1416}
87b8ce69 1417
77372afb
YW
1418static uint32_t sysconf_clock_ticks_cached(void) {
1419 static thread_local uint32_t hz = 0;
87b8ce69
SS
1420 long r;
1421
1422 if (hz == 0) {
1423 r = sysconf(_SC_CLK_TCK);
1424
1425 assert(r > 0);
70887c5f 1426 hz = r;
87b8ce69
SS
1427 }
1428
77372afb
YW
1429 return hz;
1430}
1431
1432uint32_t usec_to_jiffies(usec_t u) {
1433 uint32_t hz = sysconf_clock_ticks_cached();
1434 return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
1435}
1436
1437usec_t jiffies_to_usec(uint32_t j) {
1438 uint32_t hz = sysconf_clock_ticks_cached();
1439 return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
87b8ce69 1440}
1007ec60
LP
1441
1442usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
1443 usec_t a, b;
1444
1445 if (x == USEC_INFINITY)
1446 return USEC_INFINITY;
1447 if (map_clock_id(from) == map_clock_id(to))
1448 return x;
1449
1450 a = now(from);
1451 b = now(to);
1452
1453 if (x > a)
1454 /* x lies in the future */
1455 return usec_add(b, usec_sub_unsigned(x, a));
1456 else
1457 /* x lies in the past */
1458 return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
1459}
9a9a4f10
LP
1460
1461bool in_utc_timezone(void) {
1462 tzset();
1463
1464 return timezone == 0 && daylight == 0;
1465}
4f811d27
LP
1466
1467int time_change_fd(void) {
1468
1469 /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1470 static const struct itimerspec its = {
1471 .it_value.tv_sec = TIME_T_MAX,
1472 };
1473
1474 _cleanup_close_ int fd;
1475
1476 assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
1477
1478 /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1479 * CLOCK_MONOTONIC. */
1480
1481 fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1482 if (fd < 0)
1483 return -errno;
1484
1485 if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) < 0)
1486 return -errno;
1487
1488 return TAKE_FD(fd);
1489}