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