1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
12 #include "alloc-util.h"
13 #include "calendarspec.h"
14 #include "errno-util.h"
18 #include "memstream-util.h"
19 #include "parse-util.h"
20 #include "process-util.h"
21 #include "sort-util.h"
22 #include "string-util.h"
24 #include "time-util.h"
26 #define BITS_WEEKDAYS 127
30 /* An arbitrary limit on the length of the chains of components. We don't want to
31 * build a very long linked list, which would be slow to iterate over and might cause
32 * our stack to overflow. It's unlikely that legitimate uses require more than a few
33 * linked components anyway. */
34 #define CALENDARSPEC_COMPONENTS_MAX 240
36 /* Let's make sure that the microsecond component is safe to be stored in an 'int' */
37 assert_cc(INT_MAX
>= USEC_PER_SEC
);
39 static CalendarComponent
* chain_free(CalendarComponent
*c
) {
41 CalendarComponent
*n
= c
->next
;
42 free_and_replace(c
, n
);
47 DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarComponent
*, chain_free
);
49 CalendarSpec
* calendar_spec_free(CalendarSpec
*c
) {
58 chain_free(c
->minute
);
59 chain_free(c
->microsecond
);
65 static int component_compare(CalendarComponent
* const *a
, CalendarComponent
* const *b
) {
68 r
= CMP((*a
)->start
, (*b
)->start
);
72 r
= CMP((*a
)->stop
, (*b
)->stop
);
76 return CMP((*a
)->repeat
, (*b
)->repeat
);
79 static void normalize_chain(CalendarComponent
**c
) {
83 for (CalendarComponent
*i
= *c
; i
; i
= i
->next
) {
86 /* While we're counting the chain, also normalize 'stop'
87 * so the length of the range is a multiple of 'repeat'. */
88 if (i
->stop
> i
->start
&& i
->repeat
> 0)
89 i
->stop
-= (i
->stop
- i
->start
) % i
->repeat
;
91 /* If a repeat value is specified, but it cannot even be triggered once, let's suppress it.
93 * Similarly, if the stop value is the same as the start value, then let's just make this a
94 * non-repeating chain element. */
95 if ((i
->stop
> i
->start
&& i
->repeat
> 0 && i
->start
+ i
->repeat
> i
->stop
) ||
96 i
->start
== i
->stop
) {
105 CalendarComponent
**b
, **j
;
106 b
= j
= newa(CalendarComponent
*, n
);
107 for (CalendarComponent
*i
= *c
; i
; i
= i
->next
)
110 typesafe_qsort(b
, n
, component_compare
);
113 CalendarComponent
*next
= b
[n
-1];
115 /* Drop non-unique entries */
116 for (size_t k
= n
-1; k
> 0; k
--) {
117 if (component_compare(&b
[k
-1], &next
) == 0) {
129 static void fix_year(CalendarComponent
*c
) {
130 /* Turns 12 → 2012, 89 → 1989 */
133 if (c
->start
>= 0 && c
->start
< 70)
136 if (c
->stop
>= 0 && c
->stop
< 70)
139 if (c
->start
>= 70 && c
->start
< 100)
142 if (c
->stop
>= 70 && c
->stop
< 100)
149 static void calendar_spec_normalize(CalendarSpec
*c
) {
152 if (streq_ptr(c
->timezone
, "UTC")) {
154 c
->timezone
= mfree(c
->timezone
);
157 if (c
->weekdays_bits
<= 0 || c
->weekdays_bits
>= BITS_WEEKDAYS
)
158 c
->weekdays_bits
= -1;
160 if (c
->end_of_month
&& !c
->day
)
161 c
->end_of_month
= false;
165 normalize_chain(&c
->year
);
166 normalize_chain(&c
->month
);
167 normalize_chain(&c
->day
);
168 normalize_chain(&c
->hour
);
169 normalize_chain(&c
->minute
);
170 normalize_chain(&c
->microsecond
);
173 static bool chain_valid(CalendarComponent
*c
, int from
, int to
, bool end_of_month
) {
179 /* Forbid dates more than 28 days from the end of the month */
183 if (c
->start
< from
|| c
->start
> to
)
186 /* Avoid overly large values that could cause overflow */
187 if (c
->repeat
> to
- from
)
191 * c->repeat must be short enough so at least one repetition may
192 * occur before the end of the interval. For dates scheduled
193 * relative to the end of the month, c->start and c->stop
194 * correspond to the Nth last day of the month.
197 if (c
->stop
< from
|| c
->stop
> to
)
200 if (c
->start
+ c
->repeat
> c
->stop
)
203 if (end_of_month
&& c
->start
- c
->repeat
< from
)
206 if (!end_of_month
&& c
->start
+ c
->repeat
> to
)
211 return chain_valid(c
->next
, from
, to
, end_of_month
);
216 _pure_
bool calendar_spec_valid(CalendarSpec
*c
) {
219 if (c
->weekdays_bits
> BITS_WEEKDAYS
)
222 if (!chain_valid(c
->year
, MIN_YEAR
, MAX_YEAR
, false))
225 if (!chain_valid(c
->month
, 1, 12, false))
228 if (!chain_valid(c
->day
, 1, 31, c
->end_of_month
))
231 if (!chain_valid(c
->hour
, 0, 23, false))
234 if (!chain_valid(c
->minute
, 0, 59, false))
237 if (!chain_valid(c
->microsecond
, 0, 60*USEC_PER_SEC
-1, false))
243 static void format_weekdays(FILE *f
, const CalendarSpec
*c
) {
244 static const char *const days
[] = {
255 bool need_comma
= false;
259 assert(c
->weekdays_bits
> 0 && c
->weekdays_bits
<= BITS_WEEKDAYS
);
261 for (x
= 0, l
= -1; x
< (int) ELEMENTSOF(days
); x
++) {
263 if (c
->weekdays_bits
& (1 << x
)) {
278 fputs(x
> l
+ 2 ? ".." : ",", f
);
286 if (l
>= 0 && x
> l
+ 1) {
287 fputs(x
> l
+ 2 ? ".." : ",", f
);
292 static bool chain_is_star(const CalendarComponent
*c
, bool usec
) {
293 /* Return true if the whole chain can be replaced by '*'.
294 * This happens when the chain is empty or one of the components covers all. */
298 for (; c
; c
= c
->next
)
299 if (c
->start
== 0 && c
->stop
< 0 && c
->repeat
== USEC_PER_SEC
)
304 static void _format_chain(FILE *f
, int space
, const CalendarComponent
*c
, bool start
, bool usec
) {
305 int d
= usec
? (int) USEC_PER_SEC
: 1;
309 if (start
&& chain_is_star(c
, usec
)) {
314 assert(c
->start
>= 0);
316 fprintf(f
, "%0*i", space
, c
->start
/ d
);
317 if (c
->start
% d
> 0)
318 fprintf(f
, ".%06i", c
->start
% d
);
321 fprintf(f
, "..%0*i", space
, c
->stop
/ d
);
323 fprintf(f
, ".%06i", c
->stop
% d
);
325 if (c
->repeat
> 0 && !(c
->stop
> 0 && c
->repeat
== d
))
326 fprintf(f
, "/%i", c
->repeat
/ d
);
327 if (c
->repeat
% d
> 0)
328 fprintf(f
, ".%06i", c
->repeat
% d
);
332 _format_chain(f
, space
, c
->next
, false, usec
);
336 static void format_chain(FILE *f
, int space
, const CalendarComponent
*c
, bool usec
) {
337 _format_chain(f
, space
, c
, /* start = */ true, usec
);
340 int calendar_spec_to_string(const CalendarSpec
*c
, char **ret
) {
341 _cleanup_(memstream_done
) MemStream m
= {};
347 f
= memstream_init(&m
);
351 if (c
->weekdays_bits
> 0 && c
->weekdays_bits
<= BITS_WEEKDAYS
) {
352 format_weekdays(f
, c
);
356 format_chain(f
, 4, c
->year
, false);
358 format_chain(f
, 2, c
->month
, false);
359 fputc(c
->end_of_month
? '~' : '-', f
);
360 format_chain(f
, 2, c
->day
, false);
362 format_chain(f
, 2, c
->hour
, false);
364 format_chain(f
, 2, c
->minute
, false);
366 format_chain(f
, 2, c
->microsecond
, true);
370 else if (c
->timezone
) {
372 fputs(c
->timezone
, f
);
373 } else if (IN_SET(c
->dst
, 0, 1)) {
375 /* If daylight saving is explicitly on or off, let's show the used timezone. */
379 if (!isempty(tzname
[c
->dst
])) {
381 fputs(tzname
[c
->dst
], f
);
385 return memstream_finalize(&m
, ret
, NULL
);
388 static int parse_weekdays(const char **p
, CalendarSpec
*c
) {
389 static const struct {
419 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
422 if (!startswith_no_case(*p
, day_nr
[i
].name
))
425 skip
= strlen(day_nr
[i
].name
);
427 if (!IN_SET((*p
)[skip
], 0, '-', '.', ',', ' '))
430 c
->weekdays_bits
|= 1 << day_nr
[i
].nr
;
433 if (l
> day_nr
[i
].nr
)
436 for (int j
= l
+ 1; j
< day_nr
[i
].nr
; j
++)
437 c
->weekdays_bits
|= 1 << j
;
444 /* Couldn't find this prefix, so let's assume the
445 weekday was not specified and let's continue with
447 if (i
>= ELEMENTSOF(day_nr
))
448 return first
? 0 : -EINVAL
;
450 /* We reached the end of the string */
454 /* We reached the end of the weekday spec part */
456 *p
+= strspn(*p
, " ");
470 /* Support ranges with "-" for backwards compatibility */
471 } else if (**p
== '-') {
478 } else if (**p
== ',') {
483 /* Allow a trailing comma but not an open range */
484 if (IN_SET(**p
, 0, ' ')) {
485 *p
+= strspn(*p
, " ");
486 return l
< 0 ? 0 : -EINVAL
;
493 static int parse_one_number(const char *p
, const char **e
, unsigned long *ret
) {
498 value
= strtoul(p
, &ee
, 10);
509 static int parse_component_decimal(const char **p
, bool usec
, int *res
) {
511 const char *e
= NULL
;
514 if (!ascii_isdigit(**p
))
517 r
= parse_one_number(*p
, &e
, &value
);
522 if (value
* USEC_PER_SEC
/ USEC_PER_SEC
!= value
)
525 value
*= USEC_PER_SEC
;
527 /* One "." is a decimal point, but ".." is a range separator */
528 if (e
[0] == '.' && e
[1] != '.') {
532 r
= parse_fractional_part_u(&e
, 6, &add
);
536 if (add
+ value
< value
)
551 static int const_chain(int value
, CalendarComponent
**c
) {
552 CalendarComponent
*cc
= NULL
;
556 cc
= new(CalendarComponent
, 1);
560 *cc
= (CalendarComponent
) {
572 static int calendarspec_from_time_t(CalendarSpec
*c
, time_t time
) {
573 _cleanup_(chain_freep
) CalendarComponent
574 *year
= NULL
, *month
= NULL
, *day
= NULL
,
575 *hour
= NULL
, *minute
= NULL
, *us
= NULL
;
579 if (!gmtime_r(&time
, &tm
))
582 if (tm
.tm_year
> INT_MAX
- 1900)
585 r
= const_chain(tm
.tm_year
+ 1900, &year
);
589 r
= const_chain(tm
.tm_mon
+ 1, &month
);
593 r
= const_chain(tm
.tm_mday
, &day
);
597 r
= const_chain(tm
.tm_hour
, &hour
);
601 r
= const_chain(tm
.tm_min
, &minute
);
605 r
= const_chain(tm
.tm_sec
* USEC_PER_SEC
, &us
);
610 c
->year
= TAKE_PTR(year
);
611 c
->month
= TAKE_PTR(month
);
612 c
->day
= TAKE_PTR(day
);
613 c
->hour
= TAKE_PTR(hour
);
614 c
->minute
= TAKE_PTR(minute
);
615 c
->microsecond
= TAKE_PTR(us
);
619 static int prepend_component(const char **p
, bool usec
, unsigned nesting
, CalendarComponent
**c
) {
620 int r
, start
, stop
= -1, repeat
= 0;
621 CalendarComponent
*cc
;
627 if (nesting
> CALENDARSPEC_COMPONENTS_MAX
)
630 r
= parse_component_decimal(&e
, usec
, &start
);
634 if (e
[0] == '.' && e
[1] == '.') {
636 r
= parse_component_decimal(&e
, usec
, &stop
);
640 repeat
= usec
? USEC_PER_SEC
: 1;
645 r
= parse_component_decimal(&e
, usec
, &repeat
);
652 /* If no repeat value is specified for the μs component, then let's explicitly refuse ranges
653 * below 1s because our default repeat granularity is beyond that. */
656 if (start
> INT_MAX
- repeat
)
659 if (usec
&& stop
>= 0 && start
+ repeat
> stop
)
663 if (!IN_SET(*e
, 0, ' ', ',', '-', '~', ':'))
666 cc
= new(CalendarComponent
, 1);
670 *cc
= (CalendarComponent
) {
682 return prepend_component(p
, usec
, nesting
+ 1, c
);
688 static int parse_chain(const char **p
, bool usec
, CalendarComponent
**c
) {
689 _cleanup_(chain_freep
) CalendarComponent
*cc
= NULL
;
700 r
= const_chain(0, c
);
703 (*c
)->repeat
= USEC_PER_SEC
;
711 r
= prepend_component(&t
, usec
, 0, &cc
);
720 static int parse_date(const char **p
, CalendarSpec
*c
) {
721 _cleanup_(chain_freep
) CalendarComponent
*first
= NULL
, *second
= NULL
, *third
= NULL
;
734 /* @TIMESTAMP — UNIX time in seconds since the epoch */
739 r
= parse_one_number(t
+ 1, &t
, &value
);
744 if ((unsigned long) time
!= value
)
747 r
= calendarspec_from_time_t(c
, time
);
752 return 1; /* finito, don't parse H:M:S after that */
755 r
= parse_chain(&t
, false, &first
);
759 /* Already the end? A ':' as separator? In that case this was a time, not a date */
760 if (IN_SET(*t
, 0, ':'))
764 c
->end_of_month
= true;
769 r
= parse_chain(&t
, false, &second
);
773 /* Got two parts, hence it's month and day */
774 if (IN_SET(*t
, 0, ' ')) {
775 *p
= t
+ strspn(t
, " ");
776 c
->month
= TAKE_PTR(first
);
777 c
->day
= TAKE_PTR(second
);
779 } else if (c
->end_of_month
)
783 c
->end_of_month
= true;
788 r
= parse_chain(&t
, false, &third
);
792 if (!IN_SET(*t
, 0, ' '))
795 /* Got three parts, hence it is year, month and day */
796 *p
= t
+ strspn(t
, " ");
797 c
->year
= TAKE_PTR(first
);
798 c
->month
= TAKE_PTR(second
);
799 c
->day
= TAKE_PTR(third
);
803 static int parse_calendar_time(const char **p
, CalendarSpec
*c
) {
804 _cleanup_(chain_freep
) CalendarComponent
*h
= NULL
, *m
= NULL
, *s
= NULL
;
814 /* If no time is specified at all, then this means 00:00:00 */
818 r
= parse_chain(&t
, false, &h
);
826 r
= parse_chain(&t
, false, &m
);
830 /* Already at the end? Then it's hours and minutes, and seconds are 0 */
838 r
= parse_chain(&t
, true, &s
);
842 /* At the end? Then it's hours, minutes and seconds */
849 r
= const_chain(0, &h
);
853 r
= const_chain(0, &m
);
858 r
= const_chain(0, &s
);
864 c
->hour
= TAKE_PTR(h
);
865 c
->minute
= TAKE_PTR(m
);
866 c
->microsecond
= TAKE_PTR(s
);
871 int calendar_spec_from_string(const char *p
, CalendarSpec
**ret
) {
873 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
874 _cleanup_free_
char *p_tmp
= NULL
;
879 c
= new(CalendarSpec
, 1);
883 *c
= (CalendarSpec
) {
888 utc
= endswith_no_case(p
, " UTC");
891 p
= p_tmp
= strndup(p
, utc
- p
);
895 const char *e
= NULL
;
900 /* Check if the local timezone was specified? */
901 for (j
= 0; j
<= 1; j
++) {
902 if (isempty(tzname
[j
]))
905 e
= endswith_no_case(p
, tzname
[j
]);
916 /* Found one of the two timezones specified? */
917 if (IN_SET(j
, 0, 1)) {
918 p
= p_tmp
= strndup(p
, e
- p
- 1);
924 const char *last_space
;
926 last_space
= strrchr(p
, ' ');
927 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1, LOG_DEBUG
)) {
928 c
->timezone
= strdup(last_space
+ 1);
932 p
= p_tmp
= strndup(p
, last_space
- p
);
942 if (strcaseeq(p
, "minutely")) {
943 r
= const_chain(0, &c
->microsecond
);
947 } else if (strcaseeq(p
, "hourly")) {
948 r
= const_chain(0, &c
->minute
);
951 r
= const_chain(0, &c
->microsecond
);
955 } else if (strcaseeq(p
, "daily")) {
956 r
= const_chain(0, &c
->hour
);
959 r
= const_chain(0, &c
->minute
);
962 r
= const_chain(0, &c
->microsecond
);
966 } else if (strcaseeq(p
, "monthly")) {
967 r
= const_chain(1, &c
->day
);
970 r
= const_chain(0, &c
->hour
);
973 r
= const_chain(0, &c
->minute
);
976 r
= const_chain(0, &c
->microsecond
);
980 } else if (STRCASE_IN_SET(p
,
983 "anually") /* backwards compatibility */ ) {
985 r
= const_chain(1, &c
->month
);
988 r
= const_chain(1, &c
->day
);
991 r
= const_chain(0, &c
->hour
);
994 r
= const_chain(0, &c
->minute
);
997 r
= const_chain(0, &c
->microsecond
);
1001 } else if (strcaseeq(p
, "weekly")) {
1003 c
->weekdays_bits
= 1;
1005 r
= const_chain(0, &c
->hour
);
1008 r
= const_chain(0, &c
->minute
);
1011 r
= const_chain(0, &c
->microsecond
);
1015 } else if (strcaseeq(p
, "quarterly")) {
1017 r
= const_chain(1, &c
->month
);
1020 r
= const_chain(4, &c
->month
);
1023 r
= const_chain(7, &c
->month
);
1026 r
= const_chain(10, &c
->month
);
1029 r
= const_chain(1, &c
->day
);
1032 r
= const_chain(0, &c
->hour
);
1035 r
= const_chain(0, &c
->minute
);
1038 r
= const_chain(0, &c
->microsecond
);
1042 } else if (STRCASE_IN_SET(p
,
1048 r
= const_chain(1, &c
->month
);
1051 r
= const_chain(7, &c
->month
);
1054 r
= const_chain(1, &c
->day
);
1057 r
= const_chain(0, &c
->hour
);
1060 r
= const_chain(0, &c
->minute
);
1063 r
= const_chain(0, &c
->microsecond
);
1068 r
= parse_weekdays(&p
, c
);
1072 r
= parse_date(&p
, c
);
1077 r
= parse_calendar_time(&p
, c
);
1086 calendar_spec_normalize(c
);
1088 if (!calendar_spec_valid(c
))
1096 static int find_end_of_month(const struct tm
*tm
, bool utc
, int day
) {
1100 t
.tm_mday
= 1 - day
;
1102 if (mktime_or_timegm(&t
, utc
) < 0 ||
1103 t
.tm_mon
!= tm
->tm_mon
)
1109 static int find_matching_component(
1110 const CalendarSpec
*spec
,
1111 const CalendarComponent
*c
,
1112 const struct tm
*tm
, /* tm is only used for end-of-month calculations */
1120 /* Finds the *earliest* matching time specified by one of the CalendarCompoment items in chain c.
1121 * If no matches can be found, returns -ENOENT.
1122 * Otherwise, updates *val to the matching time. 1 is returned if *val was changed, 0 otherwise.
1128 bool end_of_month
= spec
->end_of_month
&& c
== spec
->day
;
1134 start
= find_end_of_month(tm
, spec
->utc
, c
->start
);
1135 stop
= find_end_of_month(tm
, spec
->utc
, c
->stop
);
1138 SWAP_TWO(start
, stop
);
1144 if (start
>= *val
) {
1146 if (!d_set
|| start
< d
) {
1151 } else if (c
->repeat
> 0) {
1154 k
= start
+ ROUND_UP(*val
- start
, c
->repeat
);
1156 if ((!d_set
|| k
< d
) && (stop
< 0 || k
<= stop
)) {
1173 static int tm_within_bounds(struct tm
*tm
, bool utc
) {
1179 * Set an upper bound on the year so impossible dates like "*-02-31"
1180 * don't cause find_next() to loop forever. tm_year contains years
1181 * since 1900, so adjust it accordingly.
1183 if (tm
->tm_year
+ 1900 > MAX_YEAR
)
1187 if (mktime_or_timegm(&t
, utc
) < 0)
1188 return negative_errno();
1191 * Did any normalization take place? If so, it was out of bounds before.
1192 * Normalization could skip next elapse, e.g. result of normalizing 3-33
1193 * is 4-2. This skips 4-1. So reset the sub time unit if upper unit was
1194 * out of bounds. Normalization has occurred implies find_matching_component() > 0,
1195 * other sub time units are already reset in find_next().
1197 if ((cmp
= CMP(t
.tm_year
, tm
->tm_year
)) != 0)
1199 else if ((cmp
= CMP(t
.tm_mon
, tm
->tm_mon
)) != 0)
1201 else if ((cmp
= CMP(t
.tm_mday
, tm
->tm_mday
)) != 0)
1203 else if ((cmp
= CMP(t
.tm_hour
, tm
->tm_hour
)) != 0)
1205 else if ((cmp
= CMP(t
.tm_min
, tm
->tm_min
)) != 0)
1208 cmp
= CMP(t
.tm_sec
, tm
->tm_sec
);
1211 return -EDEADLK
; /* Refuse to go backward */
1217 static bool matches_weekday(int weekdays_bits
, const struct tm
*tm
, bool utc
) {
1221 if (weekdays_bits
< 0 || weekdays_bits
>= BITS_WEEKDAYS
)
1225 if (mktime_or_timegm(&t
, utc
) < 0)
1228 k
= t
.tm_wday
== 0 ? 6 : t
.tm_wday
- 1;
1229 return (weekdays_bits
& (1 << k
));
1232 /* A safety valve: if we get stuck in the calculation, return an error.
1233 * C.f. https://bugzilla.redhat.com/show_bug.cgi?id=1941335. */
1234 #define MAX_CALENDAR_ITERATIONS 1000
1236 static int find_next(const CalendarSpec
*spec
, struct tm
*tm
, usec_t
*usec
) {
1241 /* Returns -ENOENT if the expression is not going to elapse anymore */
1249 for (unsigned iteration
= 0; iteration
< MAX_CALENDAR_ITERATIONS
; iteration
++) {
1250 /* Normalize the current date */
1251 (void) mktime_or_timegm(&c
, spec
->utc
);
1252 c
.tm_isdst
= spec
->dst
;
1255 r
= find_matching_component(spec
, spec
->year
, &c
, &c
.tm_year
);
1261 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1265 if (tm_within_bounds(&c
, spec
->utc
) <= 0)
1269 r
= find_matching_component(spec
, spec
->month
, &c
, &c
.tm_mon
);
1274 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1276 if (r
< 0 || (r
= tm_within_bounds(&c
, spec
->utc
)) < 0) {
1280 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1286 r
= find_matching_component(spec
, spec
->day
, &c
, &c
.tm_mday
);
1288 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1289 if (r
< 0 || (r
= tm_within_bounds(&c
, spec
->utc
)) < 0) {
1292 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1298 if (!matches_weekday(spec
->weekdays_bits
, &c
, spec
->utc
)) {
1300 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1304 r
= find_matching_component(spec
, spec
->hour
, &c
, &c
.tm_hour
);
1306 c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1307 if (r
< 0 || (r
= tm_within_bounds(&c
, spec
->utc
)) < 0) {
1309 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1313 /* The next hour we set might be missing if there
1314 * are time zone changes. Let's try again starting at
1315 * normalized time. */
1318 r
= find_matching_component(spec
, spec
->minute
, &c
, &c
.tm_min
);
1320 c
.tm_sec
= tm_usec
= 0;
1321 if (r
< 0 || (r
= tm_within_bounds(&c
, spec
->utc
)) < 0) {
1323 c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1329 c
.tm_sec
= c
.tm_sec
* USEC_PER_SEC
+ tm_usec
;
1330 r
= find_matching_component(spec
, spec
->microsecond
, &c
, &c
.tm_sec
);
1331 tm_usec
= c
.tm_sec
% USEC_PER_SEC
;
1332 c
.tm_sec
/= USEC_PER_SEC
;
1334 if (r
< 0 || (r
= tm_within_bounds(&c
, spec
->utc
)) < 0) {
1336 c
.tm_sec
= tm_usec
= 0;
1347 /* It seems we entered an infinite loop. Let's gracefully return an error instead of hanging or
1348 * aborting. This code is also exercised when timers.target is brought up during early boot, so
1349 * aborting here is problematic and hard to diagnose for users. */
1350 _cleanup_free_
char *s
= NULL
;
1351 (void) calendar_spec_to_string(spec
, &s
);
1352 return log_warning_errno(SYNTHETIC_ERRNO(EDEADLK
),
1353 "Infinite loop in calendar calculation: %s", strna(s
));
1356 static int calendar_spec_next_usec_impl(const CalendarSpec
*spec
, usec_t usec
, usec_t
*ret_next
) {
1364 if (usec
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
1368 t
= (time_t) (usec
/ USEC_PER_SEC
);
1369 assert_se(localtime_or_gmtime_r(&t
, &tm
, spec
->utc
));
1370 tm_usec
= usec
% USEC_PER_SEC
;
1372 r
= find_next(spec
, &tm
, &tm_usec
);
1376 t
= mktime_or_timegm(&tm
, spec
->utc
);
1381 *ret_next
= (usec_t
) t
* USEC_PER_SEC
+ tm_usec
;
1386 typedef struct SpecNextResult
{
1391 int calendar_spec_next_usec(const CalendarSpec
*spec
, usec_t usec
, usec_t
*ret_next
) {
1392 SpecNextResult
*shared
, tmp
;
1397 if (isempty(spec
->timezone
))
1398 return calendar_spec_next_usec_impl(spec
, usec
, ret_next
);
1400 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
1401 if (shared
== MAP_FAILED
)
1402 return negative_errno();
1404 r
= safe_fork("(sd-calendar)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGKILL
|FORK_WAIT
, NULL
);
1406 (void) munmap(shared
, sizeof *shared
);
1412 /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
1413 colon_tz
= strjoina(":", spec
->timezone
);
1415 if (setenv("TZ", colon_tz
, 1) != 0) {
1416 shared
->return_value
= negative_errno();
1417 _exit(EXIT_FAILURE
);
1422 shared
->return_value
= calendar_spec_next_usec_impl(spec
, usec
, &shared
->next
);
1424 _exit(EXIT_SUCCESS
);
1428 if (munmap(shared
, sizeof *shared
) < 0)
1429 return negative_errno();
1431 if (tmp
.return_value
== 0 && ret_next
)
1432 *ret_next
= tmp
.next
;
1434 return tmp
.return_value
;