Coverage Report

Created: 2022-04-19 08:24

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