]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/time-util.c
Make systemctl --root look for files in the proper places
[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;
4d89874a
ZJS
145 } else {
146 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
147 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
148 }
149
9a98c7a1
LP
150 return tv;
151}
152
153char *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
966204e0 160 if (t <= 0 || t == (usec_t) -1)
9a98c7a1
LP
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
f02d8367
ZJS
171char *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
966204e0 178 if (t <= 0 || t == (usec_t) -1)
f02d8367
ZJS
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
bbb8486e 193char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 194 const char *s;
9a98c7a1
LP
195 usec_t n, d;
196
197 n = now(CLOCK_REALTIME);
198
1fcf71f5 199 if (t <= 0 || (t == (usec_t) -1))
9a98c7a1
LP
200 return NULL;
201
1fcf71f5
LP
202 if (n > t) {
203 d = n - t;
204 s = "ago";
205 } else {
206 d = t - n;
207 s = "left";
208 }
9a98c7a1
LP
209
210 if (d >= USEC_PER_YEAR)
1fcf71f5 211 snprintf(buf, l, "%llu years %llu months %s",
9a98c7a1 212 (unsigned long long) (d / USEC_PER_YEAR),
1fcf71f5 213 (unsigned long long) ((d % USEC_PER_YEAR) / USEC_PER_MONTH), s);
9a98c7a1 214 else if (d >= USEC_PER_MONTH)
1fcf71f5 215 snprintf(buf, l, "%llu months %llu days %s",
9a98c7a1 216 (unsigned long long) (d / USEC_PER_MONTH),
1fcf71f5 217 (unsigned long long) ((d % USEC_PER_MONTH) / USEC_PER_DAY), s);
9a98c7a1 218 else if (d >= USEC_PER_WEEK)
1fcf71f5 219 snprintf(buf, l, "%llu weeks %llu days %s",
9a98c7a1 220 (unsigned long long) (d / USEC_PER_WEEK),
1fcf71f5 221 (unsigned long long) ((d % USEC_PER_WEEK) / USEC_PER_DAY), s);
9a98c7a1 222 else if (d >= 2*USEC_PER_DAY)
1fcf71f5 223 snprintf(buf, l, "%llu days %s", (unsigned long long) (d / USEC_PER_DAY), s);
9a98c7a1 224 else if (d >= 25*USEC_PER_HOUR)
1fcf71f5
LP
225 snprintf(buf, l, "1 day %lluh %s",
226 (unsigned long long) ((d - USEC_PER_DAY) / USEC_PER_HOUR), s);
9a98c7a1 227 else if (d >= 6*USEC_PER_HOUR)
1fcf71f5
LP
228 snprintf(buf, l, "%lluh %s",
229 (unsigned long long) (d / USEC_PER_HOUR), s);
9a98c7a1 230 else if (d >= USEC_PER_HOUR)
1fcf71f5 231 snprintf(buf, l, "%lluh %llumin %s",
9a98c7a1 232 (unsigned long long) (d / USEC_PER_HOUR),
1fcf71f5 233 (unsigned long long) ((d % USEC_PER_HOUR) / USEC_PER_MINUTE), s);
9a98c7a1 234 else if (d >= 5*USEC_PER_MINUTE)
1fcf71f5
LP
235 snprintf(buf, l, "%llumin %s",
236 (unsigned long long) (d / USEC_PER_MINUTE), s);
9a98c7a1 237 else if (d >= USEC_PER_MINUTE)
1fcf71f5 238 snprintf(buf, l, "%llumin %llus %s",
9a98c7a1 239 (unsigned long long) (d / USEC_PER_MINUTE),
1fcf71f5 240 (unsigned long long) ((d % USEC_PER_MINUTE) / USEC_PER_SEC), s);
9a98c7a1 241 else if (d >= USEC_PER_SEC)
1fcf71f5
LP
242 snprintf(buf, l, "%llus %s",
243 (unsigned long long) (d / USEC_PER_SEC), s);
9a98c7a1 244 else if (d >= USEC_PER_MSEC)
1fcf71f5
LP
245 snprintf(buf, l, "%llums %s",
246 (unsigned long long) (d / USEC_PER_MSEC), s);
9a98c7a1 247 else if (d > 0)
1fcf71f5
LP
248 snprintf(buf, l, "%lluus %s",
249 (unsigned long long) d, s);
9a98c7a1
LP
250 else
251 snprintf(buf, l, "now");
252
253 buf[l-1] = 0;
254 return buf;
255}
256
2fa4092c 257char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
258 static const struct {
259 const char *suffix;
260 usec_t usec;
261 } table[] = {
b719810d
LP
262 { "y", USEC_PER_YEAR },
263 { "month", USEC_PER_MONTH },
9a98c7a1
LP
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;
2fa4092c 275 bool something = false;
9a98c7a1
LP
276
277 assert(buf);
278 assert(l > 0);
279
280 if (t == (usec_t) -1)
281 return NULL;
282
7c537b2e
LP
283 if (t <= 0) {
284 snprintf(p, l, "0");
285 p[l-1] = 0;
286 return p;
287 }
288
7f602784 289 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
290
291 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 292 int k = 0;
9a98c7a1 293 size_t n;
2fa4092c
LP
294 bool done = false;
295 usec_t a, b;
296
7c537b2e
LP
297 if (t <= 0)
298 break;
2fa4092c 299
7c537b2e 300 if (t < accuracy && something)
2fa4092c 301 break;
9a98c7a1
LP
302
303 if (t < table[i].usec)
304 continue;
305
306 if (l <= 1)
307 break;
308
2fa4092c
LP
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
9a98c7a1
LP
351 n = MIN((size_t) k, l);
352
353 l -= n;
354 p += n;
355
2fa4092c 356 something = true;
9a98c7a1
LP
357 }
358
359 *p = 0;
360
361 return buf;
362}
363
364void 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
379void dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
380 unsigned long long a, b;
381
382 assert(value);
383 assert(t);
384
b5dd8148 385 if (sscanf(value, "%llu %llu", &a, &b) != 2)
9a98c7a1
LP
386 log_debug("Failed to parse finish timestamp value %s", value);
387 else {
388 t->realtime = a;
389 t->monotonic = b;
390 }
391}
392
393int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
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
9a98c7a1
LP
414 const char *k;
415 struct tm tm, copy;
416 time_t x;
417 usec_t plus = 0, minus = 0, ret;
92134489
LP
418 int r, weekday = -1;
419 unsigned i;
9a98c7a1
LP
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
5ba6e094 435 * @2147483647 (seconds since epoch)
9a98c7a1
LP
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] == '+') {
7f602784 464 r = parse_sec(t+1, &plus);
9a98c7a1
LP
465 if (r < 0)
466 return r;
467
468 goto finish;
9a98c7a1 469
5ba6e094 470 } else if (t[0] == '-') {
7f602784 471 r = parse_sec(t+1, &minus);
9a98c7a1
LP
472 if (r < 0)
473 return r;
474
475 goto finish;
decad910 476
5ba6e094
LP
477 } else if (t[0] == '@')
478 return parse_sec(t + 1, usec);
479
480 else if (endswith(t, " ago")) {
decad910
LP
481 _cleanup_free_ char *z;
482
483 z = strndup(t, strlen(t) - 4);
484 if (!z)
485 return -ENOMEM;
486
7f602784 487 r = parse_sec(z, &minus);
decad910
LP
488 if (r < 0)
489 return r;
490
1fcf71f5
LP
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
decad910 503 goto finish;
9a98c7a1
LP
504 }
505
92134489
LP
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
9a98c7a1
LP
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
573finish:
574 x = mktime(&tm);
575 if (x == (time_t) -1)
576 return -EINVAL;
577
92134489
LP
578 if (weekday >= 0 && tm.tm_wday != weekday)
579 return -EINVAL;
580
9a98c7a1
LP
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
7f602784 594int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
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;
cb0dac05 631 bool something = false;
9a98c7a1
LP
632
633 assert(t);
634 assert(usec);
635
636 p = t;
cb0dac05
LP
637 for (;;) {
638 long long l, z = 0;
9a98c7a1 639 char *e;
cb0dac05
LP
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 }
9a98c7a1
LP
650
651 errno = 0;
652 l = strtoll(p, &e, 10);
653
8333c77e 654 if (errno > 0)
9a98c7a1
LP
655 return -errno;
656
657 if (l < 0)
658 return -ERANGE;
659
cb0dac05
LP
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)
9a98c7a1
LP
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)) {
cb0dac05
LP
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;
9a98c7a1 689 p = e + strlen(table[i].suffix);
cb0dac05
LP
690
691 something = true;
9a98c7a1
LP
692 break;
693 }
694
695 if (i >= ELEMENTSOF(table))
696 return -EINVAL;
697
cb0dac05 698 }
9a98c7a1
LP
699
700 *usec = r;
701
702 return 0;
703}
704
705int 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;
cb0dac05 744 bool something = false;
9a98c7a1
LP
745
746 assert(t);
747 assert(nsec);
748
749 p = t;
cb0dac05
LP
750 for (;;) {
751 long long l, z = 0;
9a98c7a1 752 char *e;
cb0dac05
LP
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 }
9a98c7a1
LP
763
764 errno = 0;
765 l = strtoll(p, &e, 10);
766
8333c77e 767 if (errno > 0)
9a98c7a1
LP
768 return -errno;
769
770 if (l < 0)
771 return -ERANGE;
772
cb0dac05
LP
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)
9a98c7a1
LP
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)) {
cb0dac05
LP
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;
9a98c7a1 802 p = e + strlen(table[i].suffix);
cb0dac05
LP
803
804 something = true;
9a98c7a1
LP
805 break;
806 }
807
808 if (i >= ELEMENTSOF(table))
809 return -EINVAL;
810
cb0dac05 811 }
9a98c7a1
LP
812
813 *nsec = r;
814
815 return 0;
816}
03cc26dd
LP
817
818bool 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}