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