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