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