chiark / gitweb /
Use %m instead of strerror(errno) where appropiate
[elogind.git] / src / shared / 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
26 #include "util.h"
27 #include "time-util.h"
28
29 usec_t now(clockid_t clock_id) {
30         struct timespec ts;
31
32         assert_se(clock_gettime(clock_id, &ts) == 0);
33
34         return timespec_load(&ts);
35 }
36
37 dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
38         assert(ts);
39
40         ts->realtime = now(CLOCK_REALTIME);
41         ts->monotonic = now(CLOCK_MONOTONIC);
42
43         return ts;
44 }
45
46 dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
47         int64_t delta;
48         assert(ts);
49
50         if (u == (usec_t) -1) {
51                 ts->realtime = ts->monotonic = (usec_t) -1;
52                 return ts;
53         }
54
55         ts->realtime = u;
56
57         if (u == 0)
58                 ts->monotonic = 0;
59         else {
60                 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
61
62                 ts->monotonic = now(CLOCK_MONOTONIC);
63
64                 if ((int64_t) ts->monotonic > delta)
65                         ts->monotonic -= delta;
66                 else
67                         ts->monotonic = 0;
68         }
69
70         return ts;
71 }
72
73 dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
74         int64_t delta;
75         assert(ts);
76
77         if (u == (usec_t) -1) {
78                 ts->realtime = ts->monotonic = (usec_t) -1;
79                 return ts;
80         }
81
82         ts->monotonic = u;
83         delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
84
85         ts->realtime = now(CLOCK_REALTIME);
86         if ((int64_t) ts->realtime > delta)
87                 ts->realtime -= delta;
88         else
89                 ts->realtime = 0;
90
91         return ts;
92 }
93
94 usec_t timespec_load(const struct timespec *ts) {
95         assert(ts);
96
97         if (ts->tv_sec == (time_t) -1 &&
98             ts->tv_nsec == (long) -1)
99                 return (usec_t) -1;
100
101         if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
102                 return (usec_t) -1;
103
104         return
105                 (usec_t) ts->tv_sec * USEC_PER_SEC +
106                 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
107 }
108
109 struct timespec *timespec_store(struct timespec *ts, usec_t u)  {
110         assert(ts);
111
112         if (u == (usec_t) -1) {
113                 ts->tv_sec = (time_t) -1;
114                 ts->tv_nsec = (long) -1;
115                 return ts;
116         }
117
118         ts->tv_sec = (time_t) (u / USEC_PER_SEC);
119         ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
120
121         return ts;
122 }
123
124 usec_t timeval_load(const struct timeval *tv) {
125         assert(tv);
126
127         if (tv->tv_sec == (time_t) -1 &&
128             tv->tv_usec == (suseconds_t) -1)
129                 return (usec_t) -1;
130
131         if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
132                 return (usec_t) -1;
133
134         return
135                 (usec_t) tv->tv_sec * USEC_PER_SEC +
136                 (usec_t) tv->tv_usec;
137 }
138
139 struct timeval *timeval_store(struct timeval *tv, usec_t u) {
140         assert(tv);
141
142         if (u == (usec_t) -1) {
143                 tv->tv_sec = (time_t) -1;
144                 tv->tv_usec = (suseconds_t) -1;
145         } else {
146                 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
147                 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
148         }
149
150         return tv;
151 }
152
153 char *format_timestamp(char *buf, size_t l, usec_t t) {
154         struct tm tm;
155         time_t sec;
156
157         assert(buf);
158         assert(l > 0);
159
160         if (t <= 0 || t == (usec_t) -1)
161                 return NULL;
162
163         sec = (time_t) (t / USEC_PER_SEC);
164
165         if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", localtime_r(&sec, &tm)) <= 0)
166                 return NULL;
167
168         return buf;
169 }
170
171 char *format_timestamp_us(char *buf, size_t l, usec_t t) {
172         struct tm tm;
173         time_t sec;
174
175         assert(buf);
176         assert(l > 0);
177
178         if (t <= 0 || t == (usec_t) -1)
179                 return NULL;
180
181         sec = (time_t) (t / USEC_PER_SEC);
182         localtime_r(&sec, &tm);
183
184         if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
185                 return NULL;
186         snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
187         if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
188                 return NULL;
189
190         return buf;
191 }
192
193 char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
194         const char *s;
195         usec_t n, d;
196
197         n = now(CLOCK_REALTIME);
198
199         if (t <= 0 || (t == (usec_t) -1))
200                 return NULL;
201
202         if (n > t) {
203                 d = n - t;
204                 s = "ago";
205         } else {
206                 d = t - n;
207                 s = "left";
208         }
209
210         if (d >= USEC_PER_YEAR)
211                 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
212                          d / USEC_PER_YEAR,
213                          (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
214         else if (d >= USEC_PER_MONTH)
215                 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
216                          d / USEC_PER_MONTH,
217                          (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
218         else if (d >= USEC_PER_WEEK)
219                 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
220                          d / USEC_PER_WEEK,
221                          (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
222         else if (d >= 2*USEC_PER_DAY)
223                 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
224         else if (d >= 25*USEC_PER_HOUR)
225                 snprintf(buf, l, "1 day " USEC_FMT "h %s",
226                          (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
227         else if (d >= 6*USEC_PER_HOUR)
228                 snprintf(buf, l, USEC_FMT "h %s",
229                          d / USEC_PER_HOUR, s);
230         else if (d >= USEC_PER_HOUR)
231                 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
232                          d / USEC_PER_HOUR,
233                          (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
234         else if (d >= 5*USEC_PER_MINUTE)
235                 snprintf(buf, l, USEC_FMT "min %s",
236                          d / USEC_PER_MINUTE, s);
237         else if (d >= USEC_PER_MINUTE)
238                 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
239                          d / USEC_PER_MINUTE,
240                          (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
241         else if (d >= USEC_PER_SEC)
242                 snprintf(buf, l, USEC_FMT "s %s",
243                          d / USEC_PER_SEC, s);
244         else if (d >= USEC_PER_MSEC)
245                 snprintf(buf, l, USEC_FMT "ms %s",
246                          d / USEC_PER_MSEC, s);
247         else if (d > 0)
248                 snprintf(buf, l, USEC_FMT"us %s",
249                          d, s);
250         else
251                 snprintf(buf, l, "now");
252
253         buf[l-1] = 0;
254         return buf;
255 }
256
257 char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
258         static const struct {
259                 const char *suffix;
260                 usec_t usec;
261         } table[] = {
262                 { "y", USEC_PER_YEAR },
263                 { "month", USEC_PER_MONTH },
264                 { "w", USEC_PER_WEEK },
265                 { "d", USEC_PER_DAY },
266                 { "h", USEC_PER_HOUR },
267                 { "min", USEC_PER_MINUTE },
268                 { "s", USEC_PER_SEC },
269                 { "ms", USEC_PER_MSEC },
270                 { "us", 1 },
271         };
272
273         unsigned i;
274         char *p = buf;
275         bool something = false;
276
277         assert(buf);
278         assert(l > 0);
279
280         if (t == (usec_t) -1)
281                 return NULL;
282
283         if (t <= 0) {
284                 snprintf(p, l, "0");
285                 p[l-1] = 0;
286                 return p;
287         }
288
289         /* The result of this function can be parsed with parse_sec */
290
291         for (i = 0; i < ELEMENTSOF(table); i++) {
292                 int k = 0;
293                 size_t n;
294                 bool done = false;
295                 usec_t a, b;
296
297                 if (t <= 0)
298                         break;
299
300                 if (t < accuracy && something)
301                         break;
302
303                 if (t < table[i].usec)
304                         continue;
305
306                 if (l <= 1)
307                         break;
308
309                 a = t / table[i].usec;
310                 b = t % table[i].usec;
311
312                 /* Let's see if we should shows this in dot notation */
313                 if (t < USEC_PER_MINUTE && b > 0) {
314                         usec_t cc;
315                         int j;
316
317                         j = 0;
318                         for (cc = table[i].usec; cc > 1; cc /= 10)
319                                 j++;
320
321                         for (cc = accuracy; cc > 1; cc /= 10) {
322                                 b /= 10;
323                                 j--;
324                         }
325
326                         if (j > 0) {
327                                 k = snprintf(p, l,
328                                              "%s"USEC_FMT".%0*llu%s",
329                                              p > buf ? " " : "",
330                                              a,
331                                              j,
332                                              (unsigned long long) b,
333                                              table[i].suffix);
334
335                                 t = 0;
336                                 done = true;
337                         }
338                 }
339
340                 /* No? Then let's show it normally */
341                 if (!done) {
342                         k = snprintf(p, l,
343                                      "%s"USEC_FMT"%s",
344                                      p > buf ? " " : "",
345                                      a,
346                                      table[i].suffix);
347
348                         t = b;
349                 }
350
351                 n = MIN((size_t) k, l);
352
353                 l -= n;
354                 p += n;
355
356                 something = true;
357         }
358
359         *p = 0;
360
361         return buf;
362 }
363
364 void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
365
366         assert(f);
367         assert(name);
368         assert(t);
369
370         if (!dual_timestamp_is_set(t))
371                 return;
372
373         fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
374                 name,
375                 t->realtime,
376                 t->monotonic);
377 }
378
379 void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
380         unsigned long long a, b;
381
382         assert(value);
383         assert(t);
384
385         if (sscanf(value, "%llu %llu", &a, &b) != 2)
386                 log_debug("Failed to parse finish timestamp value %s", value);
387         else {
388                 t->realtime = a;
389                 t->monotonic = b;
390         }
391 }
392
393 int parse_timestamp(const char *t, usec_t *usec) {
394         static const struct {
395                 const char *name;
396                 const int nr;
397         } day_nr[] = {
398                 { "Sunday",    0 },
399                 { "Sun",       0 },
400                 { "Monday",    1 },
401                 { "Mon",       1 },
402                 { "Tuesday",   2 },
403                 { "Tue",       2 },
404                 { "Wednesday", 3 },
405                 { "Wed",       3 },
406                 { "Thursday",  4 },
407                 { "Thu",       4 },
408                 { "Friday",    5 },
409                 { "Fri",       5 },
410                 { "Saturday",  6 },
411                 { "Sat",       6 },
412         };
413
414         const char *k;
415         struct tm tm, copy;
416         time_t x;
417         usec_t plus = 0, minus = 0, ret;
418         int r, weekday = -1;
419         unsigned i;
420
421         /*
422          * Allowed syntaxes:
423          *
424          *   2012-09-22 16:34:22
425          *   2012-09-22 16:34     (seconds will be set to 0)
426          *   2012-09-22           (time will be set to 00:00:00)
427          *   16:34:22             (date will be set to today)
428          *   16:34                (date will be set to today, seconds to 0)
429          *   now
430          *   yesterday            (time is set to 00:00:00)
431          *   today                (time is set to 00:00:00)
432          *   tomorrow             (time is set to 00:00:00)
433          *   +5min
434          *   -5days
435          *   @2147483647          (seconds since epoch)
436          *
437          */
438
439         assert(t);
440         assert(usec);
441
442         x = time(NULL);
443         assert_se(localtime_r(&x, &tm));
444         tm.tm_isdst = -1;
445
446         if (streq(t, "now"))
447                 goto finish;
448
449         else if (streq(t, "today")) {
450                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
451                 goto finish;
452
453         } else if (streq(t, "yesterday")) {
454                 tm.tm_mday --;
455                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
456                 goto finish;
457
458         } else if (streq(t, "tomorrow")) {
459                 tm.tm_mday ++;
460                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
461                 goto finish;
462
463         } else if (t[0] == '+') {
464                 r = parse_sec(t+1, &plus);
465                 if (r < 0)
466                         return r;
467
468                 goto finish;
469
470         } else if (t[0] == '-') {
471                 r = parse_sec(t+1, &minus);
472                 if (r < 0)
473                         return r;
474
475                 goto finish;
476
477         } else if (t[0] == '@')
478                 return parse_sec(t + 1, usec);
479
480         else if (endswith(t, " ago")) {
481                 _cleanup_free_ char *z;
482
483                 z = strndup(t, strlen(t) - 4);
484                 if (!z)
485                         return -ENOMEM;
486
487                 r = parse_sec(z, &minus);
488                 if (r < 0)
489                         return r;
490
491                 goto finish;
492         } else if (endswith(t, " left")) {
493                 _cleanup_free_ char *z;
494
495                 z = strndup(t, strlen(t) - 4);
496                 if (!z)
497                         return -ENOMEM;
498
499                 r = parse_sec(z, &plus);
500                 if (r < 0)
501                         return r;
502
503                 goto finish;
504         }
505
506         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
507                 size_t skip;
508
509                 if (!startswith_no_case(t, day_nr[i].name))
510                         continue;
511
512                 skip = strlen(day_nr[i].name);
513                 if (t[skip] != ' ')
514                         continue;
515
516                 weekday = day_nr[i].nr;
517                 t += skip + 1;
518                 break;
519         }
520
521         copy = tm;
522         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
523         if (k && *k == 0)
524                 goto finish;
525
526         tm = copy;
527         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
528         if (k && *k == 0)
529                 goto finish;
530
531         tm = copy;
532         k = strptime(t, "%y-%m-%d %H:%M", &tm);
533         if (k && *k == 0) {
534                 tm.tm_sec = 0;
535                 goto finish;
536         }
537
538         tm = copy;
539         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
540         if (k && *k == 0) {
541                 tm.tm_sec = 0;
542                 goto finish;
543         }
544
545         tm = copy;
546         k = strptime(t, "%y-%m-%d", &tm);
547         if (k && *k == 0) {
548                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
549                 goto finish;
550         }
551
552         tm = copy;
553         k = strptime(t, "%Y-%m-%d", &tm);
554         if (k && *k == 0) {
555                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
556                 goto finish;
557         }
558
559         tm = copy;
560         k = strptime(t, "%H:%M:%S", &tm);
561         if (k && *k == 0)
562                 goto finish;
563
564         tm = copy;
565         k = strptime(t, "%H:%M", &tm);
566         if (k && *k == 0) {
567                 tm.tm_sec = 0;
568                 goto finish;
569         }
570
571         return -EINVAL;
572
573 finish:
574         x = mktime(&tm);
575         if (x == (time_t) -1)
576                 return -EINVAL;
577
578         if (weekday >= 0 && tm.tm_wday != weekday)
579                 return -EINVAL;
580
581         ret = (usec_t) x * USEC_PER_SEC;
582
583         ret += plus;
584         if (ret > minus)
585                 ret -= minus;
586         else
587                 ret = 0;
588
589         *usec = ret;
590
591         return 0;
592 }
593
594 int parse_sec(const char *t, usec_t *usec) {
595         static const struct {
596                 const char *suffix;
597                 usec_t usec;
598         } table[] = {
599                 { "seconds", USEC_PER_SEC },
600                 { "second", USEC_PER_SEC },
601                 { "sec", USEC_PER_SEC },
602                 { "s", USEC_PER_SEC },
603                 { "minutes", USEC_PER_MINUTE },
604                 { "minute", USEC_PER_MINUTE },
605                 { "min", USEC_PER_MINUTE },
606                 { "months", USEC_PER_MONTH },
607                 { "month", USEC_PER_MONTH },
608                 { "msec", USEC_PER_MSEC },
609                 { "ms", USEC_PER_MSEC },
610                 { "m", USEC_PER_MINUTE },
611                 { "hours", USEC_PER_HOUR },
612                 { "hour", USEC_PER_HOUR },
613                 { "hr", USEC_PER_HOUR },
614                 { "h", USEC_PER_HOUR },
615                 { "days", USEC_PER_DAY },
616                 { "day", USEC_PER_DAY },
617                 { "d", USEC_PER_DAY },
618                 { "weeks", USEC_PER_WEEK },
619                 { "week", USEC_PER_WEEK },
620                 { "w", USEC_PER_WEEK },
621                 { "years", USEC_PER_YEAR },
622                 { "year", USEC_PER_YEAR },
623                 { "y", USEC_PER_YEAR },
624                 { "usec", 1ULL },
625                 { "us", 1ULL },
626                 { "", USEC_PER_SEC }, /* default is sec */
627         };
628
629         const char *p;
630         usec_t r = 0;
631         bool something = false;
632
633         assert(t);
634         assert(usec);
635
636         p = t;
637         for (;;) {
638                 long long l, z = 0;
639                 char *e;
640                 unsigned i, n = 0;
641
642                 p += strspn(p, WHITESPACE);
643
644                 if (*p == 0) {
645                         if (!something)
646                                 return -EINVAL;
647
648                         break;
649                 }
650
651                 errno = 0;
652                 l = strtoll(p, &e, 10);
653
654                 if (errno > 0)
655                         return -errno;
656
657                 if (l < 0)
658                         return -ERANGE;
659
660                 if (*e == '.') {
661                         char *b = e + 1;
662
663                         errno = 0;
664                         z = strtoll(b, &e, 10);
665                         if (errno > 0)
666                                 return -errno;
667
668                         if (z < 0)
669                                 return -ERANGE;
670
671                         if (e == b)
672                                 return -EINVAL;
673
674                         n = e - b;
675
676                 } else if (e == p)
677                         return -EINVAL;
678
679                 e += strspn(e, WHITESPACE);
680
681                 for (i = 0; i < ELEMENTSOF(table); i++)
682                         if (startswith(e, table[i].suffix)) {
683                                 usec_t k = (usec_t) z * table[i].usec;
684
685                                 for (; n > 0; n--)
686                                         k /= 10;
687
688                                 r += (usec_t) l * table[i].usec + k;
689                                 p = e + strlen(table[i].suffix);
690
691                                 something = true;
692                                 break;
693                         }
694
695                 if (i >= ELEMENTSOF(table))
696                         return -EINVAL;
697
698         }
699
700         *usec = r;
701
702         return 0;
703 }
704
705 int parse_nsec(const char *t, nsec_t *nsec) {
706         static const struct {
707                 const char *suffix;
708                 nsec_t nsec;
709         } table[] = {
710                 { "seconds", NSEC_PER_SEC },
711                 { "second", NSEC_PER_SEC },
712                 { "sec", NSEC_PER_SEC },
713                 { "s", NSEC_PER_SEC },
714                 { "minutes", NSEC_PER_MINUTE },
715                 { "minute", NSEC_PER_MINUTE },
716                 { "min", NSEC_PER_MINUTE },
717                 { "months", NSEC_PER_MONTH },
718                 { "month", NSEC_PER_MONTH },
719                 { "msec", NSEC_PER_MSEC },
720                 { "ms", NSEC_PER_MSEC },
721                 { "m", NSEC_PER_MINUTE },
722                 { "hours", NSEC_PER_HOUR },
723                 { "hour", NSEC_PER_HOUR },
724                 { "hr", NSEC_PER_HOUR },
725                 { "h", NSEC_PER_HOUR },
726                 { "days", NSEC_PER_DAY },
727                 { "day", NSEC_PER_DAY },
728                 { "d", NSEC_PER_DAY },
729                 { "weeks", NSEC_PER_WEEK },
730                 { "week", NSEC_PER_WEEK },
731                 { "w", NSEC_PER_WEEK },
732                 { "years", NSEC_PER_YEAR },
733                 { "year", NSEC_PER_YEAR },
734                 { "y", NSEC_PER_YEAR },
735                 { "usec", NSEC_PER_USEC },
736                 { "us", NSEC_PER_USEC },
737                 { "nsec", 1ULL },
738                 { "ns", 1ULL },
739                 { "", 1ULL }, /* default is nsec */
740         };
741
742         const char *p;
743         nsec_t r = 0;
744         bool something = false;
745
746         assert(t);
747         assert(nsec);
748
749         p = t;
750         for (;;) {
751                 long long l, z = 0;
752                 char *e;
753                 unsigned i, n = 0;
754
755                 p += strspn(p, WHITESPACE);
756
757                 if (*p == 0) {
758                         if (!something)
759                                 return -EINVAL;
760
761                         break;
762                 }
763
764                 errno = 0;
765                 l = strtoll(p, &e, 10);
766
767                 if (errno > 0)
768                         return -errno;
769
770                 if (l < 0)
771                         return -ERANGE;
772
773                 if (*e == '.') {
774                         char *b = e + 1;
775
776                         errno = 0;
777                         z = strtoll(b, &e, 10);
778                         if (errno > 0)
779                                 return -errno;
780
781                         if (z < 0)
782                                 return -ERANGE;
783
784                         if (e == b)
785                                 return -EINVAL;
786
787                         n = e - b;
788
789                 } else if (e == p)
790                         return -EINVAL;
791
792                 e += strspn(e, WHITESPACE);
793
794                 for (i = 0; i < ELEMENTSOF(table); i++)
795                         if (startswith(e, table[i].suffix)) {
796                                 nsec_t k = (nsec_t) z * table[i].nsec;
797
798                                 for (; n > 0; n--)
799                                         k /= 10;
800
801                                 r += (nsec_t) l * table[i].nsec + k;
802                                 p = e + strlen(table[i].suffix);
803
804                                 something = true;
805                                 break;
806                         }
807
808                 if (i >= ELEMENTSOF(table))
809                         return -EINVAL;
810
811         }
812
813         *nsec = r;
814
815         return 0;
816 }
817
818 bool ntp_synced(void) {
819         struct timex txc = {};
820
821         if (adjtimex(&txc) < 0)
822                 return false;
823
824         if (txc.status & STA_UNSYNC)
825                 return false;
826
827         return true;
828 }