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