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