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