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