Bug Summary

File:build-scan/../src/basic/calendarspec.c
Warning:line 595, column 24
Potential leak of memory pointed to by 'day'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name calendarspec.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I src/basic/libbasic.a.p -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -I . -I .. -I /usr/include/blkid -I /usr/include/libmount -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility default -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/basic/calendarspec.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <alloca.h>
4#include <ctype.h>
5#include <errno(*__errno_location ()).h>
6#include <limits.h>
7#include <stddef.h>
8#include <stdio.h>
9#include <stdio_ext.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/mman.h>
13#include <time.h>
14
15#include "alloc-util.h"
16#include "calendarspec.h"
17#include "fileio.h"
18#include "macro.h"
19#include "parse-util.h"
20#include "process-util.h"
21#include "string-util.h"
22#include "time-util.h"
23
24#define BITS_WEEKDAYS127 127
25#define MIN_YEAR1970 1970
26#define MAX_YEAR2199 2199
27
28/* An arbitrary limit on the length of the chains of components. We don't want to
29 * build a very long linked list, which would be slow to iterate over and might cause
30 * our stack to overflow. It's unlikely that legitimate uses require more than a few
31 * linked compenents anyway. */
32#define CALENDARSPEC_COMPONENTS_MAX240 240
33
34static void free_chain(CalendarComponent *c) {
35 CalendarComponent *n;
36
37 while (c) {
38 n = c->next;
39 free(c);
40 c = n;
41 }
42}
43
44CalendarSpec* calendar_spec_free(CalendarSpec *c) {
45
46 if (!c)
47 return NULL((void*)0);
48
49 free_chain(c->year);
50 free_chain(c->month);
51 free_chain(c->day);
52 free_chain(c->hour);
53 free_chain(c->minute);
54 free_chain(c->microsecond);
55 free(c->timezone);
56
57 return mfree(c);
58}
59
60static int component_compare(const void *_a, const void *_b) {
61 CalendarComponent * const *a = _a, * const *b = _b;
62
63 if ((*a)->start < (*b)->start)
64 return -1;
65 if ((*a)->start > (*b)->start)
66 return 1;
67
68 if ((*a)->stop < (*b)->stop)
69 return -1;
70 if ((*a)->stop > (*b)->stop)
71 return 1;
72
73 if ((*a)->repeat < (*b)->repeat)
74 return -1;
75 if ((*a)->repeat > (*b)->repeat)
76 return 1;
77
78 return 0;
79}
80
81static void normalize_chain(CalendarComponent **c) {
82 CalendarComponent **b, *i, **j, *next;
83 size_t n = 0, k;
84
85 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 85,
__PRETTY_FUNCTION__); } while (0)
;
86
87 for (i = *c; i; i = i->next) {
88 n++;
89
90 /*
91 * While we're counting the chain, also normalize `stop`
92 * so the length of the range is a multiple of `repeat`
93 */
94 if (i->stop > i->start && i->repeat > 0)
95 i->stop -= (i->stop - i->start) % i->repeat;
96
97 }
98
99 if (n <= 1)
100 return;
101
102 j = b = newa(CalendarComponent*, n)({ do { if ((__builtin_expect(!!(!(!size_multiply_overflow(sizeof
(CalendarComponent*), n))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("!size_multiply_overflow(sizeof(CalendarComponent*), n)"),
"../src/basic/calendarspec.c", 102, __PRETTY_FUNCTION__); } while
(0); (CalendarComponent**) __builtin_alloca (sizeof(CalendarComponent
*)*(n)); })
;
103 for (i = *c; i; i = i->next)
104 *(j++) = i;
105
106 qsort(b, n, sizeof(CalendarComponent*), component_compare);
107
108 b[n-1]->next = NULL((void*)0);
109 next = b[n-1];
110
111 /* Drop non-unique entries */
112 for (k = n-1; k > 0; k--) {
113 if (component_compare(&b[k-1], &next) == 0) {
114 free(b[k-1]);
115 continue;
116 }
117
118 b[k-1]->next = next;
119 next = b[k-1];
120 }
121
122 *c = next;
123}
124
125static void fix_year(CalendarComponent *c) {
126 /* Turns 12 → 2012, 89 → 1989 */
127
128 while (c) {
129 if (c->start >= 0 && c->start < 70)
130 c->start += 2000;
131
132 if (c->stop >= 0 && c->stop < 70)
133 c->stop += 2000;
134
135 if (c->start >= 70 && c->start < 100)
136 c->start += 1900;
137
138 if (c->stop >= 70 && c->stop < 100)
139 c->stop += 1900;
140
141 c = c->next;
142 }
143}
144
145int calendar_spec_normalize(CalendarSpec *c) {
146 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 146
, __PRETTY_FUNCTION__); } while (0)
;
147
148 if (streq_ptr(c->timezone, "UTC")) {
149 c->utc = true1;
150 c->timezone = mfree(c->timezone);
151 }
152
153 if (c->weekdays_bits <= 0 || c->weekdays_bits >= BITS_WEEKDAYS127)
154 c->weekdays_bits = -1;
155
156 if (c->end_of_month && !c->day)
157 c->end_of_month = false0;
158
159 fix_year(c->year);
160
161 normalize_chain(&c->year);
162 normalize_chain(&c->month);
163 normalize_chain(&c->day);
164 normalize_chain(&c->hour);
165 normalize_chain(&c->minute);
166 normalize_chain(&c->microsecond);
167
168 return 0;
169}
170
171_pure___attribute__ ((pure)) static bool_Bool chain_valid(CalendarComponent *c, int from, int to, bool_Bool end_of_month) {
172 assert(to >= from)do { if ((__builtin_expect(!!(!(to >= from)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("to >= from"), "../src/basic/calendarspec.c"
, 172, __PRETTY_FUNCTION__); } while (0)
;
173
174 if (!c)
175 return true1;
176
177 /* Forbid dates more than 28 days from the end of the month */
178 if (end_of_month)
179 to -= 3;
180
181 if (c->start < from || c->start > to)
182 return false0;
183
184 /* Avoid overly large values that could cause overflow */
185 if (c->repeat > to - from)
186 return false0;
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 false0;
197
198 if (c->start + c->repeat > c->stop)
199 return false0;
200 } else {
201 if (end_of_month && c->start - c->repeat < from)
202 return false0;
203
204 if (!end_of_month && c->start + c->repeat > to)
205 return false0;
206 }
207
208 if (c->next)
209 return chain_valid(c->next, from, to, end_of_month);
210
211 return true1;
212}
213
214_pure___attribute__ ((pure)) bool_Bool calendar_spec_valid(CalendarSpec *c) {
215 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 215
, __PRETTY_FUNCTION__); } while (0)
;
216
217 if (c->weekdays_bits > BITS_WEEKDAYS127)
218 return false0;
219
220 if (!chain_valid(c->year, MIN_YEAR1970, MAX_YEAR2199, false0))
221 return false0;
222
223 if (!chain_valid(c->month, 1, 12, false0))
224 return false0;
225
226 if (!chain_valid(c->day, 1, 31, c->end_of_month))
227 return false0;
228
229 if (!chain_valid(c->hour, 0, 23, false0))
230 return false0;
231
232 if (!chain_valid(c->minute, 0, 59, false0))
233 return false0;
234
235 if (!chain_valid(c->microsecond, 0, 60*USEC_PER_SEC((usec_t) 1000000ULL)-1, false0))
236 return false0;
237
238 return true1;
239}
240
241static 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_Bool need_comma = false0;
254
255 assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("f"), "../src/basic/calendarspec.c", 255
, __PRETTY_FUNCTION__); } while (0)
;
256 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 256
, __PRETTY_FUNCTION__); } while (0)
;
257 assert(c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS)do { if ((__builtin_expect(!!(!(c->weekdays_bits > 0 &&
c->weekdays_bits <= 127)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS"
), "../src/basic/calendarspec.c", 257, __PRETTY_FUNCTION__); }
while (0)
;
258
259 for (x = 0, l = -1; x < (int) ELEMENTSOF(days)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(days), typeof(&*(days))), sizeof(days)/sizeof((days
)[0]), ((void)0)))
; 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 = true1;
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
290static void format_chain(FILE *f, int space, const CalendarComponent *c, bool_Bool usec) {
291 int d = usec ? (int) USEC_PER_SEC((usec_t) 1000000ULL) : 1;
292
293 assert(f)do { if ((__builtin_expect(!!(!(f)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("f"), "../src/basic/calendarspec.c", 293
, __PRETTY_FUNCTION__); } while (0)
;
294
295 if (!c) {
296 fputc('*', f);
297 return;
298 }
299
300 if (usec && c->start == 0 && c->repeat == USEC_PER_SEC((usec_t) 1000000ULL) && !c->next) {
301 fputc('*', f);
302 return;
303 }
304
305 assert(c->start >= 0)do { if ((__builtin_expect(!!(!(c->start >= 0)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c->start >= 0"), "../src/basic/calendarspec.c"
, 305, __PRETTY_FUNCTION__); } while (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
327int calendar_spec_to_string(const CalendarSpec *c, char **p) {
328 char *buf = NULL((void*)0);
329 size_t sz = 0;
330 FILE *f;
331 int r;
332
333 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 333
, __PRETTY_FUNCTION__); } while (0)
;
334 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 334
, __PRETTY_FUNCTION__); } while (0)
;
335
336 f = open_memstream(&buf, &sz);
337 if (!f)
338 return -ENOMEM12;
339
340 (void) __fsetlocking(f, FSETLOCKING_BYCALLERFSETLOCKING_BYCALLER);
341
342 if (c->weekdays_bits > 0 && c->weekdays_bits <= BITS_WEEKDAYS127) {
343 format_weekdays(f, c);
344 fputc(' ', f);
345 }
346
347 format_chain(f, 4, c->year, false0);
348 fputc('-', f);
349 format_chain(f, 2, c->month, false0);
350 fputc(c->end_of_month ? '~' : '-', f);
351 format_chain(f, 2, c->day, false0);
352 fputc(' ', f);
353 format_chain(f, 2, c->hour, false0);
354 fputc(':', f);
355 format_chain(f, 2, c->minute, false0);
356 fputc(':', f);
357 format_chain(f, 2, c->microsecond, true1);
358
359 if (c->utc)
360 fputs(" UTC", f);
361 else if (c->timezone != NULL((void*)0)) {
362 fputc(' ', f);
363 fputs(c->timezone, f);
364 } else if (IN_SET(c->dst, 0, 1)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, 1})/sizeof(int)]; switch(c->dst) {
case 0: case 1: _found = 1; break; default: break; } _found;
})
) {
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
389static 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_Bool first = true1;
412
413 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 413
, __PRETTY_FUNCTION__); } while (0)
;
414 assert(*p)do { if ((__builtin_expect(!!(!(*p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("*p"), "../src/basic/calendarspec.c", 414
, __PRETTY_FUNCTION__); } while (0)
;
415 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 415
, __PRETTY_FUNCTION__); } while (0)
;
416
417 for (;;) {
418 size_t i;
419
420 for (i = 0; i < ELEMENTSOF(day_nr)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(day_nr), typeof(&*(day_nr))), sizeof(day_nr)/sizeof
((day_nr)[0]), ((void)0)))
; 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, '-', '.', ',', ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, '-', '.', ',', ' '})/sizeof(int)]; switch
((*p)[skip]) { case 0: case '-': case '.': case ',': case ' '
: _found = 1; break; default: break; } _found; })
)
429 return -EINVAL22;
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 -EINVAL22;
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)__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(day_nr), typeof(&*(day_nr))), sizeof(day_nr)/sizeof
((day_nr)[0]), ((void)0)))
)
451 return first ? 0 : -EINVAL22;
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 -EINVAL22;
466
467 if ((*p)[1] != '.')
468 return -EINVAL22;
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 -EINVAL22;
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, ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, ' '})/sizeof(int)]; switch(**p) { case
0: case ' ': _found = 1; break; default: break; } _found; })
) {
488 *p += strspn(*p, " ");
489 return l < 0 ? 0 : -EINVAL22;
490 }
491
492 first = false0;
493 }
494}
495
496static int parse_one_number(const char *p, const char **e, unsigned long *ret) {
497 char *ee = NULL((void*)0);
498 unsigned long value;
499
500 errno(*__errno_location ()) = 0;
501 value = strtoul(p, &ee, 10);
502 if (errno(*__errno_location ()) > 0)
503 return -errno(*__errno_location ());
504 if (ee == p)
505 return -EINVAL22;
506
507 *ret = value;
508 *e = ee;
509 return 0;
510}
511
512static int parse_component_decimal(const char **p, bool_Bool usec, int *res) {
513 unsigned long value;
514 const char *e = NULL((void*)0);
515 int r;
516
517 if (!isdigit(**p)((*__ctype_b_loc ())[(int) ((**p))] & (unsigned short int
) _ISdigit)
)
518 return -EINVAL22;
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_t) 1000000ULL) / USEC_PER_SEC((usec_t) 1000000ULL) != value)
526 return -ERANGE34;
527
528 value *= USEC_PER_SEC((usec_t) 1000000ULL);
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 -ERANGE34;
541 value += add;
542 }
543 }
544
545 if (value > INT_MAX2147483647)
546 return -ERANGE34;
547
548 *p = e;
549 *res = value;
550
551 return 0;
552}
553
554static int const_chain(int value, CalendarComponent **c) {
555 CalendarComponent *cc = NULL((void*)0);
556
557 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 557
, __PRETTY_FUNCTION__); } while (0)
;
55
Taking false branch
56
Loop condition is false. Exiting loop
558
559 cc = new0(CalendarComponent, 1)((CalendarComponent*) calloc((1), sizeof(CalendarComponent)));
57
Memory is allocated
560 if (!cc)
58
Assuming 'cc' is non-null
59
Taking false branch
561 return -ENOMEM12;
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
573static int calendarspec_from_time_t(CalendarSpec *c, time_t time) {
574 struct tm tm;
575 CalendarComponent *year = NULL((void*)0), *month = NULL((void*)0), *day = NULL((void*)0), *hour = NULL((void*)0), *minute = NULL((void*)0), *us = NULL((void*)0);
576 int r;
577
578 if (!gmtime_r(&time, &tm))
50
Assuming the condition is false
51
Taking false branch
579 return -ERANGE34;
580
581 r = const_chain(tm.tm_year + 1900, &year);
582 if (r
51.1
'r' is >= 0
< 0)
52
Taking false branch
583 return r;
584
585 r = const_chain(tm.tm_mon + 1, &month);
586 if (r
52.1
'r' is >= 0
< 0)
53
Taking false branch
587 return r;
588
589 r = const_chain(tm.tm_mday, &day);
54
Calling 'const_chain'
60
Returned allocated memory via 2nd parameter
590 if (r
60.1
'r' is >= 0
< 0)
61
Taking false branch
591 return r;
592
593 r = const_chain(tm.tm_hour, &hour);
594 if (r
61.1
'r' is < 0
< 0)
62
Taking true branch
595 return r;
63
Potential leak of memory pointed to by 'day'
596
597 r = const_chain(tm.tm_min, &minute);
598 if (r < 0)
599 return r;
600
601 r = const_chain(tm.tm_sec * USEC_PER_SEC((usec_t) 1000000ULL), &us);
602 if (r < 0)
603 return r;
604
605 c->utc = true1;
606 c->year = year;
607 c->month = month;
608 c->day = day;
609 c->hour = hour;
610 c->minute = minute;
611 c->microsecond = us;
612 return 0;
613}
614
615static int prepend_component(const char **p, bool_Bool usec, unsigned nesting, CalendarComponent **c) {
616 int r, start, stop = -1, repeat = 0;
617 CalendarComponent *cc;
618 const char *e = *p;
619
620 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 620
, __PRETTY_FUNCTION__); } while (0)
;
621 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 621
, __PRETTY_FUNCTION__); } while (0)
;
622
623 if (nesting > CALENDARSPEC_COMPONENTS_MAX240)
624 return -ENOBUFS105;
625
626 r = parse_component_decimal(&e, usec, &start);
627 if (r < 0)
628 return r;
629
630 if (e[0] == '.' && e[1] == '.') {
631 e += 2;
632 r = parse_component_decimal(&e, usec, &stop);
633 if (r < 0)
634 return r;
635
636 repeat = usec ? USEC_PER_SEC((usec_t) 1000000ULL) : 1;
637 }
638
639 if (*e == '/') {
640 e++;
641 r = parse_component_decimal(&e, usec, &repeat);
642 if (r < 0)
643 return r;
644
645 if (repeat == 0)
646 return -ERANGE34;
647 }
648
649 if (!IN_SET(*e, 0, ' ', ',', '-', '~', ':')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, ' ', ',', '-', '~', ':'})/sizeof(int)
]; switch(*e) { case 0: case ' ': case ',': case '-': case '~'
: case ':': _found = 1; break; default: break; } _found; })
)
650 return -EINVAL22;
651
652 cc = new0(CalendarComponent, 1)((CalendarComponent*) calloc((1), sizeof(CalendarComponent)));
653 if (!cc)
654 return -ENOMEM12;
655
656 cc->start = start;
657 cc->stop = stop;
658 cc->repeat = repeat;
659 cc->next = *c;
660
661 *p = e;
662 *c = cc;
663
664 if (*e ==',') {
665 *p += 1;
666 return prepend_component(p, usec, nesting + 1, c);
667 }
668
669 return 0;
670}
671
672static int parse_chain(const char **p, bool_Bool usec, CalendarComponent **c) {
673 const char *t;
674 CalendarComponent *cc = NULL((void*)0);
675 int r;
676
677 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 677
, __PRETTY_FUNCTION__); } while (0)
;
678 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 678
, __PRETTY_FUNCTION__); } while (0)
;
679
680 t = *p;
681
682 if (t[0] == '*') {
683 if (usec) {
684 r = const_chain(0, c);
685 if (r < 0)
686 return r;
687 (*c)->repeat = USEC_PER_SEC((usec_t) 1000000ULL);
688 } else
689 *c = NULL((void*)0);
690
691 *p = t + 1;
692 return 0;
693 }
694
695 r = prepend_component(&t, usec, 0, &cc);
696 if (r < 0) {
697 free_chain(cc);
698 return r;
699 }
700
701 *p = t;
702 *c = cc;
703 return 0;
704}
705
706static int parse_date(const char **p, CalendarSpec *c) {
707 const char *t;
708 int r;
709 CalendarComponent *first, *second, *third;
710
711 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 711
, __PRETTY_FUNCTION__); } while (0)
;
36
Taking false branch
37
Loop condition is false. Exiting loop
712 assert(*p)do { if ((__builtin_expect(!!(!(*p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("*p"), "../src/basic/calendarspec.c", 712
, __PRETTY_FUNCTION__); } while (0)
;
38
Assuming the condition is false
39
Taking false branch
40
Loop condition is false. Exiting loop
713 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 713
, __PRETTY_FUNCTION__); } while (0)
;
41
Taking false branch
42
Loop condition is false. Exiting loop
714
715 t = *p;
716
717 if (*t == 0)
43
Assuming the condition is false
44
Taking false branch
718 return 0;
719
720 /* @TIMESTAMP — UNIX time in seconds since the epoch */
721 if (*t == '@') {
45
Assuming the condition is true
46
Taking true branch
722 unsigned long value;
723 time_t time;
724
725 r = parse_one_number(t + 1, &t, &value);
726 if (r
46.1
'r' is >= 0
< 0)
47
Taking false branch
727 return r;
728
729 time = value;
730 if ((unsigned long) time
47.1
'time' is equal to 'value'
!= value)
48
Taking false branch
731 return -ERANGE34;
732
733 r = calendarspec_from_time_t(c, time);
49
Calling 'calendarspec_from_time_t'
734 if (r < 0)
735 return r;
736
737 *p = t;
738 return 1; /* finito, don't parse H:M:S after that */
739 }
740
741 r = parse_chain(&t, false0, &first);
742 if (r < 0)
743 return r;
744
745 /* Already the end? A ':' as separator? In that case this was a time, not a date */
746 if (IN_SET(*t, 0, ':')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, ':'})/sizeof(int)]; switch(*t) { case
0: case ':': _found = 1; break; default: break; } _found; })
) {
747 free_chain(first);
748 return 0;
749 }
750
751 if (*t == '~')
752 c->end_of_month = true1;
753 else if (*t != '-') {
754 free_chain(first);
755 return -EINVAL22;
756 }
757
758 t++;
759 r = parse_chain(&t, false0, &second);
760 if (r < 0) {
761 free_chain(first);
762 return r;
763 }
764
765 /* Got two parts, hence it's month and day */
766 if (IN_SET(*t, 0, ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, ' '})/sizeof(int)]; switch(*t) { case
0: case ' ': _found = 1; break; default: break; } _found; })
) {
767 *p = t + strspn(t, " ");
768 c->month = first;
769 c->day = second;
770 return 0;
771 } else if (c->end_of_month) {
772 free_chain(first);
773 free_chain(second);
774 return -EINVAL22;
775 }
776
777 if (*t == '~')
778 c->end_of_month = true1;
779 else if (*t != '-') {
780 free_chain(first);
781 free_chain(second);
782 return -EINVAL22;
783 }
784
785 t++;
786 r = parse_chain(&t, false0, &third);
787 if (r < 0) {
788 free_chain(first);
789 free_chain(second);
790 return r;
791 }
792
793 /* Got three parts, hence it is year, month and day */
794 if (IN_SET(*t, 0, ' ')({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, ' '})/sizeof(int)]; switch(*t) { case
0: case ' ': _found = 1; break; default: break; } _found; })
) {
795 *p = t + strspn(t, " ");
796 c->year = first;
797 c->month = second;
798 c->day = third;
799 return 0;
800 }
801
802 free_chain(first);
803 free_chain(second);
804 free_chain(third);
805 return -EINVAL22;
806}
807
808static int parse_calendar_time(const char **p, CalendarSpec *c) {
809 CalendarComponent *h = NULL((void*)0), *m = NULL((void*)0), *s = NULL((void*)0);
810 const char *t;
811 int r;
812
813 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 813
, __PRETTY_FUNCTION__); } while (0)
;
814 assert(*p)do { if ((__builtin_expect(!!(!(*p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("*p"), "../src/basic/calendarspec.c", 814
, __PRETTY_FUNCTION__); } while (0)
;
815 assert(c)do { if ((__builtin_expect(!!(!(c)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("c"), "../src/basic/calendarspec.c", 815
, __PRETTY_FUNCTION__); } while (0)
;
816
817 t = *p;
818
819 /* If no time is specified at all, then this means 00:00:00 */
820 if (*t == 0)
821 goto null_hour;
822
823 r = parse_chain(&t, false0, &h);
824 if (r < 0)
825 goto fail;
826
827 if (*t != ':') {
828 r = -EINVAL22;
829 goto fail;
830 }
831
832 t++;
833 r = parse_chain(&t, false0, &m);
834 if (r < 0)
835 goto fail;
836
837 /* Already at the end? Then it's hours and minutes, and seconds are 0 */
838 if (*t == 0)
839 goto null_second;
840
841 if (*t != ':') {
842 r = -EINVAL22;
843 goto fail;
844 }
845
846 t++;
847 r = parse_chain(&t, true1, &s);
848 if (r < 0)
849 goto fail;
850
851 /* At the end? Then it's hours, minutes and seconds */
852 if (*t == 0)
853 goto finish;
854
855 r = -EINVAL22;
856 goto fail;
857
858null_hour:
859 r = const_chain(0, &h);
860 if (r < 0)
861 goto fail;
862
863 r = const_chain(0, &m);
864 if (r < 0)
865 goto fail;
866
867null_second:
868 r = const_chain(0, &s);
869 if (r < 0)
870 goto fail;
871
872finish:
873 *p = t;
874 c->hour = h;
875 c->minute = m;
876 c->microsecond = s;
877
878 return 0;
879
880fail:
881 free_chain(h);
882 free_chain(m);
883 free_chain(s);
884 return r;
885}
886
887int calendar_spec_from_string(const char *p, CalendarSpec **spec) {
888 const char *utc;
889 _cleanup_(calendar_spec_freep)__attribute__((cleanup(calendar_spec_freep))) CalendarSpec *c = NULL((void*)0);
890 int r;
891
892 assert(p)do { if ((__builtin_expect(!!(!(p)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("p"), "../src/basic/calendarspec.c", 892
, __PRETTY_FUNCTION__); } while (0)
;
1
Assuming 'p' is non-null
2
Taking false branch
3
Loop condition is false. Exiting loop
893 assert(spec)do { if ((__builtin_expect(!!(!(spec)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("spec"), "../src/basic/calendarspec.c", 893
, __PRETTY_FUNCTION__); } while (0)
;
4
Assuming 'spec' is non-null
5
Taking false branch
6
Loop condition is false. Exiting loop
894
895 c = new0(CalendarSpec, 1)((CalendarSpec*) calloc((1), sizeof(CalendarSpec)));
896 if (!c)
7
Assuming 'c' is non-null
8
Taking false branch
897 return -ENOMEM12;
898 c->dst = -1;
899 c->timezone = NULL((void*)0);
900
901 utc = endswith_no_case(p, " UTC");
902 if (utc) {
9
Assuming 'utc' is non-null
10
Taking true branch
903 c->utc = true1;
904 p = strndupa(p, utc - p)(__extension__ ({ const char *__old = (p); size_t __len = strnlen
(__old, (utc - p)); char *__new = (char *) __builtin_alloca (
__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old
, __len); }))
;
905 } else {
906 const char *e = NULL((void*)0);
907 int j;
908
909 tzset();
910
911 /* Check if the local timezone was specified? */
912 for (j = 0; j <= 1; j++) {
913 if (isempty(tzname[j]))
914 continue;
915
916 e = endswith_no_case(p, tzname[j]);
917 if (!e)
918 continue;
919 if (e == p)
920 continue;
921 if (e[-1] != ' ')
922 continue;
923
924 break;
925 }
926
927 /* Found one of the two timezones specified? */
928 if (IN_SET(j, 0, 1)({ _Bool _found = 0; static __attribute__ ((unused)) char _static_assert__macros_need_to_be_extended
[20 - sizeof((int[]){0, 1})/sizeof(int)]; switch(j) { case 0:
case 1: _found = 1; break; default: break; } _found; })
) {
929 p = strndupa(p, e - p - 1)(__extension__ ({ const char *__old = (p); size_t __len = strnlen
(__old, (e - p - 1)); char *__new = (char *) __builtin_alloca
(__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old
, __len); }))
;
930 c->dst = j;
931 } else {
932 const char *last_space;
933
934 last_space = strrchr(p, ' ');
935 if (last_space != NULL((void*)0) && timezone_is_valid(last_space + 1, LOG_DEBUG7)) {
936 c->timezone = strdup(last_space + 1);
937 if (!c->timezone)
938 return -ENOMEM12;
939
940 p = strndupa(p, last_space - p)(__extension__ ({ const char *__old = (p); size_t __len = strnlen
(__old, (last_space - p)); char *__new = (char *) __builtin_alloca
(__len + 1); __new[__len] = '\0'; (char *) memcpy (__new, __old
, __len); }))
;
941 }
942 }
943 }
944
945 if (isempty(p))
11
Taking false branch
946 return -EINVAL22;
947
948 if (strcaseeq(p, "minutely")(strcasecmp((p),("minutely")) == 0)) {
12
Assuming the condition is false
13
Taking false branch
949 r = const_chain(0, &c->microsecond);
950 if (r < 0)
951 return r;
952
953 } else if (strcaseeq(p, "hourly")(strcasecmp((p),("hourly")) == 0)) {
14
Assuming the condition is false
15
Taking false branch
954 r = const_chain(0, &c->minute);
955 if (r < 0)
956 return r;
957 r = const_chain(0, &c->microsecond);
958 if (r < 0)
959 return r;
960
961 } else if (strcaseeq(p, "daily")(strcasecmp((p),("daily")) == 0)) {
16
Assuming the condition is false
17
Taking false branch
962 r = const_chain(0, &c->hour);
963 if (r < 0)
964 return r;
965 r = const_chain(0, &c->minute);
966 if (r < 0)
967 return r;
968 r = const_chain(0, &c->microsecond);
969 if (r < 0)
970 return r;
971
972 } else if (strcaseeq(p, "monthly")(strcasecmp((p),("monthly")) == 0)) {
18
Assuming the condition is false
19
Taking false branch
973 r = const_chain(1, &c->day);
974 if (r < 0)
975 return r;
976 r = const_chain(0, &c->hour);
977 if (r < 0)
978 return r;
979 r = const_chain(0, &c->minute);
980 if (r < 0)
981 return r;
982 r = const_chain(0, &c->microsecond);
983 if (r < 0)
984 return r;
985
986 } else if (strcaseeq(p, "annually")(strcasecmp((p),("annually")) == 0) ||
20
Assuming the condition is false
23
Taking false branch
987 strcaseeq(p, "yearly")(strcasecmp((p),("yearly")) == 0) ||
21
Assuming the condition is false
988 strcaseeq(p, "anually")(strcasecmp((p),("anually")) == 0) /* backwards compatibility */ ) {
22
Assuming the condition is false
989
990 r = const_chain(1, &c->month);
991 if (r < 0)
992 return r;
993 r = const_chain(1, &c->day);
994 if (r < 0)
995 return r;
996 r = const_chain(0, &c->hour);
997 if (r < 0)
998 return r;
999 r = const_chain(0, &c->minute);
1000 if (r < 0)
1001 return r;
1002 r = const_chain(0, &c->microsecond);
1003 if (r < 0)
1004 return r;
1005
1006 } else if (strcaseeq(p, "weekly")(strcasecmp((p),("weekly")) == 0)) {
24
Assuming the condition is false
25
Taking false branch
1007
1008 c->weekdays_bits = 1;
1009
1010 r = const_chain(0, &c->hour);
1011 if (r < 0)
1012 return r;
1013 r = const_chain(0, &c->minute);
1014 if (r < 0)
1015 return r;
1016 r = const_chain(0, &c->microsecond);
1017 if (r < 0)
1018 return r;
1019
1020 } else if (strcaseeq(p, "quarterly")(strcasecmp((p),("quarterly")) == 0)) {
26
Assuming the condition is false
27
Taking false branch
1021
1022 r = const_chain(1, &c->month);
1023 if (r < 0)
1024 return r;
1025 r = const_chain(4, &c->month);
1026 if (r < 0)
1027 return r;
1028 r = const_chain(7, &c->month);
1029 if (r < 0)
1030 return r;
1031 r = const_chain(10, &c->month);
1032 if (r < 0)
1033 return r;
1034 r = const_chain(1, &c->day);
1035 if (r < 0)
1036 return r;
1037 r = const_chain(0, &c->hour);
1038 if (r < 0)
1039 return r;
1040 r = const_chain(0, &c->minute);
1041 if (r < 0)
1042 return r;
1043 r = const_chain(0, &c->microsecond);
1044 if (r < 0)
1045 return r;
1046
1047 } else if (strcaseeq(p, "biannually")(strcasecmp((p),("biannually")) == 0) ||
28
Assuming the condition is false
32
Taking false branch
1048 strcaseeq(p, "bi-annually")(strcasecmp((p),("bi-annually")) == 0) ||
29
Assuming the condition is false
1049 strcaseeq(p, "semiannually")(strcasecmp((p),("semiannually")) == 0) ||
30
Assuming the condition is false
1050 strcaseeq(p, "semi-annually")(strcasecmp((p),("semi-annually")) == 0)) {
31
Assuming the condition is false
1051
1052 r = const_chain(1, &c->month);
1053 if (r < 0)
1054 return r;
1055 r = const_chain(7, &c->month);
1056 if (r < 0)
1057 return r;
1058 r = const_chain(1, &c->day);
1059 if (r < 0)
1060 return r;
1061 r = const_chain(0, &c->hour);
1062 if (r < 0)
1063 return r;
1064 r = const_chain(0, &c->minute);
1065 if (r < 0)
1066 return r;
1067 r = const_chain(0, &c->microsecond);
1068 if (r < 0)
1069 return r;
1070
1071 } else {
1072 r = parse_weekdays(&p, c);
1073 if (r < 0)
33
Assuming 'r' is >= 0
34
Taking false branch
1074 return r;
1075
1076 r = parse_date(&p, c);
35
Calling 'parse_date'
1077 if (r < 0)
1078 return r;
1079
1080 if (r == 0) {
1081 r = parse_calendar_time(&p, c);
1082 if (r < 0)
1083 return r;
1084 }
1085
1086 if (*p != 0)
1087 return -EINVAL22;
1088 }
1089
1090 r = calendar_spec_normalize(c);
1091 if (r < 0)
1092 return r;
1093
1094 if (!calendar_spec_valid(c))
1095 return -EINVAL22;
1096
1097 *spec = TAKE_PTR(c)({ typeof(c) _ptr_ = (c); (c) = ((void*)0); _ptr_; });
1098 return 0;
1099}
1100
1101static int find_end_of_month(struct tm *tm, bool_Bool utc, int day) {
1102 struct tm t = *tm;
1103
1104 t.tm_mon++;
1105 t.tm_mday = 1 - day;
1106
1107 if (mktime_or_timegm(&t, utc) < 0 ||
1108 t.tm_mon != tm->tm_mon)
1109 return -1;
1110
1111 return t.tm_mday;
1112}
1113
1114static int find_matching_component(const CalendarSpec *spec, const CalendarComponent *c,
1115 struct tm *tm, int *val) {
1116 const CalendarComponent *p = c;
1117 int start, stop, d = -1;
1118 bool_Bool d_set = false0;
1119 int r;
1120
1121 assert(val)do { if ((__builtin_expect(!!(!(val)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("val"), "../src/basic/calendarspec.c", 1121
, __PRETTY_FUNCTION__); } while (0)
;
1122
1123 if (!c)
1124 return 0;
1125
1126 while (c) {
1127 start = c->start;
1128 stop = c->stop;
1129
1130 if (spec->end_of_month && p == spec->day) {
1131 start = find_end_of_month(tm, spec->utc, start);
1132 stop = find_end_of_month(tm, spec->utc, stop);
1133
1134 if (stop > 0)
1135 SWAP_TWO(start, stop)do { typeof(start) _t = (start); (start) = (stop); (stop) = (
_t); } while (0)
;
1136 }
1137
1138 if (start >= *val) {
1139
1140 if (!d_set || start < d) {
1141 d = start;
1142 d_set = true1;
1143 }
1144
1145 } else if (c->repeat > 0) {
1146 int k;
1147
1148 k = start + c->repeat * DIV_ROUND_UP(*val - start, c->repeat)({ const typeof((*val - start)) __unique_prefix_X15 = ((*val -
start)); const typeof((c->repeat)) __unique_prefix_Y16 = (
(c->repeat)); (__unique_prefix_X15 / __unique_prefix_Y16 +
!!(__unique_prefix_X15 % __unique_prefix_Y16)); })
;
1149
1150 if ((!d_set || k < d) && (stop < 0 || k <= stop)) {
1151 d = k;
1152 d_set = true1;
1153 }
1154 }
1155
1156 c = c->next;
1157 }
1158
1159 if (!d_set)
1160 return -ENOENT2;
1161
1162 r = *val != d;
1163 *val = d;
1164 return r;
1165}
1166
1167static bool_Bool tm_out_of_bounds(const struct tm *tm, bool_Bool utc) {
1168 struct tm t;
1169 assert(tm)do { if ((__builtin_expect(!!(!(tm)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("tm"), "../src/basic/calendarspec.c", 1169
, __PRETTY_FUNCTION__); } while (0)
;
1170
1171 t = *tm;
1172
1173 if (mktime_or_timegm(&t, utc) < 0)
1174 return true1;
1175
1176 /*
1177 * Set an upper bound on the year so impossible dates like "*-02-31"
1178 * don't cause find_next() to loop forever. tm_year contains years
1179 * since 1900, so adjust it accordingly.
1180 */
1181 if (tm->tm_year + 1900 > MAX_YEAR2199)
1182 return true1;
1183
1184 /* Did any normalization take place? If so, it was out of bounds before */
1185 return
1186 t.tm_year != tm->tm_year ||
1187 t.tm_mon != tm->tm_mon ||
1188 t.tm_mday != tm->tm_mday ||
1189 t.tm_hour != tm->tm_hour ||
1190 t.tm_min != tm->tm_min ||
1191 t.tm_sec != tm->tm_sec;
1192}
1193
1194static bool_Bool matches_weekday(int weekdays_bits, const struct tm *tm, bool_Bool utc) {
1195 struct tm t;
1196 int k;
1197
1198 if (weekdays_bits < 0 || weekdays_bits >= BITS_WEEKDAYS127)
1199 return true1;
1200
1201 t = *tm;
1202 if (mktime_or_timegm(&t, utc) < 0)
1203 return false0;
1204
1205 k = t.tm_wday == 0 ? 6 : t.tm_wday - 1;
1206 return (weekdays_bits & (1 << k));
1207}
1208
1209static int find_next(const CalendarSpec *spec, struct tm *tm, usec_t *usec) {
1210 struct tm c;
1211 int tm_usec;
1212 int r;
1213
1214 assert(spec)do { if ((__builtin_expect(!!(!(spec)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("spec"), "../src/basic/calendarspec.c", 1214
, __PRETTY_FUNCTION__); } while (0)
;
1215 assert(tm)do { if ((__builtin_expect(!!(!(tm)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("tm"), "../src/basic/calendarspec.c", 1215
, __PRETTY_FUNCTION__); } while (0)
;
1216
1217 c = *tm;
1218 tm_usec = *usec;
1219
1220 for (;;) {
1221 /* Normalize the current date */
1222 (void) mktime_or_timegm(&c, spec->utc);
1223 c.tm_isdst = spec->dst;
1224
1225 c.tm_year += 1900;
1226 r = find_matching_component(spec, spec->year, &c, &c.tm_year);
1227 c.tm_year -= 1900;
1228
1229 if (r > 0) {
1230 c.tm_mon = 0;
1231 c.tm_mday = 1;
1232 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1233 }
1234 if (r < 0)
1235 return r;
1236 if (tm_out_of_bounds(&c, spec->utc))
1237 return -ENOENT2;
1238
1239 c.tm_mon += 1;
1240 r = find_matching_component(spec, spec->month, &c, &c.tm_mon);
1241 c.tm_mon -= 1;
1242
1243 if (r > 0) {
1244 c.tm_mday = 1;
1245 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1246 }
1247 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1248 c.tm_year++;
1249 c.tm_mon = 0;
1250 c.tm_mday = 1;
1251 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1252 continue;
1253 }
1254
1255 r = find_matching_component(spec, spec->day, &c, &c.tm_mday);
1256 if (r > 0)
1257 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1258 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1259 c.tm_mon++;
1260 c.tm_mday = 1;
1261 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1262 continue;
1263 }
1264
1265 if (!matches_weekday(spec->weekdays_bits, &c, spec->utc)) {
1266 c.tm_mday++;
1267 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1268 continue;
1269 }
1270
1271 r = find_matching_component(spec, spec->hour, &c, &c.tm_hour);
1272 if (r > 0)
1273 c.tm_min = c.tm_sec = tm_usec = 0;
1274 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1275 c.tm_mday++;
1276 c.tm_hour = c.tm_min = c.tm_sec = tm_usec = 0;
1277 continue;
1278 }
1279
1280 r = find_matching_component(spec, spec->minute, &c, &c.tm_min);
1281 if (r > 0)
1282 c.tm_sec = tm_usec = 0;
1283 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1284 c.tm_hour++;
1285 c.tm_min = c.tm_sec = tm_usec = 0;
1286 continue;
1287 }
1288
1289 c.tm_sec = c.tm_sec * USEC_PER_SEC((usec_t) 1000000ULL) + tm_usec;
1290 r = find_matching_component(spec, spec->microsecond, &c, &c.tm_sec);
1291 tm_usec = c.tm_sec % USEC_PER_SEC((usec_t) 1000000ULL);
1292 c.tm_sec /= USEC_PER_SEC((usec_t) 1000000ULL);
1293
1294 if (r < 0 || tm_out_of_bounds(&c, spec->utc)) {
1295 c.tm_min++;
1296 c.tm_sec = tm_usec = 0;
1297 continue;
1298 }
1299
1300 *tm = c;
1301 *usec = tm_usec;
1302 return 0;
1303 }
1304}
1305
1306static int calendar_spec_next_usec_impl(const CalendarSpec *spec, usec_t usec, usec_t *next) {
1307 struct tm tm;
1308 time_t t;
1309 int r;
1310 usec_t tm_usec;
1311
1312 assert(spec)do { if ((__builtin_expect(!!(!(spec)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("spec"), "../src/basic/calendarspec.c", 1312
, __PRETTY_FUNCTION__); } while (0)
;
1313 assert(next)do { if ((__builtin_expect(!!(!(next)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("next"), "../src/basic/calendarspec.c", 1313
, __PRETTY_FUNCTION__); } while (0)
;
1314
1315 if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX((usec_t) 253402214399000000))
1316 return -EINVAL22;
1317
1318 usec++;
1319 t = (time_t) (usec / USEC_PER_SEC((usec_t) 1000000ULL));
1320 assert_se(localtime_or_gmtime_r(&t, &tm, spec->utc))do { if ((__builtin_expect(!!(!(localtime_or_gmtime_r(&t,
&tm, spec->utc))),0))) log_assert_failed_realm(LOG_REALM_SYSTEMD
, ("localtime_or_gmtime_r(&t, &tm, spec->utc)"), "../src/basic/calendarspec.c"
, 1320, __PRETTY_FUNCTION__); } while (0)
;
1321 tm_usec = usec % USEC_PER_SEC((usec_t) 1000000ULL);
1322
1323 r = find_next(spec, &tm, &tm_usec);
1324 if (r < 0)
1325 return r;
1326
1327 t = mktime_or_timegm(&tm, spec->utc);
1328 if (t < 0)
1329 return -EINVAL22;
1330
1331 *next = (usec_t) t * USEC_PER_SEC((usec_t) 1000000ULL) + tm_usec;
1332 return 0;
1333}
1334
1335typedef struct SpecNextResult {
1336 usec_t next;
1337 int return_value;
1338} SpecNextResult;
1339
1340int calendar_spec_next_usec(const CalendarSpec *spec, usec_t usec, usec_t *next) {
1341 SpecNextResult *shared, tmp;
1342 int r;
1343
1344 if (isempty(spec->timezone))
1345 return calendar_spec_next_usec_impl(spec, usec, next);
1346
1347 shared = mmap(NULL((void*)0), sizeof *shared, PROT_READ0x1|PROT_WRITE0x2, MAP_SHARED0x01|MAP_ANONYMOUS0x20, -1, 0);
1348 if (shared == MAP_FAILED((void *) -1))
1349 return negative_errno();
1350
1351 r = safe_fork("(sd-calendar)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG|FORK_WAIT, NULL((void*)0));
1352 if (r < 0) {
1353 (void) munmap(shared, sizeof *shared);
1354 return r;
1355 }
1356 if (r == 0) {
1357 if (setenv("TZ", spec->timezone, 1) != 0) {
1358 shared->return_value = negative_errno();
1359 _exit(EXIT_FAILURE1);
1360 }
1361
1362 tzset();
1363
1364 shared->return_value = calendar_spec_next_usec_impl(spec, usec, &shared->next);
1365
1366 _exit(EXIT_SUCCESS0);
1367 }
1368
1369 tmp = *shared;
1370 if (munmap(shared, sizeof *shared) < 0)
1371 return negative_errno();
1372
1373 if (tmp.return_value == 0)
1374 *next = tmp.next;
1375
1376 return tmp.return_value;
1377}