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