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