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