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