]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/time-util.c
core: add unit_dbus_interface_from_type() to unit-name.h
[thirdparty/systemd.git] / src / basic / 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>
77ff2de9 25#include <sys/timerfd.h>
9a98c7a1
LP
26
27#include "util.h"
28#include "time-util.h"
75683450 29#include "strv.h"
9a98c7a1
LP
30
31usec_t now(clockid_t clock_id) {
32 struct timespec ts;
33
34 assert_se(clock_gettime(clock_id, &ts) == 0);
35
36 return timespec_load(&ts);
37}
38
39dual_timestamp* dual_timestamp_get(dual_timestamp *ts) {
40 assert(ts);
41
42 ts->realtime = now(CLOCK_REALTIME);
43 ts->monotonic = now(CLOCK_MONOTONIC);
44
45 return ts;
46}
47
48dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
49 int64_t delta;
50 assert(ts);
51
75a5f1d8
LP
52 if (u == USEC_INFINITY || u <= 0) {
53 ts->realtime = ts->monotonic = u;
cae0c5e0
LP
54 return ts;
55 }
56
9a98c7a1
LP
57 ts->realtime = u;
58
75a5f1d8
LP
59 delta = (int64_t) now(CLOCK_REALTIME) - (int64_t) u;
60 ts->monotonic = now(CLOCK_MONOTONIC);
9a98c7a1 61
75a5f1d8
LP
62 if ((int64_t) ts->monotonic > delta)
63 ts->monotonic -= delta;
64 else
65 ts->monotonic = 0;
9a98c7a1
LP
66
67 return ts;
68}
69
cae0c5e0
LP
70dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
71 int64_t delta;
72 assert(ts);
73
3a43da28
KS
74 if (u == USEC_INFINITY) {
75 ts->realtime = ts->monotonic = USEC_INFINITY;
cae0c5e0
LP
76 return ts;
77 }
78
79 ts->monotonic = u;
80 delta = (int64_t) now(CLOCK_MONOTONIC) - (int64_t) u;
81
82 ts->realtime = now(CLOCK_REALTIME);
83 if ((int64_t) ts->realtime > delta)
84 ts->realtime -= delta;
85 else
86 ts->realtime = 0;
87
88 return ts;
89}
90
fbe55073
LP
91dual_timestamp* dual_timestamp_from_boottime_or_monotonic(dual_timestamp *ts, usec_t u) {
92 int64_t delta;
93
94 if (u == USEC_INFINITY) {
95 ts->realtime = ts->monotonic = USEC_INFINITY;
96 return ts;
97 }
98 ts->realtime = now(CLOCK_REALTIME);
99 ts->monotonic = now(CLOCK_MONOTONIC);
100
101 delta = (int64_t) now(clock_boottime_or_monotonic()) - (int64_t) u;
102
103 if ((int64_t) ts->realtime > delta)
104 ts->realtime -= delta;
105 else
106 ts->realtime = 0;
107
108 if ((int64_t) ts->monotonic > delta)
109 ts->monotonic -= delta;
110 else
111 ts->monotonic = 0;
112
113 return ts;
114}
115
116
9a98c7a1
LP
117usec_t timespec_load(const struct timespec *ts) {
118 assert(ts);
119
120 if (ts->tv_sec == (time_t) -1 &&
121 ts->tv_nsec == (long) -1)
3a43da28 122 return USEC_INFINITY;
9a98c7a1
LP
123
124 if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
3a43da28 125 return USEC_INFINITY;
9a98c7a1
LP
126
127 return
128 (usec_t) ts->tv_sec * USEC_PER_SEC +
129 (usec_t) ts->tv_nsec / NSEC_PER_USEC;
130}
131
132struct timespec *timespec_store(struct timespec *ts, usec_t u) {
133 assert(ts);
134
3a43da28 135 if (u == USEC_INFINITY) {
9a98c7a1
LP
136 ts->tv_sec = (time_t) -1;
137 ts->tv_nsec = (long) -1;
138 return ts;
139 }
140
141 ts->tv_sec = (time_t) (u / USEC_PER_SEC);
142 ts->tv_nsec = (long int) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
143
144 return ts;
145}
146
147usec_t timeval_load(const struct timeval *tv) {
148 assert(tv);
149
150 if (tv->tv_sec == (time_t) -1 &&
151 tv->tv_usec == (suseconds_t) -1)
3a43da28 152 return USEC_INFINITY;
9a98c7a1
LP
153
154 if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
3a43da28 155 return USEC_INFINITY;
9a98c7a1
LP
156
157 return
158 (usec_t) tv->tv_sec * USEC_PER_SEC +
159 (usec_t) tv->tv_usec;
160}
161
162struct timeval *timeval_store(struct timeval *tv, usec_t u) {
163 assert(tv);
164
3a43da28 165 if (u == USEC_INFINITY) {
9a98c7a1
LP
166 tv->tv_sec = (time_t) -1;
167 tv->tv_usec = (suseconds_t) -1;
4d89874a
ZJS
168 } else {
169 tv->tv_sec = (time_t) (u / USEC_PER_SEC);
170 tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
9a98c7a1
LP
171 }
172
9a98c7a1
LP
173 return tv;
174}
175
5ab99e07 176static char *format_timestamp_internal(char *buf, size_t l, usec_t t, bool utc) {
9a98c7a1
LP
177 struct tm tm;
178 time_t sec;
179
180 assert(buf);
181 assert(l > 0);
182
3a43da28 183 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
184 return NULL;
185
186 sec = (time_t) (t / USEC_PER_SEC);
187
a62e83b4
JS
188 if (utc)
189 gmtime_r(&sec, &tm);
190 else
191 localtime_r(&sec, &tm);
192 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S %Z", &tm) <= 0)
9a98c7a1
LP
193 return NULL;
194
195 return buf;
196}
197
a62e83b4
JS
198char *format_timestamp(char *buf, size_t l, usec_t t) {
199 return format_timestamp_internal(buf, l, t, false);
200}
201
5ab99e07
LP
202char *format_timestamp_utc(char *buf, size_t l, usec_t t) {
203 return format_timestamp_internal(buf, l, t, true);
204}
205
206static char *format_timestamp_internal_us(char *buf, size_t l, usec_t t, bool utc) {
f02d8367
ZJS
207 struct tm tm;
208 time_t sec;
209
210 assert(buf);
211 assert(l > 0);
212
3a43da28 213 if (t <= 0 || t == USEC_INFINITY)
f02d8367
ZJS
214 return NULL;
215
216 sec = (time_t) (t / USEC_PER_SEC);
a62e83b4
JS
217 if (utc)
218 gmtime_r(&sec, &tm);
219 else
220 localtime_r(&sec, &tm);
f02d8367
ZJS
221
222 if (strftime(buf, l, "%a %Y-%m-%d %H:%M:%S", &tm) <= 0)
223 return NULL;
609e002e 224 snprintf(buf + strlen(buf), l - strlen(buf), ".%06llu", (unsigned long long) (t % USEC_PER_SEC));
f02d8367
ZJS
225 if (strftime(buf + strlen(buf), l - strlen(buf), " %Z", &tm) <= 0)
226 return NULL;
227
228 return buf;
229}
230
5ab99e07
LP
231char *format_timestamp_us(char *buf, size_t l, usec_t t) {
232 return format_timestamp_internal_us(buf, l, t, false);
233}
234
235char *format_timestamp_us_utc(char *buf, size_t l, usec_t t) {
236 return format_timestamp_internal_us(buf, l, t, true);
237}
238
bbb8486e 239char *format_timestamp_relative(char *buf, size_t l, usec_t t) {
1fcf71f5 240 const char *s;
9a98c7a1
LP
241 usec_t n, d;
242
65de0395 243 if (t <= 0 || t == USEC_INFINITY)
9a98c7a1
LP
244 return NULL;
245
65de0395 246 n = now(CLOCK_REALTIME);
1fcf71f5
LP
247 if (n > t) {
248 d = n - t;
249 s = "ago";
250 } else {
251 d = t - n;
252 s = "left";
253 }
9a98c7a1
LP
254
255 if (d >= USEC_PER_YEAR)
609e002e 256 snprintf(buf, l, USEC_FMT " years " USEC_FMT " months %s",
de0671ee
ZJS
257 d / USEC_PER_YEAR,
258 (d % USEC_PER_YEAR) / USEC_PER_MONTH, s);
9a98c7a1 259 else if (d >= USEC_PER_MONTH)
609e002e 260 snprintf(buf, l, USEC_FMT " months " USEC_FMT " days %s",
de0671ee
ZJS
261 d / USEC_PER_MONTH,
262 (d % USEC_PER_MONTH) / USEC_PER_DAY, s);
9a98c7a1 263 else if (d >= USEC_PER_WEEK)
609e002e 264 snprintf(buf, l, USEC_FMT " weeks " USEC_FMT " days %s",
de0671ee
ZJS
265 d / USEC_PER_WEEK,
266 (d % USEC_PER_WEEK) / USEC_PER_DAY, s);
9a98c7a1 267 else if (d >= 2*USEC_PER_DAY)
609e002e 268 snprintf(buf, l, USEC_FMT " days %s", d / USEC_PER_DAY, s);
9a98c7a1 269 else if (d >= 25*USEC_PER_HOUR)
609e002e 270 snprintf(buf, l, "1 day " USEC_FMT "h %s",
de0671ee 271 (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
9a98c7a1 272 else if (d >= 6*USEC_PER_HOUR)
609e002e 273 snprintf(buf, l, USEC_FMT "h %s",
de0671ee 274 d / USEC_PER_HOUR, s);
9a98c7a1 275 else if (d >= USEC_PER_HOUR)
609e002e 276 snprintf(buf, l, USEC_FMT "h " USEC_FMT "min %s",
de0671ee
ZJS
277 d / USEC_PER_HOUR,
278 (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
9a98c7a1 279 else if (d >= 5*USEC_PER_MINUTE)
609e002e 280 snprintf(buf, l, USEC_FMT "min %s",
de0671ee 281 d / USEC_PER_MINUTE, s);
9a98c7a1 282 else if (d >= USEC_PER_MINUTE)
609e002e 283 snprintf(buf, l, USEC_FMT "min " USEC_FMT "s %s",
de0671ee
ZJS
284 d / USEC_PER_MINUTE,
285 (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
9a98c7a1 286 else if (d >= USEC_PER_SEC)
609e002e 287 snprintf(buf, l, USEC_FMT "s %s",
de0671ee 288 d / USEC_PER_SEC, s);
9a98c7a1 289 else if (d >= USEC_PER_MSEC)
609e002e 290 snprintf(buf, l, USEC_FMT "ms %s",
de0671ee 291 d / USEC_PER_MSEC, s);
9a98c7a1 292 else if (d > 0)
de0671ee
ZJS
293 snprintf(buf, l, USEC_FMT"us %s",
294 d, s);
9a98c7a1
LP
295 else
296 snprintf(buf, l, "now");
297
298 buf[l-1] = 0;
299 return buf;
300}
301
2fa4092c 302char *format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
9a98c7a1
LP
303 static const struct {
304 const char *suffix;
305 usec_t usec;
306 } table[] = {
b719810d
LP
307 { "y", USEC_PER_YEAR },
308 { "month", USEC_PER_MONTH },
9a98c7a1
LP
309 { "w", USEC_PER_WEEK },
310 { "d", USEC_PER_DAY },
311 { "h", USEC_PER_HOUR },
312 { "min", USEC_PER_MINUTE },
313 { "s", USEC_PER_SEC },
314 { "ms", USEC_PER_MSEC },
315 { "us", 1 },
316 };
317
318 unsigned i;
319 char *p = buf;
2fa4092c 320 bool something = false;
9a98c7a1
LP
321
322 assert(buf);
323 assert(l > 0);
324
bb1fada8
LP
325 if (t == USEC_INFINITY) {
326 strncpy(p, "infinity", l-1);
327 p[l-1] = 0;
328 return p;
329 }
330
331 if (t <= 0) {
332 strncpy(p, "0", l-1);
7c537b2e
LP
333 p[l-1] = 0;
334 return p;
335 }
336
7f602784 337 /* The result of this function can be parsed with parse_sec */
9a98c7a1
LP
338
339 for (i = 0; i < ELEMENTSOF(table); i++) {
7759ecb2 340 int k = 0;
9a98c7a1 341 size_t n;
2fa4092c
LP
342 bool done = false;
343 usec_t a, b;
344
7c537b2e
LP
345 if (t <= 0)
346 break;
2fa4092c 347
7c537b2e 348 if (t < accuracy && something)
2fa4092c 349 break;
9a98c7a1
LP
350
351 if (t < table[i].usec)
352 continue;
353
354 if (l <= 1)
355 break;
356
2fa4092c
LP
357 a = t / table[i].usec;
358 b = t % table[i].usec;
359
360 /* Let's see if we should shows this in dot notation */
361 if (t < USEC_PER_MINUTE && b > 0) {
362 usec_t cc;
363 int j;
364
365 j = 0;
366 for (cc = table[i].usec; cc > 1; cc /= 10)
367 j++;
368
369 for (cc = accuracy; cc > 1; cc /= 10) {
370 b /= 10;
371 j--;
372 }
373
374 if (j > 0) {
375 k = snprintf(p, l,
de0671ee 376 "%s"USEC_FMT".%0*llu%s",
2fa4092c 377 p > buf ? " " : "",
de0671ee 378 a,
2fa4092c
LP
379 j,
380 (unsigned long long) b,
381 table[i].suffix);
382
383 t = 0;
384 done = true;
385 }
386 }
387
388 /* No? Then let's show it normally */
389 if (!done) {
390 k = snprintf(p, l,
de0671ee 391 "%s"USEC_FMT"%s",
2fa4092c 392 p > buf ? " " : "",
de0671ee 393 a,
2fa4092c
LP
394 table[i].suffix);
395
396 t = b;
397 }
398
9a98c7a1
LP
399 n = MIN((size_t) k, l);
400
401 l -= n;
402 p += n;
403
2fa4092c 404 something = true;
9a98c7a1
LP
405 }
406
407 *p = 0;
408
409 return buf;
410}
411
412void dual_timestamp_serialize(FILE *f, const char *name, dual_timestamp *t) {
413
414 assert(f);
415 assert(name);
416 assert(t);
417
418 if (!dual_timestamp_is_set(t))
419 return;
420
de0671ee 421 fprintf(f, "%s="USEC_FMT" "USEC_FMT"\n",
9a98c7a1 422 name,
de0671ee
ZJS
423 t->realtime,
424 t->monotonic);
9a98c7a1
LP
425}
426
e911de99 427int dual_timestamp_deserialize(const char *value, dual_timestamp *t) {
9a98c7a1
LP
428 unsigned long long a, b;
429
430 assert(value);
431 assert(t);
432
e911de99
LP
433 if (sscanf(value, "%llu %llu", &a, &b) != 2) {
434 log_debug("Failed to parse finish timestamp value %s.", value);
435 return -EINVAL;
9a98c7a1 436 }
e911de99
LP
437
438 t->realtime = a;
439 t->monotonic = b;
440
441 return 0;
9a98c7a1
LP
442}
443
444int parse_timestamp(const char *t, usec_t *usec) {
92134489
LP
445 static const struct {
446 const char *name;
447 const int nr;
448 } day_nr[] = {
449 { "Sunday", 0 },
450 { "Sun", 0 },
451 { "Monday", 1 },
452 { "Mon", 1 },
453 { "Tuesday", 2 },
454 { "Tue", 2 },
455 { "Wednesday", 3 },
456 { "Wed", 3 },
457 { "Thursday", 4 },
458 { "Thu", 4 },
459 { "Friday", 5 },
460 { "Fri", 5 },
461 { "Saturday", 6 },
462 { "Sat", 6 },
463 };
464
9a98c7a1
LP
465 const char *k;
466 struct tm tm, copy;
467 time_t x;
468 usec_t plus = 0, minus = 0, ret;
92134489
LP
469 int r, weekday = -1;
470 unsigned i;
9a98c7a1
LP
471
472 /*
473 * Allowed syntaxes:
474 *
475 * 2012-09-22 16:34:22
476 * 2012-09-22 16:34 (seconds will be set to 0)
477 * 2012-09-22 (time will be set to 00:00:00)
478 * 16:34:22 (date will be set to today)
479 * 16:34 (date will be set to today, seconds to 0)
480 * now
481 * yesterday (time is set to 00:00:00)
482 * today (time is set to 00:00:00)
483 * tomorrow (time is set to 00:00:00)
484 * +5min
485 * -5days
5ba6e094 486 * @2147483647 (seconds since epoch)
9a98c7a1
LP
487 *
488 */
489
490 assert(t);
491 assert(usec);
492
493 x = time(NULL);
494 assert_se(localtime_r(&x, &tm));
6a741b4a 495 tm.tm_isdst = -1;
9a98c7a1
LP
496
497 if (streq(t, "now"))
498 goto finish;
499
500 else if (streq(t, "today")) {
501 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
502 goto finish;
503
504 } else if (streq(t, "yesterday")) {
505 tm.tm_mday --;
506 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
507 goto finish;
508
509 } else if (streq(t, "tomorrow")) {
510 tm.tm_mday ++;
511 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
512 goto finish;
513
514 } else if (t[0] == '+') {
7f602784 515 r = parse_sec(t+1, &plus);
9a98c7a1
LP
516 if (r < 0)
517 return r;
518
519 goto finish;
9a98c7a1 520
5ba6e094 521 } else if (t[0] == '-') {
7f602784 522 r = parse_sec(t+1, &minus);
9a98c7a1
LP
523 if (r < 0)
524 return r;
525
526 goto finish;
decad910 527
5ba6e094
LP
528 } else if (t[0] == '@')
529 return parse_sec(t + 1, usec);
530
531 else if (endswith(t, " ago")) {
decad910
LP
532 _cleanup_free_ char *z;
533
534 z = strndup(t, strlen(t) - 4);
535 if (!z)
536 return -ENOMEM;
537
7f602784 538 r = parse_sec(z, &minus);
decad910
LP
539 if (r < 0)
540 return r;
541
1fcf71f5
LP
542 goto finish;
543 } else if (endswith(t, " left")) {
544 _cleanup_free_ char *z;
545
546 z = strndup(t, strlen(t) - 4);
547 if (!z)
548 return -ENOMEM;
549
550 r = parse_sec(z, &plus);
551 if (r < 0)
552 return r;
553
decad910 554 goto finish;
9a98c7a1
LP
555 }
556
92134489
LP
557 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
558 size_t skip;
559
560 if (!startswith_no_case(t, day_nr[i].name))
561 continue;
562
563 skip = strlen(day_nr[i].name);
564 if (t[skip] != ' ')
565 continue;
566
567 weekday = day_nr[i].nr;
568 t += skip + 1;
569 break;
570 }
571
9a98c7a1
LP
572 copy = tm;
573 k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
574 if (k && *k == 0)
575 goto finish;
576
577 tm = copy;
578 k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
579 if (k && *k == 0)
580 goto finish;
581
582 tm = copy;
583 k = strptime(t, "%y-%m-%d %H:%M", &tm);
584 if (k && *k == 0) {
585 tm.tm_sec = 0;
586 goto finish;
587 }
588
589 tm = copy;
590 k = strptime(t, "%Y-%m-%d %H:%M", &tm);
591 if (k && *k == 0) {
592 tm.tm_sec = 0;
593 goto finish;
594 }
595
596 tm = copy;
597 k = strptime(t, "%y-%m-%d", &tm);
598 if (k && *k == 0) {
599 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
600 goto finish;
601 }
602
603 tm = copy;
604 k = strptime(t, "%Y-%m-%d", &tm);
605 if (k && *k == 0) {
606 tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
607 goto finish;
608 }
609
610 tm = copy;
611 k = strptime(t, "%H:%M:%S", &tm);
612 if (k && *k == 0)
613 goto finish;
614
615 tm = copy;
616 k = strptime(t, "%H:%M", &tm);
617 if (k && *k == 0) {
618 tm.tm_sec = 0;
619 goto finish;
620 }
621
622 return -EINVAL;
623
624finish:
625 x = mktime(&tm);
626 if (x == (time_t) -1)
627 return -EINVAL;
628
92134489
LP
629 if (weekday >= 0 && tm.tm_wday != weekday)
630 return -EINVAL;
631
9a98c7a1
LP
632 ret = (usec_t) x * USEC_PER_SEC;
633
634 ret += plus;
635 if (ret > minus)
636 ret -= minus;
637 else
638 ret = 0;
639
640 *usec = ret;
641
642 return 0;
643}
644
7f602784 645int parse_sec(const char *t, usec_t *usec) {
9a98c7a1
LP
646 static const struct {
647 const char *suffix;
648 usec_t usec;
649 } table[] = {
650 { "seconds", USEC_PER_SEC },
651 { "second", USEC_PER_SEC },
652 { "sec", USEC_PER_SEC },
653 { "s", USEC_PER_SEC },
654 { "minutes", USEC_PER_MINUTE },
655 { "minute", USEC_PER_MINUTE },
656 { "min", USEC_PER_MINUTE },
657 { "months", USEC_PER_MONTH },
658 { "month", USEC_PER_MONTH },
659 { "msec", USEC_PER_MSEC },
660 { "ms", USEC_PER_MSEC },
661 { "m", USEC_PER_MINUTE },
662 { "hours", USEC_PER_HOUR },
663 { "hour", USEC_PER_HOUR },
664 { "hr", USEC_PER_HOUR },
665 { "h", USEC_PER_HOUR },
666 { "days", USEC_PER_DAY },
667 { "day", USEC_PER_DAY },
668 { "d", USEC_PER_DAY },
669 { "weeks", USEC_PER_WEEK },
670 { "week", USEC_PER_WEEK },
671 { "w", USEC_PER_WEEK },
672 { "years", USEC_PER_YEAR },
673 { "year", USEC_PER_YEAR },
674 { "y", USEC_PER_YEAR },
675 { "usec", 1ULL },
676 { "us", 1ULL },
677 { "", USEC_PER_SEC }, /* default is sec */
678 };
679
b1d6dcf5 680 const char *p, *s;
9a98c7a1 681 usec_t r = 0;
cb0dac05 682 bool something = false;
9a98c7a1
LP
683
684 assert(t);
685 assert(usec);
686
687 p = t;
b1d6dcf5
ZJS
688
689 p += strspn(p, WHITESPACE);
690 s = startswith(p, "infinity");
691 if (s) {
692 s += strspn(s, WHITESPACE);
693 if (*s != 0)
694 return -EINVAL;
695
696 *usec = USEC_INFINITY;
697 return 0;
698 }
699
cb0dac05
LP
700 for (;;) {
701 long long l, z = 0;
9a98c7a1 702 char *e;
cb0dac05
LP
703 unsigned i, n = 0;
704
705 p += strspn(p, WHITESPACE);
706
707 if (*p == 0) {
708 if (!something)
709 return -EINVAL;
710
711 break;
712 }
9a98c7a1
LP
713
714 errno = 0;
715 l = strtoll(p, &e, 10);
716
8333c77e 717 if (errno > 0)
9a98c7a1
LP
718 return -errno;
719
720 if (l < 0)
721 return -ERANGE;
722
cb0dac05
LP
723 if (*e == '.') {
724 char *b = e + 1;
725
726 errno = 0;
727 z = strtoll(b, &e, 10);
728 if (errno > 0)
729 return -errno;
730
731 if (z < 0)
732 return -ERANGE;
733
734 if (e == b)
735 return -EINVAL;
736
737 n = e - b;
738
739 } else if (e == p)
9a98c7a1
LP
740 return -EINVAL;
741
742 e += strspn(e, WHITESPACE);
743
744 for (i = 0; i < ELEMENTSOF(table); i++)
745 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
746 usec_t k = (usec_t) z * table[i].usec;
747
748 for (; n > 0; n--)
749 k /= 10;
750
751 r += (usec_t) l * table[i].usec + k;
9a98c7a1 752 p = e + strlen(table[i].suffix);
cb0dac05
LP
753
754 something = true;
9a98c7a1
LP
755 break;
756 }
757
758 if (i >= ELEMENTSOF(table))
759 return -EINVAL;
760
cb0dac05 761 }
9a98c7a1
LP
762
763 *usec = r;
764
765 return 0;
766}
767
768int parse_nsec(const char *t, nsec_t *nsec) {
769 static const struct {
770 const char *suffix;
771 nsec_t nsec;
772 } table[] = {
773 { "seconds", NSEC_PER_SEC },
774 { "second", NSEC_PER_SEC },
775 { "sec", NSEC_PER_SEC },
776 { "s", NSEC_PER_SEC },
777 { "minutes", NSEC_PER_MINUTE },
778 { "minute", NSEC_PER_MINUTE },
779 { "min", NSEC_PER_MINUTE },
780 { "months", NSEC_PER_MONTH },
781 { "month", NSEC_PER_MONTH },
782 { "msec", NSEC_PER_MSEC },
783 { "ms", NSEC_PER_MSEC },
784 { "m", NSEC_PER_MINUTE },
785 { "hours", NSEC_PER_HOUR },
786 { "hour", NSEC_PER_HOUR },
787 { "hr", NSEC_PER_HOUR },
788 { "h", NSEC_PER_HOUR },
789 { "days", NSEC_PER_DAY },
790 { "day", NSEC_PER_DAY },
791 { "d", NSEC_PER_DAY },
792 { "weeks", NSEC_PER_WEEK },
793 { "week", NSEC_PER_WEEK },
794 { "w", NSEC_PER_WEEK },
795 { "years", NSEC_PER_YEAR },
796 { "year", NSEC_PER_YEAR },
797 { "y", NSEC_PER_YEAR },
798 { "usec", NSEC_PER_USEC },
799 { "us", NSEC_PER_USEC },
800 { "nsec", 1ULL },
801 { "ns", 1ULL },
802 { "", 1ULL }, /* default is nsec */
803 };
804
e73c78c2 805 const char *p, *s;
9a98c7a1 806 nsec_t r = 0;
cb0dac05 807 bool something = false;
9a98c7a1
LP
808
809 assert(t);
810 assert(nsec);
811
812 p = t;
e73c78c2
LP
813
814 p += strspn(p, WHITESPACE);
815 s = startswith(p, "infinity");
816 if (s) {
817 s += strspn(s, WHITESPACE);
8e8933ca 818 if (*s != 0)
e73c78c2
LP
819 return -EINVAL;
820
821 *nsec = NSEC_INFINITY;
822 return 0;
823 }
824
cb0dac05
LP
825 for (;;) {
826 long long l, z = 0;
9a98c7a1 827 char *e;
cb0dac05
LP
828 unsigned i, n = 0;
829
830 p += strspn(p, WHITESPACE);
831
832 if (*p == 0) {
833 if (!something)
834 return -EINVAL;
835
836 break;
837 }
9a98c7a1
LP
838
839 errno = 0;
840 l = strtoll(p, &e, 10);
841
8333c77e 842 if (errno > 0)
9a98c7a1
LP
843 return -errno;
844
845 if (l < 0)
846 return -ERANGE;
847
cb0dac05
LP
848 if (*e == '.') {
849 char *b = e + 1;
850
851 errno = 0;
852 z = strtoll(b, &e, 10);
853 if (errno > 0)
854 return -errno;
855
856 if (z < 0)
857 return -ERANGE;
858
859 if (e == b)
860 return -EINVAL;
861
862 n = e - b;
863
864 } else if (e == p)
9a98c7a1
LP
865 return -EINVAL;
866
867 e += strspn(e, WHITESPACE);
868
869 for (i = 0; i < ELEMENTSOF(table); i++)
870 if (startswith(e, table[i].suffix)) {
cb0dac05
LP
871 nsec_t k = (nsec_t) z * table[i].nsec;
872
873 for (; n > 0; n--)
874 k /= 10;
875
876 r += (nsec_t) l * table[i].nsec + k;
9a98c7a1 877 p = e + strlen(table[i].suffix);
cb0dac05
LP
878
879 something = true;
9a98c7a1
LP
880 break;
881 }
882
883 if (i >= ELEMENTSOF(table))
884 return -EINVAL;
885
cb0dac05 886 }
9a98c7a1
LP
887
888 *nsec = r;
889
890 return 0;
891}
03cc26dd
LP
892
893bool ntp_synced(void) {
894 struct timex txc = {};
895
896 if (adjtimex(&txc) < 0)
897 return false;
898
899 if (txc.status & STA_UNSYNC)
900 return false;
901
902 return true;
903}
75683450
LP
904
905int get_timezones(char ***ret) {
906 _cleanup_fclose_ FILE *f = NULL;
907 _cleanup_strv_free_ char **zones = NULL;
908 size_t n_zones = 0, n_allocated = 0;
909
910 assert(ret);
911
912 zones = strv_new("UTC", NULL);
913 if (!zones)
914 return -ENOMEM;
915
916 n_allocated = 2;
917 n_zones = 1;
918
919 f = fopen("/usr/share/zoneinfo/zone.tab", "re");
920 if (f) {
921 char l[LINE_MAX];
922
923 FOREACH_LINE(l, f, return -errno) {
924 char *p, *w;
925 size_t k;
926
927 p = strstrip(l);
928
929 if (isempty(p) || *p == '#')
930 continue;
931
932 /* Skip over country code */
933 p += strcspn(p, WHITESPACE);
934 p += strspn(p, WHITESPACE);
935
936 /* Skip over coordinates */
937 p += strcspn(p, WHITESPACE);
938 p += strspn(p, WHITESPACE);
939
940 /* Found timezone name */
941 k = strcspn(p, WHITESPACE);
942 if (k <= 0)
943 continue;
944
945 w = strndup(p, k);
946 if (!w)
947 return -ENOMEM;
948
949 if (!GREEDY_REALLOC(zones, n_allocated, n_zones + 2)) {
950 free(w);
951 return -ENOMEM;
952 }
953
954 zones[n_zones++] = w;
955 zones[n_zones] = NULL;
956 }
957
958 strv_sort(zones);
959
960 } else if (errno != ENOENT)
961 return -errno;
962
963 *ret = zones;
964 zones = NULL;
965
966 return 0;
967}
968
969bool timezone_is_valid(const char *name) {
970 bool slash = false;
971 const char *p, *t;
972 struct stat st;
973
974 if (!name || *name == 0 || *name == '/')
975 return false;
976
977 for (p = name; *p; p++) {
978 if (!(*p >= '0' && *p <= '9') &&
979 !(*p >= 'a' && *p <= 'z') &&
980 !(*p >= 'A' && *p <= 'Z') &&
981 !(*p == '-' || *p == '_' || *p == '+' || *p == '/'))
982 return false;
983
984 if (*p == '/') {
985
986 if (slash)
987 return false;
988
989 slash = true;
990 } else
991 slash = false;
992 }
993
994 if (slash)
995 return false;
996
63c372cb 997 t = strjoina("/usr/share/zoneinfo/", name);
75683450
LP
998 if (stat(t, &st) < 0)
999 return false;
1000
1001 if (!S_ISREG(st.st_mode))
1002 return false;
1003
1004 return true;
1005}
77ff2de9
TG
1006
1007clockid_t clock_boottime_or_monotonic(void) {
1008 static clockid_t clock = -1;
1009 int fd;
1010
1011 if (clock != -1)
1012 return clock;
1013
1014 fd = timerfd_create(CLOCK_BOOTTIME, TFD_NONBLOCK|TFD_CLOEXEC);
1015 if (fd < 0)
1016 clock = CLOCK_MONOTONIC;
1017 else {
1018 safe_close(fd);
1019 clock = CLOCK_BOOTTIME;
1020 }
1021
1022 return clock;
1023}