chiark / gitweb /
33d0822f335ace06ff2583a4869f535c7254297a
[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 || t <= 0) {
300                 strncpy(p, t == USEC_INFINITY ? "infinity" : "0", l);
301                 p[l-1] = 0;
302                 return p;
303         }
304
305         /* The result of this function can be parsed with parse_sec */
306
307         for (i = 0; i < ELEMENTSOF(table); i++) {
308                 int k = 0;
309                 size_t n;
310                 bool done = false;
311                 usec_t a, b;
312
313                 if (t <= 0)
314                         break;
315
316                 if (t < accuracy && something)
317                         break;
318
319                 if (t < table[i].usec)
320                         continue;
321
322                 if (l <= 1)
323                         break;
324
325                 a = t / table[i].usec;
326                 b = t % table[i].usec;
327
328                 /* Let's see if we should shows this in dot notation */
329                 if (t < USEC_PER_MINUTE && b > 0) {
330                         usec_t cc;
331                         int j;
332
333                         j = 0;
334                         for (cc = table[i].usec; cc > 1; cc /= 10)
335                                 j++;
336
337                         for (cc = accuracy; cc > 1; cc /= 10) {
338                                 b /= 10;
339                                 j--;
340                         }
341
342                         if (j > 0) {
343                                 k = snprintf(p, l,
344                                              "%s"USEC_FMT".%0*llu%s",
345                                              p > buf ? " " : "",
346                                              a,
347                                              j,
348                                              (unsigned long long) b,
349                                              table[i].suffix);
350
351                                 t = 0;
352                                 done = true;
353                         }
354                 }
355
356                 /* No? Then let's show it normally */
357                 if (!done) {
358                         k = snprintf(p, l,
359                                      "%s"USEC_FMT"%s",
360                                      p > buf ? " " : "",
361                                      a,
362                                      table[i].suffix);
363
364                         t = b;
365                 }
366
367                 n = MIN((size_t) k, l);
368
369                 l -= n;
370                 p += n;
371
372                 something = true;
373         }
374
375         *p = 0;
376
377         return buf;
378 }
379
380 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
381
382         assert(f);
383         assert(name);
384         assert(t);
385
386         if (!dual_timestamp_is_set(t))
387                 return;
388
389         fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
390                 name,
391                 t->realtime,
392                 t->monotonic);
393 }
394
395 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
396         unsigned long long a, b;
397
398         assert(value);
399         assert(t);
400
401         if (sscanf(value, "%llu %llu", &a, &b) != 2)
402                 log_debug("Failed to parse finish timestamp value %s", value);
403         else {
404                 t->realtime = a;
405                 t->monotonic = b;
406         }
407 }
408
409 int parse_timestamp(const char *t, usec_t *usec) {
410         static const struct {
411                 const char *name;
412                 const int nr;
413         } day_nr[] = {
414                 { "Sunday",    0 },
415                 { "Sun",       0 },
416                 { "Monday",    1 },
417                 { "Mon",       1 },
418                 { "Tuesday",   2 },
419                 { "Tue",       2 },
420                 { "Wednesday", 3 },
421                 { "Wed",       3 },
422                 { "Thursday",  4 },
423                 { "Thu",       4 },
424                 { "Friday",    5 },
425                 { "Fri",       5 },
426                 { "Saturday",  6 },
427                 { "Sat",       6 },
428         };
429
430         const char *k;
431         struct tm tm, copy;
432         time_t x;
433         usec_t plus = 0, minus = 0, ret;
434         int r, weekday = -1;
435         unsigned i;
436
437         /*
438          * Allowed syntaxes:
439          *
440          *   2012-09-22 16:34:22
441          *   2012-09-22 16:34     (seconds will be set to 0)
442          *   2012-09-22           (time will be set to 00:00:00)
443          *   16:34:22             (date will be set to today)
444          *   16:34                (date will be set to today, seconds to 0)
445          *   now
446          *   yesterday            (time is set to 00:00:00)
447          *   today                (time is set to 00:00:00)
448          *   tomorrow             (time is set to 00:00:00)
449          *   +5min
450          *   -5days
451          *   @2147483647          (seconds since epoch)
452          *
453          */
454
455         assert(t);
456         assert(usec);
457
458         x = time(NULL);
459         assert_se(localtime_r(&x, &tm));
460         tm.tm_isdst = -1;
461
462         if (streq(t, "now"))
463                 goto finish;
464
465         else if (streq(t, "today")) {
466                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
467                 goto finish;
468
469         } else if (streq(t, "yesterday")) {
470                 tm.tm_mday --;
471                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
472                 goto finish;
473
474         } else if (streq(t, "tomorrow")) {
475                 tm.tm_mday ++;
476                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
477                 goto finish;
478
479         } else if (t[0] == '+') {
480                 r = parse_sec(t+1, &plus);
481                 if (r < 0)
482                         return r;
483
484                 goto finish;
485
486         } else if (t[0] == '-') {
487                 r = parse_sec(t+1, &minus);
488                 if (r < 0)
489                         return r;
490
491                 goto finish;
492
493         } else if (t[0] == '@')
494                 return parse_sec(t + 1, usec);
495
496         else if (endswith(t, " ago")) {
497                 _cleanup_free_ char *z;
498
499                 z = strndup(t, strlen(t) - 4);
500                 if (!z)
501                         return -ENOMEM;
502
503                 r = parse_sec(z, &minus);
504                 if (r < 0)
505                         return r;
506
507                 goto finish;
508         } else if (endswith(t, " left")) {
509                 _cleanup_free_ char *z;
510
511                 z = strndup(t, strlen(t) - 4);
512                 if (!z)
513                         return -ENOMEM;
514
515                 r = parse_sec(z, &plus);
516                 if (r < 0)
517                         return r;
518
519                 goto finish;
520         }
521
522         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
523                 size_t skip;
524
525                 if (!startswith_no_case(t, day_nr[i].name))
526                         continue;
527
528                 skip = strlen(day_nr[i].name);
529                 if (t[skip] != ' ')
530                         continue;
531
532                 weekday = day_nr[i].nr;
533                 t += skip + 1;
534                 break;
535         }
536
537         copy = tm;
538         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
539         if (k && *k == 0)
540                 goto finish;
541
542         tm = copy;
543         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
544         if (k && *k == 0)
545                 goto finish;
546
547         tm = copy;
548         k = strptime(t, "%y-%m-%d %H:%M", &tm);
549         if (k && *k == 0) {
550                 tm.tm_sec = 0;
551                 goto finish;
552         }
553
554         tm = copy;
555         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
556         if (k && *k == 0) {
557                 tm.tm_sec = 0;
558                 goto finish;
559         }
560
561         tm = copy;
562         k = strptime(t, "%y-%m-%d", &tm);
563         if (k && *k == 0) {
564                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
565                 goto finish;
566         }
567
568         tm = copy;
569         k = strptime(t, "%Y-%m-%d", &tm);
570         if (k && *k == 0) {
571                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
572                 goto finish;
573         }
574
575         tm = copy;
576         k = strptime(t, "%H:%M:%S", &tm);
577         if (k && *k == 0)
578                 goto finish;
579
580         tm = copy;
581         k = strptime(t, "%H:%M", &tm);
582         if (k && *k == 0) {
583                 tm.tm_sec = 0;
584                 goto finish;
585         }
586
587         return -EINVAL;
588
589 finish:
590         x = mktime(&tm);
591         if (x == (time_t) -1)
592                 return -EINVAL;
593
594         if (weekday >= 0 && tm.tm_wday != weekday)
595                 return -EINVAL;
596
597         ret = (usec_t) x * USEC_PER_SEC;
598
599         ret += plus;
600         if (ret > minus)
601                 ret -= minus;
602         else
603                 ret = 0;
604
605         *usec = ret;
606
607         return 0;
608 }
609
610 int parse_sec(const char *t, usec_t *usec) {
611         static const struct {
612                 const char *suffix;
613                 usec_t usec;
614         } table[] = {
615                 { "seconds", USEC_PER_SEC },
616                 { "second", USEC_PER_SEC },
617                 { "sec", USEC_PER_SEC },
618                 { "s", USEC_PER_SEC },
619                 { "minutes", USEC_PER_MINUTE },
620                 { "minute", USEC_PER_MINUTE },
621                 { "min", USEC_PER_MINUTE },
622                 { "months", USEC_PER_MONTH },
623                 { "month", USEC_PER_MONTH },
624                 { "msec", USEC_PER_MSEC },
625                 { "ms", USEC_PER_MSEC },
626                 { "m", USEC_PER_MINUTE },
627                 { "hours", USEC_PER_HOUR },
628                 { "hour", USEC_PER_HOUR },
629                 { "hr", USEC_PER_HOUR },
630                 { "h", USEC_PER_HOUR },
631                 { "days", USEC_PER_DAY },
632                 { "day", USEC_PER_DAY },
633                 { "d", USEC_PER_DAY },
634                 { "weeks", USEC_PER_WEEK },
635                 { "week", USEC_PER_WEEK },
636                 { "w", USEC_PER_WEEK },
637                 { "years", USEC_PER_YEAR },
638                 { "year", USEC_PER_YEAR },
639                 { "y", USEC_PER_YEAR },
640                 { "usec", 1ULL },
641                 { "us", 1ULL },
642                 { "", USEC_PER_SEC }, /* default is sec */
643         };
644
645         const char *p, *s;
646         usec_t r = 0;
647         bool something = false;
648
649         assert(t);
650         assert(usec);
651
652         p = t;
653
654         p += strspn(p, WHITESPACE);
655         s = startswith(p, "infinity");
656         if (s) {
657                 s += strspn(s, WHITESPACE);
658                 if (*s != 0)
659                         return -EINVAL;
660
661                 *usec = USEC_INFINITY;
662                 return 0;
663         }
664
665         for (;;) {
666                 long long l, z = 0;
667                 char *e;
668                 unsigned i, n = 0;
669
670                 p += strspn(p, WHITESPACE);
671
672                 if (*p == 0) {
673                         if (!something)
674                                 return -EINVAL;
675
676                         break;
677                 }
678
679                 errno = 0;
680                 l = strtoll(p, &e, 10);
681
682                 if (errno > 0)
683                         return -errno;
684
685                 if (l < 0)
686                         return -ERANGE;
687
688                 if (*e == '.') {
689                         char *b = e + 1;
690
691                         errno = 0;
692                         z = strtoll(b, &e, 10);
693                         if (errno > 0)
694                                 return -errno;
695
696                         if (z < 0)
697                                 return -ERANGE;
698
699                         if (e == b)
700                                 return -EINVAL;
701
702                         n = e - b;
703
704                 } else if (e == p)
705                         return -EINVAL;
706
707                 e += strspn(e, WHITESPACE);
708
709                 for (i = 0; i < ELEMENTSOF(table); i++)
710                         if (startswith(e, table[i].suffix)) {
711                                 usec_t k = (usec_t) z * table[i].usec;
712
713                                 for (; n > 0; n--)
714                                         k /= 10;
715
716                                 r += (usec_t) l * table[i].usec + k;
717                                 p = e + strlen(table[i].suffix);
718
719                                 something = true;
720                                 break;
721                         }
722
723                 if (i >= ELEMENTSOF(table))
724                         return -EINVAL;
725
726         }
727
728         *usec = r;
729
730         return 0;
731 }
732
733 int parse_nsec(const char *t, nsec_t *nsec) {
734         static const struct {
735                 const char *suffix;
736                 nsec_t nsec;
737         } table[] = {
738                 { "seconds", NSEC_PER_SEC },
739                 { "second", NSEC_PER_SEC },
740                 { "sec", NSEC_PER_SEC },
741                 { "s", NSEC_PER_SEC },
742                 { "minutes", NSEC_PER_MINUTE },
743                 { "minute", NSEC_PER_MINUTE },
744                 { "min", NSEC_PER_MINUTE },
745                 { "months", NSEC_PER_MONTH },
746                 { "month", NSEC_PER_MONTH },
747                 { "msec", NSEC_PER_MSEC },
748                 { "ms", NSEC_PER_MSEC },
749                 { "m", NSEC_PER_MINUTE },
750                 { "hours", NSEC_PER_HOUR },
751                 { "hour", NSEC_PER_HOUR },
752                 { "hr", NSEC_PER_HOUR },
753                 { "h", NSEC_PER_HOUR },
754                 { "days", NSEC_PER_DAY },
755                 { "day", NSEC_PER_DAY },
756                 { "d", NSEC_PER_DAY },
757                 { "weeks", NSEC_PER_WEEK },
758                 { "week", NSEC_PER_WEEK },
759                 { "w", NSEC_PER_WEEK },
760                 { "years", NSEC_PER_YEAR },
761                 { "year", NSEC_PER_YEAR },
762                 { "y", NSEC_PER_YEAR },
763                 { "usec", NSEC_PER_USEC },
764                 { "us", NSEC_PER_USEC },
765                 { "nsec", 1ULL },
766                 { "ns", 1ULL },
767                 { "", 1ULL }, /* default is nsec */
768         };
769
770         const char *p, *s;
771         nsec_t r = 0;
772         bool something = false;
773
774         assert(t);
775         assert(nsec);
776
777         p = t;
778
779         p += strspn(p, WHITESPACE);
780         s = startswith(p, "infinity");
781         if (s) {
782                 s += strspn(s, WHITESPACE);
783                 if (!*s != 0)
784                         return -EINVAL;
785
786                 *nsec = NSEC_INFINITY;
787                 return 0;
788         }
789
790         for (;;) {
791                 long long l, z = 0;
792                 char *e;
793                 unsigned i, n = 0;
794
795                 p += strspn(p, WHITESPACE);
796
797                 if (*p == 0) {
798                         if (!something)
799                                 return -EINVAL;
800
801                         break;
802                 }
803
804                 errno = 0;
805                 l = strtoll(p, &e, 10);
806
807                 if (errno > 0)
808                         return -errno;
809
810                 if (l < 0)
811                         return -ERANGE;
812
813                 if (*e == '.') {
814                         char *b = e + 1;
815
816                         errno = 0;
817                         z = strtoll(b, &e, 10);
818                         if (errno > 0)
819                                 return -errno;
820
821                         if (z < 0)
822                                 return -ERANGE;
823
824                         if (e == b)
825                                 return -EINVAL;
826
827                         n = e - b;
828
829                 } else if (e == p)
830                         return -EINVAL;
831
832                 e += strspn(e, WHITESPACE);
833
834                 for (i = 0; i < ELEMENTSOF(table); i++)
835                         if (startswith(e, table[i].suffix)) {
836                                 nsec_t k = (nsec_t) z * table[i].nsec;
837
838                                 for (; n > 0; n--)
839                                         k /= 10;
840
841                                 r += (nsec_t) l * table[i].nsec + k;
842                                 p = e + strlen(table[i].suffix);
843
844                                 something = true;
845                                 break;
846                         }
847
848                 if (i >= ELEMENTSOF(table))
849                         return -EINVAL;
850
851         }
852
853         *nsec = r;
854
855         return 0;
856 }
857
858 bool ntp_synced(void) {
859         struct timex txc = {};
860
861         if (adjtimex(&txc) < 0)
862                 return false;
863
864         if (txc.status & STA_UNSYNC)
865                 return false;
866
867         return true;
868 }
869
870 int get_timezones(char ***ret) {
871         _cleanup_fclose_ FILE *f = NULL;
872         _cleanup_strv_free_ char **zones = NULL;
873         size_t n_zones = 0, n_allocated = 0;
874
875         assert(ret);
876
877         zones = strv_new("UTC", NULL);
878         if (!zones)
879                 return -ENOMEM;
880
881         n_allocated = 2;
882         n_zones = 1;
883
884         f = fopen("/usr/share/zoneinfo/zone.tab", "re");
885         if (f) {
886                 char l[LINE_MAX];
887
888                 FOREACH_LINE(l, f, return -errno) {
889                         char *p, *w;
890                         size_t k;
891
892                         p = strstrip(l);
893
894                         if (isempty(p) || *p == '#')
895                                 continue;
896
897                         /* Skip over country code */
898                         p += strcspn(p, WHITESPACE);
899                         p += strspn(p, WHITESPACE);
900
901                         /* Skip over coordinates */
902                         p += strcspn(p, WHITESPACE);
903                         p += strspn(p, WHITESPACE);
904
905                         /* Found timezone name */
906                         k = strcspn(p, WHITESPACE);
907                         if (k <= 0)
908                                 continue;
909
910                         w = strndup(p, k);
911                         if (!w)
912                                 return -ENOMEM;
913
914                         if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
915                                 free(w);
916                                 return -ENOMEM;
917                         }
918
919                         zones[n_zones++] = w;
920                         zones[n_zones] = NULL;
921                 }
922
923                 strv_sort(zones);
924
925         } else if (errno != ENOENT)
926                 return -errno;
927
928         *ret = zones;
929         zones = NULL;
930
931         return 0;
932 }
933
934 bool timezone_is_valid(const char *name) {
935         bool slash = false;
936         const char *p, *t;
937         struct stat st;
938
939         if (!name || *name == 0 || *name == '/')
940                 return false;
941
942         for (p = name; *p; p++) {
943                 if (!(*p >= '0' && *p <= '9') &&
944                     !(*p >= 'a' && *p <= 'z') &&
945                     !(*p >= 'A' && *p <= 'Z') &&
946                     !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
947                         return false;
948
949                 if (*p == '/') {
950
951                         if (slash)
952                                 return false;
953
954                         slash = true;
955                 } else
956                         slash = false;
957         }
958
959         if (slash)
960                 return false;
961
962         t = strappenda("/usr/share/zoneinfo/", name);
963         if (stat(t, &st) < 0)
964                 return false;
965
966         if (!S_ISREG(st.st_mode))
967                 return false;
968
969         return true;
970 }
971
972 clockid_t clock_boottime_or_monotonic(void) {
973         static clockid_t clock = -1;
974         int fd;
975
976         if (clock != -1)
977                 return clock;
978
979         fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
980         if (fd < 0)
981                 clock = CLOCK_MONOTONIC;
982         else {
983                 safe_close(fd);
984                 clock = CLOCK_BOOTTIME;
985         }
986
987         return clock;
988 }