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