]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/time-util.c
util-lib: move a number of fs operations into fs-util.[ch]
[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 "fd-util.h"
27 #include "fileio.h"
28 #include "fs-util.h"
29 #include "path-util.h"
30 #include "string-util.h"
31 #include "strv.h"
32 #include "time-util.h"
33 #include "util.h"
34
35 usec_t now(clockid_t clock_id) {
36 struct timespec ts;
37
38 assert_se(clock_gettime(clock_id, &ts) == 0);
39
40 return timespec_load(&ts);
41 }
42
43 nsec_t now_nsec(clockid_t clock_id) {
44 struct timespec ts;
45
46 assert_se(clock_gettime(clock_id, &ts) == 0);
47
48 return timespec_load_nsec(&ts);
49 }
50
51 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
52 assert(ts);
53
54 ts->realtime = now(CLOCK_REALTIME);
55 ts->monotonic = now(CLOCK_MONOTONIC);
56
57 return ts;
58 }
59
60 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
61 int64_t delta;
62 assert(ts);
63
64 if (u == USEC_INFINITY || u <= 0) {
65 ts->realtime = ts->monotonic = u;
66 return ts;
67 }
68
69 ts->realtime = u;
70
71 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
72 ts->monotonic = now(CLOCK_MONOTONIC);
73
74 if ((int64_t) ts->monotonic > delta)
75 ts->monotonic -= delta;
76 else
77 ts->monotonic = 0;
78
79 return ts;
80 }
81
82 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
83 int64_t delta;
84 assert(ts);
85
86 if (u == USEC_INFINITY) {
87 ts->realtime = ts->monotonic = USEC_INFINITY;
88 return ts;
89 }
90
91 ts->monotonic = u;
92 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
93
94 ts->realtime = now(CLOCK_REALTIME);
95 if ((int64_t) ts->realtime > delta)
96 ts->realtime -= delta;
97 else
98 ts->realtime = 0;
99
100 return ts;
101 }
102
103 dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
104 int64_t delta;
105
106 if (u == USEC_INFINITY) {
107 ts->realtime = ts->monotonic = USEC_INFINITY;
108 return ts;
109 }
110 ts->realtime = now(CLOCK_REALTIME);
111 ts->monotonic = now(CLOCK_MONOTONIC);
112
113 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
114
115 if ((int64_t) ts->realtime > delta)
116 ts->realtime -= delta;
117 else
118 ts->realtime = 0;
119
120 if ((int64_t) ts->monotonic > delta)
121 ts->monotonic -= delta;
122 else
123 ts->monotonic = 0;
124
125 return ts;
126 }
127
128
129 usec_t timespec_load(const struct timespec *ts) {
130 assert(ts);
131
132 if (ts->tv_sec == (time_t) -1 &&
133 ts->tv_nsec == (long) -1)
134 return USEC_INFINITY;
135
136 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
137 return USEC_INFINITY;
138
139 return
140 (usec_t) ts->tv_sec * USEC_PER_SEC +
141 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
142 }
143
144 nsec_t timespec_load_nsec(const struct timespec *ts) {
145 assert(ts);
146
147 if (ts->tv_sec == (time_t) -1 &&
148 ts->tv_nsec == (long) -1)
149 return NSEC_INFINITY;
150
151 return
152 (nsec_t) ts->tv_sec * NSEC_PER_SEC +
153 (nsec_t) ts->tv_nsec;
154 }
155
156 struct timespec *timespec_store(struct timespec *ts, usec_t u) {
157 assert(ts);
158
159 if (u == USEC_INFINITY) {
160 ts->tv_sec = (time_t) -1;
161 ts->tv_nsec = (long) -1;
162 return ts;
163 }
164
165 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
166 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
167
168 return ts;
169 }
170
171 usec_t timeval_load(const struct timeval *tv) {
172 assert(tv);
173
174 if (tv->tv_sec == (time_t) -1 &&
175 tv->tv_usec == (suseconds_t) -1)
176 return USEC_INFINITY;
177
178 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
179 return USEC_INFINITY;
180
181 return
182 (usec_t) tv->tv_sec * USEC_PER_SEC +
183 (usec_t) tv->tv_usec;
184 }
185
186 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
187 assert(tv);
188
189 if (u == USEC_INFINITY) {
190 tv->tv_sec = (time_t) -1;
191 tv->tv_usec = (suseconds_t) -1;
192 } else {
193 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
194 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
195 }
196
197 return tv;
198 }
199
200 static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
201 struct tm tm;
202 time_t sec;
203
204 assert(buf);
205 assert(l > 0);
206
207 if (t <= 0 || t == USEC_INFINITY)
208 return NULL;
209
210 sec = (time_t) (t / USEC_PER_SEC);
211 localtime_or_gmtime_r(&sec, &tm, utc);
212
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 localtime_or_gmtime_r(&sec, &tm, utc);
239
240 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
241 return NULL;
242 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
243 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
244 return NULL;
245
246 return buf;
247 }
248
249 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
250 return format_timestamp_internal_us(buf, l, t, false);
251 }
252
253 char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
254 return format_timestamp_internal_us(buf, l, t, 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 parse_timestamp(const char *t, usec_t *usec) {
463 static const struct {
464 const char *name;
465 const int nr;
466 } day_nr[] = {
467 { "Sunday", 0 },
468 { "Sun", 0 },
469 { "Monday", 1 },
470 { "Mon", 1 },
471 { "Tuesday", 2 },
472 { "Tue", 2 },
473 { "Wednesday", 3 },
474 { "Wed", 3 },
475 { "Thursday", 4 },
476 { "Thu", 4 },
477 { "Friday", 5 },
478 { "Fri", 5 },
479 { "Saturday", 6 },
480 { "Sat", 6 },
481 };
482
483 const char *k;
484 const char *utc;
485 struct tm tm, copy;
486 time_t x;
487 usec_t x_usec, plus = 0, minus = 0, ret;
488 int r, weekday = -1;
489 unsigned i;
490
491 /*
492 * Allowed syntaxes:
493 *
494 * 2012-09-22 16:34:22
495 * 2012-09-22 16:34 (seconds will be set to 0)
496 * 2012-09-22 (time will be set to 00:00:00)
497 * 16:34:22 (date will be set to today)
498 * 16:34 (date will be set to today, seconds to 0)
499 * now
500 * yesterday (time is set to 00:00:00)
501 * today (time is set to 00:00:00)
502 * tomorrow (time is set to 00:00:00)
503 * +5min
504 * -5days
505 * @2147483647 (seconds since epoch)
506 *
507 */
508
509 assert(t);
510 assert(usec);
511
512 if (t[0] == '@')
513 return parse_sec(t + 1, usec);
514
515 ret = now(CLOCK_REALTIME);
516
517 if (streq(t, "now"))
518 goto finish;
519
520 else if (t[0] == '+') {
521 r = parse_sec(t+1, &plus);
522 if (r < 0)
523 return r;
524
525 goto finish;
526
527 } else if (t[0] == '-') {
528 r = parse_sec(t+1, &minus);
529 if (r < 0)
530 return r;
531
532 goto finish;
533
534 } else if ((k = endswith(t, " ago"))) {
535 t = strndupa(t, k - t);
536
537 r = parse_sec(t, &minus);
538 if (r < 0)
539 return r;
540
541 goto finish;
542
543 } else if ((k = endswith(t, " left"))) {
544 t = strndupa(t, k - t);
545
546 r = parse_sec(t, &plus);
547 if (r < 0)
548 return r;
549
550 goto finish;
551 }
552
553 utc = endswith_no_case(t, " UTC");
554 if (utc)
555 t = strndupa(t, utc - t);
556
557 x = ret / USEC_PER_SEC;
558 x_usec = 0;
559
560 assert_se(localtime_or_gmtime_r(&x, &tm, utc));
561 tm.tm_isdst = -1;
562
563 if (streq(t, "today")) {
564 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
565 goto from_tm;
566
567 } else if (streq(t, "yesterday")) {
568 tm.tm_mday --;
569 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
570 goto from_tm;
571
572 } else if (streq(t, "tomorrow")) {
573 tm.tm_mday ++;
574 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
575 goto from_tm;
576 }
577
578
579 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
580 size_t skip;
581
582 if (!startswith_no_case(t, day_nr[i].name))
583 continue;
584
585 skip = strlen(day_nr[i].name);
586 if (t[skip] != ' ')
587 continue;
588
589 weekday = day_nr[i].nr;
590 t += skip + 1;
591 break;
592 }
593
594 copy = tm;
595 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
596 if (k) {
597 if (*k == '.')
598 goto parse_usec;
599 else if (*k == 0)
600 goto from_tm;
601 }
602
603 tm = copy;
604 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
605 if (k) {
606 if (*k == '.')
607 goto parse_usec;
608 else if (*k == 0)
609 goto from_tm;
610 }
611
612 tm = copy;
613 k = strptime(t, "%y-%m-%d %H:%M", &tm);
614 if (k && *k == 0) {
615 tm.tm_sec = 0;
616 goto from_tm;
617 }
618
619 tm = copy;
620 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
621 if (k && *k == 0) {
622 tm.tm_sec = 0;
623 goto from_tm;
624 }
625
626 tm = copy;
627 k = strptime(t, "%y-%m-%d", &tm);
628 if (k && *k == 0) {
629 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
630 goto from_tm;
631 }
632
633 tm = copy;
634 k = strptime(t, "%Y-%m-%d", &tm);
635 if (k && *k == 0) {
636 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
637 goto from_tm;
638 }
639
640 tm = copy;
641 k = strptime(t, "%H:%M:%S", &tm);
642 if (k) {
643 if (*k == '.')
644 goto parse_usec;
645 else if (*k == 0)
646 goto from_tm;
647 }
648
649 tm = copy;
650 k = strptime(t, "%H:%M", &tm);
651 if (k && *k == 0) {
652 tm.tm_sec = 0;
653 goto from_tm;
654 }
655
656 return -EINVAL;
657
658 parse_usec:
659 {
660 char *end;
661 unsigned long long val;
662 size_t l;
663
664 k++;
665 if (*k < '0' || *k > '9')
666 return -EINVAL;
667
668 /* base 10 instead of base 0, .09 is not base 8 */
669 errno = 0;
670 val = strtoull(k, &end, 10);
671 if (*end || errno)
672 return -EINVAL;
673
674 l = end-k;
675
676 /* val has l digits, make them 6 */
677 for (; l < 6; l++)
678 val *= 10;
679 for (; l > 6; l--)
680 val /= 10;
681
682 x_usec = val;
683 }
684
685 from_tm:
686 x = mktime_or_timegm(&tm, utc);
687 if (x == (time_t) -1)
688 return -EINVAL;
689
690 if (weekday >= 0 && tm.tm_wday != weekday)
691 return -EINVAL;
692
693 ret = (usec_t) x * USEC_PER_SEC + x_usec;
694
695 finish:
696 ret += plus;
697 if (ret > minus)
698 ret -= minus;
699 else
700 ret = 0;
701
702 *usec = ret;
703
704 return 0;
705 }
706
707 int parse_sec(const char *t, usec_t *usec) {
708 static const struct {
709 const char *suffix;
710 usec_t usec;
711 } table[] = {
712 { "seconds", USEC_PER_SEC },
713 { "second", USEC_PER_SEC },
714 { "sec", USEC_PER_SEC },
715 { "s", USEC_PER_SEC },
716 { "minutes", USEC_PER_MINUTE },
717 { "minute", USEC_PER_MINUTE },
718 { "min", USEC_PER_MINUTE },
719 { "months", USEC_PER_MONTH },
720 { "month", USEC_PER_MONTH },
721 { "msec", USEC_PER_MSEC },
722 { "ms", USEC_PER_MSEC },
723 { "m", USEC_PER_MINUTE },
724 { "hours", USEC_PER_HOUR },
725 { "hour", USEC_PER_HOUR },
726 { "hr", USEC_PER_HOUR },
727 { "h", USEC_PER_HOUR },
728 { "days", USEC_PER_DAY },
729 { "day", USEC_PER_DAY },
730 { "d", USEC_PER_DAY },
731 { "weeks", USEC_PER_WEEK },
732 { "week", USEC_PER_WEEK },
733 { "w", USEC_PER_WEEK },
734 { "years", USEC_PER_YEAR },
735 { "year", USEC_PER_YEAR },
736 { "y", USEC_PER_YEAR },
737 { "usec", 1ULL },
738 { "us", 1ULL },
739 { "", USEC_PER_SEC }, /* default is sec */
740 };
741
742 const char *p, *s;
743 usec_t r = 0;
744 bool something = false;
745
746 assert(t);
747 assert(usec);
748
749 p = t;
750
751 p += strspn(p, WHITESPACE);
752 s = startswith(p, "infinity");
753 if (s) {
754 s += strspn(s, WHITESPACE);
755 if (*s != 0)
756 return -EINVAL;
757
758 *usec = USEC_INFINITY;
759 return 0;
760 }
761
762 for (;;) {
763 long long l, z = 0;
764 char *e;
765 unsigned i, n = 0;
766
767 p += strspn(p, WHITESPACE);
768
769 if (*p == 0) {
770 if (!something)
771 return -EINVAL;
772
773 break;
774 }
775
776 errno = 0;
777 l = strtoll(p, &e, 10);
778
779 if (errno > 0)
780 return -errno;
781
782 if (l < 0)
783 return -ERANGE;
784
785 if (*e == '.') {
786 char *b = e + 1;
787
788 errno = 0;
789 z = strtoll(b, &e, 10);
790 if (errno > 0)
791 return -errno;
792
793 if (z < 0)
794 return -ERANGE;
795
796 if (e == b)
797 return -EINVAL;
798
799 n = e - b;
800
801 } else if (e == p)
802 return -EINVAL;
803
804 e += strspn(e, WHITESPACE);
805
806 for (i = 0; i < ELEMENTSOF(table); i++)
807 if (startswith(e, table[i].suffix)) {
808 usec_t k = (usec_t) z * table[i].usec;
809
810 for (; n > 0; n--)
811 k /= 10;
812
813 r += (usec_t) l * table[i].usec + k;
814 p = e + strlen(table[i].suffix);
815
816 something = true;
817 break;
818 }
819
820 if (i >= ELEMENTSOF(table))
821 return -EINVAL;
822
823 }
824
825 *usec = r;
826
827 return 0;
828 }
829
830 int parse_nsec(const char *t, nsec_t *nsec) {
831 static const struct {
832 const char *suffix;
833 nsec_t nsec;
834 } table[] = {
835 { "seconds", NSEC_PER_SEC },
836 { "second", NSEC_PER_SEC },
837 { "sec", NSEC_PER_SEC },
838 { "s", NSEC_PER_SEC },
839 { "minutes", NSEC_PER_MINUTE },
840 { "minute", NSEC_PER_MINUTE },
841 { "min", NSEC_PER_MINUTE },
842 { "months", NSEC_PER_MONTH },
843 { "month", NSEC_PER_MONTH },
844 { "msec", NSEC_PER_MSEC },
845 { "ms", NSEC_PER_MSEC },
846 { "m", NSEC_PER_MINUTE },
847 { "hours", NSEC_PER_HOUR },
848 { "hour", NSEC_PER_HOUR },
849 { "hr", NSEC_PER_HOUR },
850 { "h", NSEC_PER_HOUR },
851 { "days", NSEC_PER_DAY },
852 { "day", NSEC_PER_DAY },
853 { "d", NSEC_PER_DAY },
854 { "weeks", NSEC_PER_WEEK },
855 { "week", NSEC_PER_WEEK },
856 { "w", NSEC_PER_WEEK },
857 { "years", NSEC_PER_YEAR },
858 { "year", NSEC_PER_YEAR },
859 { "y", NSEC_PER_YEAR },
860 { "usec", NSEC_PER_USEC },
861 { "us", NSEC_PER_USEC },
862 { "nsec", 1ULL },
863 { "ns", 1ULL },
864 { "", 1ULL }, /* default is nsec */
865 };
866
867 const char *p, *s;
868 nsec_t r = 0;
869 bool something = false;
870
871 assert(t);
872 assert(nsec);
873
874 p = t;
875
876 p += strspn(p, WHITESPACE);
877 s = startswith(p, "infinity");
878 if (s) {
879 s += strspn(s, WHITESPACE);
880 if (*s != 0)
881 return -EINVAL;
882
883 *nsec = NSEC_INFINITY;
884 return 0;
885 }
886
887 for (;;) {
888 long long l, z = 0;
889 char *e;
890 unsigned i, n = 0;
891
892 p += strspn(p, WHITESPACE);
893
894 if (*p == 0) {
895 if (!something)
896 return -EINVAL;
897
898 break;
899 }
900
901 errno = 0;
902 l = strtoll(p, &e, 10);
903
904 if (errno > 0)
905 return -errno;
906
907 if (l < 0)
908 return -ERANGE;
909
910 if (*e == '.') {
911 char *b = e + 1;
912
913 errno = 0;
914 z = strtoll(b, &e, 10);
915 if (errno > 0)
916 return -errno;
917
918 if (z < 0)
919 return -ERANGE;
920
921 if (e == b)
922 return -EINVAL;
923
924 n = e - b;
925
926 } else if (e == p)
927 return -EINVAL;
928
929 e += strspn(e, WHITESPACE);
930
931 for (i = 0; i < ELEMENTSOF(table); i++)
932 if (startswith(e, table[i].suffix)) {
933 nsec_t k = (nsec_t) z * table[i].nsec;
934
935 for (; n > 0; n--)
936 k /= 10;
937
938 r += (nsec_t) l * table[i].nsec + k;
939 p = e + strlen(table[i].suffix);
940
941 something = true;
942 break;
943 }
944
945 if (i >= ELEMENTSOF(table))
946 return -EINVAL;
947
948 }
949
950 *nsec = r;
951
952 return 0;
953 }
954
955 bool ntp_synced(void) {
956 struct timex txc = {};
957
958 if (adjtimex(&txc) < 0)
959 return false;
960
961 if (txc.status & STA_UNSYNC)
962 return false;
963
964 return true;
965 }
966
967 int get_timezones(char ***ret) {
968 _cleanup_fclose_ FILE *f = NULL;
969 _cleanup_strv_free_ char **zones = NULL;
970 size_t n_zones = 0, n_allocated = 0;
971
972 assert(ret);
973
974 zones = strv_new("UTC", NULL);
975 if (!zones)
976 return -ENOMEM;
977
978 n_allocated = 2;
979 n_zones = 1;
980
981 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
982 if (f) {
983 char l[LINE_MAX];
984
985 FOREACH_LINE(l, f, return -errno) {
986 char *p, *w;
987 size_t k;
988
989 p = strstrip(l);
990
991 if (isempty(p) || *p == '#')
992 continue;
993
994 /* Skip over country code */
995 p += strcspn(p, WHITESPACE);
996 p += strspn(p, WHITESPACE);
997
998 /* Skip over coordinates */
999 p += strcspn(p, WHITESPACE);
1000 p += strspn(p, WHITESPACE);
1001
1002 /* Found timezone name */
1003 k = strcspn(p, WHITESPACE);
1004 if (k <= 0)
1005 continue;
1006
1007 w = strndup(p, k);
1008 if (!w)
1009 return -ENOMEM;
1010
1011 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
1012 free(w);
1013 return -ENOMEM;
1014 }
1015
1016 zones[n_zones++] = w;
1017 zones[n_zones] = NULL;
1018 }
1019
1020 strv_sort(zones);
1021
1022 } else if (errno != ENOENT)
1023 return -errno;
1024
1025 *ret = zones;
1026 zones = NULL;
1027
1028 return 0;
1029 }
1030
1031 bool timezone_is_valid(const char *name) {
1032 bool slash = false;
1033 const char *p, *t;
1034 struct stat st;
1035
1036 if (isempty(name))
1037 return false;
1038
1039 if (name[0] == '/')
1040 return false;
1041
1042 for (p = name; *p; p++) {
1043 if (!(*p >= '0' && *p <= '9') &&
1044 !(*p >= 'a' && *p <= 'z') &&
1045 !(*p >= 'A' && *p <= 'Z') &&
1046 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
1047 return false;
1048
1049 if (*p == '/') {
1050
1051 if (slash)
1052 return false;
1053
1054 slash = true;
1055 } else
1056 slash = false;
1057 }
1058
1059 if (slash)
1060 return false;
1061
1062 t = strjoina("/usr/share/zoneinfo/", name);
1063 if (stat(t, &st) < 0)
1064 return false;
1065
1066 if (!S_ISREG(st.st_mode))
1067 return false;
1068
1069 return true;
1070 }
1071
1072 clockid_t clock_boottime_or_monotonic(void) {
1073 static clockid_t clock = -1;
1074 int fd;
1075
1076 if (clock != -1)
1077 return clock;
1078
1079 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1080 if (fd < 0)
1081 clock = CLOCK_MONOTONIC;
1082 else {
1083 safe_close(fd);
1084 clock = CLOCK_BOOTTIME;
1085 }
1086
1087 return clock;
1088 }
1089
1090 int get_timezone(char **tz) {
1091 _cleanup_free_ char *t = NULL;
1092 const char *e;
1093 char *z;
1094 int r;
1095
1096 r = readlink_malloc("/etc/localtime", &t);
1097 if (r < 0)
1098 return r; /* returns EINVAL if not a symlink */
1099
1100 e = path_startswith(t, "/usr/share/zoneinfo/");
1101 if (!e)
1102 e = path_startswith(t, "../usr/share/zoneinfo/");
1103 if (!e)
1104 return -EINVAL;
1105
1106 if (!timezone_is_valid(e))
1107 return -EINVAL;
1108
1109 z = strdup(e);
1110 if (!z)
1111 return -ENOMEM;
1112
1113 *tz = z;
1114 return 0;
1115 }
1116
1117 time_t mktime_or_timegm(struct tm *tm, bool utc) {
1118 return utc ? timegm(tm) : mktime(tm);
1119 }
1120
1121 struct tm *localtime_or_gmtime_r(const time_t *t, struct tm *tm, bool utc) {
1122 return utc ? gmtime_r(t, tm) : localtime_r(t, tm);
1123 }