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