• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 15057632786

15 May 2025 09:01PM UTC coverage: 72.267% (+0.02%) from 72.244%
15057632786

push

github

bluca
man: document how to hook stuff into system wakeup

Fixes: #6364

298523 of 413084 relevant lines covered (72.27%)

738132.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.05
/src/basic/time-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <ctype.h>
4
#include <errno.h>
5
#include <limits.h>
6
#include <stdlib.h>
7
#include <sys/mman.h>
8
#include <sys/time.h>
9
#include <sys/timerfd.h>
10
#include <sys/types.h>
11
#include <threads.h>
12
#include <unistd.h>
13

14
#include "alloc-util.h"
15
#include "errno-util.h"
16
#include "extract-word.h"
17
#include "fd-util.h"
18
#include "fileio.h"
19
#include "fs-util.h"
20
#include "io-util.h"
21
#include "log.h"
22
#include "macro.h"
23
#include "parse-util.h"
24
#include "path-util.h"
25
#include "process-util.h"
26
#include "stat-util.h"
27
#include "string-table.h"
28
#include "string-util.h"
29
#include "strv.h"
30
#include "time-util.h"
31

32
static clockid_t map_clock_id(clockid_t c) {
39,820,592✔
33

34
        /* Some more exotic archs (s390, ppc, …) lack the "ALARM" flavour of the clocks. Thus,
35
         * clock_gettime() will fail for them. Since they are essentially the same as their non-ALARM
36
         * pendants (their only difference is when timers are set on them), let's just map them
37
         * accordingly. This way, we can get the correct time even on those archs. */
38

39
        switch (c) {
39,820,592✔
40

41
        case CLOCK_BOOTTIME_ALARM:
42
                return CLOCK_BOOTTIME;
43

44
        case CLOCK_REALTIME_ALARM:
8✔
45
                return CLOCK_REALTIME;
8✔
46

47
        default:
39,820,583✔
48
                return c;
39,820,583✔
49
        }
50
}
51

52
usec_t now(clockid_t clock_id) {
39,817,786✔
53
        struct timespec ts;
39,817,786✔
54

55
        assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
39,817,786✔
56

57
        return timespec_load(&ts);
39,817,786✔
58
}
59

60
nsec_t now_nsec(clockid_t clock_id) {
622✔
61
        struct timespec ts;
622✔
62

63
        assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
622✔
64

65
        return timespec_load_nsec(&ts);
622✔
66
}
67

68
dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {
1,883,701✔
69
        assert(ts);
1,883,701✔
70

71
        ts->realtime = now(CLOCK_REALTIME);
1,883,701✔
72
        ts->monotonic = now(CLOCK_MONOTONIC);
1,883,701✔
73

74
        return ts;
1,883,701✔
75
}
76

77
triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
3,828,483✔
78
        assert(ts);
3,828,483✔
79

80
        ts->realtime = now(CLOCK_REALTIME);
3,828,483✔
81
        ts->monotonic = now(CLOCK_MONOTONIC);
3,828,483✔
82
        ts->boottime = now(CLOCK_BOOTTIME);
3,828,483✔
83

84
        return ts;
3,828,483✔
85
}
86

87
usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base) {
7,951✔
88

89
        /* Maps the time 'from' between two clocks, based on a common reference point where the first clock
90
         * is at 'from_base' and the second clock at 'to_base'. Basically calculates:
91
         *
92
         *         from - from_base + to_base
93
         *
94
         * But takes care of overflows/underflows and avoids signed operations. */
95

96
        if (from >= from_base) { /* In the future */
7,951✔
97
                usec_t delta = from - from_base;
19✔
98

99
                if (to_base >= USEC_INFINITY - delta) /* overflow? */
19✔
100
                        return USEC_INFINITY;
101

102
                return to_base + delta;
19✔
103

104
        } else { /* In the past */
105
                usec_t delta = from_base - from;
7,932✔
106

107
                if (to_base <= delta) /* underflow? */
7,932✔
108
                        return 0;
109

110
                return to_base - delta;
7,925✔
111
        }
112
}
113

114
usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
564✔
115

116
        /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
117
         * onto itself */
118
        if (map_clock_id(from_clock) == map_clock_id(to_clock))
564✔
119
                return from;
120

121
        /* Keep infinity as is */
122
        if (from == USEC_INFINITY)
564✔
123
                return from;
124

125
        return map_clock_usec_raw(from, now(from_clock), now(to_clock));
528✔
126
}
127

128
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
13✔
129
        assert(ts);
13✔
130

131
        if (!timestamp_is_set(u)) {
13✔
132
                ts->realtime = ts->monotonic = u;
×
133
                return ts;
×
134
        }
135

136
        ts->realtime = u;
13✔
137
        ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
13✔
138
        return ts;
13✔
139
}
140

141
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
292✔
142
        usec_t nowr;
292✔
143

144
        assert(ts);
292✔
145

146
        if (!timestamp_is_set(u)) {
292✔
147
                ts->realtime = ts->monotonic = ts->boottime = u;
×
148
                return ts;
×
149
        }
150

151
        nowr = now(CLOCK_REALTIME);
292✔
152

153
        ts->realtime = u;
292✔
154
        ts->monotonic = map_clock_usec_raw(u, nowr, now(CLOCK_MONOTONIC));
292✔
155
        ts->boottime = map_clock_usec_raw(u, nowr, now(CLOCK_BOOTTIME));
292✔
156

157
        return ts;
292✔
158
}
159

160
triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) {
×
161
        usec_t nowb;
×
162

163
        assert(ts);
×
164

165
        if (u == USEC_INFINITY) {
×
166
                ts->realtime = ts->monotonic = ts->boottime = u;
×
167
                return ts;
×
168
        }
169

170
        nowb = now(CLOCK_BOOTTIME);
×
171

172
        ts->boottime = u;
×
173
        ts->monotonic = map_clock_usec_raw(u, nowb, now(CLOCK_MONOTONIC));
×
174
        ts->realtime = map_clock_usec_raw(u, nowb, now(CLOCK_REALTIME));
×
175

176
        return ts;
×
177
}
178

179
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
237✔
180
        assert(ts);
237✔
181

182
        if (u == USEC_INFINITY) {
237✔
183
                ts->realtime = ts->monotonic = USEC_INFINITY;
×
184
                return ts;
×
185
        }
186

187
        ts->monotonic = u;
237✔
188
        ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
237✔
189
        return ts;
237✔
190
}
191

192
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
663✔
193
        usec_t nowm;
663✔
194

195
        assert(ts);
663✔
196

197
        if (u == USEC_INFINITY) {
663✔
198
                ts->realtime = ts->monotonic = USEC_INFINITY;
×
199
                return ts;
×
200
        }
201

202
        nowm = now(CLOCK_BOOTTIME);
663✔
203
        ts->monotonic = map_clock_usec_raw(u, nowm, now(CLOCK_MONOTONIC));
663✔
204
        ts->realtime = map_clock_usec_raw(u, nowm, now(CLOCK_REALTIME));
663✔
205
        return ts;
663✔
206
}
207

208
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
4,330,394✔
209
        assert(ts);
4,330,394✔
210

211
        switch (clock) {
4,330,394✔
212

213
        case CLOCK_REALTIME:
2,670,776✔
214
        case CLOCK_REALTIME_ALARM:
215
                return ts->realtime;
2,670,776✔
216

217
        case CLOCK_MONOTONIC:
1,619,757✔
218
                return ts->monotonic;
1,619,757✔
219

220
        case CLOCK_BOOTTIME:
39,861✔
221
        case CLOCK_BOOTTIME_ALARM:
222
                return ts->boottime;
39,861✔
223

224
        default:
225
                return USEC_INFINITY;
226
        }
227
}
228

229
usec_t timespec_load(const struct timespec *ts) {
40,163,735✔
230
        assert(ts);
40,163,735✔
231

232
        if (ts->tv_sec < 0 || ts->tv_nsec < 0)
40,163,735✔
233
                return USEC_INFINITY;
234

235
        if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
40,163,735✔
236
                return USEC_INFINITY;
237

238
        return
40,163,735✔
239
                (usec_t) ts->tv_sec * USEC_PER_SEC +
40,163,735✔
240
                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
241
}
242

243
nsec_t timespec_load_nsec(const struct timespec *ts) {
1,446✔
244
        assert(ts);
1,446✔
245

246
        if (ts->tv_sec < 0 || ts->tv_nsec < 0)
1,446✔
247
                return NSEC_INFINITY;
248

249
        if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
1,446✔
250
                return NSEC_INFINITY;
251

252
        return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
1,446✔
253
}
254

255
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
2,629,330✔
256
        assert(ts);
