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