chiark / gitweb /
Remove APPARMOR, BLKID and SECCOMP checks and feature.
[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         struct tm tm, copy;
501         time_t x;
502         usec_t plus = 0, minus = 0, ret;
503         int r, weekday = -1;
504         unsigned i;
505
506         /*
507          * Allowed syntaxes:
508          *
509          *   2012-09-22 16:34:22
510          *   2012-09-22 16:34     (seconds will be set to 0)
511          *   2012-09-22           (time will be set to 00:00:00)
512          *   16:34:22             (date will be set to today)
513          *   16:34                (date will be set to today, seconds to 0)
514          *   now
515          *   yesterday            (time is set to 00:00:00)
516          *   today                (time is set to 00:00:00)
517          *   tomorrow             (time is set to 00:00:00)
518          *   +5min
519          *   -5days
520          *   @2147483647          (seconds since epoch)
521          *
522          */
523
524         assert(t);
525         assert(usec);
526
527         x = time(NULL);
528         assert_se(localtime_r(&x, &tm));
529         tm.tm_isdst = -1;
530
531         if (streq(t, "now"))
532                 goto finish;
533
534         else if (streq(t, "today")) {
535                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
536                 goto finish;
537
538         } else if (streq(t, "yesterday")) {
539                 tm.tm_mday --;
540                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
541                 goto finish;
542
543         } else if (streq(t, "tomorrow")) {
544                 tm.tm_mday ++;
545                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
546                 goto finish;
547
548         } else if (t[0] == '+') {
549                 r = parse_sec(t+1, &plus);
550                 if (r < 0)
551                         return r;
552
553                 goto finish;
554
555         } else if (t[0] == '-') {
556                 r = parse_sec(t+1, &minus);
557                 if (r < 0)
558                         return r;
559
560                 goto finish;
561
562         } else if (t[0] == '@')
563                 return parse_sec(t + 1, usec);
564
565         else if (endswith(t, " ago")) {
566                 _cleanup_free_ char *z;
567
568                 z = strndup(t, strlen(t) - 4);
569                 if (!z)
570                         return -ENOMEM;
571
572                 r = parse_sec(z, &minus);
573                 if (r < 0)
574                         return r;
575
576                 goto finish;
577         } else if (endswith(t, " left")) {
578                 _cleanup_free_ char *z;
579
580                 z = strndup(t, strlen(t) - 4);
581                 if (!z)
582                         return -ENOMEM;
583
584                 r = parse_sec(z, &plus);
585                 if (r < 0)
586                         return r;
587
588                 goto finish;
589         }
590
591         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
592                 size_t skip;
593
594                 if (!startswith_no_case(t, day_nr[i].name))
595                         continue;
596
597                 skip = strlen(day_nr[i].name);
598                 if (t[skip] != ' ')
599                         continue;
600
601                 weekday = day_nr[i].nr;
602                 t += skip + 1;
603                 break;
604         }
605
606         copy = tm;
607         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
608         if (k && *k == 0)
609                 goto finish;
610
611         tm = copy;
612         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
613         if (k && *k == 0)
614                 goto finish;
615
616         tm = copy;
617         k = strptime(t, "%y-%m-%d %H:%M", &tm);
618         if (k && *k == 0) {
619                 tm.tm_sec = 0;
620                 goto finish;
621         }
622
623         tm = copy;
624         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
625         if (k && *k == 0) {
626                 tm.tm_sec = 0;
627                 goto finish;
628         }
629
630         tm = copy;
631         k = strptime(t, "%y-%m-%d", &tm);
632         if (k && *k == 0) {
633                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
634                 goto finish;
635         }
636
637         tm = copy;
638         k = strptime(t, "%Y-%m-%d", &tm);
639         if (k && *k == 0) {
640                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
641                 goto finish;
642         }
643
644         tm = copy;
645         k = strptime(t, "%H:%M:%S", &tm);
646         if (k && *k == 0)
647                 goto finish;
648
649         tm = copy;
650         k = strptime(t, "%H:%M", &tm);
651         if (k && *k == 0) {
652                 tm.tm_sec = 0;
653                 goto finish;
654         }
655
656         return -EINVAL;
657
658 finish:
659         x = mktime(&tm);
660         if (x == (time_t) -1)
661                 return -EINVAL;
662
663         if (weekday >= 0 && tm.tm_wday != weekday)
664                 return -EINVAL;
665
666         ret = (usec_t) x * USEC_PER_SEC;
667
668         ret += plus;
669         if (ret > minus)
670                 ret -= minus;
671         else
672                 ret = 0;
673
674         *usec = ret;
675
676         return 0;
677 }
678 #endif // 0
679
680 int parse_sec(const char *t, usec_t *usec) {
681         static const struct {
682                 const char *suffix;
683                 usec_t usec;
684         } table[] = {
685                 { "seconds", USEC_PER_SEC },
686                 { "second", USEC_PER_SEC },
687                 { "sec", USEC_PER_SEC },
688                 { "s", USEC_PER_SEC },
689                 { "minutes", USEC_PER_MINUTE },
690                 { "minute", USEC_PER_MINUTE },
691                 { "min", USEC_PER_MINUTE },
692                 { "months", USEC_PER_MONTH },
693                 { "month", USEC_PER_MONTH },
694                 { "msec", USEC_PER_MSEC },
695                 { "ms", USEC_PER_MSEC },
696                 { "m", USEC_PER_MINUTE },
697                 { "hours", USEC_PER_HOUR },
698                 { "hour", USEC_PER_HOUR },
699                 { "hr", USEC_PER_HOUR },
700                 { "h", USEC_PER_HOUR },
701                 { "days", USEC_PER_DAY },
702                 { "day", USEC_PER_DAY },
703                 { "d", USEC_PER_DAY },
704                 { "weeks", USEC_PER_WEEK },
705                 { "week", USEC_PER_WEEK },
706                 { "w", USEC_PER_WEEK },
707                 { "years", USEC_PER_YEAR },
708                 { "year", USEC_PER_YEAR },
709                 { "y", USEC_PER_YEAR },
710                 { "usec", 1ULL },
711                 { "us", 1ULL },
712                 { "", USEC_PER_SEC }, /* default is sec */
713         };
714
715         const char *p, *s;
716         usec_t r = 0;
717         bool something = false;
718
719         assert(t);
720         assert(usec);
721
722         p = t;
723
724         p += strspn(p, WHITESPACE);
725         s = startswith(p, "infinity");
726         if (s) {
727                 s += strspn(s, WHITESPACE);
728                 if (*s != 0)
729                         return -EINVAL;
730
731                 *usec = USEC_INFINITY;
732                 return 0;
733         }
734
735         for (;;) {
736                 long long l, z = 0;
737                 char *e;
738                 unsigned i, n = 0;
739
740                 p += strspn(p, WHITESPACE);
741
742                 if (*p == 0) {
743                         if (!something)
744                                 return -EINVAL;
745
746                         break;
747                 }
748
749                 errno = 0;
750                 l = strtoll(p, &e, 10);
751
752                 if (errno > 0)
753                         return -errno;
754
755                 if (l < 0)
756                         return -ERANGE;
757
758                 if (*e == '.') {
759                         char *b = e + 1;
760
761                         errno = 0;
762                         z = strtoll(b, &e, 10);
763                         if (errno > 0)
764                                 return -errno;
765
766                         if (z < 0)
767                                 return -ERANGE;
768
769                         if (e == b)
770                                 return -EINVAL;
771
772                         n = e - b;
773
774                 } else if (e == p)
775                         return -EINVAL;
776
777                 e += strspn(e, WHITESPACE);
778
779                 for (i = 0; i < ELEMENTSOF(table); i++)
780                         if (startswith(e, table[i].suffix)) {
781                                 usec_t k = (usec_t) z * table[i].usec;
782
783                                 for (; n > 0; n--)
784                                         k /= 10;
785
786                                 r += (usec_t) l * table[i].usec + k;
787                                 p = e + strlen(table[i].suffix);
788
789                                 something = true;
790                                 break;
791                         }
792
793                 if (i >= ELEMENTSOF(table))
794                         return -EINVAL;
795
796         }
797
798         *usec = r;
799
800         return 0;
801 }
802
803 int parse_nsec(const char *t, nsec_t *nsec) {
804         static const struct {
805                 const char *suffix;
806                 nsec_t nsec;
807         } table[] = {
808                 { "seconds", NSEC_PER_SEC },
809                 { "second", NSEC_PER_SEC },
810                 { "sec", NSEC_PER_SEC },
811                 { "s", NSEC_PER_SEC },
812                 { "minutes", NSEC_PER_MINUTE },
813                 { "minute", NSEC_PER_MINUTE },
814                 { "min", NSEC_PER_MINUTE },
815                 { "months", NSEC_PER_MONTH },
816                 { "month", NSEC_PER_MONTH },
817                 { "msec", NSEC_PER_MSEC },
818                 { "ms", NSEC_PER_MSEC },
819                 { "m", NSEC_PER_MINUTE },
820                 { "hours", NSEC_PER_HOUR },
821                 { "hour", NSEC_PER_HOUR },
822                 { "hr", NSEC_PER_HOUR },
823                 { "h", NSEC_PER_HOUR },
824                 { "days", NSEC_PER_DAY },
825                 { "day", NSEC_PER_DAY },
826                 { "d", NSEC_PER_DAY },
827                 { "weeks", NSEC_PER_WEEK },
828                 { "week", NSEC_PER_WEEK },
829                 { "w", NSEC_PER_WEEK },
830                 { "years", NSEC_PER_YEAR },
831                 { "year", NSEC_PER_YEAR },
832                 { "y", NSEC_PER_YEAR },
833                 { "usec", NSEC_PER_USEC },
834                 { "us", NSEC_PER_USEC },
835                 { "nsec", 1ULL },
836                 { "ns", 1ULL },
837                 { "", 1ULL }, /* default is nsec */
838         };
839
840         const char *p, *s;
841         nsec_t r = 0;
842         bool something = false;
843
844         assert(t);
845         assert(nsec);
846
847         p = t;
848
849         p += strspn(p, WHITESPACE);
850         s = startswith(p, "infinity");
851         if (s) {
852                 s += strspn(s, WHITESPACE);
853                 if (*s != 0)
854                         return -EINVAL;
855
856                 *nsec = NSEC_INFINITY;
857                 return 0;
858         }
859
860         for (;;) {
861                 long long l, z = 0;
862                 char *e;
863                 unsigned i, n = 0;
864
865                 p += strspn(p, WHITESPACE);
866
867                 if (*p == 0) {
868                         if (!something)
869                                 return -EINVAL;
870
871                         break;
872                 }
873
874                 errno = 0;
875                 l = strtoll(p, &e, 10);
876
877                 if (errno > 0)
878                         return -errno;
879
880                 if (l < 0)
881                         return -ERANGE;
882
883                 if (*e == '.') {
884                         char *b = e + 1;
885
886                         errno = 0;
887                         z = strtoll(b, &e, 10);
888                         if (errno > 0)
889                                 return -errno;
890
891                         if (z < 0)
892                                 return -ERANGE;
893
894                         if (e == b)
895                                 return -EINVAL;
896
897                         n = e - b;
898
899                 } else if (e == p)
900                         return -EINVAL;
901
902                 e += strspn(e, WHITESPACE);
903
904                 for (i = 0; i < ELEMENTSOF(table); i++)
905                         if (startswith(e, table[i].suffix)) {
906                                 nsec_t k = (nsec_t) z * table[i].nsec;
907
908                                 for (; n > 0; n--)
909                                         k /= 10;
910
911                                 r += (nsec_t) l * table[i].nsec + k;
912                                 p = e + strlen(table[i].suffix);
913
914                                 something = true;
915                                 break;
916                         }
917
918                 if (i >= ELEMENTSOF(table))
919                         return -EINVAL;
920
921         }
922
923         *nsec = r;
924
925         return 0;
926 }
927
928 /// UNNEEDED by elogind
929 #if 0
930 bool ntp_synced(void) {
931         struct timex txc = {};
932
933         if (adjtimex(&txc) < 0)
934                 return false;
935
936         if (txc.status & STA_UNSYNC)
937                 return false;
938
939         return true;
940 }
941
942 int get_timezones(char ***ret) {
943         _cleanup_fclose_ FILE *f = NULL;
944         _cleanup_strv_free_ char **zones = NULL;
945         size_t n_zones = 0, n_allocated = 0;
946
947         assert(ret);
948
949         zones = strv_new("UTC", NULL);
950         if (!zones)
951                 return -ENOMEM;
952
953         n_allocated = 2;
954         n_zones = 1;
955
956         f = fopen("/usr/share/zoneinfo/zone.tab", "re");
957         if (f) {
958                 char l[LINE_MAX];
959
960                 FOREACH_LINE(l, f, return -errno) {
961                         char *p, *w;
962                         size_t k;
963
964                         p = strstrip(l);
965
966                         if (isempty(p) || *p == '#')
967                                 continue;
968
969                         /* Skip over country code */
970                         p += strcspn(p, WHITESPACE);
971                         p += strspn(p, WHITESPACE);
972
973                         /* Skip over coordinates */
974                         p += strcspn(p, WHITESPACE);
975                         p += strspn(p, WHITESPACE);
976
977                         /* Found timezone name */
978                         k = strcspn(p, WHITESPACE);
979                         if (k <= 0)
980                                 continue;
981
982                         w = strndup(p, k);
983                         if (!w)
984                                 return -ENOMEM;
985
986                         if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
987                                 free(w);
988                                 return -ENOMEM;
989                         }
990
991                         zones[n_zones++] = w;
992                         zones[n_zones] = NULL;
993                 }
994
995                 strv_sort(zones);
996
997         } else if (errno != ENOENT)
998                 return -errno;
999
1000         *ret = zones;
1001         zones = NULL;
1002
1003         return 0;
1004 }
1005 #endif // 0
1006
1007 bool timezone_is_valid(const char *name) {
1008         bool slash = false;
1009         const char *p, *t;
1010         struct stat st;
1011
1012         if (isempty(name))
1013                 return false;
1014
1015         if (name[0] == '/')
1016                 return false;
1017
1018         for (p = name; *p; p++) {
1019                 if (!(*p >= '0' && *p <= '9') &&
1020                     !(*p >= 'a' && *p <= 'z') &&
1021                     !(*p >= 'A' && *p <= 'Z') &&
1022                     !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
1023                         return false;
1024
1025                 if (*p == '/') {
1026
1027                         if (slash)
1028                                 return false;
1029
1030                         slash = true;
1031                 } else
1032                         slash = false;
1033         }
1034
1035         if (slash)
1036                 return false;
1037
1038         t = strjoina("/usr/share/zoneinfo/", name);
1039         if (stat(t, &st) < 0)
1040                 return false;
1041
1042         if (!S_ISREG(st.st_mode))
1043                 return false;
1044
1045         return true;
1046 }
1047
1048 clockid_t clock_boottime_or_monotonic(void) {
1049         static clockid_t clock = -1;
1050         int fd;
1051
1052         if (clock != -1)
1053                 return clock;
1054
1055         fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1056         if (fd < 0)
1057                 clock = CLOCK_MONOTONIC;
1058         else {
1059                 safe_close(fd);
1060                 clock = CLOCK_BOOTTIME;
1061         }
1062
1063         return clock;
1064 }
1065
1066 int get_timezone(char **tz) {
1067         _cleanup_free_ char *t = NULL;
1068         const char *e;
1069         char *z;
1070         int r;
1071
1072         r = readlink_malloc("/etc/localtime", &t);
1073         if (r < 0)
1074                 return r; /* returns EINVAL if not a symlink */
1075
1076         e = path_startswith(t, "/usr/share/zoneinfo/");
1077         if (!e)
1078                 e = path_startswith(t, "../usr/share/zoneinfo/");
1079         if (!e)
1080                 return -EINVAL;
1081
1082         if (!timezone_is_valid(e))
1083                 return -EINVAL;
1084
1085         z = strdup(e);
1086         if (!z)
1087                 return -ENOMEM;
1088
1089         *tz = z;
1090         return 0;
1091 }