2,629,330✔
257

258
        if (u == USEC_INFINITY ||
2,629,330✔
259
            u / USEC_PER_SEC >= TIME_T_MAX) {
260
                ts->tv_sec = (time_t) -1;
×
261
                ts->tv_nsec = -1L;
×
262
                return ts;
×
263
        }
264

265
        ts->tv_sec = (time_t) (u / USEC_PER_SEC);
2,629,330✔
266
        ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
2,629,330✔
267

268
        return ts;
2,629,330✔
269
}
270

271
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
56✔
272
        assert(ts);
56✔
273

274
        if (n == NSEC_INFINITY ||
56✔
275
            n / NSEC_PER_SEC >= TIME_T_MAX) {
276
                ts->tv_sec = (time_t) -1;
×
277
                ts->tv_nsec = -1L;
×
278
                return ts;
×
279
        }
280

281
        ts->tv_sec = (time_t) (n / NSEC_PER_SEC);
56✔
282
        ts->tv_nsec = (long) (n % NSEC_PER_SEC);
56✔
283

284
        return ts;
56✔
285
}
286

287
usec_t timeval_load(const struct timeval *tv) {
1,369,114✔
288
        assert(tv);
1,369,114✔
289

290
        if (tv->tv_sec < 0 || tv->tv_usec < 0)
1,369,114✔
291
                return USEC_INFINITY;
292

293
        if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
1,369,114✔
294
                return USEC_INFINITY;
295

296
        return
1,369,114✔
297
                (usec_t) tv->tv_sec * USEC_PER_SEC +
1,369,114✔
298
                (usec_t) tv->tv_usec;
299
}
300

301
struct timeval *timeval_store(struct timeval *tv, usec_t u) {
158,194✔
302
        assert(tv);
158,194✔
303

304
        if (u == USEC_INFINITY ||
158,194✔
305
            u / USEC_PER_SEC > TIME_T_MAX) {
306
                tv->tv_sec = (time_t) -1;
×
307
                tv->tv_usec = (suseconds_t) -1;
×
308
        } else {
309
                tv->tv_sec = (time_t) (u / USEC_PER_SEC);
158,194✔
310
                tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
158,194✔
311
        }
312

313
        return tv;
158,194✔
314
}
315

316
char* format_timestamp_style(
5,764✔
317
                char *buf,
318
                size_t l,
319
                usec_t t,
320
                TimestampStyle style) {
321

322
        /* The weekdays in non-localized (English) form. We use this instead of the localized form, so that
323
         * our generated timestamps may be parsed with parse_timestamp(), and always read the same. */
324
        static const char * const weekdays[] = {
5,764✔
325
                [0] = "Sun",
326
                [1] = "Mon",
327
                [2] = "Tue",
328
                [3] = "Wed",
329
                [4] = "Thu",
330
                [5] = "Fri",
331
                [6] = "Sat",
332
        };
333

334
        struct tm tm;
5,764✔
335
        bool utc, us;
5,764✔
336
        size_t n;
5,764✔
337

338
        assert(buf);
5,764✔
339
        assert(style >= 0);
5,764✔
340
        assert(style < _TIMESTAMP_STYLE_MAX);
5,764✔
341

342
        if (!timestamp_is_set(t))
5,764✔
343
                return NULL; /* Timestamp is unset */
5,764✔
344

345
        if (style == TIMESTAMP_UNIX) {
4,212✔
346
                if (l < (size_t) (1 + 1 + 1))
100✔
347
                        return NULL; /* not enough space for even the shortest of forms */
348

349
                return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC);  /* round down μs → s */
100✔
350
        }
351

352
        utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
4,112✔
353
        us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC);
4,112✔
354

355
        if (l < (size_t) (3 +                   /* week day */
3,644✔
356
                          1 + 10 +              /* space and date */
357
                          style == TIMESTAMP_DATE ? 0 :
358
                          (1 + 8 +              /* space and time */
359
                           (us ? 1 + 6 : 0) +   /* "." and microsecond part */
4,112✔
360
                           1 + (utc ? 3 : 1)) + /* space and shortest possible zone */
7,887✔
361
                          1))
362
                return NULL; /* Not enough space even for the shortest form. */
363

364
        /* Let's not format times with years > 9999 */
365
        if (t > USEC_TIMESTAMP_FORMATTABLE_MAX) {
4,112✔
366
                static const char* const xxx[_TIMESTAMP_STYLE_MAX] = {
4✔
367
                        [TIMESTAMP_PRETTY] = "--- XXXX-XX-XX XX:XX:XX",
368
                        [TIMESTAMP_US]     = "--- XXXX-XX-XX XX:XX:XX.XXXXXX",
369
                        [TIMESTAMP_UTC]    = "--- XXXX-XX-XX XX:XX:XX UTC",
370
                        [TIMESTAMP_US_UTC] = "--- XXXX-XX-XX XX:XX:XX.XXXXXX UTC",
371
                        [TIMESTAMP_DATE]   = "--- XXXX-XX-XX",
372
                };
373

374
                assert(l >= strlen(xxx[style]) + 1);
4✔
375
                return strcpy(buf, xxx[style]);
4✔
376
        }
377

378
        if (localtime_or_gmtime_usec(t, utc, &tm) < 0)
4,108✔
379
                return NULL;
380

381
        /* Start with the week day */
382
        assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
4,108✔
383
        memcpy(buf, weekdays[tm.tm_wday], 4);
4,108✔
384

385
        if (style == TIMESTAMP_DATE) {
4,108✔
386
                /* Special format string if only date should be shown. */
387
                if (strftime(buf + 3, l - 3, " %Y-%m-%d", &tm) <= 0)
104✔
388
                        return NULL; /* Doesn't fit */
389

390
                return buf;
104✔
391
        }
392

393
        /* Add the main components */
394
        if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
4,004✔
395
                return NULL; /* Doesn't fit */
396

397
        /* Append the microseconds part, if that's requested */
398
        if (us) {
4,004✔
399
                n = strlen(buf);
467✔
400
                if (n + 8 > l)
467✔
401
                        return NULL; /* Microseconds part doesn't fit. */
402

403
                sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
467✔
404
        }
405

406
        /* Append the timezone */
407
        n = strlen(buf);
4,004✔
408
        if (utc) {
4,004✔
409
                /* If this is UTC then let's explicitly use the "UTC" string here, because gmtime_r()
410
                 * normally uses the obsolete "GMT" instead. */
411
                if (n + 5 > l)
230✔
412
                        return NULL; /* "UTC" doesn't fit. */
413

414
                strcpy(buf + n, " UTC");
230✔
415

416
        } else if (!isempty(tm.tm_zone)) {
3,774✔
417
                size_t tn;
3,774✔
418

419
                /* An explicit timezone is specified, let's use it, if it fits */
420
                tn = strlen(tm.tm_zone);
3,774✔
421
                if (n + 1 + tn + 1 > l) {
3,774✔
422
                        /* The full time zone does not fit in. Yuck. */
423

424
                        if (n + 1 + _POSIX_TZNAME_MAX + 1 > l)
×
425
                                return NULL; /* Not even enough space for the POSIX minimum (of 6)? In that
×
426
                                              * case, complain that it doesn't fit. */
427

428
                        /* So the time zone doesn't fit in fully, but the caller passed enough space for the
429
                         * POSIX minimum time zone length. In this case suppress the timezone entirely, in
430
                         * order not to dump an overly long, hard to read string on the user. This should be
431
                         * safe, because the user will assume the local timezone anyway if none is shown. And
432
                         * so does parse_timestamp(). */
433
                } else {
434
                        buf[n++] = ' ';
3,774✔
435
                        strcpy(buf + n, tm.tm_zone);
3,774✔
436
                }
437
        }
438

439
        return buf;
440
}
441

442
char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, clockid_t clock, bool implicit_left) {
1,372✔
443
        const char *s;
1,372✔
444
        usec_t n, d;
1,372✔
445

446
        assert(buf);
1,372✔
447

448
        if (!timestamp_is_set(t))
1,372✔
449
                return NULL;
450

451
        n = now(clock);
1,366✔
452
        if (n > t) {
1,366✔
453
                d = n - t;
1,175✔
454
                s = " ago";
1,175✔
455
        } else {
456
                d = t - n;
191✔
457
                s = implicit_left ? "" : " left";
191✔
458
        }
459

460
        if (d >= USEC_PER_YEAR) {
1,366✔
461
                usec_t years = d / USEC_PER_YEAR;
735✔
462
                usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
735✔
463

464
                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
2,184✔
465
                                years,
466
                                years == 1 ? "year" : "years",
467
                                months,
468
                                months == 1 ? "month" : "months",
469
                                s);
470
        } else if (d >= USEC_PER_MONTH) {
631✔
471
                usec_t months = d / USEC_PER_MONTH;
13✔
472
                usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
13✔
473

474
                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
31✔
475
                                months,
476
                                months == 1 ? "month" : "months",
477
                                days,
478
                                days == 1 ? "day" : "days",
479
                                s);
