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