]> git.proxmox.com Git - systemd.git/blob - src/basic/calendarspec.c
New upstream version 236
[systemd.git] / src / basic / calendarspec.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2012 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <alloca.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdio_ext.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mman.h>
31 #include <time.h>
32
33 #include "alloc-util.h"
34 #include "calendarspec.h"
35 #include "fileio.h"
36 #include "macro.h"
37 #include "parse-util.h"
38 #include "string-util.h"
39 #include "time-util.h"
40
41 #define BITS_WEEKDAYS 127
42 #define MIN_YEAR 1970
43 #define MAX_YEAR 2199
44
45 static void free_chain(CalendarComponent *c) {
46 CalendarComponent *n;
47
48 while (c) {
49 n = c->next;
50 free(c);
51 c = n;
52 }
53 }
54
55 CalendarSpec* calendar_spec_free(CalendarSpec *c) {
56
57 if (!c)
58 return NULL;
59
60 free_chain(c->year);
61 free_chain(c->month);
62 free_chain(c->day);
63 free_chain(c->hour);
64 free_chain(c->minute);
65 free_chain(c->microsecond);
66 free(c->timezone);
67
68 return mfree(c);
69 }
70
71 static int component_compare(const void *_a, const void *_b) {
72 CalendarComponent * const *a = _a, * const *b = _b;
73
74 if ((*a)->start < (*b)->start)
75 return -1;
76 if ((*a)->start > (*b)->start)
77 return 1;
78
79 if ((*a)->stop < (*b)->stop)
80 return -1;
81 if ((*a)->stop > (*b)->stop)
82 return 1;
83
84 if ((*a)->repeat < (*b)->repeat)
85 return -1;
86 if ((*a)->repeat > (*b)->repeat)
87 return 1;
88
89 return 0;
90 }
91
92 static void normalize_chain(CalendarComponent **c) {
93 unsigned n = 0, k;
94 CalendarComponent **b, *i, **j, *next;
95
96 assert(c);
97
98 for (i = *c; i; i = i->next) {
99 n++;
100
101 /*
102 * While we're counting the chain, also normalize `stop`
103 * so the length of the range is a multiple of `repeat`
104 */
105 if (i->stop > i->start && i->repeat > 0)
106 i->stop -= (i->stop - i->start) % i->repeat;
107
108 }
109
110 if (n <= 1)
111 return;
112
113 j = b = alloca(sizeof(CalendarComponent*) * n);
114 for (i = *c; i; i = i->next)
115 *(j++) = i;
116
117 qsort(b, n, sizeof(CalendarComponent*), component_compare);
118
119 b[n-1]->next = NULL;
120 next = b[n-1];
121
122 /* Drop non-unique entries */
123 for (k = n-1; k > 0; k--) {
124 if (component_compare(&b[k-1], &next) == 0) {
125 free(b[k-1]);
126 continue;
127 }
128
129 b[k-1]->next = next;
130 next = b[k-1];
131 }
132
133 *c = next;
134 }
135
136 static void fix_year(CalendarComponent *c) {
137 /* Turns 12 → 2012, 89 → 1989 */
138
139 while (c) {
140 if (c->start >= 0 && c->start < 70)
141 c->start += 2000;
142
143 if (c->stop >= 0 && c->stop < 70)
144 c->stop += 2000;
145
146 if (c->start >= 70 && c->start < 100)
147 c->start += 1900;
148
149 if (c->stop >= 70 && c->stop < 100)
150 c->stop += 1900;
151
152 c = c->next;
153 }
154 }
155
156 int calendar_spec_normalize(CalendarSpec *c) {
157 assert(c);
158
159 if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS)
160 c->weekdays_bits = -1;
161
162 if (c->end_of_month && !c->day)
163 c->end_of_month = false;
164
165 fix_year(c->year);
166
167 normalize_chain(&c->year);
168 normalize_chain(&c->month);
169 normalize_chain(&c->day);
170 normalize_chain(&c->hour);
171 normalize_chain(&c->minute);
172 normalize_chain(&c->microsecond);
173
174 return 0;
175 }
176
177 _pure_ static bool chain_valid(CalendarComponent *c, int from, int to, bool end_of_month) {
178 if (!c)
179 return true;
180
181 /* Forbid dates more than 28 days from the end of the month */
182 if (end_of_month)
183 to -= 3;
184
185 if (c->start < from || c->start > to)
186 return false;
187
188 /*
189 * c->repeat must be short enough so at least one repetition may
190 * occur before the end of the interval. For dates scheduled
191 * relative to the end of the month, c->start and c->stop
192 * correspond to the Nth last day of the month.
193 */
194 if (c->stop >= 0) {
195 if (c->stop < from || c ->stop > to)
196 return false;
197
198 if (c->start + c->repeat > c->stop)
199 return false;
200 } else {
201 if (end_of_month && c->start - c->repeat < from)
202 return false;
203
204 if (!end_of_month && c->start + c->repeat > to)
205 return false;
206 }
207
208 if (c->next)
209 return chain_valid(c->next, from, to, end_of_month);
210
211 return true;
212 }
213
214 _pure_ bool calendar_spec_valid(CalendarSpec *c) {
215 assert(c);
216
217 if (c->weekdays_bits > BITS_WEEKDAYS)
218 return false;
219
220 if (!chain_valid(c->year, MIN_YEAR, MAX_YEAR, false))
221 return false;
222
223 if (!chain_valid(c->month, 1, 12, false))
224 return false;
225
226 if (!chain_valid(c->day, 1, 31, c->end_of_month))
227 return false;
228
229 if (!chain_valid(c->hour, 0, 23, false))
230 return false;
231
232 if (!chain_valid(c->minute, 0, 59, false))
233 return false;
234
235 if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC-1, false))
236 return false;
237
238 return true;
239 }
240
241 static void format_weekdays(FILE *f, const CalendarSpec *c) {
242 static const char *const days[] = {
243 "Mon",
244 "Tue",
245 "Wed",
246 "Thu",
247 "Fri",
248 "Sat",
249 "Sun"
250 };
251
252 int l, x;
253 bool need_comma = false;
254
255 assert(f);
256 assert(c);
257 assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS);
258
259 for (x = 0, l = -1; x < (int) ELEMENTSOF(days); x++) {
260
261 if (c->weekdays_bits & (1 << x)) {
262
263 if (l < 0) {
264 if (need_comma)
265 fputc(',', f);
266 else
267 need_comma = true;
268
269 fputs(days[x], f);
270 l = x;
271 }
272
273 } else if (l >= 0) {
274
275 if (x > l + 1) {
276 fputs(x > l + 2 ? ".." : ",", f);
277 fputs(days[x-1], f);
278 }
279
280 l = -1;
281 }
282 }
283
284 if (l >= 0 && x > l + 1) {
285 fputs(x > l + 2 ? ".." : ",", f);
286 fputs(days[x-1], f);
287 }
288 }
289
290 static void format_chain(FILE *f, int space, const CalendarComponent *c, bool usec) {
291 int d = usec ? (int) USEC_PER_SEC : 1;
292
293 assert(f);
294
295 if (!c) {
296 fputc('*', f);
297 return;
298 }
299
300 if (usec && c->start == 0 && c->repeat == USEC_PER_SEC && !c->next) {
301 fputc('*', f);
302 return;
303 }
304
305 assert(c->start >= 0);
306
307 fprintf(f, "%0*i", space, c->start / d);
308 if (c->start % d > 0)
309 fprintf(f, ".%06i", c->start % d);
310
311 if (c->stop > 0)
312 fprintf(f, "..%0*i", space, c->stop / d);
313 if (c->stop % d > 0)
314 fprintf(f, ".%06i", c->stop % d);
315
316 if (c->repeat > 0 && !(c->stop > 0 && c->repeat == d))
317 fprintf(f, "/%i", c->repeat / d);
318 if (c->repeat % d > 0)
319 fprintf(f, ".%06i", c->repeat % d);
320
321 if (c->next) {
322 fputc(',', f);
323 format_chain(f, space, c->next, usec);
324 }
325 }
326
327 int calendar_spec_to_string(const CalendarSpec *c, char **p) {
328 char *buf = NULL;
329 size_t sz = 0;
330 FILE *f;
331 int r;
332
333 assert(c);
334 assert(p);
335
336 f = open_memstream(&buf, &sz);
337 if (!f)
338 return -ENOMEM;
339
340 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
341
342 if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS) {
343 format_weekdays(f, c);
344 fputc(' ', f);
345 }
346
347 format_chain(f, 4, c->year, false);
348 fputc('-', f);
349 format_chain(f, 2, c->month, false);
350 fputc(c->end_of_month ? '~' : '-', f);
351 format_chain(f, 2, c->day, false);
352 fputc(' ', f);
353 format_chain(f, 2, c->hour, false);
354 fputc(':', f);
355 format_chain(f, 2, c->minute, false);
356 fputc(':', f);
357 format_chain(f, 2, c->microsecond, true);
358
359 if (c->utc)
360 fputs(" UTC", f);
361 else if (c->timezone != NULL) {
362 fputc(' ', f);
363 fputs(c->timezone, f);
364 } else if (IN_SET(c->dst, 0, 1)) {
365
366 /* If daylight saving is explicitly on or off, let's show the used timezone. */
367
368 tzset();
369
370 if (!isempty(tzname[c->dst])) {
371 fputc(' ', f);
372 fputs(tzname[c->dst], f);
373 }
374 }
375
376 r = fflush_and_check(f);
377 if (r < 0) {
378 free(buf);
379 fclose(f);
380 return r;
381 }
382
383 fclose(f);
384
385 *p = buf;
386 return 0;
387 }
388
389 static int parse_weekdays(const char **p, CalendarSpec *c) {
390 static const struct {
391 const char *name;
392 const int nr;
393 } day_nr[] = {
394 { "Monday", 0 },
395 { "Mon", 0 },
396 { "Tuesday", 1 },
397 { "Tue", 1 },
398 { "Wednesday", 2 },
399 { "Wed", 2 },
400 { "Thursday", 3 },
401 { "Thu", 3 },
402 { "Friday", 4 },
403 { "Fri", 4 },
404 { "Saturday", 5 },
405 { "Sat", 5 },
406 { "Sunday", 6 },
407 { "Sun", 6 }
408 };
409
410 int l = -1;
411 bool first = true;
412
413 assert(p);
414 assert(*p);
415 assert(c);
416
417 for (;;) {
418 unsigned i;
419
420 for (i = 0; i < ELEMENTSOF(day_nr); i++) {
421 size_t skip;
422
423 if (!startswith_no_case(*p, day_nr[i].name))
424 continue;
425
426 skip = strlen(day_nr[i].name);
427
428 if (!IN_SET((*p)[skip], 0, '-', '.', ',', ' '))
429 return -EINVAL;
430
431 c->weekdays_bits |= 1 << day_nr[i].nr;
432
433 if (l >= 0) {
434 int j;
435
436 if (l > day_nr[i].nr)
437 return -EINVAL;
438
439 for (j = l + 1; j < day_nr[i].nr; j++)
440 c->weekdays_bits |= 1 << j;
441 }
442
443 *p += skip;
444 break;
445 }
446
447 /* Couldn't find this prefix, so let's assume the
448 weekday was not specified and let's continue with
449 the date */
450 if (i >= ELEMENTSOF(day_nr))
451 return first ? 0 : -EINVAL;
452
453 /* We reached the end of the string */
454 if (**p == 0)
455 return 0;
456
457 /* We reached the end of the weekday spec part */
458 if (**p == ' ') {
459 *p += strspn(*p, " ");
460 return 0;
461 }
462
463 if (**p == '.') {
464 if (l >= 0)
465 return -EINVAL;
466
467 if ((*p)[1] != '.')
468 return -EINVAL;
469
470 l = day_nr[i].nr;
471 *p += 2;
472
473 /* Support ranges with "-" for backwards compatibility */
474 } else if (**p == '-') {
475 if (l >= 0)
476 return -EINVAL;
477
478 l = day_nr[i].nr;
479 *p += 1;
480
481 } else if (**p == ',') {
482 l = -1;
483 *p += 1;
484 }
485
486 /* Allow a trailing comma but not an open range */
487 if (IN_SET(**p, 0, ' ')) {
488 *p += strspn(*p, " ");
489 return l < 0 ? 0 : -EINVAL;
490 }
491
492 first = false;
493 }
494 }
495
496 static int parse_one_number(const char *p, const char **e, unsigned long *ret) {
497 char *ee = NULL;
498 unsigned long value;
499
500 errno = 0;
501 value = strtoul(p, &ee, 10);
502 if (errno > 0)
503 return -errno;
504 if (ee == p)
505 return -EINVAL;
506
507 *ret = value;
508 *e = ee;
509 return 0;
510 }
511
512 static int parse_component_decimal(const char **p, bool usec, int *res) {
513 unsigned long value;
514 const char *e = NULL;
515 int r;
516
517 if (!isdigit(**p))
518 return -EINVAL;
519
520 r = parse_one_number(*p, &e, &value);
521 if (r < 0)
522 return r;
523
524 if (usec) {
525 if (value * USEC_PER_SEC / USEC_PER_SEC != value)
526 return -ERANGE;
527
528 value *= USEC_PER_SEC;
529
530 /* One "." is a decimal point, but ".." is a range separator */
531 if (e[0] == '.' && e[1] != '.') {
532 unsigned add;
533
534 e++;
535 r = parse_fractional_part_u(&e, 6, &add);
536 if (r < 0)
537 return r;
538
539 if (add + value < value)
540 return -ERANGE;
541 value += add;
542 }
543 }
544
545 if (value > INT_MAX)
546 return -ERANGE;
547
548 *p = e;
549 *res = value;
550
551 return 0;
552 }
553
554 static int const_chain(int value, CalendarComponent **c) {
555 CalendarComponent *cc = NULL;
556
557 assert(c);
558
559 cc = new0(CalendarComponent, 1);
560 if (!cc)
561 return -ENOMEM;
562
563 cc->start = value;
564 cc->stop = -1;
565 cc->repeat = 0;
566 cc->next = *c;
567
568 *c = cc;
569
570 return 0;
571 }
572
573 static int calendarspec_from_time_t(CalendarSpec *c, time_t time) {
574 struct tm tm;
575 CalendarComponent *year = NULL, *month = NULL, *day = NULL, *hour = NULL, *minute = NULL, *us = NULL;
576 int r;
577
578 assert_se(gmtime_r(&time, &tm));
579
580 r = const_chain(tm.tm_year + 1900, &year);
581 if (r < 0)
582 return r;
583
584 r = const_chain(tm.tm_mon + 1, &month);
585 if (r < 0)
586 return r;
587
588 r = const_chain(tm.tm_mday, &day);
589 if (r < 0)
590 return r;
591
592 r = const_chain(tm.tm_hour, &hour);
593 if (r < 0)
594 return r;
595
596 r = const_chain(tm.tm_min, &minute);
597 if (r < 0)
598 return r;
599
600 r = const_chain(tm.tm_sec * USEC_PER_SEC, &us);
601 if (r < 0)
602 return r;
603
604 c->utc = true;
605 c->year = year;
606 c->month = month;
607 c->day = day;
608 c->hour = hour;
609 c->minute = minute;
610 c->microsecond = us;
611 return 0;
612 }
613
614 static int prepend_component(const char **p, bool usec, CalendarComponent **c) {
615 int r, start, stop = -1, repeat = 0;
616 CalendarComponent *cc;
617 const char *e;
618
619 assert(p);
620 assert(c);
621
622 e = *p;
623
624 r = parse_component_decimal(&e, usec, &start);
625 if (r < 0)
626 return r;
627
628 if (e[0] == '.' && e[1] == '.') {
629 e += 2;
630 r = parse_component_decimal(&e, usec, &stop);
631 if (r < 0)
632 return r;
633
634 repeat = usec ? USEC_PER_SEC : 1;
635 }
636
637 if (*e == '/') {
638 e++;
639 r = parse_component_decimal(&e, usec, &repeat);
640 if (r < 0)
641 return r;
642
643 if (repeat == 0)
644 return -ERANGE;
645 }
646
647 if (!IN_SET(*e, 0, ' ', ',', '-', '~', ':'))
648 return -EINVAL;
649
650 cc = new0(CalendarComponent, 1);
651 if (!cc)
652 return -ENOMEM;
653
654 cc->start = start;
655 cc->stop = stop;
656 cc->repeat = repeat;
657 cc->next = *c;
658
659 *p = e;
660 *c = cc;
661
662 if (*e ==',') {
663 *p += 1;
664 return prepend_component(p, usec, c);
665 }
666
667 return 0;
668 }
669
670 static int parse_chain(const char **p, bool usec, CalendarComponent **c) {
671 const char *t;
672 CalendarComponent *cc = NULL;
673 int r;
674
675 assert(p);
676 assert(c);
677
678 t = *p;
679
680 if (t[0] == '*') {
681 if (usec) {
682 r = const_chain(0, c);
683 if (r < 0)
684 return r;
685 (*c)->repeat = USEC_PER_SEC;
686 } else
687 *c = NULL;
688
689 *p = t + 1;
690 return 0;
691 }
692
693 r = prepend_component(&t, usec, &cc);
694 if (r < 0) {
695 free_chain(cc);
696 return r;
697 }
698
699 *p = t;
700 *c = cc;
701 return 0;
702 }
703
704 static int parse_date(const char **p, CalendarSpec *c) {
705 const char *t;
706 int r;
707 CalendarComponent *first, *second, *third;
708
709 assert(p);
710 assert(*p);
711 assert(c);
712
713 t = *p;
714
715 if (*t == 0)
716 return 0;
717
718 /* @TIMESTAMP — UNIX time in seconds since the epoch */
719 if (*t == '@') {
720 unsigned long value;
721 time_t time;
722
723 r = parse_one_number(t + 1, &t, &value);
724 if (r < 0)
725 return r;
726
727 time = value;
728 if ((unsigned long) time != value)
729 return -ERANGE;
730
731 r = calendarspec_from_time_t(c, time);
732 if (r < 0)
733 return r;
734
735 *p = t;
736 return 1; /* finito, don't parse H:M:S after that */
737 }
738
739 r = parse_chain(&t, false, &first);
740 if (r < 0)
741 return r;
742
743 /* Already the end? A ':' as separator? In that case this was a time, not a date */
744 if (IN_SET(*t, 0, ':')) {
745 free_chain(first);
746 return 0;
747 }
748
749 if (*t == '~')
750 c->end_of_month = true;
751 else if (*t != '-') {
752 free_chain(first);
753 return -EINVAL;
754 }
755
756 t++;
757 r = parse_chain(&t, false, &second);
758 if (r < 0) {
759 free_chain(first);
760 return r;
761 }
762
763 /* Got two parts, hence it's month and day */
764 if (IN_SET(*t, 0, ' ')) {
765 *p = t + strspn(t, " ");
766 c->month = first;
767 c->day = second;
768 return 0;
769 } else if (c->end_of_month) {
770 free_chain(first);
771 free_chain(second);
772 return -EINVAL;
773 }
774
775 if (*t == '~')
776 c->end_of_month = true;
777 else if (*t != '-') {
778 free_chain(first);
779 free_chain(second);
780 return -EINVAL;
781 }
782
783 t++;
784 r = parse_chain(&t, false, &third);
785 if (r < 0) {
786 free_chain(first);
787 free_chain(second);
788 return r;
789 }
790
791 /* Got three parts, hence it is year, month and day */
792 if (IN_SET(*t, 0, ' ')) {
793 *p = t + strspn(t, " ");
794 c->year = first;
795 c->month = second;
796 c->day = third;
797 return 0;
798 }
799
800 free_chain(first);
801 free_chain(second);
802 free_chain(third);
803 return -EINVAL;
804 }
805
806 static int parse_calendar_time(const char **p, CalendarSpec *c) {
807 CalendarComponent *h = NULL, *m = NULL, *s = NULL;
808 const char *t;
809 int r;
810
811 assert(p);
812 assert(*p);
813 assert(c);
814
815 t = *p;
816
817 /* If no time is specified at all, then this means 00:00:00 */
818 if (*t == 0)
819 goto null_hour;
820
821 r = parse_chain(&t, false, &h);
822 if (r < 0)
823 goto fail;
824
825 if (*t != ':') {
826 r = -EINVAL;
827 goto fail;
828 }
829
830 t++;
831 r = parse_chain(&t, false, &m);
832 if (r < 0)
833 goto fail;
834
835 /* Already at the end? Then it's hours and minutes, and seconds are 0 */
836 if (*t == 0)
837 goto null_second;
838
839 if (*t != ':') {
840 r = -EINVAL;
841 goto fail;
842 }
843
844 t++;
845 r = parse_chain(&t, true, &s);
846 if (r < 0)
847 goto fail;
848
849 /* At the end? Then it's hours, minutes and seconds */
850 if (*t == 0)
851 goto finish;
852
853 r = -EINVAL;
854 goto fail;
855
856 null_hour:
857 r = const_chain(0, &h);
858 if (r < 0)
859 goto fail;
860
861 r = const_chain(0, &m);
862 if (r < 0)
863 goto fail;
864
865 null_second:
866 r = const_chain(0, &s);
867 if (r < 0)
868 goto fail;
869
870 finish:
871 *p = t;
872 c->hour = h;
873 c->minute = m;
874 c->microsecond = s;
875
876 return 0;
877
878 fail:
879 free_chain(h);
880 free_chain(m);
881 free_chain(s);
882 return r;
883 }
884
885 int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
886 const char *utc;
887 CalendarSpec *c;
888 int r;
889
890 assert(p);
891 assert(spec);
892
893 c = new0(CalendarSpec, 1);
894 if (!c)
895 return -ENOMEM;
896 c->dst = -1;
897 c->timezone = NULL;
898
899 utc = endswith_no_case(p, " UTC");
900 if (utc) {
901 c->utc = true;
902 p = strndupa(p, utc - p);
903 } else {
904 const char *e = NULL;
905 int j;
906
907 tzset();
908
909 /* Check if the local timezone was specified? */
910 for (j = 0; j <= 1; j++) {
911 if (isempty(tzname[j]))
912 continue;
913
914 e = endswith_no_case(p, tzname[j]);
915 if (!e)
916 continue;
917 if (e == p)
918 continue;
919 if (e[-1] != ' ')
920 continue;
921
922 break;
923 }
924
925 /* Found one of the two timezones specified? */
926 if (IN_SET(j, 0, 1)) {
927 p = strndupa(p, e - p - 1);
928 c->dst = j;
929 } else {
930 const char *last_space;
931
932 last_space = strrchr(p, ' ');
933 if (last_space != NULL && timezone_is_valid(last_space + 1)) {
934 c->timezone = strdup(last_space + 1);
935 if (!c->timezone) {
936 r = -ENOMEM;
937 goto fail;
938 }
939
940 p = strndupa(p, last_space - p);
941 }
942 }
943 }
944
945 if (isempty(p)) {
946 r = -EINVAL;
947 goto fail;
948 }
949
950 if (strcaseeq(p, "minutely")) {
951 r = const_chain(0, &c->microsecond);
952 if (r < 0)
953 goto fail;
954
955 } else if (strcaseeq(p, "hourly")) {
956 r = const_chain(0, &c->minute);
957 if (r < 0)
958 goto fail;
959 r = const_chain(0, &c->microsecond);
960 if (r < 0)
961 goto fail;
962
963 } else if (strcaseeq(p, "daily")) {
964 r = const_chain(0, &c->hour);
965 if (r < 0)
966 goto fail;
967 r = const_chain(0, &c->minute);
968 if (r < 0)
969 goto fail;
970 r = const_chain(0, &c->microsecond);
971 if (r < 0)
972 goto fail;
973
974 } else if (strcaseeq(p, "monthly")) {
975 r = const_chain(1, &c->day);
976 if (r < 0)
977 goto fail;
978 r = const_chain(0, &c->hour);
979 if (r < 0)
980 goto fail;
981 r = const_chain(0, &c->minute);
982 if (r < 0)
983 goto fail;
984 r = const_chain(0, &c->microsecond);
985 if (r < 0)
986 goto fail;
987
988 } else if (strcaseeq(p, "annually") ||
989 strcaseeq(p, "yearly") ||
990 strcaseeq(p, "anually") /* backwards compatibility */ ) {
991
992 r = const_chain(1, &c->month);
993 if (r < 0)
994 goto fail;
995 r = const_chain(1, &c->day);
996 if (r < 0)
997 goto fail;
998 r = const_chain(0, &c->hour);
999 if (r < 0)
1000 goto fail;
1001 r = const_chain(0, &c->minute);
1002 if (r < 0)
1003 goto fail;
1004 r = const_chain(0, &c->microsecond);
1005 if (r < 0)
1006 goto fail;
1007
1008 } else if (strcaseeq(p, "weekly")) {
1009
1010 c->weekdays_bits = 1;
1011
1012 r = const_chain(0, &c->hour);
1013 if (r < 0)
1014 goto fail;
1015 r = const_chain(0, &c->minute);
1016 if (r < 0)
1017 goto fail;
1018 r = const_chain(0, &c->microsecond);
1019 if (r < 0)
1020 goto fail;
1021
1022 } else if (strcaseeq(p, "quarterly")) {
1023
1024 r = const_chain(1, &c->month);
1025 if (r < 0)
1026 goto fail;
1027 r = const_chain(4, &c->month);
1028 if (r < 0)
1029 goto fail;
1030 r = const_chain(7, &c->month);
1031 if (r < 0)
1032 goto fail;
1033 r = const_chain(10, &c->month);
1034 if (r < 0)
1035 goto fail;
1036 r = const_chain(1, &c->day);
1037 if (r < 0)
1038 goto fail;
1039 r = const_chain(0, &c->hour);
1040 if (r < 0)
1041 goto fail;
1042 r = const_chain(0, &c->minute);
1043 if (r < 0)
1044 goto fail;
1045 r = const_chain(0, &c->microsecond);
1046 if (r < 0)
1047 goto fail;
1048
1049 } else if (strcaseeq(p, "biannually") ||
1050 strcaseeq(p, "bi-annually") ||
1051 strcaseeq(p, "semiannually") ||
1052 strcaseeq(p, "semi-annually")) {
1053
1054 r = const_chain(1, &c->month);
1055 if (r < 0)
1056 goto fail;
1057 r = const_chain(7, &c->month);
1058 if (r < 0)
1059 goto fail;
1060 r = const_chain(1, &c->day);
1061 if (r < 0)
1062 goto fail;
1063 r = const_chain(0, &c->hour);
1064 if (r < 0)
1065 goto fail;
1066 r = const_chain(0, &c->minute);
1067 if (r < 0)
1068 goto fail;
1069 r = const_chain(0, &c->microsecond);
1070 if (r < 0)
1071 goto fail;
1072
1073 } else {
1074 r = parse_weekdays(&p, c);
1075 if (r < 0)
1076 goto fail;
1077
1078 r = parse_date(&p, c);
1079 if (r < 0)
1080 goto fail;
1081
1082 if (r == 0) {
1083 r = parse_calendar_time(&p, c);
1084 if (r < 0)
1085 goto fail;
1086 }
1087
1088 if (*p != 0) {
1089 r = -EINVAL;
1090 goto fail;
1091 }
1092 }
1093
1094 r = calendar_spec_normalize(c);
1095 if (r < 0)
1096 goto fail;
1097
1098 if (!calendar_spec_valid(c)) {
1099 r = -EINVAL;
1100 goto fail;
1101 }
1102
1103 *spec = c;
1104 return 0;
1105
1106 fail:
1107 calendar_spec_free(c);
1108 return r;
1109 }
1110
1111 static int find_end_of_month(struct tm *tm, bool utc, int day) {
1112 struct tm t = *tm;
1113
1114 t.tm_mon++;
1115 t.tm_mday = 1 - day;
1116
1117 if (mktime_or_timegm(&t, utc) < 0 ||
1118 t.tm_mon != tm->tm_mon)
1119 return -1;
1120
1121 return t.tm_mday;
1122 }
1123
1124 static int find_matching_component(const CalendarSpec *spec, const CalendarComponent *c,
1125 struct tm *tm, int *val) {
1126 const CalendarComponent *p = c;
1127 int start, stop, d = -1;
1128 bool d_set = false;
1129 int r;
1130
1131 assert(val);
1132
1133 if (!c)
1134 return 0;
1135
1136 while (c) {
1137 start = c->start;
1138 stop = c->stop;
1139
1140 if (spec->end_of_month && p == spec->day) {
1141 start = find_end_of_month(tm, spec->utc, start);
1142 stop = find_end_of_month(tm, spec->utc, stop);
1143
1144 if (stop > 0)
1145 SWAP_TWO(start, stop);
1146 }
1147
1148 if (start >= *val) {
1149
1150 if (!d_set || start < d) {
1151 d = start;
1152 d_set = true;
1153 }
1154
1155 } else if (c->repeat > 0) {
1156 int k;
1157
1158 k = start + c->repeat * ((*val - start + c->repeat - 1) / c->repeat);
1159
1160 if ((!d_set || k < d) && (stop < 0 || k <= stop)) {
1161 d = k;
1162 d_set = true;
1163 }
1164 }
1165
1166 c = c->next;
1167 }
1168
1169 if (!d_set)
1170 return -ENOENT;
1171
1172 r = *val != d;
1173 *val = d;
1174 return r;
1175 }
1176
1177 static bool tm_out_of_bounds(const struct tm *tm, bool utc) {
1178 struct tm t;
1179 assert(tm);
1180
1181 t = *tm;
1182
1183 if (mktime_or_timegm(&t, utc) < 0)
1184 return true;
1185
1186 /*
1187 * Set an upper bound on the year so impossible dates like "*-02-31"
1188 * don't cause find_next() to loop forever. tm_year contains years
1189 * since 1900, so adjust it accordingly.
1190 */
1191 if (tm->tm_year + 1900 > MAX_YEAR)
1192 return true;
1193
1194 /* Did any normalization take place? If so, it was out of bounds before */
1195 return
1196 t.tm_year != tm->tm_year ||
1197 t.tm_mon != tm->tm_mon ||
1198 t.tm_mday != tm->tm_mday ||
1199 t.tm_hour != tm->tm_hour ||
1200 t.tm_min != tm->tm_min ||
1201 t.tm_sec != tm->tm_sec;
1202 }
1203
1204 static bool matches_weekday(int weekdays_bits, const struct tm *tm, bool utc) {
1205 struct tm t;
1206 int k;
1207
1208 if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS)
1209 return true;
1210
1211 t = *tm;
1212 if (mktime_or_timegm(&t, utc) < 0)
1213 return false;
1214
1215 k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
1216 return (weekdays_bits & (1 << k));
1217 }
1218
1219 static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
1220 struct tm c;
1221 int tm_usec;
1222 int r;
1223
1224 assert(spec);
1225 assert(tm);
1226
1227 c = *tm;
1228 tm_usec = *usec;
1229
1230 for (;;) {
1231 /* Normalize the current date */
1232 (void) mktime_or_timegm(&c, spec->utc);
1233 c.tm_isdst = spec->dst;
1234
1235 c.tm_year += 1900;
1236 r = find_matching_component(spec, spec->year, &c, &c.tm_year);
1237 c.tm_year -= 1900;
1238
1239 if (r > 0) {
1240 c.tm_mon = 0;
1241 c.tm_mday = 1;
1242 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1243 }
1244 if (r < 0)
1245 return r;
1246 if (tm_out_of_bounds(&c, spec->utc))
1247 return -ENOENT;
1248
1249 c.tm_mon += 1;
1250 r = find_matching_component(spec, spec->month, &c, &c.tm_mon);
1251 c.tm_mon -= 1;
1252
1253 if (r > 0) {
1254 c.tm_mday = 1;
1255 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1256 }
1257 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1258 c.tm_year++;
1259 c.tm_mon = 0;
1260 c.tm_mday = 1;
1261 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1262 continue;
1263 }
1264
1265 r = find_matching_component(spec, spec->day, &c, &c.tm_mday);
1266 if (r > 0)
1267 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1268 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1269 c.tm_mon++;
1270 c.tm_mday = 1;
1271 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1272 continue;
1273 }
1274
1275 if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
1276 c.tm_mday++;
1277 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1278 continue;
1279 }
1280
1281 r = find_matching_component(spec, spec->hour, &c, &c.tm_hour);
1282 if (r > 0)
1283 c.tm_min = c.tm_sec = tm_usec = 0;
1284 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1285 c.tm_mday++;
1286 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1287 continue;
1288 }
1289
1290 r = find_matching_component(spec, spec->minute, &c, &c.tm_min);
1291 if (r > 0)
1292 c.tm_sec = tm_usec = 0;
1293 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1294 c.tm_hour++;
1295 c.tm_min = c.tm_sec = tm_usec = 0;
1296 continue;
1297 }
1298
1299 c.tm_sec = c.tm_sec * USEC_PER_SEC + tm_usec;
1300 r = find_matching_component(spec, spec->microsecond, &c, &c.tm_sec);
1301 tm_usec = c.tm_sec % USEC_PER_SEC;
1302 c.tm_sec /= USEC_PER_SEC;
1303
1304 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1305 c.tm_min++;
1306 c.tm_sec = tm_usec = 0;
1307 continue;
1308 }
1309
1310 *tm = c;
1311 *usec = tm_usec;
1312 return 0;
1313 }
1314 }
1315
1316 static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *next) {
1317 struct tm tm;
1318 time_t t;
1319 int r;
1320 usec_t tm_usec;
1321
1322 assert(spec);
1323 assert(next);
1324
1325 if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
1326 return -EINVAL;
1327
1328 usec++;
1329 t = (time_t) (usec / USEC_PER_SEC);
1330 assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc));
1331 tm_usec = usec % USEC_PER_SEC;
1332
1333 r = find_next(spec, &tm, &tm_usec);
1334 if (r < 0)
1335 return r;
1336
1337 t = mktime_or_timegm(&tm, spec->utc);
1338 if (t < 0)
1339 return -EINVAL;
1340
1341 *next = (usec_t) t * USEC_PER_SEC + tm_usec;
1342 return 0;
1343 }
1344
1345 typedef struct SpecNextResult {
1346 usec_t next;
1347 int return_value;
1348 } SpecNextResult;
1349
1350 int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
1351 pid_t pid;
1352 SpecNextResult *shared;
1353 SpecNextResult tmp;
1354 int r;
1355
1356 if (isempty(spec->timezone))
1357 return calendar_spec_next_usec_impl(spec, usec, next);
1358
1359 shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
1360 if (shared == MAP_FAILED)
1361 return negative_errno();
1362
1363 pid = fork();
1364
1365 if (pid == -1) {
1366 int fork_errno = errno;
1367 (void) munmap(shared, sizeof *shared);
1368 return -fork_errno;
1369 }
1370
1371 if (pid == 0) {
1372 if (setenv("TZ", spec->timezone, 1) != 0) {
1373 shared->return_value = negative_errno();
1374 _exit(EXIT_FAILURE);
1375 }
1376
1377 tzset();
1378
1379 shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next);
1380
1381 _exit(EXIT_SUCCESS);
1382 }
1383
1384 r = wait_for_terminate(pid, NULL);
1385 if (r < 0) {
1386 (void) munmap(shared, sizeof *shared);
1387 return r;
1388 }
1389
1390 tmp = *shared;
1391 if (munmap(shared, sizeof *shared) != 0)
1392 return negative_errno();
1393
1394 if (tmp.return_value == 0)
1395 *next = tmp.next;
1396
1397 return tmp.return_value;
1398 }