1 /* SPDX-License-Identifier: LGPL-2.1+ */
14 #include "alloc-util.h"
15 #include "calendarspec.h"
16 #include "errno-util.h"
19 #include "parse-util.h"
20 #include "process-util.h"
21 #include "sort-util.h"
22 #include "string-util.h"
23 #include "time-util.h"
25 #define BITS_WEEKDAYS 127
29 /* An arbitrary limit on the length of the chains of components. We don't want to
30 * build a very long linked list, which would be slow to iterate over and might cause
31 * our stack to overflow. It's unlikely that legitimate uses require more than a few
32 * linked compenents anyway. */
33 #define CALENDARSPEC_COMPONENTS_MAX 240
35 static void chain_free(CalendarComponent
*c
) {
45 DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarComponent
*, chain_free
);
47 CalendarSpec
* calendar_spec_free(CalendarSpec
*c
) {
56 chain_free(c
->minute
);
57 chain_free(c
->microsecond
);
63 static int component_compare(CalendarComponent
* const *a
, CalendarComponent
* const *b
) {
66 r
= CMP((*a
)->start
, (*b
)->start
);
70 r
= CMP((*a
)->stop
, (*b
)->stop
);
74 return CMP((*a
)->repeat
, (*b
)->repeat
);
77 static void normalize_chain(CalendarComponent
**c
) {
78 CalendarComponent
**b
, *i
, **j
, *next
;
83 for (i
= *c
; i
; i
= i
->next
) {
87 * While we're counting the chain, also normalize `stop`
88 * so the length of the range is a multiple of `repeat`
90 if (i
->stop
> i
->start
&& i
->repeat
> 0)
91 i
->stop
-= (i
->stop
- i
->start
) % i
->repeat
;
98 j
= b
= newa(CalendarComponent
*, n
);
99 for (i
= *c
; i
; i
= i
->next
)
102 typesafe_qsort(b
, n
, component_compare
);
107 /* Drop non-unique entries */
108 for (k
= n
-1; k
> 0; k
--) {
109 if (component_compare(&b
[k
-1], &next
) == 0) {
121 static void fix_year(CalendarComponent
*c
) {
122 /* Turns 12 → 2012, 89 → 1989 */
125 if (c
->start
>= 0 && c
->start
< 70)
128 if (c
->stop
>= 0 && c
->stop
< 70)
131 if (c
->start
>= 70 && c
->start
< 100)
134 if (c
->stop
>= 70 && c
->stop
< 100)
141 int calendar_spec_normalize(CalendarSpec
*c
) {
144 if (streq_ptr(c
->timezone
, "UTC")) {
146 c
->timezone
= mfree(c
->timezone
);
149 if (c
->weekdays_bits
<= 0 || c
->weekdays_bits
>= BITS_WEEKDAYS
)
150 c
->weekdays_bits
= -1;
152 if (c
->end_of_month
&& !c
->day
)
153 c
->end_of_month
= false;
157 normalize_chain(&c
->year
);
158 normalize_chain(&c
->month
);
159 normalize_chain(&c
->day
);
160 normalize_chain(&c
->hour
);
161 normalize_chain(&c
->minute
);
162 normalize_chain(&c
->microsecond
);
167 _pure_
static bool chain_valid(CalendarComponent
*c
, int from
, int to
, bool end_of_month
) {
173 /* Forbid dates more than 28 days from the end of the month */
177 if (c
->start
< from
|| c
->start
> to
)
180 /* Avoid overly large values that could cause overflow */
181 if (c
->repeat
> to
- from
)
185 * c->repeat must be short enough so at least one repetition may
186 * occur before the end of the interval. For dates scheduled
187 * relative to the end of the month, c->start and c->stop
188 * correspond to the Nth last day of the month.
191 if (c
->stop
< from
|| c
->stop
> to
)
194 if (c
->start
+ c
->repeat
> c
->stop
)
197 if (end_of_month
&& c
->start
- c
->repeat
< from
)
200 if (!end_of_month
&& c
->start
+ c
->repeat
> to
)
205 return chain_valid(c
->next
, from
, to
, end_of_month
);
210 _pure_
bool calendar_spec_valid(CalendarSpec
*c
) {
213 if (c
->weekdays_bits
> BITS_WEEKDAYS
)
216 if (!chain_valid(c
->year
, MIN_YEAR
, MAX_YEAR
, false))
219 if (!chain_valid(c
->month
, 1, 12, false))
222 if (!chain_valid(c
->day
, 1, 31, c
->end_of_month
))
225 if (!chain_valid(c
->hour
, 0, 23, false))
228 if (!chain_valid(c
->minute
, 0, 59, false))
231 if (!chain_valid(c
->microsecond
, 0, 60*USEC_PER_SEC
-1, false))
237 static void format_weekdays(FILE *f
, const CalendarSpec
*c
) {
238 static const char *const days
[] = {
249 bool need_comma
= false;
253 assert(c
->weekdays_bits
> 0 && c
->weekdays_bits
<= BITS_WEEKDAYS
);
255 for (x
= 0, l
= -1; x
< (int) ELEMENTSOF(days
); x
++) {
257 if (c
->weekdays_bits
& (1 << x
)) {
272 fputs(x
> l
+ 2 ? ".." : ",", f
);
280 if (l
>= 0 && x
> l
+ 1) {
281 fputs(x
> l
+ 2 ? ".." : ",", f
);
286 static void format_chain(FILE *f
, int space
, const CalendarComponent
*c
, bool usec
) {
287 int d
= usec
? (int) USEC_PER_SEC
: 1;
296 if (usec
&& c
->start
== 0 && c
->repeat
== USEC_PER_SEC
&& !c
->next
) {
301 assert(c
->start
>= 0);
303 fprintf(f
, "%0*i", space
, c
->start
/ d
);
304 if (c
->start
% d
> 0)
305 fprintf(f
, ".%06i", c
->start
% d
);
308 fprintf(f
, "..%0*i", space
, c
->stop
/ d
);
310 fprintf(f
, ".%06i", c
->stop
% d
);
312 if (c
->repeat
> 0 && !(c
->stop
> 0 && c
->repeat
== d
))
313 fprintf(f
, "/%i", c
->repeat
/ d
);
314 if (c
->repeat
% d
> 0)
315 fprintf(f
, ".%06i", c
->repeat
% d
);
319 format_chain(f
, space
, c
->next
, usec
);
323 int calendar_spec_to_string(const CalendarSpec
*c
, char **p
) {
332 f
= open_memstream_unlocked(&buf
, &sz
);
336 if (c
->weekdays_bits
> 0 && c
->weekdays_bits
<= BITS_WEEKDAYS
) {
337 format_weekdays(f
, c
);
341 format_chain(f
, 4, c
->year
, false);
343 format_chain(f
, 2, c
->month
, false);
344 fputc(c
->end_of_month
? '~' : '-', f
);
345 format_chain(f
, 2, c
->day
, false);
347 format_chain(f
, 2, c
->hour
, false);
349 format_chain(f
, 2, c
->minute
, false);
351 format_chain(f
, 2, c
->microsecond
, true);
355 else if (c
->timezone
!= NULL
) {
357 fputs(c
->timezone
, f
);
358 } else if (IN_SET(c
->dst
, 0, 1)) {
360 /* If daylight saving is explicitly on or off, let's show the used timezone. */
364 if (!isempty(tzname
[c
->dst
])) {
366 fputs(tzname
[c
->dst
], f
);
370 r
= fflush_and_check(f
);
383 static int parse_weekdays(const char **p
, CalendarSpec
*c
) {
384 static const struct {
414 for (i
= 0; i
< ELEMENTSOF(day_nr
); i
++) {
417 if (!startswith_no_case(*p
, day_nr
[i
].name
))
420 skip
= strlen(day_nr
[i
].name
);
422 if (!IN_SET((*p
)[skip
], 0, '-', '.', ',', ' '))
425 c
->weekdays_bits
|= 1 << day_nr
[i
].nr
;
430 if (l
> day_nr
[i
].nr
)
433 for (j
= l
+ 1; j
< day_nr
[i
].nr
; j
++)
434 c
->weekdays_bits
|= 1 << j
;
441 /* Couldn't find this prefix, so let's assume the
442 weekday was not specified and let's continue with
444 if (i
>= ELEMENTSOF(day_nr
))
445 return first
? 0 : -EINVAL
;
447 /* We reached the end of the string */
451 /* We reached the end of the weekday spec part */
453 *p
+= strspn(*p
, " ");
467 /* Support ranges with "-" for backwards compatibility */
468 } else if (**p
== '-') {
475 } else if (**p
== ',') {
480 /* Allow a trailing comma but not an open range */
481 if (IN_SET(**p
, 0, ' ')) {
482 *p
+= strspn(*p
, " ");
483 return l
< 0 ? 0 : -EINVAL
;
490 static int parse_one_number(const char *p
, const char **e
, unsigned long *ret
) {
495 value
= strtoul(p
, &ee
, 10);
506 static int parse_component_decimal(const char **p
, bool usec
, int *res
) {
508 const char *e
= NULL
;
514 r
= parse_one_number(*p
, &e
, &value
);
519 if (value
* USEC_PER_SEC
/ USEC_PER_SEC
!= value
)
522 value
*= USEC_PER_SEC
;
524 /* One "." is a decimal point, but ".." is a range separator */
525 if (e
[0] == '.' && e
[1] != '.') {
529 r
= parse_fractional_part_u(&e
, 6, &add
);
533 if (add
+ value
< value
)
548 static int const_chain(int value
, CalendarComponent
**c
) {
549 CalendarComponent
*cc
= NULL
;
553 cc
= new(CalendarComponent
, 1);
557 *cc
= (CalendarComponent
) {
569 static int calendarspec_from_time_t(CalendarSpec
*c
, time_t time
) {
570 _cleanup_(chain_freep
) CalendarComponent
571 *year
= NULL
, *month
= NULL
, *day
= NULL
,
572 *hour
= NULL
, *minute
= NULL
, *us
= NULL
;
576 if (!gmtime_r(&time
, &tm
))
579 if (tm
.tm_year
> INT_MAX
- 1900)
582 r
= const_chain(tm
.tm_year
+ 1900, &year
);
586 r
= const_chain(tm
.tm_mon
+ 1, &month
);
590 r
= const_chain(tm
.tm_mday
, &day
);
594 r
= const_chain(tm
.tm_hour
, &hour
);
598 r
= const_chain(tm
.tm_min
, &minute
);
602 r
= const_chain(tm
.tm_sec
* USEC_PER_SEC
, &us
);
607 c
->year
= TAKE_PTR(year
);
608 c
->month
= TAKE_PTR(month
);
609 c
->day
= TAKE_PTR(day
);
610 c
->hour
= TAKE_PTR(hour
);
611 c
->minute
= TAKE_PTR(minute
);
612 c
->microsecond
= TAKE_PTR(us
);
616 static int prepend_component(const char **p
, bool usec
, unsigned nesting
, CalendarComponent
**c
) {
617 int r
, start
, stop
= -1, repeat
= 0;
618 CalendarComponent
*cc
;
624 if (nesting
> CALENDARSPEC_COMPONENTS_MAX
)
627 r
= parse_component_decimal(&e
, usec
, &start
);
631 if (e
[0] == '.' && e
[1] == '.') {
633 r
= parse_component_decimal(&e
, usec
, &stop
);
637 repeat
= usec
? USEC_PER_SEC
: 1;
642 r
= parse_component_decimal(&e
, usec
, &repeat
);
650 if (!IN_SET(*e
, 0, ' ', ',', '-', '~', ':'))
653 cc
= new(CalendarComponent
, 1);
657 *cc
= (CalendarComponent
) {
669 return prepend_component(p
, usec
, nesting
+ 1, c
);
675 static int parse_chain(const char **p
, bool usec
, CalendarComponent
**c
) {
676 _cleanup_(chain_freep
) CalendarComponent
*cc
= NULL
;
687 r
= const_chain(0, c
);
690 (*c
)->repeat
= USEC_PER_SEC
;
698 r
= prepend_component(&t
, usec
, 0, &cc
);
707 static int parse_date(const char **p
, CalendarSpec
*c
) {
708 _cleanup_(chain_freep
) CalendarComponent
*first
= NULL
, *second
= NULL
, *third
= NULL
;
721 /* @TIMESTAMP — UNIX time in seconds since the epoch */
726 r
= parse_one_number(t
+ 1, &t
, &value
);
731 if ((unsigned long) time
!= value
)
734 r
= calendarspec_from_time_t(c
, time
);
739 return 1; /* finito, don't parse H:M:S after that */
742 r
= parse_chain(&t
, false, &first
);
746 /* Already the end? A ':' as separator? In that case this was a time, not a date */
747 if (IN_SET(*t
, 0, ':'))
751 c
->end_of_month
= true;
756 r
= parse_chain(&t
, false, &second
);
760 /* Got two parts, hence it's month and day */
761 if (IN_SET(*t
, 0, ' ')) {
762 *p
= t
+ strspn(t
, " ");
763 c
->month
= TAKE_PTR(first
);
764 c
->day
= TAKE_PTR(second
);
766 } else if (c
->end_of_month
)
770 c
->end_of_month
= true;
775 r
= parse_chain(&t
, false, &third
);
779 if (!IN_SET(*t
, 0, ' '))
782 /* Got three parts, hence it is year, month and day */
783 *p
= t
+ strspn(t
, " ");
784 c
->year
= TAKE_PTR(first
);
785 c
->month
= TAKE_PTR(second
);
786 c
->day
= TAKE_PTR(third
);
790 static int parse_calendar_time(const char **p
, CalendarSpec
*c
) {
791 _cleanup_(chain_freep
) CalendarComponent
*h
= NULL
, *m
= NULL
, *s
= NULL
;
801 /* If no time is specified at all, then this means 00:00:00 */
805 r
= parse_chain(&t
, false, &h
);
813 r
= parse_chain(&t
, false, &m
);
817 /* Already at the end? Then it's hours and minutes, and seconds are 0 */
825 r
= parse_chain(&t
, true, &s
);
829 /* At the end? Then it's hours, minutes and seconds */
836 r
= const_chain(0, &h
);
840 r
= const_chain(0, &m
);
845 r
= const_chain(0, &s
);
851 c
->hour
= TAKE_PTR(h
);
852 c
->minute
= TAKE_PTR(m
);
853 c
->microsecond
= TAKE_PTR(s
);
858 int calendar_spec_from_string(const char *p
, CalendarSpec
**spec
) {
860 _cleanup_(calendar_spec_freep
) CalendarSpec
*c
= NULL
;
861 _cleanup_free_
char *p_tmp
= NULL
;
867 c
= new(CalendarSpec
, 1);
871 *c
= (CalendarSpec
) {
876 utc
= endswith_no_case(p
, " UTC");
879 p
= p_tmp
= strndup(p
, utc
- p
);
883 const char *e
= NULL
;
888 /* Check if the local timezone was specified? */
889 for (j
= 0; j
<= 1; j
++) {
890 if (isempty(tzname
[j
]))
893 e
= endswith_no_case(p
, tzname
[j
]);
904 /* Found one of the two timezones specified? */
905 if (IN_SET(j
, 0, 1)) {
906 p
= p_tmp
= strndup(p
, e
- p
- 1);
912 const char *last_space
;
914 last_space
= strrchr(p
, ' ');
915 if (last_space
!= NULL
&& timezone_is_valid(last_space
+ 1, LOG_DEBUG
)) {
916 c
->timezone
= strdup(last_space
+ 1);
920 p
= p_tmp
= strndup(p
, last_space
- p
);
930 if (strcaseeq(p
, "minutely")) {
931 r
= const_chain(0, &c
->microsecond
);
935 } else if (strcaseeq(p
, "hourly")) {
936 r
= const_chain(0, &c
->minute
);
939 r
= const_chain(0, &c
->microsecond
);
943 } else if (strcaseeq(p
, "daily")) {
944 r
= const_chain(0, &c
->hour
);
947 r
= const_chain(0, &c
->minute
);
950 r
= const_chain(0, &c
->microsecond
);
954 } else if (strcaseeq(p
, "monthly")) {
955 r
= const_chain(1, &c
->day
);
958 r
= const_chain(0, &c
->hour
);
961 r
= const_chain(0, &c
->minute
);
964 r
= const_chain(0, &c
->microsecond
);
968 } else if (strcaseeq(p
, "annually") ||
969 strcaseeq(p
, "yearly") ||
970 strcaseeq(p
, "anually") /* backwards compatibility */ ) {
972 r
= const_chain(1, &c
->month
);
975 r
= const_chain(1, &c
->day
);
978 r
= const_chain(0, &c
->hour
);
981 r
= const_chain(0, &c
->minute
);
984 r
= const_chain(0, &c
->microsecond
);
988 } else if (strcaseeq(p
, "weekly")) {
990 c
->weekdays_bits
= 1;
992 r
= const_chain(0, &c
->hour
);
995 r
= const_chain(0, &c
->minute
);
998 r
= const_chain(0, &c
->microsecond
);
1002 } else if (strcaseeq(p
, "quarterly")) {
1004 r
= const_chain(1, &c
->month
);
1007 r
= const_chain(4, &c
->month
);
1010 r
= const_chain(7, &c
->month
);
1013 r
= const_chain(10, &c
->month
);
1016 r
= const_chain(1, &c
->day
);
1019 r
= const_chain(0, &c
->hour
);
1022 r
= const_chain(0, &c
->minute
);
1025 r
= const_chain(0, &c
->microsecond
);
1029 } else if (strcaseeq(p
, "biannually") ||
1030 strcaseeq(p
, "bi-annually") ||
1031 strcaseeq(p
, "semiannually") ||
1032 strcaseeq(p
, "semi-annually")) {
1034 r
= const_chain(1, &c
->month
);
1037 r
= const_chain(7, &c
->month
);
1040 r
= const_chain(1, &c
->day
);
1043 r
= const_chain(0, &c
->hour
);
1046 r
= const_chain(0, &c
->minute
);
1049 r
= const_chain(0, &c
->microsecond
);
1054 r
= parse_weekdays(&p
, c
);
1058 r
= parse_date(&p
, c
);
1063 r
= parse_calendar_time(&p
, c
);
1072 r
= calendar_spec_normalize(c
);
1076 if (!calendar_spec_valid(c
))
1079 *spec
= TAKE_PTR(c
);
1083 static int find_end_of_month(struct tm
*tm
, bool utc
, int day
) {
1087 t
.tm_mday
= 1 - day
;
1089 if (mktime_or_timegm(&t
, utc
) < 0 ||
1090 t
.tm_mon
!= tm
->tm_mon
)
1096 static int find_matching_component(const CalendarSpec
*spec
, const CalendarComponent
*c
,
1097 struct tm
*tm
, int *val
) {
1098 const CalendarComponent
*p
= c
;
1099 int start
, stop
, d
= -1;
1112 if (spec
->end_of_month
&& p
== spec
->day
) {
1113 start
= find_end_of_month(tm
, spec
->utc
, start
);
1114 stop
= find_end_of_month(tm
, spec
->utc
, stop
);
1117 SWAP_TWO(start
, stop
);
1120 if (start
>= *val
) {
1122 if (!d_set
|| start
< d
) {
1127 } else if (c
->repeat
> 0) {
1130 k
= start
+ c
->repeat
* DIV_ROUND_UP(*val
- start
, c
->repeat
);
1132 if ((!d_set
|| k
< d
) && (stop
< 0 || k
<= stop
)) {
1149 static bool tm_out_of_bounds(const struct tm
*tm
, bool utc
) {
1155 if (mktime_or_timegm(&t
, utc
) < 0)
1159 * Set an upper bound on the year so impossible dates like "*-02-31"
1160 * don't cause find_next() to loop forever. tm_year contains years
1161 * since 1900, so adjust it accordingly.
1163 if (tm
->tm_year
+ 1900 > MAX_YEAR
)
1166 /* Did any normalization take place? If so, it was out of bounds before */
1168 t
.tm_year
!= tm
->tm_year
||
1169 t
.tm_mon
!= tm
->tm_mon
||
1170 t
.tm_mday
!= tm
->tm_mday
||
1171 t
.tm_hour
!= tm
->tm_hour
||
1172 t
.tm_min
!= tm
->tm_min
||
1173 t
.tm_sec
!= tm
->tm_sec
;
1176 static bool matches_weekday(int weekdays_bits
, const struct tm
*tm
, bool utc
) {
1180 if (weekdays_bits
< 0 || weekdays_bits
>= BITS_WEEKDAYS
)
1184 if (mktime_or_timegm(&t
, utc
) < 0)
1187 k
= t
.tm_wday
== 0 ? 6 : t
.tm_wday
- 1;
1188 return (weekdays_bits
& (1 << k
));
1191 static int find_next(const CalendarSpec
*spec
, struct tm
*tm
, usec_t
*usec
) {
1196 /* Returns -ENOENT if the expression is not going to elapse anymore */
1205 /* Normalize the current date */
1206 (void) mktime_or_timegm(&c
, spec
->utc
);
1207 c
.tm_isdst
= spec
->dst
;
1210 r
= find_matching_component(spec
, spec
->year
, &c
, &c
.tm_year
);
1216 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1220 if (tm_out_of_bounds(&c
, spec
->utc
))
1224 r
= find_matching_component(spec
, spec
->month
, &c
, &c
.tm_mon
);
1229 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1231 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1235 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1239 r
= find_matching_component(spec
, spec
->day
, &c
, &c
.tm_mday
);
1241 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1242 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1245 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1249 if (!matches_weekday(spec
->weekdays_bits
, &c
, spec
->utc
)) {
1251 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1255 r
= find_matching_component(spec
, spec
->hour
, &c
, &c
.tm_hour
);
1257 c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1258 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1260 c
.tm_hour
= c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1264 r
= find_matching_component(spec
, spec
->minute
, &c
, &c
.tm_min
);
1266 c
.tm_sec
= tm_usec
= 0;
1267 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1269 c
.tm_min
= c
.tm_sec
= tm_usec
= 0;
1273 c
.tm_sec
= c
.tm_sec
* USEC_PER_SEC
+ tm_usec
;
1274 r
= find_matching_component(spec
, spec
->microsecond
, &c
, &c
.tm_sec
);
1275 tm_usec
= c
.tm_sec
% USEC_PER_SEC
;
1276 c
.tm_sec
/= USEC_PER_SEC
;
1278 if (r
< 0 || tm_out_of_bounds(&c
, spec
->utc
)) {
1280 c
.tm_sec
= tm_usec
= 0;
1290 static int calendar_spec_next_usec_impl(const CalendarSpec
*spec
, usec_t usec
, usec_t
*ret_next
) {
1298 if (usec
> USEC_TIMESTAMP_FORMATTABLE_MAX
)
1302 t
= (time_t) (usec
/ USEC_PER_SEC
);
1303 assert_se(localtime_or_gmtime_r(&t
, &tm
, spec
->utc
));
1304 tm_usec
= usec
% USEC_PER_SEC
;
1306 r
= find_next(spec
, &tm
, &tm_usec
);
1310 t
= mktime_or_timegm(&tm
, spec
->utc
);
1315 *ret_next
= (usec_t
) t
* USEC_PER_SEC
+ tm_usec
;
1320 typedef struct SpecNextResult
{
1325 int calendar_spec_next_usec(const CalendarSpec
*spec
, usec_t usec
, usec_t
*ret_next
) {
1326 SpecNextResult
*shared
, tmp
;
1331 if (isempty(spec
->timezone
))
1332 return calendar_spec_next_usec_impl(spec
, usec
, ret_next
);
1334 shared
= mmap(NULL
, sizeof *shared
, PROT_READ
|PROT_WRITE
, MAP_SHARED
|MAP_ANONYMOUS
, -1, 0);
1335 if (shared
== MAP_FAILED
)
1336 return negative_errno();
1338 r
= safe_fork("(sd-calendar)", FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG
|FORK_WAIT
, NULL
);
1340 (void) munmap(shared
, sizeof *shared
);
1344 if (setenv("TZ", spec
->timezone
, 1) != 0) {
1345 shared
->return_value
= negative_errno();
1346 _exit(EXIT_FAILURE
);
1351 shared
->return_value
= calendar_spec_next_usec_impl(spec
, usec
, &shared
->next
);
1353 _exit(EXIT_SUCCESS
);
1357 if (munmap(shared
, sizeof *shared
) < 0)
1358 return negative_errno();
1360 if (tmp
.return_value
== 0 && ret_next
)
1361 *ret_next
= tmp
.next
;
1363 return tmp
.return_value
;