chiark / gitweb /
systemd-run: make sure --nice=, --uid=, --gid=, --setenv= also work in --scope mode
[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", 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, "%llu years %llu months %s",
212                          (unsigned long long) (d / USEC_PER_YEAR),
213                          (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH), s);
214         else if (d >= USEC_PER_MONTH)
215                 snprintf(buf, l, "%llu months %llu days %s",
216                          (unsigned long long) (d / USEC_PER_MONTH),
217                          (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY), s);
218         else if (d >= USEC_PER_WEEK)
219                 snprintf(buf, l, "%llu weeks %llu days %s",
220                          (unsigned long long) (d / USEC_PER_WEEK),
221                          (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY), s);
222         else if (d >= 2*USEC_PER_DAY)
223                 snprintf(buf, l, "%llu days %s", (unsigned long long) (d / USEC_PER_DAY), s);
224         else if (d >= 25*USEC_PER_HOUR)
225                 snprintf(buf, l, "1 day %lluh %s",
226                          (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR), s);
227         else if (d >= 6*USEC_PER_HOUR)
228                 snprintf(buf, l, "%lluh %s",
229                          (unsigned long long) (d / USEC_PER_HOUR), s);
230         else if (d >= USEC_PER_HOUR)
231                 snprintf(buf, l, "%lluh %llumin %s",
232                          (unsigned long long) (d / USEC_PER_HOUR),
233                          (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE), s);
234         else if (d >= 5*USEC_PER_MINUTE)
235                 snprintf(buf, l, "%llumin %s",
236                          (unsigned long long) (d / USEC_PER_MINUTE), s);
237         else if (d >= USEC_PER_MINUTE)
238                 snprintf(buf, l, "%llumin %llus %s",
239                          (unsigned long long) (d / USEC_PER_MINUTE),
240                          (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC), s);
241         else if (d >= USEC_PER_SEC)
242                 snprintf(buf, l, "%llus %s",
243                          (unsigned long long) (d / USEC_PER_SEC), s);
244         else if (d >= USEC_PER_MSEC)
245                 snprintf(buf, l, "%llums %s",
246                          (unsigned long long) (d / USEC_PER_MSEC), s);
247         else if (d > 0)
248                 snprintf(buf, l, "%lluus %s",
249                          (unsigned long long) 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%llu.%0*llu%s",
329                                              p > buf ? " " : "",
330                                              (unsigned long long) 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%llu%s",
344                                      p > buf ? " " : "",
345                                      (unsigned long long) 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=%llu %llu\n",
374                 name,
375                 (unsigned long long) t->realtime,
376                 (unsigned long long) 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          *
436          */
437
438         assert(t);
439         assert(usec);
440
441         x = time(NULL);
442         assert_se(localtime_r(&x, &tm));
443         tm.tm_isdst = -1;
444
445         if (streq(t, "now"))
446                 goto finish;
447
448         else if (streq(t, "today")) {
449                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
450                 goto finish;
451
452         } else if (streq(t, "yesterday")) {
453                 tm.tm_mday --;
454                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
455                 goto finish;
456
457         } else if (streq(t, "tomorrow")) {
458                 tm.tm_mday ++;
459                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
460                 goto finish;
461
462         } else if (t[0] == '+') {
463
464                 r = parse_sec(t+1, &plus);
465                 if (r < 0)
466                         return r;
467
468                 goto finish;
469         } else if (t[0] == '-') {
470
471                 r = parse_sec(t+1, &minus);
472                 if (r < 0)
473                         return r;
474
475                 goto finish;
476
477         } else if (endswith(t, " ago")) {
478                 _cleanup_free_ char *z;
479
480                 z = strndup(t, strlen(t) - 4);
481                 if (!z)
482                         return -ENOMEM;
483
484                 r = parse_sec(z, &minus);
485                 if (r < 0)
486                         return r;
487
488                 goto finish;
489         } else if (endswith(t, " left")) {
490                 _cleanup_free_ char *z;
491
492                 z = strndup(t, strlen(t) - 4);
493                 if (!z)
494                         return -ENOMEM;
495
496                 r = parse_sec(z, &plus);
497                 if (r < 0)
498                         return r;
499
500                 goto finish;
501         }
502
503         for (i = 0; i < ELEMENTSOF(day_nr); i++) {
504                 size_t skip;
505
506                 if (!startswith_no_case(t, day_nr[i].name))
507                         continue;
508
509                 skip = strlen(day_nr[i].name);
510                 if (t[skip] != ' ')
511                         continue;
512
513                 weekday = day_nr[i].nr;
514                 t += skip + 1;
515                 break;
516         }
517
518         copy = tm;
519         k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
520         if (k && *k == 0)
521                 goto finish;
522
523         tm = copy;
524         k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
525         if (k && *k == 0)
526                 goto finish;
527
528         tm = copy;
529         k = strptime(t, "%y-%m-%d %H:%M", &tm);
530         if (k && *k == 0) {
531                 tm.tm_sec = 0;
532                 goto finish;
533         }
534
535         tm = copy;
536         k = strptime(t, "%Y-%m-%d %H:%M", &tm);
537         if (k && *k == 0) {
538                 tm.tm_sec = 0;
539                 goto finish;
540         }
541
542         tm = copy;
543         k = strptime(t, "%y-%m-%d", &tm);
544         if (k && *k == 0) {
545                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
546                 goto finish;
547         }
548
549         tm = copy;
550         k = strptime(t, "%Y-%m-%d", &tm);
551         if (k && *k == 0) {
552                 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
553                 goto finish;
554         }
555
556         tm = copy;
557         k = strptime(t, "%H:%M:%S", &tm);
558         if (k && *k == 0)
559                 goto finish;
560
561         tm = copy;
562         k = strptime(t, "%H:%M", &tm);
563         if (k && *k == 0) {
564                 tm.tm_sec = 0;
565                 goto finish;
566         }
567
568         return -EINVAL;
569
570 finish:
571         x = mktime(&tm);
572         if (x == (time_t) -1)
573                 return -EINVAL;
574
575         if (weekday >= 0 && tm.tm_wday != weekday)
576                 return -EINVAL;
577
578         ret = (usec_t) x * USEC_PER_SEC;
579
580         ret += plus;
581         if (ret > minus)
582                 ret -= minus;
583         else
584                 ret = 0;
585
586         *usec = ret;
587
588         return 0;
589 }
590
591 int parse_sec(const char *t, usec_t *usec) {
592         static const struct {
593                 const char *suffix;
594                 usec_t usec;
595         } table[] = {
596                 { "seconds", USEC_PER_SEC },
597                 { "second", USEC_PER_SEC },
598                 { "sec", USEC_PER_SEC },
599                 { "s", USEC_PER_SEC },
600                 { "minutes", USEC_PER_MINUTE },
601                 { "minute", USEC_PER_MINUTE },
602                 { "min", USEC_PER_MINUTE },
603                 { "months", USEC_PER_MONTH },
604                 { "month", USEC_PER_MONTH },
605                 { "msec", USEC_PER_MSEC },
606                 { "ms", USEC_PER_MSEC },
607                 { "m", USEC_PER_MINUTE },
608                 { "hours", USEC_PER_HOUR },
609                 { "hour", USEC_PER_HOUR },
610                 { "hr", USEC_PER_HOUR },
611                 { "h", USEC_PER_HOUR },
612                 { "days", USEC_PER_DAY },
613                 { "day", USEC_PER_DAY },
614                 { "d", USEC_PER_DAY },
615                 { "weeks", USEC_PER_WEEK },
616                 { "week", USEC_PER_WEEK },
617                 { "w", USEC_PER_WEEK },
618                 { "years", USEC_PER_YEAR },
619                 { "year", USEC_PER_YEAR },
620                 { "y", USEC_PER_YEAR },
621                 { "usec", 1ULL },
622                 { "us", 1ULL },
623                 { "", USEC_PER_SEC }, /* default is sec */
624         };
625
626         const char *p;
627         usec_t r = 0;
628         bool something = false;
629
630         assert(t);
631         assert(usec);
632
633         p = t;
634         for (;;) {
635                 long long l, z = 0;
636                 char *e;
637                 unsigned i, n = 0;
638
639                 p += strspn(p, WHITESPACE);
640
641                 if (*p == 0) {
642                         if (!something)
643                                 return -EINVAL;
644
645                         break;
646                 }
647
648                 errno = 0;
649                 l = strtoll(p, &e, 10);
650
651                 if (errno > 0)
652                         return -errno;
653
654                 if (l < 0)
655                         return -ERANGE;
656
657                 if (*e == '.') {
658                         char *b = e + 1;
659
660                         errno = 0;
661                         z = strtoll(b, &e, 10);
662                         if (errno > 0)
663                                 return -errno;
664
665                         if (z < 0)
666                                 return -ERANGE;
667
668                         if (e == b)
669                                 return -EINVAL;
670
671                         n = e - b;
672
673                 } else if (e == p)
674                         return -EINVAL;
675
676                 e += strspn(e, WHITESPACE);
677
678                 for (i = 0; i < ELEMENTSOF(table); i++)
679                         if (startswith(e, table[i].suffix)) {
680                                 usec_t k = (usec_t) z * table[i].usec;
681
682                                 for (; n > 0; n--)
683                                         k /= 10;
684
685                                 r += (usec_t) l * table[i].usec + k;
686                                 p = e + strlen(table[i].suffix);
687
688                                 something = true;
689                                 break;
690                         }
691
692                 if (i >= ELEMENTSOF(table))
693                         return -EINVAL;
694
695         }
696
697         *usec = r;
698
699         return 0;
700 }
701
702 int parse_nsec(const char *t, nsec_t *nsec) {
703         static const struct {
704                 const char *suffix;
705                 nsec_t nsec;
706         } table[] = {
707                 { "seconds", NSEC_PER_SEC },
708                 { "second", NSEC_PER_SEC },
709                 { "sec", NSEC_PER_SEC },
710                 { "s", NSEC_PER_SEC },
711                 { "minutes", NSEC_PER_MINUTE },
712                 { "minute", NSEC_PER_MINUTE },
713                 { "min", NSEC_PER_MINUTE },
714                 { "months", NSEC_PER_MONTH },
715                 { "month", NSEC_PER_MONTH },
716                 { "msec", NSEC_PER_MSEC },
717                 { "ms", NSEC_PER_MSEC },
718                 { "m", NSEC_PER_MINUTE },
719                 { "hours", NSEC_PER_HOUR },
720                 { "hour", NSEC_PER_HOUR },
721                 { "hr", NSEC_PER_HOUR },
722                 { "h", NSEC_PER_HOUR },
723                 { "days", NSEC_PER_DAY },
724                 { "day", NSEC_PER_DAY },
725                 { "d", NSEC_PER_DAY },
726                 { "weeks", NSEC_PER_WEEK },
727                 { "week", NSEC_PER_WEEK },
728                 { "w", NSEC_PER_WEEK },
729                 { "years", NSEC_PER_YEAR },
730                 { "year", NSEC_PER_YEAR },
731                 { "y", NSEC_PER_YEAR },
732                 { "usec", NSEC_PER_USEC },
733                 { "us", NSEC_PER_USEC },
734                 { "nsec", 1ULL },
735                 { "ns", 1ULL },
736                 { "", 1ULL }, /* default is nsec */
737         };
738
739         const char *p;
740         nsec_t r = 0;
741         bool something = false;
742
743         assert(t);
744         assert(nsec);
745
746         p = t;
747         for (;;) {
748                 long long l, z = 0;
749                 char *e;
750                 unsigned i, n = 0;
751
752                 p += strspn(p, WHITESPACE);
753
754                 if (*p == 0) {
755                         if (!something)
756                                 return -EINVAL;
757
758                         break;
759                 }
760
761                 errno = 0;
762                 l = strtoll(p, &e, 10);
763
764                 if (errno > 0)
765                         return -errno;
766
767                 if (l < 0)
768                         return -ERANGE;
769
770                 if (*e == '.') {
771                         char *b = e + 1;
772
773                         errno = 0;
774                         z = strtoll(b, &e, 10);
775                         if (errno > 0)
776                                 return -errno;
777
778                         if (z < 0)
779                                 return -ERANGE;
780
781                         if (e == b)
782                                 return -EINVAL;
783
784                         n = e - b;
785
786                 } else if (e == p)
787                         return -EINVAL;
788
789                 e += strspn(e, WHITESPACE);
790
791                 for (i = 0; i < ELEMENTSOF(table); i++)
792                         if (startswith(e, table[i].suffix)) {
793                                 nsec_t k = (nsec_t) z * table[i].nsec;
794
795                                 for (; n > 0; n--)
796                                         k /= 10;
797
798                                 r += (nsec_t) l * table[i].nsec + k;
799                                 p = e + strlen(table[i].suffix);
800
801                                 something = true;
802                                 break;
803                         }
804
805                 if (i >= ELEMENTSOF(table))
806                         return -EINVAL;
807
808         }
809
810         *nsec = r;
811
812         return 0;
813 }
814
815 bool ntp_synced(void) {
816         struct timex txc = {};
817
818         if (adjtimex(&txc) < 0)
819                 return false;
820
821         if (txc.status & STA_UNSYNC)
822                 return false;
823
824         return true;
825 }