]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
barrier: convert msecs to usecs in test-code
[thirdparty/systemd.git] / src / shared / time-util.c
CommitLineData
9a98c7a1
LP
1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <time.h>
23#include <string.h>
03cc26dd 24#include <sys/timex.h>
77ff2de9 25#include <sys/timerfd.h>
9a98c7a1
LP
26
27#include "util.h"
28#include "time-util.h"
75683450 29#include "strv.h"
9a98c7a1
LP
30
31usec_t now(clockid_t clock_id) {
32 struct timespec ts;
33
34 assert_se(clock_gettime(clock_id, &ts) == 0);
35
36 return timespec_load(&ts);
37}
38
39dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
40 assert(ts);
41
42 ts->realtime = now(CLOCK_REALTIME);
43 ts->monotonic = now(CLOCK_MONOTONIC);
44
45 return ts;
46}
47
48dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
49 int64_t delta;
50 assert(ts);
51
cae0c5e0
LP
52 if (u == (usec_t) -1) {
53 ts->realtime = ts->monotonic = (usec_t) -1;
54 return ts;
55 }
56
9a98c7a1
LP
57 ts->realtime = u;
58
59 if (u == 0)
60 ts->monotonic = 0;
61 else {
62 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
63
64 ts->monotonic = now(CLOCK_MONOTONIC);
65
66 if ((int64_t) ts->monotonic > delta)
67 ts->monotonic -= delta;
68 else
69 ts->monotonic = 0;
70 }
71
72 return ts;
73}
74
cae0c5e0
LP
75dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
76 int64_t delta;
77 assert(ts);
78
79 if (u == (usec_t) -1) {
80 ts->realtime = ts->monotonic = (usec_t) -1;
81 return ts;
82 }
83
84 ts->monotonic = u;
85 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
86
87 ts->realtime = now(CLOCK_REALTIME);
88 if ((int64_t) ts->realtime > delta)
89 ts->realtime -= delta;
90 else
91 ts->realtime = 0;
92
93 return ts;
94}
95
9a98c7a1
LP
96usec_t timespec_load(const struct timespec *ts) {
97 assert(ts);
98
99 if (ts->tv_sec == (time_t) -1 &&
100 ts->tv_nsec == (long) -1)
101 return (usec_t) -1;
102
103 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
104 return (usec_t) -1;
105
106 return
107 (usec_t) ts->tv_sec * USEC_PER_SEC +
108 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
109}
110
111struct timespec *timespec_store(struct timespec *ts, usec_t u) {
112 assert(ts);
113
114 if (u == (usec_t) -1) {
115 ts->tv_sec = (time_t) -1;
116 ts->tv_nsec = (long) -1;
117 return ts;
118 }
119
120 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
121 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
122
123 return ts;
124}
125
126usec_t timeval_load(const struct timeval *tv) {
127 assert(tv);
128
129 if (tv->tv_sec == (time_t) -1 &&
130 tv->tv_usec == (suseconds_t) -1)
131 return (usec_t) -1;
132
133 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
134 return (usec_t) -1;
135
136 return
137 (usec_t) tv->tv_sec * USEC_PER_SEC +
138 (usec_t) tv->tv_usec;
139}
140
141struct timeval *timeval_store(struct timeval *tv, usec_t u) {
142 assert(tv);
143
144 if (u == (usec_t) -1) {
145 tv->tv_sec = (time_t) -1;
146 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
147 } else {
148 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
149 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
150 }
151
9a98c7a1
LP
152 return tv;
153}
154
155char *format_timestamp(char *buf, size_t l, usec_t t) {
156 struct tm tm;
157 time_t sec;
158
159 assert(buf);
160 assert(l > 0);
161
966204e0 162 if (t <= 0 || t == (usec_t) -1)
9a98c7a1
LP
163 return NULL;
164
165 sec = (time_t) (t / USEC_PER_SEC);
166
167 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
168 return NULL;
169
170 return buf;
171}
172
f02d8367
ZJS
173char *format_timestamp_us(char *buf, size_t l, usec_t t) {
174 struct tm tm;
175 time_t sec;
176
177 assert(buf);
178 assert(l > 0);
179
966204e0 180 if (t <= 0 || t == (usec_t) -1)
f02d8367
ZJS
181 return NULL;
182
183 sec = (time_t) (t / USEC_PER_SEC);
184 localtime_r(&sec, &tm);
185
186 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
187 return NULL;
609e002e 188 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
f02d8367
ZJS
189 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
190 return NULL;
191
192 return buf;
193}
194
bbb8486e 195char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 196 const char *s;
9a98c7a1
LP
197 usec_t n, d;
198
199 n = now(CLOCK_REALTIME);
200
1fcf71f5 201 if (t <= 0 || (t == (usec_t) -1))
9a98c7a1
LP
202 return NULL;
203
1fcf71f5
LP
204 if (n > t) {
205 d = n - t;
206 s = "ago";
207 } else {
208 d = t - n;
209 s = "left";
210 }
9a98c7a1
LP
211
212 if (d >= USEC_PER_YEAR)
609e002e 213 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
de0671ee
ZJS
214 d / USEC_PER_YEAR,
215 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
9a98c7a1 216 else if (d >= USEC_PER_MONTH)
609e002e 217 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
de0671ee
ZJS
218 d / USEC_PER_MONTH,
219 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
9a98c7a1 220 else if (d >= USEC_PER_WEEK)
609e002e 221 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
de0671ee
ZJS
222 d / USEC_PER_WEEK,
223 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
9a98c7a1 224 else if (d >= 2*USEC_PER_DAY)
609e002e 225 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
9a98c7a1 226 else if (d >= 25*USEC_PER_HOUR)
609e002e 227 snprintf(buf, l, "1 day " USEC_FMT "h %s",
de0671ee 228 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 229 else if (d >= 6*USEC_PER_HOUR)
609e002e 230 snprintf(buf, l, USEC_FMT "h %s",
de0671ee 231 d / USEC_PER_HOUR, s);
9a98c7a1 232 else if (d >= USEC_PER_HOUR)
609e002e 233 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
de0671ee
ZJS
234 d / USEC_PER_HOUR,
235 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 236 else if (d >= 5*USEC_PER_MINUTE)
609e002e 237 snprintf(buf, l, USEC_FMT "min %s",
de0671ee 238 d / USEC_PER_MINUTE, s);
9a98c7a1 239 else if (d >= USEC_PER_MINUTE)
609e002e 240 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
de0671ee
ZJS
241 d / USEC_PER_MINUTE,
242 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 243 else if (d >= USEC_PER_SEC)
609e002e 244 snprintf(buf, l, USEC_FMT "s %s",
de0671ee 245 d / USEC_PER_SEC, s);
9a98c7a1 246 else if (d >= USEC_PER_MSEC)
609e002e 247 snprintf(buf, l, USEC_FMT "ms %s",
de0671ee 248 d / USEC_PER_MSEC, s);
9a98c7a1 249 else if (d > 0)
de0671ee
ZJS
250 snprintf(buf, l, USEC_FMT"us %s",
251 d, s);
9a98c7a1
LP
252 else
253 snprintf(buf, l, "now");
254
255 buf[l-1] = 0;
256 return buf;
257}
258
2fa4092c 259char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
260 static const struct {
261 const char *suffix;
262 usec_t usec;
263 } table[] = {
b719810d
LP
264 { "y", USEC_PER_YEAR },
265 { "month", USEC_PER_MONTH },
9a98c7a1
LP
266 { "w", USEC_PER_WEEK },
267 { "d", USEC_PER_DAY },
268 { "h", USEC_PER_HOUR },
269 { "min", USEC_PER_MINUTE },
270 { "s", USEC_PER_SEC },
271 { "ms", USEC_PER_MSEC },
272 { "us", 1 },
273 };
274
275 unsigned i;
276 char *p = buf;
2fa4092c 277 bool something = false;
9a98c7a1
LP
278
279 assert(buf);
280 assert(l > 0);
281
282 if (t == (usec_t) -1)
283 return NULL;
284
7c537b2e
LP
285 if (t <= 0) {
286 snprintf(p, l, "0");
287 p[l-1] = 0;
288 return p;
289 }
290
7f602784 291 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
292
293 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 294 int k = 0;
9a98c7a1 295 size_t n;
2fa4092c
LP
296 bool done = false;
297 usec_t a, b;
298
7c537b2e
LP
299 if (t <= 0)
300 break;
2fa4092c 301
7c537b2e 302 if (t < accuracy && something)
2fa4092c 303 break;
9a98c7a1
LP
304
305 if (t < table[i].usec)
306 continue;
307
308 if (l <= 1)
309 break;
310
2fa4092c
LP
311 a = t / table[i].usec;
312 b = t % table[i].usec;
313
314 /* Let's see if we should shows this in dot notation */
315 if (t < USEC_PER_MINUTE && b > 0) {
316 usec_t cc;
317 int j;
318
319 j = 0;
320 for (cc = table[i].usec; cc > 1; cc /= 10)
321 j++;
322
323 for (cc = accuracy; cc > 1; cc /= 10) {
324 b /= 10;
325 j--;
326 }
327
328 if (j > 0) {
329 k = snprintf(p, l,
de0671ee 330 "%s"USEC_FMT".%0*llu%s",
2fa4092c 331 p > buf ? " " : "",
de0671ee 332 a,
2fa4092c
LP
333 j,
334 (unsigned long long) b,
335 table[i].suffix);
336
337 t = 0;
338 done = true;
339 }
340 }
341
342 /* No? Then let's show it normally */
343 if (!done) {
344 k = snprintf(p, l,
de0671ee 345 "%s"USEC_FMT"%s",
2fa4092c 346 p > buf ? " " : "",
de0671ee 347 a,
2fa4092c
LP
348 table[i].suffix);
349
350 t = b;
351 }
352
9a98c7a1
LP
353 n = MIN((size_t) k, l);
354
355 l -= n;
356 p += n;
357
2fa4092c 358 something = true;
9a98c7a1
LP
359 }
360
361 *p = 0;
362
363 return buf;
364}
365
366void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
367
368 assert(f);
369 assert(name);
370 assert(t);
371
372 if (!dual_timestamp_is_set(t))
373 return;
374
de0671ee 375 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
9a98c7a1 376 name,
de0671ee
ZJS
377 t->realtime,
378 t->monotonic);
9a98c7a1
LP
379}
380
381void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
382 unsigned long long a, b;
383
384 assert(value);
385 assert(t);
386
b5dd8148 387 if (sscanf(value, "%llu %llu", &a, &b) != 2)
9a98c7a1
LP
388 log_debug("Failed to parse finish timestamp value %s", value);
389 else {
390 t->realtime = a;
391 t->monotonic = b;
392 }
393}
394
395int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
396 static const struct {
397 const char *name;
398 const int nr;
399 } day_nr[] = {
400 { "Sunday", 0 },
401 { "Sun", 0 },
402 { "Monday", 1 },
403 { "Mon", 1 },
404 { "Tuesday", 2 },
405 { "Tue", 2 },
406 { "Wednesday", 3 },
407 { "Wed", 3 },
408 { "Thursday", 4 },
409 { "Thu", 4 },
410 { "Friday", 5 },
411 { "Fri", 5 },
412 { "Saturday", 6 },
413 { "Sat", 6 },
414 };
415
9a98c7a1
LP
416 const char *k;
417 struct tm tm, copy;
418 time_t x;
419 usec_t plus = 0, minus = 0, ret;
92134489
LP
420 int r, weekday = -1;
421 unsigned i;
9a98c7a1
LP
422
423 /*
424 * Allowed syntaxes:
425 *
426 * 2012-09-22 16:34:22
427 * 2012-09-22 16:34 (seconds will be set to 0)
428 * 2012-09-22 (time will be set to 00:00:00)
429 * 16:34:22 (date will be set to today)
430 * 16:34 (date will be set to today, seconds to 0)
431 * now
432 * yesterday (time is set to 00:00:00)
433 * today (time is set to 00:00:00)
434 * tomorrow (time is set to 00:00:00)
435 * +5min
436 * -5days
5ba6e094 437 * @2147483647 (seconds since epoch)
9a98c7a1
LP
438 *
439 */
440
441 assert(t);
442 assert(usec);
443
444 x = time(NULL);
445 assert_se(localtime_r(&x, &tm));
6a741b4a 446 tm.tm_isdst = -1;
9a98c7a1
LP
447
448 if (streq(t, "now"))
449 goto finish;
450
451 else if (streq(t, "today")) {
452 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
453 goto finish;
454
455 } else if (streq(t, "yesterday")) {
456 tm.tm_mday --;
457 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
458 goto finish;
459
460 } else if (streq(t, "tomorrow")) {
461 tm.tm_mday ++;
462 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
463 goto finish;
464
465 } else if (t[0] == '+') {
7f602784 466 r = parse_sec(t+1, &plus);
9a98c7a1
LP
467 if (r < 0)
468 return r;
469
470 goto finish;
9a98c7a1 471
5ba6e094 472 } else if (t[0] == '-') {
7f602784 473 r = parse_sec(t+1, &minus);
9a98c7a1
LP
474 if (r < 0)
475 return r;
476
477 goto finish;
decad910 478
5ba6e094
LP
479 } else if (t[0] == '@')
480 return parse_sec(t + 1, usec);
481
482 else if (endswith(t, " ago")) {
decad910
LP
483 _cleanup_free_ char *z;
484
485 z = strndup(t, strlen(t) - 4);
486 if (!z)
487 return -ENOMEM;
488
7f602784 489 r = parse_sec(z, &minus);
decad910
LP
490 if (r < 0)
491 return r;
492
1fcf71f5
LP
493 goto finish;
494 } else if (endswith(t, " left")) {
495 _cleanup_free_ char *z;
496
497 z = strndup(t, strlen(t) - 4);
498 if (!z)
499 return -ENOMEM;
500
501 r = parse_sec(z, &plus);
502 if (r < 0)
503 return r;
504
decad910 505 goto finish;
9a98c7a1
LP
506 }
507
92134489
LP
508 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
509 size_t skip;
510
511 if (!startswith_no_case(t, day_nr[i].name))
512 continue;
513
514 skip = strlen(day_nr[i].name);
515 if (t[skip] != ' ')
516 continue;
517
518 weekday = day_nr[i].nr;
519 t += skip + 1;
520 break;
521 }
522
9a98c7a1
LP
523 copy = tm;
524 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
525 if (k && *k == 0)
526 goto finish;
527
528 tm = copy;
529 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
530 if (k && *k == 0)
531 goto finish;
532
533 tm = copy;
534 k = strptime(t, "%y-%m-%d %H:%M", &tm);
535 if (k && *k == 0) {
536 tm.tm_sec = 0;
537 goto finish;
538 }
539
540 tm = copy;
541 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
542 if (k && *k == 0) {
543 tm.tm_sec = 0;
544 goto finish;
545 }
546
547 tm = copy;
548 k = strptime(t, "%y-%m-%d", &tm);
549 if (k && *k == 0) {
550 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
551 goto finish;
552 }
553
554 tm = copy;
555 k = strptime(t, "%Y-%m-%d", &tm);
556 if (k && *k == 0) {
557 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
558 goto finish;
559 }
560
561 tm = copy;
562 k = strptime(t, "%H:%M:%S", &tm);
563 if (k && *k == 0)
564 goto finish;
565
566 tm = copy;
567 k = strptime(t, "%H:%M", &tm);
568 if (k && *k == 0) {
569 tm.tm_sec = 0;
570 goto finish;
571 }
572
573 return -EINVAL;
574
575finish:
576 x = mktime(&tm);
577 if (x == (time_t) -1)
578 return -EINVAL;
579
92134489
LP
580 if (weekday >= 0 && tm.tm_wday != weekday)
581 return -EINVAL;
582
9a98c7a1
LP
583 ret = (usec_t) x * USEC_PER_SEC;
584
585 ret += plus;
586 if (ret > minus)
587 ret -= minus;
588 else
589 ret = 0;
590
591 *usec = ret;
592
593 return 0;
594}
595
7f602784 596int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
597 static const struct {
598 const char *suffix;
599 usec_t usec;
600 } table[] = {
601 { "seconds", USEC_PER_SEC },
602 { "second", USEC_PER_SEC },
603 { "sec", USEC_PER_SEC },
604 { "s", USEC_PER_SEC },
605 { "minutes", USEC_PER_MINUTE },
606 { "minute", USEC_PER_MINUTE },
607 { "min", USEC_PER_MINUTE },
608 { "months", USEC_PER_MONTH },
609 { "month", USEC_PER_MONTH },
610 { "msec", USEC_PER_MSEC },
611 { "ms", USEC_PER_MSEC },
612 { "m", USEC_PER_MINUTE },
613 { "hours", USEC_PER_HOUR },
614 { "hour", USEC_PER_HOUR },
615 { "hr", USEC_PER_HOUR },
616 { "h", USEC_PER_HOUR },
617 { "days", USEC_PER_DAY },
618 { "day", USEC_PER_DAY },
619 { "d", USEC_PER_DAY },
620 { "weeks", USEC_PER_WEEK },
621 { "week", USEC_PER_WEEK },
622 { "w", USEC_PER_WEEK },
623 { "years", USEC_PER_YEAR },
624 { "year", USEC_PER_YEAR },
625 { "y", USEC_PER_YEAR },
626 { "usec", 1ULL },
627 { "us", 1ULL },
628 { "", USEC_PER_SEC }, /* default is sec */
629 };
630
631 const char *p;
632 usec_t r = 0;
cb0dac05 633 bool something = false;
9a98c7a1
LP
634
635 assert(t);
636 assert(usec);
637
638 p = t;
cb0dac05
LP
639 for (;;) {
640 long long l, z = 0;
9a98c7a1 641 char *e;
cb0dac05
LP
642 unsigned i, n = 0;
643
644 p += strspn(p, WHITESPACE);
645
646 if (*p == 0) {
647 if (!something)
648 return -EINVAL;
649
650 break;
651 }
9a98c7a1
LP
652
653 errno = 0;
654 l = strtoll(p, &e, 10);
655
8333c77e 656 if (errno > 0)
9a98c7a1
LP
657 return -errno;
658
659 if (l < 0)
660 return -ERANGE;
661
cb0dac05
LP
662 if (*e == '.') {
663 char *b = e + 1;
664
665 errno = 0;
666 z = strtoll(b, &e, 10);
667 if (errno > 0)
668 return -errno;
669
670 if (z < 0)
671 return -ERANGE;
672
673 if (e == b)
674 return -EINVAL;
675
676 n = e - b;
677
678 } else if (e == p)
9a98c7a1
LP
679 return -EINVAL;
680
681 e += strspn(e, WHITESPACE);
682
683 for (i = 0; i < ELEMENTSOF(table); i++)
684 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
685 usec_t k = (usec_t) z * table[i].usec;
686
687 for (; n > 0; n--)
688 k /= 10;
689
690 r += (usec_t) l * table[i].usec + k;
9a98c7a1 691 p = e + strlen(table[i].suffix);
cb0dac05
LP
692
693 something = true;
9a98c7a1
LP
694 break;
695 }
696
697 if (i >= ELEMENTSOF(table))
698 return -EINVAL;
699
cb0dac05 700 }
9a98c7a1
LP
701
702 *usec = r;
703
704 return 0;
705}
706
707int parse_nsec(const char *t, nsec_t *nsec) {
708 static const struct {
709 const char *suffix;
710 nsec_t nsec;
711 } table[] = {
712 { "seconds", NSEC_PER_SEC },
713 { "second", NSEC_PER_SEC },
714 { "sec", NSEC_PER_SEC },
715 { "s", NSEC_PER_SEC },
716 { "minutes", NSEC_PER_MINUTE },
717 { "minute", NSEC_PER_MINUTE },
718 { "min", NSEC_PER_MINUTE },
719 { "months", NSEC_PER_MONTH },
720 { "month", NSEC_PER_MONTH },
721 { "msec", NSEC_PER_MSEC },
722 { "ms", NSEC_PER_MSEC },
723 { "m", NSEC_PER_MINUTE },
724 { "hours", NSEC_PER_HOUR },
725 { "hour", NSEC_PER_HOUR },
726 { "hr", NSEC_PER_HOUR },
727 { "h", NSEC_PER_HOUR },
728 { "days", NSEC_PER_DAY },
729 { "day", NSEC_PER_DAY },
730 { "d", NSEC_PER_DAY },
731 { "weeks", NSEC_PER_WEEK },
732 { "week", NSEC_PER_WEEK },
733 { "w", NSEC_PER_WEEK },
734 { "years", NSEC_PER_YEAR },
735 { "year", NSEC_PER_YEAR },
736 { "y", NSEC_PER_YEAR },
737 { "usec", NSEC_PER_USEC },
738 { "us", NSEC_PER_USEC },
739 { "nsec", 1ULL },
740 { "ns", 1ULL },
741 { "", 1ULL }, /* default is nsec */
742 };
743
744 const char *p;
745 nsec_t r = 0;
cb0dac05 746 bool something = false;
9a98c7a1
LP
747
748 assert(t);
749 assert(nsec);
750
751 p = t;
cb0dac05
LP
752 for (;;) {
753 long long l, z = 0;
9a98c7a1 754 char *e;
cb0dac05
LP
755 unsigned i, n = 0;
756
757 p += strspn(p, WHITESPACE);
758
759 if (*p == 0) {
760 if (!something)
761 return -EINVAL;
762
763 break;
764 }
9a98c7a1
LP
765
766 errno = 0;
767 l = strtoll(p, &e, 10);
768
8333c77e 769 if (errno > 0)
9a98c7a1
LP
770 return -errno;
771
772 if (l < 0)
773 return -ERANGE;
774
cb0dac05
LP
775 if (*e == '.') {
776 char *b = e + 1;
777
778 errno = 0;
779 z = strtoll(b, &e, 10);
780 if (errno > 0)
781 return -errno;
782
783 if (z < 0)
784 return -ERANGE;
785
786 if (e == b)
787 return -EINVAL;
788
789 n = e - b;
790
791 } else if (e == p)
9a98c7a1
LP
792 return -EINVAL;
793
794 e += strspn(e, WHITESPACE);
795
796 for (i = 0; i < ELEMENTSOF(table); i++)
797 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
798 nsec_t k = (nsec_t) z * table[i].nsec;
799
800 for (; n > 0; n--)
801 k /= 10;
802
803 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 804 p = e + strlen(table[i].suffix);
cb0dac05
LP
805
806 something = true;
9a98c7a1
LP
807 break;
808 }
809
810 if (i >= ELEMENTSOF(table))
811 return -EINVAL;
812
cb0dac05 813 }
9a98c7a1
LP
814
815 *nsec = r;
816
817 return 0;
818}
03cc26dd
LP
819
820bool ntp_synced(void) {
821 struct timex txc = {};
822
823 if (adjtimex(&txc) < 0)
824 return false;
825
826 if (txc.status & STA_UNSYNC)
827 return false;
828
829 return true;
830}
75683450
LP
831
832int get_timezones(char ***ret) {
833 _cleanup_fclose_ FILE *f = NULL;
834 _cleanup_strv_free_ char **zones = NULL;
835 size_t n_zones = 0, n_allocated = 0;
836
837 assert(ret);
838
839 zones = strv_new("UTC", NULL);
840 if (!zones)
841 return -ENOMEM;
842
843 n_allocated = 2;
844 n_zones = 1;
845
846 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
847 if (f) {
848 char l[LINE_MAX];
849
850 FOREACH_LINE(l, f, return -errno) {
851 char *p, *w;
852 size_t k;
853
854 p = strstrip(l);
855
856 if (isempty(p) || *p == '#')
857 continue;
858
859 /* Skip over country code */
860 p += strcspn(p, WHITESPACE);
861 p += strspn(p, WHITESPACE);
862
863 /* Skip over coordinates */
864 p += strcspn(p, WHITESPACE);
865 p += strspn(p, WHITESPACE);
866
867 /* Found timezone name */
868 k = strcspn(p, WHITESPACE);
869 if (k <= 0)
870 continue;
871
872 w = strndup(p, k);
873 if (!w)
874 return -ENOMEM;
875
876 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
877 free(w);
878 return -ENOMEM;
879 }
880
881 zones[n_zones++] = w;
882 zones[n_zones] = NULL;
883 }
884
885 strv_sort(zones);
886
887 } else if (errno != ENOENT)
888 return -errno;
889
890 *ret = zones;
891 zones = NULL;
892
893 return 0;
894}
895
896bool timezone_is_valid(const char *name) {
897 bool slash = false;
898 const char *p, *t;
899 struct stat st;
900
901 if (!name || *name == 0 || *name == '/')
902 return false;
903
904 for (p = name; *p; p++) {
905 if (!(*p >= '0' && *p <= '9') &&
906 !(*p >= 'a' && *p <= 'z') &&
907 !(*p >= 'A' && *p <= 'Z') &&
908 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
909 return false;
910
911 if (*p == '/') {
912
913 if (slash)
914 return false;
915
916 slash = true;
917 } else
918 slash = false;
919 }
920
921 if (slash)
922 return false;
923
924 t = strappenda("/usr/share/zoneinfo/", name);
925 if (stat(t, &st) < 0)
926 return false;
927
928 if (!S_ISREG(st.st_mode))
929 return false;
930
931 return true;
932}
77ff2de9
TG
933
934clockid_t clock_boottime_or_monotonic(void) {
935 static clockid_t clock = -1;
936 int fd;
937
938 if (clock != -1)
939 return clock;
940
941 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
942 if (fd < 0)
943 clock = CLOCK_MONOTONIC;
944 else {
945 safe_close(fd);
946 clock = CLOCK_BOOTTIME;
947 }
948
949 return clock;
950}