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