480
        } else if (d >= USEC_PER_WEEK) {
618✔
481
                usec_t weeks = d / USEC_PER_WEEK;
8✔
482
                usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
8✔
483

484
                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
16✔
485
                                weeks,
486
                                weeks == 1 ? "week" : "weeks",
487
                                days,
488
                                days == 1 ? "day" : "days",
489
                                s);
490
        } else if (d >= 2*USEC_PER_DAY)
610✔
491
                (void) snprintf(buf, l, USEC_FMT " days%s", d / USEC_PER_DAY,s);
×
492
        else if (d >= 25*USEC_PER_HOUR)
610✔
493
                (void) snprintf(buf, l, "1 day " USEC_FMT "h%s",
11✔
494
                                (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
11✔
495
        else if (d >= 6*USEC_PER_HOUR)
599✔
496
                (void) snprintf(buf, l, USEC_FMT "h%s",
20✔
497
                                d / USEC_PER_HOUR, s);
498
        else if (d >= USEC_PER_HOUR)
579✔
499
                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min%s",
×
500
                                d / USEC_PER_HOUR,
501
                                (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
×
502
        else if (d >= 5*USEC_PER_MINUTE)
579✔
503
                (void) snprintf(buf, l, USEC_FMT "min%s",
7✔
504
                                d / USEC_PER_MINUTE, s);
505
        else if (d >= USEC_PER_MINUTE)
572✔
506
                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s%s",
2✔
507
                                d / USEC_PER_MINUTE,
508
                                (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
2✔
509
        else if (d >= USEC_PER_SEC)
570✔
510
                (void) snprintf(buf, l, USEC_FMT "s%s",
480✔
511
                                d / USEC_PER_SEC, s);
512
        else if (d >= USEC_PER_MSEC)
90✔
513
                (void) snprintf(buf, l, USEC_FMT "ms%s",
87✔
514
                                d / USEC_PER_MSEC, s);
515
        else if (d > 0)
3✔
516
                (void) snprintf(buf, l, USEC_FMT"us%s",
3✔
517
                                d, s);
518
        else
519
                (void) snprintf(buf, l, "now");
×
520

521
        buf[l-1] = 0;
1,366✔
522
        return buf;
1,366✔
523
}
524

525
char* format_timespan(char *buf, size_t l, usec_t t, usec_t accuracy) {
11,876✔
526
        static const struct {
11,876✔
527
                const char *suffix;
528
                usec_t usec;
529
        } table[] = {
530
                { "y",     USEC_PER_YEAR   },
531
                { "month", USEC_PER_MONTH  },
532
                { "w",     USEC_PER_WEEK   },
533
                { "d",     USEC_PER_DAY    },
534
                { "h",     USEC_PER_HOUR   },
535
                { "min",   USEC_PER_MINUTE },
536
                { "s",     USEC_PER_SEC    },
537
                { "ms",    USEC_PER_MSEC   },
538
                { "us",    1               },
539
        };
540

541
        char *p = ASSERT_PTR(buf);
11,876✔
542
        bool something = false;
11,876✔
543

544
        assert(l > 0);
11,876✔
545

546
        if (t == USEC_INFINITY) {
11,876✔
547
                strncpy(p, "infinity", l-1);
914✔
548
                p[l-1] = 0;
914✔
549
                return p;
914✔
550
        }
551

552
        if (t <= 0) {
10,962✔
553
                strncpy(p, "0", l-1);
539✔
554
                p[l-1] = 0;
539✔
555
                return p;
539✔
556
        }
557

558
        /* The result of this function can be parsed with parse_sec */
559

560
        FOREACH_ELEMENT(i, table) {
86,649✔
561
                int k = 0;
86,564✔
562
                size_t n;
86,564✔
563
                bool done = false;
86,564✔
564
                usec_t a, b;
86,564✔
565

566
                if (t <= 0)
86,564✔
567
                        break;
568

569
                if (t < accuracy && something)
76,273✔
570
                        break;
571

572
                if (t < i->usec)
76,226✔
573
                        continue;
60,985✔
574

575
                if (l <= 1)
15,241✔
576
                        break;
577

578
                a = t / i->usec;
15,241✔
579
                b = t % i->usec;
15,241✔
580

581
                /* Let's see if we should shows this in dot notation */
582
                if (t < USEC_PER_MINUTE && b > 0) {
15,241✔
583
                        signed char j = 0;
584

585
                        for (usec_t cc = i->usec; cc > 1; cc /= 10)
46,099✔
586
                                j++;
37,992✔
587

588
                        for (usec_t cc = accuracy; cc > 1; cc /= 10) {
38,492✔
589
                                b /= 10;
30,385✔
590
                                j--;
30,385✔
591
                        }
592

593
                        if (j > 0) {
8,107✔
594
                                k = snprintf(p, l,
3,474✔
595
                                             "%s"USEC_FMT".%0*"PRI_USEC"%s",
596
                                             p > buf ? " " : "",
597
                                             a,
598
                                             j,
599
                                             b,
600
                                             i->suffix);
1,737✔
601

602
                                t = 0;
1,737✔
603
                                done = true;
1,737✔
604
                        }
605
                }
606

607
                /* No? Then let's show it normally */
608
                if (!done) {
1,737✔
609
                        k = snprintf(p, l,
27,008✔
610
                                     "%s"USEC_FMT"%s",
611
                                     p > buf ? " " : "",
612
                                     a,
613
                                     i->suffix);
13,504✔
614

615
                        t = b;
13,504✔
616
                }
617

618
                n = MIN((size_t) k, l-1);
15,241✔
619

620
                l -= n;
15,241✔
621
                p += n;
15,241✔
622

623
                something = true;
15,241✔
624
        }
625

626
        *p = 0;
10,423✔
627

628
        return buf;
10,423✔
629
}
630

631
static int parse_timestamp_impl(
2,121✔
632
                const char *t,
633
                size_t max_len,
634
                bool utc,
635
                int isdst,
636
                long gmtoff,
637
                usec_t *ret) {
638

639
        static const struct {
2,121✔
640
                const char *name;
641
                const int nr;
642
        } day_nr[] = {
643
                { "Sunday",    0 },
644
                { "Sun",       0 },
645
                { "Monday",    1 },
646
                { "Mon",       1 },
647
                { "Tuesday",   2 },
648
                { "Tue",       2 },
649
                { "Wednesday", 3 },
650
                { "Wed",       3 },
651
                { "Thursday",  4 },
652
                { "Thu",       4 },
653
                { "Friday",    5 },
654
                { "Fri",       5 },
655
                { "Saturday",  6 },
656
                { "Sat",       6 },
657
        };
658

659
        _cleanup_free_ char *t_alloc = NULL;
2,121✔
660
        usec_t usec, plus = 0, minus = 0;
2,121✔
661
        bool with_tz = false;
2,121✔
662
        int r, weekday = -1;
2,121✔
663
        unsigned fractional = 0;
2,121✔
664
        const char *k;
2,121✔
665
        struct tm tm, copy;
2,121✔
666

667
        /* Allowed syntaxes:
668
         *
669
         *   2012-09-22 16:34:22.1[2[3[4[5[6]]]]]
670
         *   2012-09-22 16:34:22  (µsec will be set to 0)
671
         *   2012-09-22 16:34     (seconds will be set to 0)
672
         *   2012-09-22T16:34:22.1[2[3[4[5[6]]]]]
673
         *   2012-09-22T16:34:22  (µsec will be set to 0)
674
         *   2012-09-22T16:34     (seconds will be set to 0)
675
         *   2012-09-22           (time will be set to 00:00:00)
676
         *   16:34:22             (date will be set to today)
677
         *   16:34                (date will be set to today, seconds to 0)
678
         *   now
679
         *   yesterday            (time is set to 00:00:00)
680
         *   today                (time is set to 00:00:00)
681
         *   tomorrow             (time is set to 00:00:00)
682
         *   +5min
683
         *   -5days
684
         *   @2147483647          (seconds since epoch)
685
         *
686
         * Note, on DST change, 00:00:00 may not exist and in that case the time part may be shifted.
687
         * E.g. "Sun 2023-03-13 America/Havana" is parsed as "Sun 2023-03-13 01:00:00 CDT".
688
         *
689
         * A simplified strptime-spelled RFC3339 ABNF looks like
690
         *   "%Y-%m-%d" "T" "%H" ":" "%M" ":" "%S" [".%N"] ("Z" / (("+" / "-") "%H:%M"))
691
         * We additionally allow no seconds and inherited timezone
692
         * for symmetry with our other syntaxes and improved interactive usability:
693
         *   "%Y-%m-%d" "T" "%H" ":" "%M" ":" ["%S" [".%N"]] ["Z" / (("+" / "-") "%H:%M")]
694
         * RFC3339 defines time-secfrac to as "." 1*DIGIT, but we limit to 6 digits,
695
         * since we're limited to 1µs resolution.
696
         * We also accept "Sat 2012-09-22T16:34:22", RFC3339 warns against it.
697
         */
698

699
        assert(t);
2,121✔
700

701
        if (max_len != SIZE_MAX) {
2,121✔
702
                /* If the input string contains timezone, then cut it here. */
703

704
                if (max_len == 0) /* Can't be the only field */
1,690✔
705
                        return -EINVAL;
706

707
                t_alloc = strndup(t, max_len);
1,690✔
708
                if (!t_alloc)
1,690✔
709
                        return -ENOMEM;
710

711
                t = t_alloc;
712
                with_tz = true;
713
        }
714

715
        if (utc) {
2,121✔
716
                /* glibc accepts gmtoff more than 24 hours, but we refuse it. */
717
                if ((usec_t) labs(gmtoff) * USEC_PER_SEC > USEC_PER_DAY)
991✔
718
                        return -EINVAL;
719
        } else {
720
                if (gmtoff != 0)
1,130✔
721
                        return -EINVAL;
722
        }
723

724
        if (t[0] == '@' && !with_tz)
2,121✔
725
                return parse_sec(t + 1, ret);
103✔
726

727
        usec = now(CLOCK_REALTIME);
2,018✔
728

729
        if (!with_tz) {
2,018✔
730
                if (streq(t, "now"))
328✔
731
                        goto finish;
5✔
732

733
                if (t[0] == '+') {
323✔
734
                        r = parse_sec(t+1, &plus);
6✔
735
                        if (r < 0)
6✔
736
                                return r;
737

738
                        goto finish;
6✔
739
                }
740

741
                if (t[0] == '-') {
317✔
742
                        r = parse_sec(t+1, &minus);
6✔
743
                        if (r < 0)
6✔
744
                                return r;
745

746
                        goto finish;
6✔
747
                }
748

749
                if ((k = endswith(t, " ago"))) {
311✔
750
                        _cleanup_free_ char *buf = NULL;
3✔
751

752
                        buf = strndup(t, k - t);
3✔
753
                        if (!buf)
3✔
754
                                return -ENOMEM;
755

756
                        r = parse_sec(buf, &minus);
3✔
757
                        if (r < 0)
3✔
758
                                return r;
759

760
                        goto finish;
3✔
761
                }
762

763
                if ((k = endswith(t, " left"))) {
308✔
764
                        _cleanup_free_ char *buf = NULL;
102✔
765

766
                        buf = strndup(t, k - t);
102✔
767
                        if (!buf)
102✔
768
                                return -ENOMEM;
769

770
                        r = parse_sec(buf, &plus);
102✔
771
                        if (r < 0)
102✔
772
                                return r;
773

774
                        goto finish;
102✔
775
                }
776
        }
777

778
        r = localtime_or_gmtime_usec(usec, utc, &tm);
1,896✔
779
        if (r < 0)
1,896✔
780
                return r;
781

782
        tm.tm_isdst = isdst;
1,896✔
783

784
        if (streq(t, "today")) {
1,896✔
785
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
13✔
786
                goto from_tm;
13✔
787

788
        } else if (streq(t, "yesterday")) {
1,883✔
789
                tm.tm_mday--;
10✔
790
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
10✔
791
                goto from_tm;
10✔
792

793
        } else if (streq(t, "tomorrow")) {
1,873✔
794
                tm.tm_mday++;
8✔
795
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
8✔
796
                goto from_tm;
8✔
797
        }
798

799
        FOREACH_ELEMENT(day, day_nr) {
19,643✔
800
                k = startswith_no_case(t, day->name);
19,330✔
801
                if (!k || *k != ' ')
19,330✔
802
                        continue;
17,778✔
803

804
                weekday = day->nr;
1,552✔
805
                t = k + 1;
1,552✔
806
                break;
1,552✔
807
        }
808

809
        copy = tm;
1,865✔
810
        k = strptime(t, "%y-%m-%d %H:%M:%S", &tm);
1,865✔
811
        if (k) {
1,865✔
812
                if (*k == '.')
100✔
813
                        goto parse_usec;
64✔
814
                else if (*k == 0)
36✔
815
                        goto from_tm;
32✔
816
        }
817

818
        /* Our "canonical" RFC3339 syntax variant */
819
        tm = copy;
1,769✔
820
        k = strptime(t, "%Y-%m-%d %H:%M:%S", &tm);
1,769✔
821
        if (k) {
1,769✔
822
                if (*k == '.')
1,471✔
823
                        goto parse_usec;
342✔
824
                else if (*k == 0)
1,129✔
825
                        goto from_tm;
1,120✔
826
        }
827

828
        /* RFC3339 syntax */
829
        tm = copy;
307✔
830
        k = strptime(t, "%Y-%m-%dT%H:%M:%S", &tm);
307✔
831
        if (k) {
307✔
832
                if (*k == '.')
42✔
833
                        goto parse_usec;
20✔
834
                else if (*k == 0)
22✔
835
                        goto from_tm;
22✔
836
        }
837

838
        /* Support OUTPUT_SHORT and OUTPUT_SHORT_PRECISE formats */
839
        tm = copy;
265✔
840
        k = strptime(t, "%b %d %H:%M:%S", &tm);
265✔
841
        if (k) {
265✔
842
                if (*k == '.')
4✔
843
                        goto parse_usec;
2✔
844
                else if (*k == 0)
2✔
845
                        goto from_tm;
2✔
846
        }
847

848
        tm = copy;
261✔
849
        k = strptime(t, "%y-%m-%d %H:%M", &tm);
261✔
850
        if (k && *k == 0) {
261✔
851
                tm.tm_sec = 0;
30✔
852
                goto from_tm;
30✔
853
        }
854

855
        /* Our "canonical" RFC3339 syntax variant without seconds */
856
        tm = copy;
231✔
857
        k = strptime(t, "%Y-%m-%d %H:%M", &tm);
231✔
858
        if (k && *k == 0) {
231✔
859
                tm.tm_sec = 0;
31✔
860
                goto from_tm;
31✔
861
        }
862

863
        /* RFC3339 syntax without seconds */
864
        tm = copy;
200✔
865
        k = strptime(t, "%Y-%m-%dT%H:%M", &tm);
200✔
866
        if (k && *k == 0) {
200✔
867
                tm.tm_sec = 0;
8✔
868
                goto from_tm;
8✔
869
        }
870

871
        tm = copy;
192✔
872
        k = strptime(t, "%y-%m-%d", &tm);
192✔
873
        if (k && *k == 0) {
192✔
874
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
×
875
                goto from_tm;
×
876
        }
877

878
        tm = copy;
192✔
879
        k = strptime(t, "%Y-%m-%d", &tm);
192✔
880
        if (k && *k == 0) {
192✔
881
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
106✔
882
                goto from_tm;
106✔
883
        }
884

885
        tm = copy;
86✔
886
        k = strptime(t, "%H:%M:%S", &tm);
86✔
887
        if (k) {
86✔
888
                if (*k == '.')
28✔
889
                        goto parse_usec;
8✔
890
                else if (*k == 0)
20✔
891
                        goto from_tm;
20✔
892
        }
893

894
        tm = copy;
58✔
895
        k = strptime(t, "%H:%M", &tm);
58✔
896
        if (k && *k == 0) {
58✔
897
                tm.tm_sec = 0;
6✔
898
                goto from_tm;
6✔
899
        }
900

901
        return -EINVAL;
902

903
parse_usec:
436✔
904
        k++;
436✔
905
        r = parse_fractional_part_u(&k, 6, &fractional);
436✔
906
        if (r < 0)
436✔
907
                return -EINVAL;
908
        if (*k != '\0')
436✔
909
                return -EINVAL;
910

911
from_tm:
420✔
912
        assert(plus == 0);
1,828✔
913
        assert(minus == 0);
1,828✔
914

915
        if (weekday >= 0 && tm.tm_wday != weekday)
1,828✔
916
                return -EINVAL;
917

918
        if (gmtoff < 0) {
1,828✔
919
                plus = -gmtoff * USEC_PER_SEC;
110✔
920

921
                /* If gmtoff is negative, the string may be too old to be parsed as UTC.
922
                 * E.g. 1969-12-31 23:00:00 -06 == 1970-01-01 05:00:00 UTC
923
                 * We assumed that gmtoff is in the range of -24:00…+24:00, hence the only date we need to
924
                 * handle here is 1969-12-31. So, let's shift the date with one day, then subtract the shift
925
                 * later. */
926
                if (tm.tm_year == 69 && tm.tm_mon == 11 && tm.tm_mday == 31) {
110✔
927
                        /* Thu 1970-01-01-00:00:00 */
928
                        tm.tm_year = 70;
96✔
929
                        tm.tm_mon = 0;
96✔
930
                        tm.tm_mday = 1;
96✔
931
                        tm.tm_wday = 4;
96✔
932
                        tm.tm_yday = 0;
96✔
933
                        minus = USEC_PER_DAY;
96✔
934
                }
935
        } else
936
                minus = gmtoff * USEC_PER_SEC;
1,718✔
937

938
        r = mktime_or_timegm_usec(&tm, utc, &usec);
1,828✔
939
        if (r < 0)
1,828✔
940
                return r;
941

942
        usec = usec_add(usec, fractional);
3,650✔
943

944
finish:
1,947✔
945
        usec = usec_add(usec, plus);
1,947✔
946

947
        if (usec < minus)
1,947✔
948
                return -EINVAL;
949

950
        usec = usec_sub_unsigned(usec, minus);
1,946✔
951

952
        if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
1,946✔
953
                return -EINVAL;
954

955
        if (ret)
1,945✔
956
                *ret = usec;
1,943✔
957
        return 0;
958
}
959

960
static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool valid_tz, usec_t *ret) {
945✔
961
        assert(t);
945✔
962

963
        tzset();
945✔
964

965
        for (int j = 0; j <= 1; j++) {
2,821✔
966
                if (isempty(tzname[j]))
1,883✔
967
                        continue;
×
968

969
                if (!streq(t + tz_offset, tzname[j]))
1,883✔
970
                        continue;
1,876✔
971

972
                /* The specified timezone matches tzname[] of the local timezone. */
973
                return parse_timestamp_impl(t, tz_offset - 1, /* utc = */ false, /* isdst = */ j, /* gmtoff = */ 0, ret);
7✔
974
        }
975

976
        /* If we know that the last word is a valid timezone (e.g. Asia/Tokyo), then simply drop the timezone
977
         * and parse the remaining string as a local time. If we know that the last word is not a timezone,
978
         * then assume that it is a part of the time and try to parse the whole string as a local time. */
979
        return parse_timestamp_impl(t, valid_tz ? tz_offset - 1 : SIZE_MAX,
938✔
980
                                    /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
981
}
982

983
typedef struct ParseTimestampResult {
984
        usec_t usec;
985
        int return_value;
986
} ParseTimestampResult;
987

988
int parse_timestamp(const char *t, usec_t *ret) {
2,088✔
989
        ParseTimestampResult *shared, tmp;
2,088✔
990
        const char *k, *tz, *current_tz;
2,088✔
991
        size_t max_len, t_len;
2,088✔
992
        struct tm tm;
2,088✔
993
        int r;
2,088✔
994

995
        assert(t);
2,088✔
996

997
        t_len = strlen(t);
2,088✔
998
        if (t_len > 2 && t[t_len - 1] == 'Z') {
2,088✔
999
                /* Try to parse as RFC3339-style welded UTC: "1985-04-12T23:20:50.52Z" */
1000
                r = parse_timestamp_impl(t, t_len - 1, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
67✔
1001
                if (r >= 0)
67✔
1002
                        return r;
2,088✔
1003
        }
1004

1005
        if (t_len > 7 && IN_SET(t[t_len - 6], '+', '-') && t[t_len - 7] != ' ') {  /* RFC3339-style welded offset: "1990-12-31T15:59:60-08:00" */
2,040✔
1006
                k = strptime(&t[t_len - 6], "%z", &tm);
143✔
1007
                if (k && *k == '\0')
143✔
1008
                        return parse_timestamp_impl(t, t_len - 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
40✔
1009
        }
1010

1011
        tz = strrchr(t, ' ');
2,014✔
1012
        if (!tz)
2,014✔
1013
                return parse_timestamp_impl(t, /* max_len = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
185✔
1014

1015
        max_len = tz - t;
1,829✔
1016
        tz++;
1,829✔
1017

1018
        /* Shortcut, parse the string as UTC. */
1019
        if (streq(tz, "UTC"))
1,829✔
1020
                return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ 0, ret);
758✔
1021

1022
        /* If the timezone is compatible with RFC-822/ISO 8601 (e.g. +06, or -03:00) then parse the string as
1023
         * UTC and shift the result. Note, this must be earlier than the timezone check with tzname[], as
1024
         * tzname[] may be in the same format. */
1025
        k = strptime(tz, "%z", &tm);
1,071✔
1026
        if (k && *k == '\0')
1,071✔
1027
                return parse_timestamp_impl(t, max_len, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
126✔
1028

1029
        /* If the last word is not a timezone file (e.g. Asia/Tokyo), then let's check if it matches
1030
         * tzname[] of the local timezone, e.g. JST or CEST. */
1031
        if (!timezone_is_valid(tz, LOG_DEBUG))
945✔
1032
                return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ false, ret);
246✔
1033

1034
        /* Shortcut. If the current $TZ is equivalent to the specified timezone, it is not necessary to fork
1035
         * the process. */
1036
        current_tz = getenv("TZ");
699✔
1037
        if (current_tz && *current_tz == ':' && streq(current_tz + 1, tz))
699✔
1038
                return parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, ret);
×
1039

1040
        /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
1041
         * the specified timezone in the child process. */
1042

1043
        shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
699✔
1044
        if (shared == MAP_FAILED)
699✔
1045
                return negative_errno();
×
1046

1047
        /* The input string may be in argv. Let's copy it. */
1048
        _cleanup_free_ char *t_copy = strdup(t);
699✔
1049
        if (!t_copy)
699✔
1050
                return -ENOMEM;
1051

1052
        t = t_copy;
699✔
1053
        assert_se(tz = endswith(t_copy, tz));
699✔
1054

1055
        r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
699✔
1056
        if (r < 0) {
1,398✔
1057
                (void) munmap(shared, sizeof *shared);
×
1058
                return r;
×
1059
        }
1060
        if (r == 0) {
1,398✔
1061
                const char *colon_tz;
699✔
1062

1063
                /* tzset(3) says $TZ should be prefixed with ":" if we reference timezone files */
1064
                colon_tz = strjoina(":", tz);
3,495✔
1065

1066
                if (setenv("TZ", colon_tz, 1) != 0) {
699✔
1067
                        shared->return_value = negative_errno();
×
1068
                        _exit(EXIT_FAILURE);
×
1069
                }
1070

1071
                shared->return_value = parse_timestamp_maybe_with_tz(t, tz - t, /* valid_tz = */ true, &shared->usec);
699✔
1072

1073
                _exit(EXIT_SUCCESS);
699✔
1074
        }
1075

1076
        tmp = *shared;
699✔
1077
        if (munmap(shared, sizeof *shared) != 0)
699✔
1078
                return negative_errno();
×
1079

1080
        if (tmp.return_value == 0 && ret)
699✔
1081
                *ret = tmp.usec;
696✔
1082

1083
        return tmp.return_value;
1084
}
1085

1086
static const char* extract_multiplier(const char *p, usec_t *ret) {
10,053✔
1087
        static const struct {
10,053✔
1088
                const char *suffix;
1089
                usec_t usec;
1090
        } table[] = {
1091
                { "seconds", USEC_PER_SEC    },
1092
                { "second",  USEC_PER_SEC    },
1093
                { "sec",     USEC_PER_SEC    },
1094
                { "s",       USEC_PER_SEC    },
1095
                { "minutes", USEC_PER_MINUTE },
1096
                { "minute",  USEC_PER_MINUTE },
1097
                { "min",     USEC_PER_MINUTE },
1098
                { "months",  USEC_PER_MONTH  },
1099
                { "month",   USEC_PER_MONTH  },
1100
                { "M",       USEC_PER_MONTH  },
1101
                { "msec",    USEC_PER_MSEC   },
1102
                { "ms",      USEC_PER_MSEC   },
1103
                { "m",       USEC_PER_MINUTE },
1104
                { "hours",   USEC_PER_HOUR   },
1105
                { "hour",    USEC_PER_HOUR   },
1106
                { "hr",      USEC_PER_HOUR   },
1107
                { "h",       USEC_PER_HOUR   },
1108
                { "days",    USEC_PER_DAY    },
1109
                { "day",     USEC_PER_DAY    },
1110
                { "d",       USEC_PER_DAY    },
1111
                { "weeks",   USEC_PER_WEEK   },
1112
                { "week",    USEC_PER_WEEK   },
1113
                { "w",       USEC_PER_WEEK   },
1114
                { "years",   USEC_PER_YEAR   },
1115
                { "year",    USEC_PER_YEAR   },
1116
                { "y",       USEC_PER_YEAR   },
1117
                { "usec",    1ULL            },
1118
                { "us",      1ULL            },
1119
                { "μs",      1ULL            }, /* U+03bc (aka GREEK SMALL LETTER MU) */
1120
                { "µs",      1ULL            }, /* U+b5 (aka MICRO SIGN) */
1121
        };
1122

1123
        assert(p);
10,053✔
1124
        assert(ret);
10,053✔
1125

1126
        FOREACH_ELEMENT(i, table) {
174,742✔
1127
                char *e;
171,166✔
1128

1129
                e = startswith(p, i->suffix);
171,166✔
1130
                if (e) {
171,166✔
1131
                        *ret = i->usec;
6,477✔
1132
                        return e;
6,477✔
1133
                }
1134
        }
1135

1136
        return p;
1137
}
1138

1139
int parse_time(const char *t, usec_t *ret, usec_t default_unit) {
10,132✔
1140
        const char *p, *s;
10,132✔
1141

1142
        assert(t);
10,132✔
1143
        assert(default_unit > 0);
10,132✔
1144

1145
        p = skip_leading_chars(t, /* bad = */ NULL);
10,132✔
1146
        s = startswith(p, "infinity");
10,132✔
1147
        if (s) {
10,132✔
1148
                if (!in_charset(s, WHITESPACE))
274✔
1149
                        return -EINVAL;
1150

1151
                if (ret)
273✔
1152
                        *ret = USEC_INFINITY;
273✔
1153
                return 0;
273✔
1154
        }
1155

1156
        usec_t usec = 0;
1157

1158
        for (bool something = false;;) {
10,037✔
1159
                usec_t multiplier = default_unit, k;
19,895✔
1160
                long long l;
19,895✔
1161
                char *e;
19,895✔
1162

1163
                p = skip_leading_chars(p, /* bad = */ NULL);
19,895✔
1164
                if (*p == 0) {
19,895✔
1165
                        if (!something)
9,820✔
1166
                                return -EINVAL;
43✔
1167

1168
                        break;
9,815✔
1169
                }
1170

1171
                if (*p == '-') /* Don't allow "-0" */
10,075✔
1172
                        return -ERANGE;
1173

1174
                errno = 0;
10,067✔
1175
                l = strtoll(p, &e, 10);
10,067✔
1176
                if (errno > 0)
10,067✔
1177
                        return -errno;
×
1178
                if (l < 0)
10,067✔
1179
                        return -ERANGE;
1180

1181
                if (*e == '.') {
10,067✔
1182
                        p = e + 1;
55✔
1183
                        p += strspn(p, DIGITS);
55✔
1184
                } else if (e == p)
10,012✔
1185
                        return -EINVAL;
1186
                else
1187
                        p = e;
1188

1189
                s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
10,053✔
1190
                if (s == p && *s != '\0')
10,053✔
1191
                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1192
                        return -EINVAL;
1193

1194
                p = s;
10,044✔
1195

1196
                if ((usec_t) l >= USEC_INFINITY / multiplier)
10,044✔
1197
                        return -ERANGE;
1198

1199
                k = (usec_t) l * multiplier;
10,042✔
1200
                if (k >= USEC_INFINITY - usec)
10,042✔
1201
                        return -ERANGE;
1202

1203
                usec += k;
10,042✔
1204

1205
                something = true;
10,042✔
1206

1207
                if (*e == '.') {
10,042✔
1208
                        usec_t m = multiplier / 10;
48✔
1209
                        const char *b;
48✔
1210

1211
                        for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
188✔
1212
                                k = (usec_t) (*b - '0') * m;
140✔
1213
                                if (k >= USEC_INFINITY - usec)
140✔
1214
                                        return -ERANGE;
1215

1216
                                usec += k;
140✔
1217
                        }
1218

1219
                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1220
                        if (b == e + 1)
48✔
1221
                                return -EINVAL;
1222
                }
1223
        }
1224

1225
        if (ret)
9,815✔
1226
                *ret = usec;
9,813✔
1227
        return 0;
1228
}
1229

1230
int parse_sec(const char *t, usec_t *ret) {
10,098✔
1231
        return parse_time(t, ret, USEC_PER_SEC);
10,098✔
1232
}
1233

1234
int parse_sec_fix_0(const char *t, usec_t *ret) {
1,864✔
1235
        usec_t k;
1,864✔
1236
        int r;
1,864✔
1237

1238
        assert(t);
1,864✔
1239
        assert(ret);
1,864✔
1240

1241
        r = parse_sec(t, &k);
1,864✔
1242
        if (r < 0)
1,864✔
1243
                return r;
1,864✔
1244

1245
        *ret = k == 0 ? USEC_INFINITY : k;
1,864✔
1246
        return r;
1,864✔
1247
}
1248

1249
int parse_sec_def_infinity(const char *t, usec_t *ret) {
7✔
1250
        assert(t);
7✔
1251
        assert(ret);
7✔
1252

1253
        t += strspn(t, WHITESPACE);
7✔
1254
        if (isempty(t)) {
7✔
1255
                *ret = USEC_INFINITY;
2✔
1256
                return 0;
2✔
1257
        }
1258
        return parse_sec(t, ret);
5✔
1259
}
1260

1261
static const char* extract_nsec_multiplier(const char *p, nsec_t *ret) {
43✔
1262
        static const struct {
43✔
1263
                const char *suffix;
1264
                nsec_t nsec;
1265
        } table[] = {
1266
                { "seconds", NSEC_PER_SEC    },
1267
                { "second",  NSEC_PER_SEC    },
1268
                { "sec",     NSEC_PER_SEC    },
1269
                { "s",       NSEC_PER_SEC    },
1270
                { "minutes", NSEC_PER_MINUTE },
1271
                { "minute",  NSEC_PER_MINUTE },
1272
                { "min",     NSEC_PER_MINUTE },
1273
                { "months",  NSEC_PER_MONTH  },
1274
                { "month",   NSEC_PER_MONTH  },
1275
                { "M",       NSEC_PER_MONTH  },
1276
                { "msec",    NSEC_PER_MSEC   },
1277
                { "ms",      NSEC_PER_MSEC   },
1278
                { "m",       NSEC_PER_MINUTE },
1279
                { "hours",   NSEC_PER_HOUR   },
1280
                { "hour",    NSEC_PER_HOUR   },
1281
                { "hr",      NSEC_PER_HOUR   },
1282
                { "h",       NSEC_PER_HOUR   },
1283
                { "days",    NSEC_PER_DAY    },
1284
                { "day",     NSEC_PER_DAY    },
1285
                { "d",       NSEC_PER_DAY    },
1286
                { "weeks",   NSEC_PER_WEEK   },
1287
                { "week",    NSEC_PER_WEEK   },
1288
                { "w",       NSEC_PER_WEEK   },
1289
                { "years",   NSEC_PER_YEAR   },
1290
                { "year",    NSEC_PER_YEAR   },
1291
                { "y",       NSEC_PER_YEAR   },
1292
                { "usec",    NSEC_PER_USEC   },
1293
                { "us",      NSEC_PER_USEC   },
1294
                { "μs",      NSEC_PER_USEC   }, /* U+03bc (aka GREEK LETTER MU) */
1295
                { "µs",      NSEC_PER_USEC   }, /* U+b5 (aka MICRO SIGN) */
1296
                { "nsec",    1ULL            },
1297
                { "ns",      1ULL            },
1298
                { "",        1ULL            }, /* default is nsec */
1299
        };
1300

1301
        assert(p);
43✔
1302
        assert(ret);
43✔
1303

1304
        FOREACH_ELEMENT(i, table) {
791✔
1305
                char *e;
791✔
1306

1307
                e = startswith(p, i->suffix);
791✔
1308
                if (e) {
791✔
1309
                        *ret = i->nsec;
43✔
1310
                        return e;
43✔
1311
                }
1312
        }
1313

1314
        return p;
1315
}
1316

1317
int parse_nsec(const char *t, nsec_t *ret) {
47✔
1318
        const char *p, *s;
47✔
1319
        nsec_t nsec = 0;
47✔
1320
        bool something = false;
47✔
1321

1322
        assert(t);
47✔
1323
        assert(ret);
47✔
1324

1325
        p = t;
47✔
1326

1327
        p += strspn(p, WHITESPACE);
47✔
1328
        s = startswith(p, "infinity");
47✔
1329
        if (s) {
47✔
1330
                s += strspn(s, WHITESPACE);
4✔
1331
                if (*s != 0)
4✔
1332
                        return -EINVAL;
1333

1334
                *ret = NSEC_INFINITY;
2✔
1335
                return 0;
2✔
1336
        }
1337

1338
        for (;;) {
29✔
1339
                nsec_t multiplier = 1, k;
72✔
1340
                long long l;
72✔
1341
                char *e;
72✔
1342

1343
                p += strspn(p, WHITESPACE);
72✔
1344

1345
                if (*p == 0) {
72✔
1346
                        if (!something)
20✔
1347
                                return -EINVAL;
24✔
1348

1349
                        break;
19✔
1350
                }
1351

1352
                if (*p == '-') /* Don't allow "-0" */
52✔
1353
                        return -ERANGE;
1354

1355
                errno = 0;
47✔
1356
                l = strtoll(p, &e, 10);
47✔
1357
                if (errno > 0)
47✔
1358
                        return -errno;
×
1359
                if (l < 0)
47✔
1360
                        return -ERANGE;
1361

1362
                if (*e == '.') {
47✔
1363
                        p = e + 1;
31✔
1364
                        p += strspn(p, DIGITS);
31✔
1365
                } else if (e == p)
16✔
1366
                        return -EINVAL;
1367
                else
1368
                        p = e;
1369

1370
                s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
43✔
1371
                if (s == p && *s != '\0')
43✔
1372
                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1373
                        return -EINVAL;
1374

1375
                p = s;
35✔
1376

1377
                if ((nsec_t) l >= NSEC_INFINITY / multiplier)
35✔
1378
                        return -ERANGE;
1379

1380
                k = (nsec_t) l * multiplier;
34✔
1381
                if (k >= NSEC_INFINITY - nsec)
34✔
1382
                        return -ERANGE;
1383

1384
                nsec += k;
34✔
1385

1386
                something = true;
34✔
1387

1388
                if (*e == '.') {
34✔
1389
                        nsec_t m = multiplier / 10;
24✔
1390
                        const char *b;
24✔
1391

1392
                        for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
56✔
1393
                                k = (nsec_t) (*b - '0') * m;
32✔
1394
                                if (k >= NSEC_INFINITY - nsec)
32✔
1395
                                        return -ERANGE;
1396

1397
                                nsec += k;
32✔
1398
                        }
1399

1400
                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1401
                        if (b == e + 1)
24✔
1402
                                return -EINVAL;
1403
                }
1404
        }
1405

1406
        *ret = nsec;
19✔
1407

1408
        return 0;
19✔
1409
}
1410

1411
static int get_timezones_from_zone1970_tab(char ***ret) {
×
1412
        _cleanup_fclose_ FILE *f = NULL;
×
1413
        _cleanup_strv_free_ char **zones = NULL;
×
1414
        int r;
×
1415

1416
        assert(ret);
×
1417

1418
        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
×
1419
        if (!f)
×
1420
                return -errno;
×
1421

1422
        for (;;) {
×
1423
                _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
×
1424

1425
                r = read_line(f, LONG_LINE_MAX, &line);
×
1426
                if (r < 0)
×
1427
                        return r;
1428
                if (r == 0)
×
1429
                        break;
1430

1431
                const char *p = line;
×
1432

1433
                /* Line format is:
1434
                 * 'country codes' 'coordinates' 'timezone' 'comments' */
1435
                r = extract_many_words(&p, NULL, 0, &cc, &co, &tz);
×
1436
                if (r < 0)
×
1437
                        continue;
×
1438

1439
                /* Lines that start with # are comments. */
1440
                if (*cc == '#')
×
1441
                        continue;
×
1442

1443
                r = strv_extend(&zones, tz);
×
1444
                if (r < 0)
×
1445
                        return r;
1446
        }
1447

1448
        *ret = TAKE_PTR(zones);
×
1449
        return 0;
×
1450
}
1451

1452
static int get_timezones_from_tzdata_zi(char ***ret) {
4✔
1453
        _cleanup_fclose_ FILE *f = NULL;
4✔
1454
        _cleanup_strv_free_ char **zones = NULL;
4✔
1455
        int r;
4✔
1456

1457
        assert(ret);
4✔
1458

1459
        f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
4✔
1460
        if (!f)
4✔
1461
                return -errno;
×
1462

1463
        for (;;) {
17,204✔
1464
                _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
17,200✔
1465

1466
                r = read_line(f, LONG_LINE_MAX, &line);
17,204✔
1467
                if (r < 0)
17,204✔
1468
                        return r;
1469
                if (r == 0)
17,204✔
1470
                        break;
1471

1472
                const char *p = line;
17,200✔
1473

1474
                /* The only lines we care about are Zone and Link lines.
1475
                 * Zone line format is:
1476
                 * 'Zone' 'timezone' ...
1477
                 * Link line format is:
1478
                 * 'Link' 'target' 'alias'
1479
                 * See 'man zic' for more detail. */
1480
                r = extract_many_words(&p, NULL, 0, &type, &f1, &f2);
17,200✔
1481
                if (r < 0)
17,200✔
1482
                        continue;
×
1483

1484
                char *tz;
17,200✔
1485
                if (IN_SET(*type, 'Z', 'z'))
17,200✔
1486
                        /* Zone lines have timezone in field 1. */
1487
                        tz = f1;
1,364✔
1488
                else if (IN_SET(*type, 'L', 'l'))
15,836✔
1489
                        /* Link lines have timezone in field 2. */
1490
                        tz = f2;
1,028✔
1491
                else
1492
                        /* Not a line we care about. */
1493
                        continue;
14,808✔
1494

1495
                r = strv_extend(&zones, tz);
2,392✔
1496
                if (r < 0)
2,392✔
1497
                        return r;
1498
        }
1499

1500
        *ret = TAKE_PTR(zones);
4✔
1501
        return 0;
4✔
1502
}
1503

1504
int get_timezones(char ***ret) {
4✔
1505
        _cleanup_strv_free_ char **zones = NULL;
4✔
1506
        int r;
4✔
1507

1508
        assert(ret);
4✔
1509

1510
        r = get_timezones_from_tzdata_zi(&zones);
4✔
1511
        if (r == -ENOENT) {
4✔
1512
                log_debug_errno(r, "Could not get timezone data from tzdata.zi, using zone1970.tab: %m");
×
1513
                r = get_timezones_from_zone1970_tab(&zones);
×
1514
                if (r == -ENOENT)
×
1515
                        log_debug_errno(r, "Could not get timezone data from zone1970.tab, using UTC: %m");
×
1516
        }
1517
        if (r < 0 && r != -ENOENT)
4✔
1518
                return r;
1519

1520
        /* Always include UTC */
1521
        r = strv_extend(&zones, "UTC");
4✔
1522
        if (r < 0)
4✔
1523
                return r;
1524

1525
        strv_sort_uniq(zones);
4✔
1526

1527
        *ret = TAKE_PTR(zones);
4✔
1528
        return 0;
4✔
1529
}
1530

1531
int verify_timezone(const char *name, int log_level) {
1,802✔
1532
        bool slash = false;
1,802✔
1533
        const char *p, *t;
1,802✔
1534
        _cleanup_close_ int fd = -EBADF;
1,802✔
1535
        char buf[4];
1,802✔
1536
        int r;
1,802✔
1537

1538
        if (isempty(name))
3,604✔
1539
                return -EINVAL;
1540

1541
        /* Always accept "UTC" as valid timezone, since it's the fallback, even if user has no timezones installed. */
1542
        if (streq(name, "UTC"))
1,800✔
1543
                return 0;
1544

1545
        if (name[0] == '/')
1,797✔
1546
                return -EINVAL;
1547

1548
        for (p = name; *p; p++) {
22,720✔
1549
                if (!ascii_isdigit(*p) &&
21,068✔
1550
                    !ascii_isalpha(*p) &&
19,950✔
1551
                    !IN_SET(*p, '-', '_', '+', '/'))
1,988✔
1552
                        return -EINVAL;
1553

1554
                if (*p == '/') {
20,924✔
1555

1556
                        if (slash)
1,376✔
1557
                                return -EINVAL;
1558

1559
                        slash = true;
1560
                } else
1561
                        slash = false;
1562
        }
1563

1564
        if (slash)
1,652✔
1565
                return -EINVAL;
1566

1567
        if (p - name >= PATH_MAX)
1,651✔
1568
                return -ENAMETOOLONG;
1569

1570
        t = strjoina("/usr/share/zoneinfo/", name);
8,255✔
1571

1572
        fd = open(t, O_RDONLY|O_CLOEXEC);
1,651✔
1573
        if (fd < 0)
1,651✔
1574
                return log_full_errno(log_level, errno, "Failed to open timezone file '%s': %m", t);
210✔
1575

1576
        r = fd_verify_regular(fd);
1,441✔
1577
        if (r < 0)
1,441✔
1578
                return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
×
1579

1580
        r = loop_read_exact(fd, buf, 4, false);
1,441✔
1581
        if (r < 0)
1,441✔
1582
                return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
×
1583

1584
        /* Magic from tzfile(5) */
1585
        if (memcmp(buf, "TZif", 4) != 0)
1,441✔
1586
                return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
×
1587
                                      "Timezone file '%s' has wrong magic bytes", t);
1588

1589
        return 0;
1590
}
1591

1592
bool clock_supported(clockid_t clock) {
284,512✔
1593
        struct timespec ts;
284,512✔
1594

1595
        switch (clock) {
284,512✔
1596

1597
        case CLOCK_MONOTONIC:
1598
        case CLOCK_REALTIME:
1599
        case CLOCK_BOOTTIME:
1600
                /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
1601
                return true;
1602

1603
        default:
1✔
1604
                /* For everything else, check properly */
1605
                return clock_gettime(clock, &ts) >= 0;
1✔
1606
        }
1607
}
1608

1609
int get_timezone(char **ret) {
51✔
1610
        _cleanup_free_ char *t = NULL;
51✔
1611
        int r;
51✔
1612

1613
        assert(ret);
51✔
1614

1615
        r = readlink_malloc("/etc/localtime", &t);
51✔
1616
        if (r == -ENOENT)
51✔
1617
                /* If the symlink does not exist, assume "UTC", like glibc does */
1618
                return strdup_to(ret, "UTC");
×
1619
        if (r < 0)
51✔
1620
                return r; /* Return EINVAL if not a symlink */
1621

1622
        const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
51✔
1623
        if (!e)
51✔
1624
                return -EINVAL;
1625
        if (!timezone_is_valid(e, LOG_DEBUG))
51✔
1626
                return -EINVAL;
1627

1628
        return strdup_to(ret, e);
51✔
1629
}
1630

1631
int mktime_or_timegm_usec(
7,736✔
1632
                struct tm *tm, /* input + normalized output */
1633
                bool utc,
1634
                usec_t *ret) {
1635

1636
        time_t t;
7,736✔
1637

1638
        assert(tm);
7,736✔
1639

1640
        if (tm->tm_year < 69) /* early check for negative (i.e. before 1970) time_t (Note that in some timezones the epoch is in the year 1969!) */
7,736✔
1641
                return -ERANGE;
1642
        if ((usec_t) tm->tm_year > CONST_MIN(USEC_INFINITY / USEC_PER_YEAR, (usec_t) TIME_T_MAX / (365U * 24U * 60U * 60U)) - 1900) /* early check for possible overrun of usec_t or time_t */
7,734✔
1643
                return -ERANGE;
1644

1645
        /* timegm()/mktime() is a bit weird to use, since it returns -1 in two cases: on error as well as a
1646
         * valid time indicating one second before the UNIX epoch. Let's treat both cases the same here, and
1647
         * return -ERANGE for anything negative, since usec_t is unsigned, and we can thus not express
1648
         * negative times anyway. */
1649

1650
        t = utc ? timegm(tm) : mktime(tm);
7,734✔
1651
        if (t < 0) /* Refuse negative times and errors */
7,734✔
1652
                return -ERANGE;
1653
        if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */
7,732✔
1654
                return -ERANGE;
1655

1656
        if (ret)
7,732✔
1657
                *ret = (usec_t) t * USEC_PER_SEC;
2,160✔
1658
        return 0;
1659
}
1660

1661
int localtime_or_gmtime_usec(
105,404✔
1662
                usec_t t,
1663
                bool utc,
1664
                struct tm *ret) {
1665

1666
        t /= USEC_PER_SEC; /* Round down */
105,404✔
1667
        if (t > (usec_t) TIME_T_MAX)
105,404✔
1668
                return -ERANGE;
1669
        time_t sec = (time_t) t;
105,404✔
1670

1671
        struct tm buf = {};
105,404✔
1672
        if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf)))
