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