]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
build-sys: add sd_j_open_container manpage alias
[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
966204e0 161 if (t <= 0 || t == (usec_t) -1)
9a98c7a1
LP
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
966204e0 179 if (t <= 0 || t == (usec_t) -1)
f02d8367
ZJS
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) {
1fcf71f5 195 const char *s;
9a98c7a1
LP
196 usec_t n, d;
197
198 n = now(CLOCK_REALTIME);
199
1fcf71f5 200 if (t <= 0 || (t == (usec_t) -1))
9a98c7a1
LP
201 return NULL;
202
1fcf71f5
LP
203 if (n > t) {
204 d = n - t;
205 s = "ago";
206 } else {
207 d = t - n;
208 s = "left";
209 }
9a98c7a1
LP
210
211 if (d >= USEC_PER_YEAR)
1fcf71f5 212 snprintf(buf, l, "%llu years %llu months %s",
9a98c7a1 213 (unsigned long long) (d / USEC_PER_YEAR),
1fcf71f5 214 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH), s);
9a98c7a1 215 else if (d >= USEC_PER_MONTH)
1fcf71f5 216 snprintf(buf, l, "%llu months %llu days %s",
9a98c7a1 217 (unsigned long long) (d / USEC_PER_MONTH),
1fcf71f5 218 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY), s);
9a98c7a1 219 else if (d >= USEC_PER_WEEK)
1fcf71f5 220 snprintf(buf, l, "%llu weeks %llu days %s",
9a98c7a1 221 (unsigned long long) (d / USEC_PER_WEEK),
1fcf71f5 222 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY), s);
9a98c7a1 223 else if (d >= 2*USEC_PER_DAY)
1fcf71f5 224 snprintf(buf, l, "%llu days %s", (unsigned long long) (d / USEC_PER_DAY), s);
9a98c7a1 225 else if (d >= 25*USEC_PER_HOUR)
1fcf71f5
LP
226 snprintf(buf, l, "1 day %lluh %s",
227 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR), s);
9a98c7a1 228 else if (d >= 6*USEC_PER_HOUR)
1fcf71f5
LP
229 snprintf(buf, l, "%lluh %s",
230 (unsigned long long) (d / USEC_PER_HOUR), s);
9a98c7a1 231 else if (d >= USEC_PER_HOUR)
1fcf71f5 232 snprintf(buf, l, "%lluh %llumin %s",
9a98c7a1 233 (unsigned long long) (d / USEC_PER_HOUR),
1fcf71f5 234 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE), s);
9a98c7a1 235 else if (d >= 5*USEC_PER_MINUTE)
1fcf71f5
LP
236 snprintf(buf, l, "%llumin %s",
237 (unsigned long long) (d / USEC_PER_MINUTE), s);
9a98c7a1 238 else if (d >= USEC_PER_MINUTE)
1fcf71f5 239 snprintf(buf, l, "%llumin %llus %s",
9a98c7a1 240 (unsigned long long) (d / USEC_PER_MINUTE),
1fcf71f5 241 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC), s);
9a98c7a1 242 else if (d >= USEC_PER_SEC)
1fcf71f5
LP
243 snprintf(buf, l, "%llus %s",
244 (unsigned long long) (d / USEC_PER_SEC), s);
9a98c7a1 245 else if (d >= USEC_PER_MSEC)
1fcf71f5
LP
246 snprintf(buf, l, "%llums %s",
247 (unsigned long long) (d / USEC_PER_MSEC), s);
9a98c7a1 248 else if (d > 0)
1fcf71f5
LP
249 snprintf(buf, l, "%lluus %s",
250 (unsigned long long) d, s);
9a98c7a1
LP
251 else
252 snprintf(buf, l, "now");
253
254 buf[l-1] = 0;
255 return buf;
256}
257
2fa4092c 258char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
259 static const struct {
260 const char *suffix;
261 usec_t usec;
262 } table[] = {
b719810d
LP
263 { "y", USEC_PER_YEAR },
264 { "month", USEC_PER_MONTH },
9a98c7a1
LP
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;
2fa4092c 276 bool something = false;
9a98c7a1
LP
277
278 assert(buf);
279 assert(l > 0);
280
281 if (t == (usec_t) -1)
282 return NULL;
283
7c537b2e
LP
284 if (t <= 0) {
285 snprintf(p, l, "0");
286 p[l-1] = 0;
287 return p;
288 }
289
7f602784 290 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
291
292 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 293 int k = 0;
9a98c7a1 294 size_t n;
2fa4092c
LP
295 bool done = false;
296 usec_t a, b;
297
7c537b2e
LP
298 if (t <= 0)
299 break;
2fa4092c 300
7c537b2e 301 if (t < accuracy && something)
2fa4092c 302 break;
9a98c7a1
LP
303
304 if (t < table[i].usec)
305 continue;
306
307 if (l <= 1)
308 break;
309
2fa4092c
LP
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
9a98c7a1
LP
352 n = MIN((size_t) k, l);
353
354 l -= n;
355 p += n;
356
2fa4092c 357 something = true;
9a98c7a1
LP
358 }
359
360 *p = 0;
361
362 return buf;
363}
364
365void 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
380void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
381 unsigned long long a, b;
382
383 assert(value);
384 assert(t);
385
b5dd8148 386 if (sscanf(value, "%llu %llu", &a, &b) != 2)
9a98c7a1
LP
387 log_debug("Failed to parse finish timestamp value %s", value);
388 else {
389 t->realtime = a;
390 t->monotonic = b;
391 }
392}
393
394int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
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
9a98c7a1
LP
415 const char *k;
416 struct tm tm, copy;
417 time_t x;
418 usec_t plus = 0, minus = 0, ret;
92134489
LP
419 int r, weekday = -1;
420 unsigned i;
9a98c7a1
LP
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));
6a741b4a 444 tm.tm_isdst = -1;
9a98c7a1
LP
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
7f602784 465 r = parse_sec(t+1, &plus);
9a98c7a1
LP
466 if (r < 0)
467 return r;
468
469 goto finish;
470 } else if (t[0] == '-') {
471
7f602784 472 r = parse_sec(t+1, &minus);
9a98c7a1
LP
473 if (r < 0)
474 return r;
475
476 goto finish;
decad910
LP
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
7f602784 485 r = parse_sec(z, &minus);
decad910
LP
486 if (r < 0)
487 return r;
488
1fcf71f5
LP
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
decad910 501 goto finish;
9a98c7a1
LP
502 }
503
92134489
LP
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
9a98c7a1
LP
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
571finish:
572 x = mktime(&tm);
573 if (x == (time_t) -1)
574 return -EINVAL;
575
92134489
LP
576 if (weekday >= 0 && tm.tm_wday != weekday)
577 return -EINVAL;
578
9a98c7a1
LP
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
7f602784 592int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
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;
cb0dac05 629 bool something = false;
9a98c7a1
LP
630
631 assert(t);
632 assert(usec);
633
634 p = t;
cb0dac05
LP
635 for (;;) {
636 long long l, z = 0;
9a98c7a1 637 char *e;
cb0dac05
LP
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 }
9a98c7a1
LP
648
649 errno = 0;
650 l = strtoll(p, &e, 10);
651
8333c77e 652 if (errno > 0)
9a98c7a1
LP
653 return -errno;
654
655 if (l < 0)
656 return -ERANGE;
657
cb0dac05
LP
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)
9a98c7a1
LP
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)) {
cb0dac05
LP
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;
9a98c7a1 687 p = e + strlen(table[i].suffix);
cb0dac05
LP
688
689 something = true;
9a98c7a1
LP
690 break;
691 }
692
693 if (i >= ELEMENTSOF(table))
694 return -EINVAL;
695
cb0dac05 696 }
9a98c7a1
LP
697
698 *usec = r;
699
700 return 0;
701}
702
703int 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;
cb0dac05 742 bool something = false;
9a98c7a1
LP
743
744 assert(t);
745 assert(nsec);
746
747 p = t;
cb0dac05
LP
748 for (;;) {
749 long long l, z = 0;
9a98c7a1 750 char *e;
cb0dac05
LP
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 }
9a98c7a1
LP
761
762 errno = 0;
763 l = strtoll(p, &e, 10);
764
8333c77e 765 if (errno > 0)
9a98c7a1
LP
766 return -errno;
767
768 if (l < 0)
769 return -ERANGE;
770
cb0dac05
LP
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)
9a98c7a1
LP
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)) {
cb0dac05
LP
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;
9a98c7a1 800 p = e + strlen(table[i].suffix);
cb0dac05
LP
801
802 something = true;
9a98c7a1
LP
803 break;
804 }
805
806 if (i >= ELEMENTSOF(table))
807 return -EINVAL;
808
cb0dac05 809 }
9a98c7a1
LP
810
811 *nsec = r;
812
813 return 0;
814}
03cc26dd
LP
815
816bool 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}