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