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