]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/time-util.c
util: rework strappenda(), and rename it strjoina()
[thirdparty/systemd.git] / src / shared / 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 void 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 else {
410 t->realtime = a;
411 t->monotonic = b;
412 }
413 }
414
415 int parse_timestamp(const char *t, usec_t *usec) {
416 static const struct {
417 const char *name;
418 const int nr;
419 } day_nr[] = {
420 { "Sunday", 0 },
421 { "Sun", 0 },
422 { "Monday", 1 },
423 { "Mon", 1 },
424 { "Tuesday", 2 },
425 { "Tue", 2 },
426 { "Wednesday", 3 },
427 { "Wed", 3 },
428 { "Thursday", 4 },
429 { "Thu", 4 },
430 { "Friday", 5 },
431 { "Fri", 5 },
432 { "Saturday", 6 },
433 { "Sat", 6 },
434 };
435
436 const char *k;
437 struct tm tm, copy;
438 time_t x;
439 usec_t plus = 0, minus = 0, ret;
440 int r, weekday = -1;
441 unsigned i;
442
443 /*
444 * Allowed syntaxes:
445 *
446 * 2012-09-22 16:34:22
447 * 2012-09-22 16:34 (seconds will be set to 0)
448 * 2012-09-22 (time will be set to 00:00:00)
449 * 16:34:22 (date will be set to today)
450 * 16:34 (date will be set to today, seconds to 0)
451 * now
452 * yesterday (time is set to 00:00:00)
453 * today (time is set to 00:00:00)
454 * tomorrow (time is set to 00:00:00)
455 * +5min
456 * -5days
457 * @2147483647 (seconds since epoch)
458 *
459 */
460
461 assert(t);
462 assert(usec);
463
464 x = time(NULL);
465 assert_se(localtime_r(&x, &tm));
466 tm.tm_isdst = -1;
467
468 if (streq(t, "now"))
469 goto finish;
470
471 else if (streq(t, "today")) {
472 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
473 goto finish;
474
475 } else if (streq(t, "yesterday")) {
476 tm.tm_mday --;
477 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
478 goto finish;
479
480 } else if (streq(t, "tomorrow")) {
481 tm.tm_mday ++;
482 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
483 goto finish;
484
485 } else if (t[0] == '+') {
486 r = parse_sec(t+1, &plus);
487 if (r < 0)
488 return r;
489
490 goto finish;
491
492 } else if (t[0] == '-') {
493 r = parse_sec(t+1, &minus);
494 if (r < 0)
495 return r;
496
497 goto finish;
498
499 } else if (t[0] == '@')
500 return parse_sec(t + 1, usec);
501
502 else if (endswith(t, " ago")) {
503 _cleanup_free_ char *z;
504
505 z = strndup(t, strlen(t) - 4);
506 if (!z)
507 return -ENOMEM;
508
509 r = parse_sec(z, &minus);
510 if (r < 0)
511 return r;
512
513 goto finish;
514 } else if (endswith(t, " left")) {
515 _cleanup_free_ char *z;
516
517 z = strndup(t, strlen(t) - 4);
518 if (!z)
519 return -ENOMEM;
520
521 r = parse_sec(z, &plus);
522 if (r < 0)
523 return r;
524
525 goto finish;
526 }
527
528 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
529 size_t skip;
530
531 if (!startswith_no_case(t, day_nr[i].name))
532 continue;
533
534 skip = strlen(day_nr[i].name);
535 if (t[skip] != ' ')
536 continue;
537
538 weekday = day_nr[i].nr;
539 t += skip + 1;
540 break;
541 }
542
543 copy = tm;
544 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
545 if (k && *k == 0)
546 goto finish;
547
548 tm = copy;
549 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
550 if (k && *k == 0)
551 goto finish;
552
553 tm = copy;
554 k = strptime(t, "%y-%m-%d %H:%M", &tm);
555 if (k && *k == 0) {
556 tm.tm_sec = 0;
557 goto finish;
558 }
559
560 tm = copy;
561 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
562 if (k && *k == 0) {
563 tm.tm_sec = 0;
564 goto finish;
565 }
566
567 tm = copy;
568 k = strptime(t, "%y-%m-%d", &tm);
569 if (k && *k == 0) {
570 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
571 goto finish;
572 }
573
574 tm = copy;
575 k = strptime(t, "%Y-%m-%d", &tm);
576 if (k && *k == 0) {
577 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
578 goto finish;
579 }
580
581 tm = copy;
582 k = strptime(t, "%H:%M:%S", &tm);
583 if (k && *k == 0)
584 goto finish;
585
586 tm = copy;
587 k = strptime(t, "%H:%M", &tm);
588 if (k && *k == 0) {
589 tm.tm_sec = 0;
590 goto finish;
591 }
592
593 return -EINVAL;
594
595 finish:
596 x = mktime(&tm);
597 if (x == (time_t) -1)
598 return -EINVAL;
599
600 if (weekday >= 0 && tm.tm_wday != weekday)
601 return -EINVAL;
602
603 ret = (usec_t) x * USEC_PER_SEC;
604
605 ret += plus;
606 if (ret > minus)
607 ret -= minus;
608 else
609 ret = 0;
610
611 *usec = ret;
612
613 return 0;
614 }
615
616 int parse_sec(const char *t, usec_t *usec) {
617 static const struct {
618 const char *suffix;
619 usec_t usec;
620 } table[] = {
621 { "seconds", USEC_PER_SEC },
622 { "second", USEC_PER_SEC },
623 { "sec", USEC_PER_SEC },
624 { "s", USEC_PER_SEC },
625 { "minutes", USEC_PER_MINUTE },
626 { "minute", USEC_PER_MINUTE },
627 { "min", USEC_PER_MINUTE },
628 { "months", USEC_PER_MONTH },
629 { "month", USEC_PER_MONTH },
630 { "msec", USEC_PER_MSEC },
631 { "ms", USEC_PER_MSEC },
632 { "m", USEC_PER_MINUTE },
633 { "hours", USEC_PER_HOUR },
634 { "hour", USEC_PER_HOUR },
635 { "hr", USEC_PER_HOUR },
636 { "h", USEC_PER_HOUR },
637 { "days", USEC_PER_DAY },
638 { "day", USEC_PER_DAY },
639 { "d", USEC_PER_DAY },
640 { "weeks", USEC_PER_WEEK },
641 { "week", USEC_PER_WEEK },
642 { "w", USEC_PER_WEEK },
643 { "years", USEC_PER_YEAR },
644 { "year", USEC_PER_YEAR },
645 { "y", USEC_PER_YEAR },
646 { "usec", 1ULL },
647 { "us", 1ULL },
648 { "", USEC_PER_SEC }, /* default is sec */
649 };
650
651 const char *p, *s;
652 usec_t r = 0;
653 bool something = false;
654
655 assert(t);
656 assert(usec);
657
658 p = t;
659
660 p += strspn(p, WHITESPACE);
661 s = startswith(p, "infinity");
662 if (s) {
663 s += strspn(s, WHITESPACE);
664 if (*s != 0)
665 return -EINVAL;
666
667 *usec = USEC_INFINITY;
668 return 0;
669 }
670
671 for (;;) {
672 long long l, z = 0;
673 char *e;
674 unsigned i, n = 0;
675
676 p += strspn(p, WHITESPACE);
677
678 if (*p == 0) {
679 if (!something)
680 return -EINVAL;
681
682 break;
683 }
684
685 errno = 0;
686 l = strtoll(p, &e, 10);
687
688 if (errno > 0)
689 return -errno;
690
691 if (l < 0)
692 return -ERANGE;
693
694 if (*e == '.') {
695 char *b = e + 1;
696
697 errno = 0;
698 z = strtoll(b, &e, 10);
699 if (errno > 0)
700 return -errno;
701
702 if (z < 0)
703 return -ERANGE;
704
705 if (e == b)
706 return -EINVAL;
707
708 n = e - b;
709
710 } else if (e == p)
711 return -EINVAL;
712
713 e += strspn(e, WHITESPACE);
714
715 for (i = 0; i < ELEMENTSOF(table); i++)
716 if (startswith(e, table[i].suffix)) {
717 usec_t k = (usec_t) z * table[i].usec;
718
719 for (; n > 0; n--)
720 k /= 10;
721
722 r += (usec_t) l * table[i].usec + k;
723 p = e + strlen(table[i].suffix);
724
725 something = true;
726 break;
727 }
728
729 if (i >= ELEMENTSOF(table))
730 return -EINVAL;
731
732 }
733
734 *usec = r;
735
736 return 0;
737 }
738
739 int parse_nsec(const char *t, nsec_t *nsec) {
740 static const struct {
741 const char *suffix;
742 nsec_t nsec;
743 } table[] = {
744 { "seconds", NSEC_PER_SEC },
745 { "second", NSEC_PER_SEC },
746 { "sec", NSEC_PER_SEC },
747 { "s", NSEC_PER_SEC },
748 { "minutes", NSEC_PER_MINUTE },
749 { "minute", NSEC_PER_MINUTE },
750 { "min", NSEC_PER_MINUTE },
751 { "months", NSEC_PER_MONTH },
752 { "month", NSEC_PER_MONTH },
753 { "msec", NSEC_PER_MSEC },
754 { "ms", NSEC_PER_MSEC },
755 { "m", NSEC_PER_MINUTE },
756 { "hours", NSEC_PER_HOUR },
757 { "hour", NSEC_PER_HOUR },
758 { "hr", NSEC_PER_HOUR },
759 { "h", NSEC_PER_HOUR },
760 { "days", NSEC_PER_DAY },
761 { "day", NSEC_PER_DAY },
762 { "d", NSEC_PER_DAY },
763 { "weeks", NSEC_PER_WEEK },
764 { "week", NSEC_PER_WEEK },
765 { "w", NSEC_PER_WEEK },
766 { "years", NSEC_PER_YEAR },
767 { "year", NSEC_PER_YEAR },
768 { "y", NSEC_PER_YEAR },
769 { "usec", NSEC_PER_USEC },
770 { "us", NSEC_PER_USEC },
771 { "nsec", 1ULL },
772 { "ns", 1ULL },
773 { "", 1ULL }, /* default is nsec */
774 };
775
776 const char *p, *s;
777 nsec_t r = 0;
778 bool something = false;
779
780 assert(t);
781 assert(nsec);
782
783 p = t;
784
785 p += strspn(p, WHITESPACE);
786 s = startswith(p, "infinity");
787 if (s) {
788 s += strspn(s, WHITESPACE);
789 if (!*s != 0)
790 return -EINVAL;
791
792 *nsec = NSEC_INFINITY;
793 return 0;
794 }
795
796 for (;;) {
797 long long l, z = 0;
798 char *e;
799 unsigned i, n = 0;
800
801 p += strspn(p, WHITESPACE);
802
803 if (*p == 0) {
804 if (!something)
805 return -EINVAL;
806
807 break;
808 }
809
810 errno = 0;
811 l = strtoll(p, &e, 10);
812
813 if (errno > 0)
814 return -errno;
815
816 if (l < 0)
817 return -ERANGE;
818
819 if (*e == '.') {
820 char *b = e + 1;
821
822 errno = 0;
823 z = strtoll(b, &e, 10);
824 if (errno > 0)
825 return -errno;
826
827 if (z < 0)
828 return -ERANGE;
829
830 if (e == b)
831 return -EINVAL;
832
833 n = e - b;
834
835 } else if (e == p)
836 return -EINVAL;
837
838 e += strspn(e, WHITESPACE);
839
840 for (i = 0; i < ELEMENTSOF(table); i++)
841 if (startswith(e, table[i].suffix)) {
842 nsec_t k = (nsec_t) z * table[i].nsec;
843
844 for (; n > 0; n--)
845 k /= 10;
846
847 r += (nsec_t) l * table[i].nsec + k;
848 p = e + strlen(table[i].suffix);
849
850 something = true;
851 break;
852 }
853
854 if (i >= ELEMENTSOF(table))
855 return -EINVAL;
856
857 }
858
859 *nsec = r;
860
861 return 0;
862 }
863
864 bool ntp_synced(void) {
865 struct timex txc = {};
866
867 if (adjtimex(&txc) < 0)
868 return false;
869
870 if (txc.status & STA_UNSYNC)
871 return false;
872
873 return true;
874 }
875
876 int get_timezones(char ***ret) {
877 _cleanup_fclose_ FILE *f = NULL;
878 _cleanup_strv_free_ char **zones = NULL;
879 size_t n_zones = 0, n_allocated = 0;
880
881 assert(ret);
882
883 zones = strv_new("UTC", NULL);
884 if (!zones)
885 return -ENOMEM;
886
887 n_allocated = 2;
888 n_zones = 1;
889
890 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
891 if (f) {
892 char l[LINE_MAX];
893
894 FOREACH_LINE(l, f, return -errno) {
895 char *p, *w;
896 size_t k;
897
898 p = strstrip(l);
899
900 if (isempty(p) || *p == '#')
901 continue;
902
903 /* Skip over country code */
904 p += strcspn(p, WHITESPACE);
905 p += strspn(p, WHITESPACE);
906
907 /* Skip over coordinates */
908 p += strcspn(p, WHITESPACE);
909 p += strspn(p, WHITESPACE);
910
911 /* Found timezone name */
912 k = strcspn(p, WHITESPACE);
913 if (k <= 0)
914 continue;
915
916 w = strndup(p, k);
917 if (!w)
918 return -ENOMEM;
919
920 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
921 free(w);
922 return -ENOMEM;
923 }
924
925 zones[n_zones++] = w;
926 zones[n_zones] = NULL;
927 }
928
929 strv_sort(zones);
930
931 } else if (errno != ENOENT)
932 return -errno;
933
934 *ret = zones;
935 zones = NULL;
936
937 return 0;
938 }
939
940 bool timezone_is_valid(const char *name) {
941 bool slash = false;
942 const char *p, *t;
943 struct stat st;
944
945 if (!name || *name == 0 || *name == '/')
946 return false;
947
948 for (p = name; *p; p++) {
949 if (!(*p >= '0' && *p <= '9') &&
950 !(*p >= 'a' && *p <= 'z') &&
951 !(*p >= 'A' && *p <= 'Z') &&
952 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
953 return false;
954
955 if (*p == '/') {
956
957 if (slash)
958 return false;
959
960 slash = true;
961 } else
962 slash = false;
963 }
964
965 if (slash)
966 return false;
967
968 t = strjoina("/usr/share/zoneinfo/", name);
969 if (stat(t, &st) < 0)
970 return false;
971
972 if (!S_ISREG(st.st_mode))
973 return false;
974
975 return true;
976 }
977
978 clockid_t clock_boottime_or_monotonic(void) {
979 static clockid_t clock = -1;
980 int fd;
981
982 if (clock != -1)
983 return clock;
984
985 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
986 if (fd < 0)
987 clock = CLOCK_MONOTONIC;
988 else {
989 safe_close(fd);
990 clock = CLOCK_BOOTTIME;
991 }
992
993 return clock;
994 }