chiark / gitweb /
man: fix name of systemd.resource-control(5)
[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 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, *s;
777         nsec_t r = 0;
778         bool something = false;
779
780         assert(t);
781         assert(nsec);
782
783         p = t;
784
785         p += strspn(p, WHITESPACE);
786         s = startswith(p, "infinity");
787         if (s) {
788                 s += strspn(s, WHITESPACE);
789                 if (*s != 0)
790                         return -EINVAL;
791
792                 *nsec = NSEC_INFINITY;
793                 return 0;
794         }
795
796         for (;;) {
797                 long long l, z = 0;
798                 char *e;
799                 unsigned i, n = 0;
800
801                 p += strspn(p, WHITESPACE);
802
803                 if (*p == 0) {
804                         if (!something)
805                                 return -EINVAL;
806
807                         break;
808                 }
809
810                 errno = 0;
811                 l = strtoll(p, &e, 10);
812
813                 if (errno > 0)
814                         return -errno;
815
816                 if (l < 0)
817                         return -ERANGE;
818
819                 if (*e == '.') {
820                         char *b = e + 1;
821
822                         errno = 0;
823                         z = strtoll(b, &e, 10);
824                         if (errno > 0)
825                                 return -errno;
826
827                         if (z < 0)
828                                 return -ERANGE;
829
830                         if (e == b)
831                                 return -EINVAL;
832
833                         n = e - b;
834
835                 } else if (e == p)
836                         return -EINVAL;
837
838                 e += strspn(e, WHITESPACE);
839
840                 for (i = 0; i < ELEMENTSOF(table); i++)
841                         if (startswith(e, table[i].suffix)) {
842                                 nsec_t k = (nsec_t) z * table[i].nsec;
843
844                                 for (; n > 0; n--)
845                                         k /= 10;
846
847                                 r += (nsec_t) l * table[i].nsec + k;
848                                 p = e + strlen(table[i].suffix);
849
850                                 something = true;
851                                 break;
852                         }
853
854                 if (i >= ELEMENTSOF(table))
855                         return -EINVAL;
856
857         }
858
859         *nsec = r;
860
861         return 0;
862 }
863
864 bool ntp_synced(void) {
865         struct timex txc = {};
866
867         if (adjtimex(&txc) < 0)
868                 return false;
869
870         if (txc.status & STA_UNSYNC)
871                 return false;
872
873         return true;
874 }
875
876 int get_timezones(char ***ret) {
877         _cleanup_fclose_ FILE *f = NULL;
878         _cleanup_strv_free_ char **zones = NULL;
879         size_t n_zones = 0, n_allocated = 0;
880
881         assert(ret);
882
883         zones = strv_new("UTC", NULL);
884         if (!zones)
885                 return -ENOMEM;
886
887         n_allocated = 2;
888         n_zones = 1;
889
890         f = fopen("/usr/share/zoneinfo/zone.tab", "re");
891         if (f) {
892                 char l[LINE_MAX];
893
894                 FOREACH_LINE(l, f, return -errno) {
895                         char *p, *w;
896                         size_t k;
897
898                         p = strstrip(l);
899
900                         if (isempty(p) || *p == '#')
901                                 continue;
902
903                         /* Skip over country code */
904                         p += strcspn(p, WHITESPACE);
905                         p += strspn(p, WHITESPACE);
906
907                         /* Skip over coordinates */
908                         p += strcspn(p, WHITESPACE);
909                         p += strspn(p, WHITESPACE);
910
911                         /* Found timezone name */
912                         k = strcspn(p, WHITESPACE);
913                         if (k <= 0)
914                                 continue;
915
916                         w = strndup(p, k);
917                         if (!w)
918                                 return -ENOMEM;
919
920                         if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
921                                 free(w);
922                                 return -ENOMEM;
923                         }
924
925                         zones[n_zones++] = w;
926                         zones[n_zones] = NULL;
927                 }
928
929                 strv_sort(zones);
930
931         } else if (errno != ENOENT)
932                 return -errno;
933
934         *ret = zones;
935         zones = NULL;
936
937         return 0;
938 }
939
940 bool timezone_is_valid(const char *name) {
941         bool slash = false;
942         const char *p, *t;
943         struct stat st;
944
945         if (!name || *name == 0 || *name == '/')
946                 return false;
947
948         for (p = name; *p; p++) {
949                 if (!(*p >= '0' && *p <= '9') &&
950                     !(*p >= 'a' && *p <= 'z') &&
951                     !(*p >= 'A' && *p <= 'Z') &&
952                     !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
953                         return false;
954
955                 if (*p == '/') {
956
957                         if (slash)
958                                 return false;
959
960                         slash = true;
961                 } else
962                         slash = false;
963         }
964
965         if (slash)
966                 return false;
967
968         t = strjoina("/usr/share/zoneinfo/", name);
969         if (stat(t, &st) < 0)
970                 return false;
971
972         if (!S_ISREG(st.st_mode))
973                 return false;
974
975         return true;
976 }
977
978 clockid_t clock_boottime_or_monotonic(void) {
979         static clockid_t clock = -1;
980         int fd;
981
982         if (clock != -1)
983                 return clock;
984
985         fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
986         if (fd < 0)
987                 clock = CLOCK_MONOTONIC;
988         else {
989                 safe_close(fd);
990                 clock = CLOCK_BOOTTIME;
991         }
992
993         return clock;
994 }