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