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