105,404✔
1673
                return -EINVAL;
1674

1675
        if (ret)
105,404✔
1676
                *ret = buf;
105,404✔
1677

1678
        return 0;
1679
}
1680

1681
static uint32_t sysconf_clock_ticks_cached(void) {
812✔
1682
        static thread_local uint32_t hz = 0;
812✔
1683
        long r;
812✔
1684

1685
        if (hz == 0) {
812✔
1686
                r = sysconf(_SC_CLK_TCK);
80✔
1687

1688
                assert(r > 0);
80✔
1689
                hz = r;
80✔
1690
        }
1691

1692
        return hz;
812✔
1693
}
1694

1695
uint32_t usec_to_jiffies(usec_t u) {
9✔
1696
        uint32_t hz = sysconf_clock_ticks_cached();
9✔
1697
        return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
9✔
1698
}
1699

1700
usec_t jiffies_to_usec(uint32_t j) {
803✔
1701
        uint32_t hz = sysconf_clock_ticks_cached();
803✔
1702
        return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
803✔
1703
}
1704

1705
usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
665✔
1706
        usec_t a, b;
665✔
1707

1708
        if (x == USEC_INFINITY)
665✔
1709
                return USEC_INFINITY;
1710
        if (map_clock_id(from) == map_clock_id(to))
