chiark / gitweb /
Cleaned up more unneeded functions and types in:
[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 /// UNNEEDED by elogind
804 #if 0
805 int parse_nsec(const char *t, nsec_t *nsec) {
806         static const struct {
807                 const char *suffix;
808                 nsec_t nsec;
809         } table[] = {
810                 { "seconds", NSEC_PER_SEC },
811                 { "second", NSEC_PER_SEC },
812                 { "sec", NSEC_PER_SEC },
813                 { "s", NSEC_PER_SEC },
814                 { "minutes", NSEC_PER_MINUTE },
815                 { "minute", NSEC_PER_MINUTE },
816                 { "min", NSEC_PER_MINUTE },
817                 { "months", NSEC_PER_MONTH },
818                 { "month", NSEC_PER_MONTH },
819                 { "msec", NSEC_PER_MSEC },
820                 { "ms", NSEC_PER_MSEC },
821                 { "m", NSEC_PER_MINUTE },
822                 { "hours", NSEC_PER_HOUR },
823                 { "hour", NSEC_PER_HOUR },
824                 { "hr", NSEC_PER_HOUR },
825                 { "h", NSEC_PER_HOUR },
826                 { "days", NSEC_PER_DAY },
827                 { "day", NSEC_PER_DAY },
828                 { "d", NSEC_PER_DAY },
829                 { "weeks", NSEC_PER_WEEK },
830                 { "week", NSEC_PER_WEEK },
831                 { "w", NSEC_PER_WEEK },
832                 { "years", NSEC_PER_YEAR },
833                 { "year", NSEC_PER_YEAR },
834                 { "y", NSEC_PER_YEAR },
835                 { "usec", NSEC_PER_USEC },
836                 { "us", NSEC_PER_USEC },
837                 { "nsec", 1ULL },
838                 { "ns", 1ULL },
839                 { "", 1ULL }, /* default is nsec */
840         };
841
842         const char *p, *s;
843         nsec_t r = 0;
844         bool something = false;
845
846         assert(t);
847         assert(nsec);
848
849         p = t;
850
851         p += strspn(p, WHITESPACE);
852         s = startswith(p, "infinity");
853         if (s) {
854                 s += strspn(s, WHITESPACE);
855                 if (*s != 0)
856                         return -EINVAL;
857
858                 *nsec = NSEC_INFINITY;
859                 return 0;
860         }
861
862         for (;;) {
863                 long long l, z = 0;
864                 char *e;
865                 unsigned i, n = 0;
866
867                 p += strspn(p, WHITESPACE);
868
869                 if (*p == 0) {
870                         if (!something)
871                                 return -EINVAL;
872
873                         break;
874                 }
875
876                 errno = 0;
877                 l = strtoll(p, &e, 10);
878
879                 if (errno > 0)
880                         return -errno;
881
882                 if (l < 0)
883                         return -ERANGE;
884
885                 if (*e == '.') {
886                         char *b = e + 1;
887
888                         errno = 0;
889                         z = strtoll(b, &e, 10);
890                         if (errno > 0)
891                                 return -errno;
892
893                         if (z < 0)
894                                 return -ERANGE;
895
896                         if (e == b)
897                                 return -EINVAL;
898
899                         n = e - b;
900
901                 } else if (e == p)
902                         return -EINVAL;
903
904                 e += strspn(e, WHITESPACE);
905
906                 for (i = 0; i < ELEMENTSOF(table); i++)
907                         if (startswith(e, table[i].suffix)) {
908                                 nsec_t k = (nsec_t) z * table[i].nsec;
909
910                                 for (; n > 0; n--)
911                                         k /= 10;
912
913                                 r += (nsec_t) l * table[i].nsec + k;
914                                 p = e + strlen(table[i].suffix);
915
916                                 something = true;
917                                 break;
918                         }
919
920                 if (i >= ELEMENTSOF(table))
921                         return -EINVAL;
922
923         }
924
925         *nsec = r;
926
927         return 0;
928 }
929
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
1006 bool timezone_is_valid(const char *name) {
1007         bool slash = false;
1008         const char *p, *t;
1009         struct stat st;
1010
1011         if (isempty(name))
1012                 return false;
1013
1014         if (name[0] == '/')
1015                 return false;
1016
1017         for (p = name; *p; p++) {
1018                 if (!(*p >= '0' && *p <= '9') &&
1019                     !(*p >= 'a' && *p <= 'z') &&
1020                     !(*p >= 'A' && *p <= 'Z') &&
1021                     !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
1022                         return false;
1023
1024                 if (*p == '/') {
1025
1026                         if (slash)
1027                                 return false;
1028
1029                         slash = true;
1030                 } else
1031                         slash = false;
1032         }
1033
1034         if (slash)
1035                 return false;
1036
1037         t = strjoina("/usr/share/zoneinfo/", name);
1038         if (stat(t, &st) < 0)
1039                 return false;
1040
1041         if (!S_ISREG(st.st_mode))
1042                 return false;
1043
1044         return true;
1045 }
1046
1047 clockid_t clock_boottime_or_monotonic(void) {
1048         static clockid_t clock = -1;
1049         int fd;
1050
1051         if (clock != -1)
1052                 return clock;
1053
1054         fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1055         if (fd < 0)
1056                 clock = CLOCK_MONOTONIC;
1057         else {
1058                 safe_close(fd);
1059                 clock = CLOCK_BOOTTIME;
1060         }
1061
1062         return clock;
1063 }
1064
1065 int get_timezone(char **tz) {
1066         _cleanup_free_ char *t = NULL;
1067         const char *e;
1068         char *z;
1069         int r;
1070
1071         r = readlink_malloc("/etc/localtime", &t);
1072         if (r < 0)
1073                 return r; /* returns EINVAL if not a symlink */
1074
1075         e = path_startswith(t, "/usr/share/zoneinfo/");
1076         if (!e)
1077                 e = path_startswith(t, "../usr/share/zoneinfo/");
1078         if (!e)
1079                 return -EINVAL;
1080
1081         if (!timezone_is_valid(e))
1082                 return -EINVAL;
1083
1084         z = strdup(e);
1085         if (!z)
1086                 return -ENOMEM;
1087
1088         *tz = z;
1089         return 0;
1090 }
1091 #endif // 0