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