528✔
1711
                return x;
1712

1713
        a = now(from);
8✔
1714
        b = now(to);
8✔
1715

1716
        if (x > a)
8✔
1717
                /* x lies in the future */
1718
                return usec_add(b, usec_sub_unsigned(x, a));
12✔
1719
        else
1720
                /* x lies in the past */
1721
                return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
6✔
1722
}
1723

1724
bool in_utc_timezone(void) {
676✔
1725
        tzset();
676✔
1726

1727
        return timezone == 0 && daylight == 0;
676✔
1728
}
1729

1730
int time_change_fd(void) {
429✔
1731

1732
        /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1733
        static const struct itimerspec its = {
429✔
1734
                .it_value.tv_sec = TIME_T_MAX,
1735
        };
1736

1737
        _cleanup_close_ int fd = -EBADF;
858✔
1738

1739
        assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
429✔
1740

1741
        /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1742
         * CLOCK_MONOTONIC. */
1743

1744
        fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
429✔
1745
        if (fd < 0)
429✔
1746
                return -errno;
×
1747

1748
        if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
429✔
1749
                return TAKE_FD(fd);
429✔
1750

1751
        /* So apparently there are systems where time_t is 64-bit, but the kernel actually doesn't support
1752
         * 64-bit time_t. In that case configuring a timer to TIME_T_MAX will fail with EOPNOTSUPP or a
1753
         * similar error. If that's the case let's try with INT32_MAX instead, maybe that works. It's a bit
1754
         * of a black magic thing though, but what can we do?
1755
         *
1756
         * We don't want this code on x86-64, hence let's conditionalize this for systems with 64-bit time_t
1757
         * but where "long" is shorter than 64-bit, i.e. 32-bit archs.
1758
         *
1759
         * See: https://github.com/systemd/systemd/issues/14362 */
1760

1761
#if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
1762
        if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EOVERFLOW) {
1763
                static const struct itimerspec its32 = {
1764
                        .it_value.tv_sec = INT32_MAX,
1765
                };
1766

1767
                if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0)
1768
                        return TAKE_FD(fd);
1769
        }
1770
#endif
1771

1772
        return -errno;
×
1773
}
1774

1775
static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
1776
        [TIMESTAMP_PRETTY] = "pretty",
1777
        [TIMESTAMP_US]     = "us",
1778
        [TIMESTAMP_UTC]    = "utc",
1779
        [TIMESTAMP_US_UTC] = "us+utc",
1780
        [TIMESTAMP_UNIX]   = "unix",
1781
};
1782

1783
/* Use the macro for enum → string to allow for aliases */
1784
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle);
×
1785

1786
/* For the string → enum mapping we use the generic implementation, but also support two aliases */
1787
TimestampStyle timestamp_style_from_string(const char *s) {
6✔
1788
        TimestampStyle t;
6✔
1789

1790
        t = (TimestampStyle) string_table_lookup_from_string(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
6✔
1791
        if (t >= 0)
6✔
1792
                return t;
1793
        if (STRPTR_IN_SET(s, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
2✔
1794
                return TIMESTAMP_US;
1✔
1795
        if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
1✔
1796
                return TIMESTAMP_US_UTC;
1✔
1797
        return t;
1798
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc