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