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

systemd / systemd / 15263807472

26 May 2025 08:53PM UTC coverage: 72.046% (-0.002%) from 72.048%
15263807472

push

github

yuwata
src/core/manager.c: log preset activity on first boot

This gives us a little more information about what units were enabled
or disabled on that first boot and will be useful for OS developers
tracking down the source of unit state.

An example with this enabled looks like:

```
NET: Registered PF_VSOCK protocol family
systemd[1]: Applying preset policy.
systemd[1]: Unit /etc/systemd/system/dnsmasq.service is masked, ignoring.
systemd[1]: Unit /etc/systemd/system/systemd-repart.service is masked, ignoring.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket'.
systemd[1]: Removed '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir.mount' → '/etc/systemd/system/var-mnt-workdir.mount'.
systemd[1]: Created symlink '/etc/systemd/system/multi-user.target.wants/var-mnt-workdir\x2dtmp.mount' → '/etc/systemd/system/var-mnt-workdir\x2dtmp.mount'.
systemd[1]: Created symlink '/etc/systemd/system/afterburn-sshkeys.target.requires/afterburn-sshkeys@core.service' → '/usr/lib/systemd/system/afterburn-sshkeys@.service'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-varlink.socket' → '/usr/lib/systemd/system/systemd-resolved-varlink.socket'.
systemd[1]: Created symlink '/etc/systemd/system/sockets.target.wants/systemd-resolved-monitor.socket' → '/usr/lib/systemd/system/systemd-resolved-monitor.socket'.
systemd[1]: Populated /etc with preset unit settings.
```

Considering it only happens on first boot and not on every boot I think
the extra information is worth the extra verbosity in the logs just for
that boot.

5 of 6 new or added lines in 1 file covered. (83.33%)

5463 existing lines in 165 files now uncovered.

299151 of 415222 relevant lines covered (72.05%)

702386.45 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 <stdlib.h>
4
#include <sys/mman.h>
5
#include <sys/timerfd.h>
6
#include <threads.h>
7
#include <unistd.h>
8

9
#include "alloc-util.h"
10
#include "errno-util.h"
11
#include "extract-word.h"
12
#include "fd-util.h"
13
#include "fileio.h"
14
#include "fs-util.h"
15
#include "io-util.h"
16
#include "log.h"
17
#include "parse-util.h"
18
#include "path-util.h"
19
#include "process-util.h"
20
#include "stat-util.h"
21
#include "stdio-util.h"
22
#include "string-table.h"
23
#include "string-util.h"
24
#include "strv.h"
25
#include "time-util.h"
26

27
static clockid_t map_clock_id(clockid_t c) {
27,193,706✔
28

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

34
        switch (c) {
27,193,706✔
35

36
        case CLOCK_BOOTTIME_ALARM:
37
                return CLOCK_BOOTTIME;
38

39
        case CLOCK_REALTIME_ALARM:
8✔
40
                return CLOCK_REALTIME;
8✔
41

42
        default:
27,193,697✔
43
                return c;
27,193,697✔
44
        }
45
}
46

47
usec_t now(clockid_t clock_id) {
27,190,906✔
48
        struct timespec ts;
27,190,906✔
49

50
        assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
27,190,906✔
51

52
        return timespec_load(&ts);
27,190,906✔
53
}
54

55
nsec_t now_nsec(clockid_t clock_id) {
622✔
56
        struct timespec ts;
622✔
57

58
        assert_se(clock_gettime(map_clock_id(clock_id), &ts) == 0);
622✔
59

60
        return timespec_load_nsec(&ts);
622✔
61
}
62

63
dual_timestamp* dual_timestamp_now(dual_timestamp *ts) {
1,912,809✔
64
        assert(ts);
1,912,809✔
65

66
        ts->realtime = now(CLOCK_REALTIME);
1,912,809✔
67
        ts->monotonic = now(CLOCK_MONOTONIC);
1,912,809✔
68

69
        return ts;
1,912,809✔
70
}
71

72
triple_timestamp* triple_timestamp_now(triple_timestamp *ts) {
3,070,915✔
73
        assert(ts);
3,070,915✔
74

75
        ts->realtime = now(CLOCK_REALTIME);
3,070,915✔
76
        ts->monotonic = now(CLOCK_MONOTONIC);
3,070,915✔
77
        ts->boottime = now(CLOCK_BOOTTIME);
3,070,915✔
78

79
        return ts;
3,070,915✔
80
}
81

82
usec_t map_clock_usec_raw(usec_t from, usec_t from_base, usec_t to_base) {
7,954✔
83

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

91
        if (from >= from_base) { /* In the future */
7,954✔
92
                usec_t delta = from - from_base;
18✔
93

94
                if (to_base >= USEC_INFINITY - delta) /* overflow? */
18✔
95
                        return USEC_INFINITY;
96

97
                return to_base + delta;
18✔
98

99
        } else { /* In the past */
100
                usec_t delta = from_base - from;
7,936✔
101

102
                if (to_base <= delta) /* underflow? */
7,936✔
103
                        return 0;
104

105
                return to_base - delta;
7,929✔
106
        }
107
}
108

109
usec_t map_clock_usec(usec_t from, clockid_t from_clock, clockid_t to_clock) {
562✔
110

111
        /* Try to avoid any inaccuracy needlessly added in case we convert from effectively the same clock
112
         * onto itself */
113
        if (map_clock_id(from_clock) == map_clock_id(to_clock))
562✔
114
                return from;
115

116
        /* Keep infinity as is */
117
        if (from == USEC_INFINITY)
562✔
118
                return from;
119

120
        return map_clock_usec_raw(from, now(from_clock), now(to_clock));
526✔
121
}
122

123
dual_timestamp* dual_timestamp_from_realtime(dual_timestamp *ts, usec_t u) {
13✔
124
        assert(ts);
13✔
125

126
        if (!timestamp_is_set(u)) {
13✔
UNCOV
127
                ts->realtime = ts->monotonic = u;
×
UNCOV
128
                return ts;
×
129
        }
130

131
        ts->realtime = u;
13✔
132
        ts->monotonic = map_clock_usec(u, CLOCK_REALTIME, CLOCK_MONOTONIC);
13✔
133
        return ts;
13✔
134
}
135

136
triple_timestamp* triple_timestamp_from_realtime(triple_timestamp *ts, usec_t u) {
302✔
137
        usec_t nowr;
302✔
138

139
        assert(ts);
302✔
140

141
        if (!timestamp_is_set(u)) {
302✔
UNCOV
142
                ts->realtime = ts->monotonic = ts->boottime = u;
×
UNCOV
143
                return ts;
×
144
        }
145

146
        nowr = now(CLOCK_REALTIME);
302✔
147

148
        ts->realtime = u;
302✔
149
        ts->monotonic = map_clock_usec_raw(u, nowr, now(CLOCK_MONOTONIC));
302✔
150
        ts->boottime = map_clock_usec_raw(u, nowr, now(CLOCK_BOOTTIME));
302✔
151

152
        return ts;
302✔
153
}
154

UNCOV
155
triple_timestamp* triple_timestamp_from_boottime(triple_timestamp *ts, usec_t u) {
×
UNCOV
156
        usec_t nowb;
×
157

UNCOV
158
        assert(ts);
×
159

160
        if (u == USEC_INFINITY) {
×
161
                ts->realtime = ts->monotonic = ts->boottime = u;
×
UNCOV
162
                return ts;
×
163
        }
164

165
        nowb = now(CLOCK_BOOTTIME);
×
166

167
        ts->boottime = u;
×
UNCOV
168
        ts->monotonic = map_clock_usec_raw(u, nowb, now(CLOCK_MONOTONIC));
×
UNCOV
169
        ts->realtime = map_clock_usec_raw(u, nowb, now(CLOCK_REALTIME));
×
170

UNCOV
171
        return ts;
×
172
}
173

174
dual_timestamp* dual_timestamp_from_monotonic(dual_timestamp *ts, usec_t u) {
235✔
175
        assert(ts);
235✔
176

177
        if (u == USEC_INFINITY) {
235✔
UNCOV
178
                ts->realtime = ts->monotonic = USEC_INFINITY;
×
UNCOV
179
                return ts;
×
180
        }
181

182
        ts->monotonic = u;
235✔
183
        ts->realtime = map_clock_usec(u, CLOCK_MONOTONIC, CLOCK_REALTIME);
235✔
184
        return ts;
235✔
185
}
186

187
dual_timestamp* dual_timestamp_from_boottime(dual_timestamp *ts, usec_t u) {
663✔
188
        usec_t nowm;
663✔
189

190
        assert(ts);
663✔
191

192
        if (u == USEC_INFINITY) {
663✔
UNCOV
193
                ts->realtime = ts->monotonic = USEC_INFINITY;
×
UNCOV
194
                return ts;
×
195
        }
196

197
        nowm = now(CLOCK_BOOTTIME);
663✔
198
        ts->monotonic = map_clock_usec_raw(u, nowm, now(CLOCK_MONOTONIC));
663✔
199
        ts->realtime = map_clock_usec_raw(u, nowm, now(CLOCK_REALTIME));
663✔
200
        return ts;
663✔
201
}
202

203
usec_t triple_timestamp_by_clock(triple_timestamp *ts, clockid_t clock) {
3,141,135✔
204
        assert(ts);
3,141,135✔
205

206
        switch (clock) {
3,141,135✔
207

208
        case CLOCK_REALTIME:
1,704,529✔
209
        case CLOCK_REALTIME_ALARM:
210
                return ts->realtime;
1,704,529✔
211

212
        case CLOCK_MONOTONIC:
1,395,921✔
213
                return ts->monotonic;
1,395,921✔
214

215
        case CLOCK_BOOTTIME:
40,685✔
216
        case CLOCK_BOOTTIME_ALARM:
217
                return ts->boottime;
40,685✔
218

219
        default:
220
                return USEC_INFINITY;
221
        }
222
}
223

224
usec_t timespec_load(const struct timespec *ts) {
27,536,321✔
225
        assert(ts);
27,536,321✔
226

227
        if (ts->tv_sec < 0 || ts->tv_nsec < 0)
27,536,321✔
228
                return USEC_INFINITY;
229

230
        if ((usec_t) ts->tv_sec > (UINT64_MAX - (ts->tv_nsec / NSEC_PER_USEC)) / USEC_PER_SEC)
27,536,321✔
231
                return USEC_INFINITY;
232

233
        return
27,536,321✔
234
                (usec_t) ts->tv_sec * USEC_PER_SEC +
27,536,321✔
235
                (usec_t) ts->tv_nsec / NSEC_PER_USEC;
236
}
237

238
nsec_t timespec_load_nsec(const struct timespec *ts) {
1,486✔
239
        assert(ts);
1,486✔
240

241
        if (ts->tv_sec < 0 || ts->tv_nsec < 0)
1,486✔
242
                return NSEC_INFINITY;
243

244
        if ((nsec_t) ts->tv_sec >= (UINT64_MAX - ts->tv_nsec) / NSEC_PER_SEC)
1,486✔
245
                return NSEC_INFINITY;
246

247
        return (nsec_t) ts->tv_sec * NSEC_PER_SEC + (nsec_t) ts->tv_nsec;
1,486✔
248
}
249

250
struct timespec *timespec_store(struct timespec *ts, usec_t u) {
2,108,482✔
251
        assert(ts);
2,108,482✔
252

253
        if (u == USEC_INFINITY ||
2,108,482✔
254
            u / USEC_PER_SEC >= TIME_T_MAX) {
UNCOV
255
                ts->tv_sec = (time_t) -1;
×
UNCOV
256
                ts->tv_nsec = -1L;
×
UNCOV
257
                return ts;
×
258
        }
259

260
        ts->tv_sec = (time_t) (u / USEC_PER_SEC);
2,108,482✔
261
        ts->tv_nsec = (long) ((u % USEC_PER_SEC) * NSEC_PER_USEC);
2,108,482✔
262

263
        return ts;
2,108,482✔
264
}
265

266
struct timespec *timespec_store_nsec(struct timespec *ts, nsec_t n) {
56✔
267
        assert(ts);
56✔
268

269
        if (n == NSEC_INFINITY ||
56✔
270
            n / NSEC_PER_SEC >= TIME_T_MAX) {
UNCOV
271
                ts->tv_sec = (time_t) -1;
×
UNCOV
272
                ts->tv_nsec = -1L;
×
UNCOV
273
                return ts;
×
274
        }
275

276
        ts->tv_sec = (time_t) (n / NSEC_PER_SEC);
56✔
277
        ts->tv_nsec = (long) (n % NSEC_PER_SEC);
56✔
278

279
        return ts;
56✔
280
}
281

282
usec_t timeval_load(const struct timeval *tv) {
679,676✔
283
        assert(tv);
679,676✔
284

285
        if (tv->tv_sec < 0 || tv->tv_usec < 0)
679,676✔
286
                return USEC_INFINITY;
287

288
        if ((usec_t) tv->tv_sec > (UINT64_MAX - tv->tv_usec) / USEC_PER_SEC)
679,676✔
289
                return USEC_INFINITY;
290

291
        return
679,676✔
292
                (usec_t) tv->tv_sec * USEC_PER_SEC +
679,676✔
293
                (usec_t) tv->tv_usec;
294
}
295

296
struct timeval *timeval_store(struct timeval *tv, usec_t u) {
157,905✔
297
        assert(tv);
157,905✔
298

299
        if (u == USEC_INFINITY ||
157,905✔
300
            u / USEC_PER_SEC > TIME_T_MAX) {
UNCOV
301
                tv->tv_sec = (time_t) -1;
×
UNCOV
302
                tv->tv_usec = (suseconds_t) -1;
×
303
        } else {
304
                tv->tv_sec = (time_t) (u / USEC_PER_SEC);
157,905✔
305
                tv->tv_usec = (suseconds_t) (u % USEC_PER_SEC);
157,905✔
306
        }
307

308
        return tv;
157,905✔
309
}
310

311
char* format_timestamp_style(
5,781✔
312
                char *buf,
313
                size_t l,
314
                usec_t t,
315
                TimestampStyle style) {
316

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

329
        struct tm tm;
5,781✔
330
        bool utc, us;
5,781✔
331
        size_t n;
5,781✔
332

333
        assert(buf);
5,781✔
334
        assert(style >= 0);
5,781✔
335
        assert(style < _TIMESTAMP_STYLE_MAX);
5,781✔
336

337
        if (!timestamp_is_set(t))
5,781✔
338
                return NULL; /* Timestamp is unset */
5,781✔
339

340
        if (style == TIMESTAMP_UNIX) {
4,229✔
341
                if (l < (size_t) (1 + 1 + 1))
100✔
342
                        return NULL; /* not enough space for even the shortest of forms */
343

344
                return snprintf_ok(buf, l, "@" USEC_FMT, t / USEC_PER_SEC);  /* round down μs → s */
100✔
345
        }
346

347
        utc = IN_SET(style, TIMESTAMP_UTC, TIMESTAMP_US_UTC, TIMESTAMP_DATE);
4,129✔
348
        us = IN_SET(style, TIMESTAMP_US, TIMESTAMP_US_UTC);
4,129✔
349

350
        if (l < (size_t) (3 +                   /* week day */
3,649✔
351
                          1 + 10 +              /* space and date */
352
                          style == TIMESTAMP_DATE ? 0 :
353
                          (1 + 8 +              /* space and time */
354
                           (us ? 1 + 6 : 0) +   /* "." and microsecond part */
4,129✔
355
                           1 + (utc ? 3 : 1)) + /* space and shortest possible zone */
7,921✔
356
                          1))
357
                return NULL; /* Not enough space even for the shortest form. */
358

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

369
                assert(l >= strlen(xxx[style]) + 1);
4✔
370
                return strcpy(buf, xxx[style]);
4✔
371
        }
372

373
        if (localtime_or_gmtime_usec(t, utc, &tm) < 0)
4,125✔
374
                return NULL;
375

376
        /* Start with the week day */
377
        assert((size_t) tm.tm_wday < ELEMENTSOF(weekdays));
4,125✔
378
        memcpy(buf, weekdays[tm.tm_wday], 4);
4,125✔
379

380
        if (style == TIMESTAMP_DATE) {
4,125✔
381
                /* Special format string if only date should be shown. */
382
                if (strftime(buf + 3, l - 3, " %Y-%m-%d", &tm) <= 0)
104✔
383
                        return NULL; /* Doesn't fit */
384

385
                return buf;
104✔
386
        }
387

388
        /* Add the main components */
389
        if (strftime(buf + 3, l - 3, " %Y-%m-%d %H:%M:%S", &tm) <= 0)
4,021✔
390
                return NULL; /* Doesn't fit */
391

392
        /* Append the microseconds part, if that's requested */
393
        if (us) {
4,021✔
394
                n = strlen(buf);
479✔
395
                if (n + 8 > l)
479✔
396
                        return NULL; /* Microseconds part doesn't fit. */
397

398
                sprintf(buf + n, ".%06"PRI_USEC, t % USEC_PER_SEC);
479✔
399
        }
400

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

409
                strcpy(buf + n, " UTC");
230✔
410

411
        } else if (!isempty(tm.tm_zone)) {
3,791✔
412
                size_t tn;
3,791✔
413

414
                /* An explicit timezone is specified, let's use it, if it fits */
415
                tn = strlen(tm.tm_zone);
3,791✔
416
                if (n + 1 + tn + 1 > l) {
3,791✔
417
                        /* The full time zone does not fit in. Yuck. */
418

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

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

434
        return buf;
435
}
436

437
char* format_timestamp_relative_full(char *buf, size_t l, usec_t t, clockid_t clock, bool implicit_left) {
1,376✔
438
        const char *s;
1,376✔
439
        usec_t n, d;
1,376✔
440

441
        assert(buf);
1,376✔
442

443
        if (!timestamp_is_set(t))
1,376✔
444
                return NULL;
445

446
        n = now(clock);
1,370✔
447
        if (n > t) {
1,370✔
448
                d = n - t;
1,180✔
449
                s = " ago";
1,180✔
450
        } else {
451
                d = t - n;
190✔
452
                s = implicit_left ? "" : " left";
190✔
453
        }
454

455
        if (d >= USEC_PER_YEAR) {
1,370✔
456
                usec_t years = d / USEC_PER_YEAR;
738✔
457
                usec_t months = (d % USEC_PER_YEAR) / USEC_PER_MONTH;
738✔
458

459
                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
2,200✔
460
                                years,
461
                                years == 1 ? "year" : "years",
462
                                months,
463
                                months == 1 ? "month" : "months",
464
                                s);
465
        } else if (d >= USEC_PER_MONTH) {
632✔
466
                usec_t months = d / USEC_PER_MONTH;
13✔
467
                usec_t days = (d % USEC_PER_MONTH) / USEC_PER_DAY;
13✔
468

469
                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
31✔
470
                                months,
471
                                months == 1 ? "month" : "months",
472
                                days,
473
                                days == 1 ? "day" : "days",
474
                                s);
475
        } else if (d >= USEC_PER_WEEK) {
619✔
476
                usec_t weeks = d / USEC_PER_WEEK;
8✔
477
                usec_t days = (d % USEC_PER_WEEK) / USEC_PER_DAY;
8✔
478

479
                (void) snprintf(buf, l, USEC_FMT " %s " USEC_FMT " %s%s",
16✔
480
                                weeks,
481
                                weeks == 1 ? "week" : "weeks",
482
                                days,
483
                                days == 1 ? "day" : "days",
484
                                s);
485
        } else if (d >= 2*USEC_PER_DAY)
611✔
UNCOV
486
                (void) snprintf(buf, l, USEC_FMT " days%s", d / USEC_PER_DAY,s);
×
487
        else if (d >= 25*USEC_PER_HOUR)
611✔
488
                (void) snprintf(buf, l, "1 day " USEC_FMT "h%s",
11✔
489
                                (d - USEC_PER_DAY) / USEC_PER_HOUR, s);
11✔
490
        else if (d >= 6*USEC_PER_HOUR)
600✔
491
                (void) snprintf(buf, l, USEC_FMT "h%s",
20✔
492
                                d / USEC_PER_HOUR, s);
493
        else if (d >= USEC_PER_HOUR)
580✔
UNCOV
494
                (void) snprintf(buf, l, USEC_FMT "h " USEC_FMT "min%s",
×
495
                                d / USEC_PER_HOUR,
UNCOV
496
                                (d % USEC_PER_HOUR) / USEC_PER_MINUTE, s);
×
497
        else if (d >= 5*USEC_PER_MINUTE)
580✔
498
                (void) snprintf(buf, l, USEC_FMT "min%s",
7✔
499
                                d / USEC_PER_MINUTE, s);
500
        else if (d >= USEC_PER_MINUTE)
573✔
501
                (void) snprintf(buf, l, USEC_FMT "min " USEC_FMT "s%s",
4✔
502
                                d / USEC_PER_MINUTE,
503
                                (d % USEC_PER_MINUTE) / USEC_PER_SEC, s);
4✔
504
        else if (d >= USEC_PER_SEC)
569✔
505
                (void) snprintf(buf, l, USEC_FMT "s%s",
480✔
506
                                d / USEC_PER_SEC, s);
507
        else if (d >= USEC_PER_MSEC)
89✔
508
                (void) snprintf(buf, l, USEC_FMT "ms%s",
86✔
509
                                d / USEC_PER_MSEC, s);
510
        else if (d > 0)
3✔
511
                (void) snprintf(buf, l, USEC_FMT"us%s",
3✔
512
                                d, s);
513
        else
UNCOV
514
                (void) snprintf(buf, l, "now");
×
515

516
        buf[l-1] = 0;
1,370✔
517
        return buf;
1,370✔
518
}
519

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

536
        char *p = ASSERT_PTR(buf);
11,529✔
537
        bool something = false;
11,529✔
538

539
        assert(l > 0);
11,529✔
540

541
        if (t == USEC_INFINITY) {
11,529✔
542
                strncpy(p, "infinity", l-1);
914✔
543
                p[l-1] = 0;
914✔
544
                return p;
914✔
545
        }
546

547
        if (t <= 0) {
10,615✔
548
                strncpy(p, "0", l-1);
565✔
549
                p[l-1] = 0;
565✔
550
                return p;
565✔
551
        }
552

553
        /* The result of this function can be parsed with parse_sec */
554

555
        FOREACH_ELEMENT(i, table) {
83,684✔
556
                int k = 0;
83,604✔
557
                size_t n;
83,604✔
558
                bool done = false;
83,604✔
559
                usec_t a, b;
83,604✔
560

561
                if (t <= 0)
83,604✔
562
                        break;
563

564
                if (t < accuracy && something)
73,668✔
565
                        break;
566

567
                if (t < i->usec)
73,634✔
568
                        continue;
59,107✔
569

570
                if (l <= 1)
14,527✔
571
                        break;
572

573
                a = t / i->usec;
14,527✔
574
                b = t % i->usec;
14,527✔
575

576
                /* Let's see if we should shows this in dot notation */
577
                if (t < USEC_PER_MINUTE && b > 0) {
14,527✔
578
                        signed char j = 0;
579

580
                        for (usec_t cc = i->usec; cc > 1; cc /= 10)
43,454✔
581
                                j++;
35,715✔
582

583
                        for (usec_t cc = accuracy; cc > 1; cc /= 10) {
35,878✔
584
                                b /= 10;
28,139✔
585
                                j--;
28,139✔
586
                        }
587

588
                        if (j > 0) {
7,739✔
589
                                k = snprintf(p, l,
3,500✔
590
                                             "%s"USEC_FMT".%0*"PRI_USEC"%s",
591
                                             p > buf ? " " : "",
592
                                             a,
593
                                             j,
594
                                             b,
595
                                             i->suffix);
1,750✔
596

597
                                t = 0;
1,750✔
598
                                done = true;
1,750✔
599
                        }
600
                }
601

602
                /* No? Then let's show it normally */
603
                if (!done) {
1,750✔
604
                        k = snprintf(p, l,
25,554✔
605
                                     "%s"USEC_FMT"%s",
606
                                     p > buf ? " " : "",
607
                                     a,
608
                                     i->suffix);
12,777✔
609

610
                        t = b;
12,777✔
611
                }
612

613
                n = MIN((size_t) k, l-1);
14,527✔
614

615
                l -= n;
14,527✔
616
                p += n;
14,527✔
617

618
                something = true;
14,527✔
619
        }
620

621
        *p = 0;
10,050✔
622

623
        return buf;
10,050✔
624
}
625

626
static int parse_timestamp_impl(
2,121✔
627
                const char *t,
628
                size_t max_len,
629
                bool utc,
630
                int isdst,
631
                long gmtoff,
632
                usec_t *ret) {
633

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

654
        _cleanup_free_ char *t_alloc = NULL;
2,121✔
655
        usec_t usec, plus = 0, minus = 0;
2,121✔
656
        bool with_tz = false;
2,121✔
657
        int r, weekday = -1;
2,121✔
658
        unsigned fractional = 0;
2,121✔
659
        const char *k;
2,121✔
660
        struct tm tm, copy;
2,121✔
661

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

694
        assert(t);
2,121✔
695

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

699
                if (max_len == 0) /* Can't be the only field */
1,690✔
700
                        return -EINVAL;
701

702
                t_alloc = strndup(t, max_len);
1,690✔
703
                if (!t_alloc)
1,690✔
704
                        return -ENOMEM;
705

706
                t = t_alloc;
707
                with_tz = true;
708
        }
709

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

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

722
        usec = now(CLOCK_REALTIME);
2,018✔
723

724
        if (!with_tz) {
2,018✔
725
                if (streq(t, "now"))
328✔
726
                        goto finish;
5✔
727

728
                if (t[0] == '+') {
323✔
729
                        r = parse_sec(t+1, &plus);
6✔
730
                        if (r < 0)
6✔
731
                                return r;
732

733
                        goto finish;
6✔
734
                }
735

736
                if (t[0] == '-') {
317✔
737
                        r = parse_sec(t+1, &minus);
6✔
738
                        if (r < 0)
6✔
739
                                return r;
740

741
                        goto finish;
6✔
742
                }
743

744
                if ((k = endswith(t, " ago"))) {
311✔
745
                        _cleanup_free_ char *buf = NULL;
4✔
746

747
                        buf = strndup(t, k - t);
4✔
748
                        if (!buf)
4✔
749
                                return -ENOMEM;
750

751
                        r = parse_sec(buf, &minus);
4✔
752
                        if (r < 0)
4✔
753
                                return r;
754

755
                        goto finish;
4✔
756
                }
757

758
                if ((k = endswith(t, " left"))) {
307✔
759
                        _cleanup_free_ char *buf = NULL;
101✔
760

761
                        buf = strndup(t, k - t);
101✔
762
                        if (!buf)
101✔
763
                                return -ENOMEM;
764

765
                        r = parse_sec(buf, &plus);
101✔
766
                        if (r < 0)
101✔
767
                                return r;
768

769
                        goto finish;
101✔
770
                }
771
        }
772

773
        r = localtime_or_gmtime_usec(usec, utc, &tm);
1,896✔
774
        if (r < 0)
1,896✔
775
                return r;
776

777
        tm.tm_isdst = isdst;
1,896✔
778

779
        if (streq(t, "today")) {
1,896✔
780
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
13✔
781
                goto from_tm;
13✔
782

783
        } else if (streq(t, "yesterday")) {
1,883✔
784
                tm.tm_mday--;
10✔
785
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
10✔
786
                goto from_tm;
10✔
787

788
        } else if (streq(t, "tomorrow")) {
1,873✔
789
                tm.tm_mday++;
8✔
790
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
8✔
791
                goto from_tm;
8✔
792
        }
793

794
        FOREACH_ELEMENT(day, day_nr) {
19,377✔
795
                k = startswith_no_case(t, day->name);
19,064✔
796
                if (!k || *k != ' ')
19,064✔
797
                        continue;
17,512✔
798

799
                weekday = day->nr;
1,552✔
800
                t = k + 1;
1,552✔
801
                break;
1,552✔
802
        }
803

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

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

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

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

843
        tm = copy;
261✔
844
        k = strptime(t, "%y-%m-%d %H:%M", &tm);
261✔
845
        if (k && *k == 0) {
261✔
846
                tm.tm_sec = 0;
30✔
847
                goto from_tm;
30✔
848
        }
849

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

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

866
        tm = copy;
192✔
867
        k = strptime(t, "%y-%m-%d", &tm);
192✔
868
        if (k && *k == 0) {
192✔
UNCOV
869
                tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
×
UNCOV
870
                goto from_tm;
×
871
        }
872

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

880
        tm = copy;
86✔
881
        k = strptime(t, "%H:%M:%S", &tm);
86✔
882
        if (k) {
86✔
883
                if (*k == '.')
28✔
884
                        goto parse_usec;
8✔
885
                else if (*k == 0)
20✔
886
                        goto from_tm;
20✔
887
        }
888

889
        tm = copy;
58✔
890
        k = strptime(t, "%H:%M", &tm);
58✔
891
        if (k && *k == 0) {
58✔
892
                tm.tm_sec = 0;
6✔
893
                goto from_tm;
6✔
894
        }
895

896
        return -EINVAL;
897

898
parse_usec:
436✔
899
        k++;
436✔
900
        r = parse_fractional_part_u(&k, 6, &fractional);
436✔
901
        if (r < 0)
436✔
902
                return -EINVAL;
903
        if (*k != '\0')
436✔
904
                return -EINVAL;
905

906
from_tm:
420✔
907
        assert(plus == 0);
1,828✔
908
        assert(minus == 0);
1,828✔
909

910
        if (weekday >= 0 && tm.tm_wday != weekday)
1,828✔
911
                return -EINVAL;
912

913
        if (gmtoff < 0) {
1,828✔
914
                plus = -gmtoff * USEC_PER_SEC;
110✔
915

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

933
        r = mktime_or_timegm_usec(&tm, utc, &usec);
1,828✔
934
        if (r < 0)
1,828✔
935
                return r;
936

937
        usec = usec_add(usec, fractional);
3,650✔
938

939
finish:
1,947✔
940
        usec = usec_add(usec, plus);
1,947✔
941

942
        if (usec < minus)
1,947✔
943
                return -EINVAL;
944

945
        usec = usec_sub_unsigned(usec, minus);
1,946✔
946

947
        if (usec > USEC_TIMESTAMP_FORMATTABLE_MAX)
1,946✔
948
                return -EINVAL;
949

950
        if (ret)
1,945✔
951
                *ret = usec;
1,943✔
952
        return 0;
953
}
954

955
static int parse_timestamp_maybe_with_tz(const char *t, size_t tz_offset, bool valid_tz, usec_t *ret) {
945✔
956
        assert(t);
945✔
957

958
        tzset();
945✔
959

960
        for (int j = 0; j <= 1; j++) {
2,821✔
961
                if (isempty(tzname[j]))
1,883✔
UNCOV
962
                        continue;
×
963

964
                if (!streq(t + tz_offset, tzname[j]))
1,883✔
965
                        continue;
1,876✔
966

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

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

978
typedef struct ParseTimestampResult {
979
        usec_t usec;
980
        int return_value;
981
} ParseTimestampResult;
982

983
int parse_timestamp(const char *t, usec_t *ret) {
2,088✔
984
        ParseTimestampResult *shared, tmp;
2,088✔
985
        const char *k, *tz, *current_tz;
2,088✔
986
        size_t max_len, t_len;
2,088✔
987
        struct tm tm;
2,088✔
988
        int r;
2,088✔
989

990
        assert(t);
2,088✔
991

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

1000
        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✔
1001
                k = strptime(&t[t_len - 6], "%z", &tm);
143✔
1002
                if (k && *k == '\0')
143✔
1003
                        return parse_timestamp_impl(t, t_len - 6, /* utc = */ true, /* isdst = */ -1, /* gmtoff = */ tm.tm_gmtoff, ret);
40✔
1004
        }
1005

1006
        tz = strrchr(t, ' ');
2,014✔
1007
        if (!tz)
2,014✔
1008
                return parse_timestamp_impl(t, /* max_len = */ SIZE_MAX, /* utc = */ false, /* isdst = */ -1, /* gmtoff = */ 0, ret);
185✔
1009

1010
        max_len = tz - t;
1,829✔
1011
        tz++;
1,829✔
1012

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

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

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

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

1035
        /* Otherwise, to avoid polluting the current environment variables, let's fork the process and set
1036
         * the specified timezone in the child process. */
1037

1038
        shared = mmap(NULL, sizeof *shared, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
699✔
1039
        if (shared == MAP_FAILED)
699✔
UNCOV
1040
                return negative_errno();
×
1041

1042
        /* The input string may be in argv. Let's copy it. */
1043
        _cleanup_free_ char *t_copy = strdup(t);
699✔
1044
        if (!t_copy)
699✔
1045
                return -ENOMEM;
1046

1047
        t = t_copy;
699✔
1048
        assert_se(tz = endswith(t_copy, tz));
699✔
1049

1050
        r = safe_fork("(sd-timestamp)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGKILL|FORK_WAIT, NULL);
699✔
1051
        if (r < 0) {
1,398✔
UNCOV
1052
                (void) munmap(shared, sizeof *shared);
×
UNCOV
1053
                return r;
×
1054
        }
1055
        if (r == 0) {
1,398✔
1056
                const char *colon_tz;
699✔
1057

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

1061
                if (setenv("TZ", colon_tz, 1) != 0) {
699✔
UNCOV
1062
                        shared->return_value = negative_errno();
×
UNCOV
1063
                        _exit(EXIT_FAILURE);
×
1064
                }
1065

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

1068
                _exit(EXIT_SUCCESS);
699✔
1069
        }
1070

1071
        tmp = *shared;
699✔
1072
        if (munmap(shared, sizeof *shared) != 0)
699✔
UNCOV
1073
                return negative_errno();
×
1074

1075
        if (tmp.return_value == 0 && ret)
699✔
1076
                *ret = tmp.usec;
696✔
1077

1078
        return tmp.return_value;
1079
}
1080

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

1118
        assert(p);
10,045✔
1119
        assert(ret);
10,045✔
1120

1121
        FOREACH_ELEMENT(i, table) {
174,746✔
1122
                char *e;
171,171✔
1123

1124
                e = startswith(p, i->suffix);
171,171✔
1125
                if (e) {
171,171✔
1126
                        *ret = i->usec;
6,470✔
1127
                        return e;
6,470✔
1128
                }
1129
        }
1130

1131
        return p;
1132
}
1133

1134
int parse_time(const char *t, usec_t *ret, usec_t default_unit) {
10,124✔
1135
        const char *p, *s;
10,124✔
1136

1137
        assert(t);
10,124✔
1138
        assert(default_unit > 0);
10,124✔
1139

1140
        p = skip_leading_chars(t, /* bad = */ NULL);
10,124✔
1141
        s = startswith(p, "infinity");
10,124✔
1142
        if (s) {
10,124✔
1143
                if (!in_charset(s, WHITESPACE))
275✔
1144
                        return -EINVAL;
1145

1146
                if (ret)
274✔
1147
                        *ret = USEC_INFINITY;
274✔
1148
                return 0;
274✔
1149
        }
1150

1151
        usec_t usec = 0;
1152

1153
        for (bool something = false;;) {
10,029✔
1154
                usec_t multiplier = default_unit, k;
19,878✔
1155
                long long l;
19,878✔
1156
                char *e;
19,878✔
1157

1158
                p = skip_leading_chars(p, /* bad = */ NULL);
19,878✔
1159
                if (*p == 0) {
19,878✔
1160
                        if (!something)
9,811✔
1161
                                return -EINVAL;
43✔
1162

1163
                        break;
9,806✔
1164
                }
1165

1166
                if (*p == '-') /* Don't allow "-0" */
10,067✔
1167
                        return -ERANGE;
1168

1169
                errno = 0;
10,059✔
1170
                l = strtoll(p, &e, 10);
10,059✔
1171
                if (errno > 0)
10,059✔
UNCOV
1172
                        return -errno;
×
1173
                if (l < 0)
10,059✔
1174
                        return -ERANGE;
1175

1176
                if (*e == '.') {
10,059✔
1177
                        p = e + 1;
55✔
1178
                        p += strspn(p, DIGITS);
55✔
1179
                } else if (e == p)
10,004✔
1180
                        return -EINVAL;
1181
                else
1182
                        p = e;
1183

1184
                s = extract_multiplier(p + strspn(p, WHITESPACE), &multiplier);
10,045✔
1185
                if (s == p && *s != '\0')
10,045✔
1186
                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1187
                        return -EINVAL;
1188

1189
                p = s;
10,036✔
1190

1191
                if ((usec_t) l >= USEC_INFINITY / multiplier)
10,036✔
1192
                        return -ERANGE;
1193

1194
                k = (usec_t) l * multiplier;
10,034✔
1195
                if (k >= USEC_INFINITY - usec)
10,034✔
1196
                        return -ERANGE;
1197

1198
                usec += k;
10,034✔
1199

1200
                something = true;
10,034✔
1201

1202
                if (*e == '.') {
10,034✔
1203
                        usec_t m = multiplier / 10;
48✔
1204
                        const char *b;
48✔
1205

1206
                        for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
188✔
1207
                                k = (usec_t) (*b - '0') * m;
140✔
1208
                                if (k >= USEC_INFINITY - usec)
140✔
1209
                                        return -ERANGE;
1210

1211
                                usec += k;
140✔
1212
                        }
1213

1214
                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1215
                        if (b == e + 1)
48✔
1216
                                return -EINVAL;
1217
                }
1218
        }
1219

1220
        if (ret)
9,806✔
1221
                *ret = usec;
9,804✔
1222
        return 0;
1223
}
1224

1225
int parse_sec(const char *t, usec_t *ret) {
10,090✔
1226
        return parse_time(t, ret, USEC_PER_SEC);
10,090✔
1227
}
1228

1229
int parse_sec_fix_0(const char *t, usec_t *ret) {
1,862✔
1230
        usec_t k;
1,862✔
1231
        int r;
1,862✔
1232

1233
        assert(t);
1,862✔
1234
        assert(ret);
1,862✔
1235

1236
        r = parse_sec(t, &k);
1,862✔
1237
        if (r < 0)
1,862✔
1238
                return r;
1,862✔
1239

1240
        *ret = k == 0 ? USEC_INFINITY : k;
1,862✔
1241
        return r;
1,862✔
1242
}
1243

1244
int parse_sec_def_infinity(const char *t, usec_t *ret) {
7✔
1245
        assert(t);
7✔
1246
        assert(ret);
7✔
1247

1248
        t += strspn(t, WHITESPACE);
7✔
1249
        if (isempty(t)) {
7✔
1250
                *ret = USEC_INFINITY;
2✔
1251
                return 0;
2✔
1252
        }
1253
        return parse_sec(t, ret);
5✔
1254
}
1255

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

1296
        assert(p);
43✔
1297
        assert(ret);
43✔
1298

1299
        FOREACH_ELEMENT(i, table) {
791✔
1300
                char *e;
791✔
1301

1302
                e = startswith(p, i->suffix);
791✔
1303
                if (e) {
791✔
1304
                        *ret = i->nsec;
43✔
1305
                        return e;
43✔
1306
                }
1307
        }
1308

1309
        return p;
1310
}
1311

1312
int parse_nsec(const char *t, nsec_t *ret) {
47✔
1313
        const char *p, *s;
47✔
1314
        nsec_t nsec = 0;
47✔
1315
        bool something = false;
47✔
1316

1317
        assert(t);
47✔
1318
        assert(ret);
47✔
1319

1320
        p = t;
47✔
1321

1322
        p += strspn(p, WHITESPACE);
47✔
1323
        s = startswith(p, "infinity");
47✔
1324
        if (s) {
47✔
1325
                s += strspn(s, WHITESPACE);
4✔
1326
                if (*s != 0)
4✔
1327
                        return -EINVAL;
1328

1329
                *ret = NSEC_INFINITY;
2✔
1330
                return 0;
2✔
1331
        }
1332

1333
        for (;;) {
29✔
1334
                nsec_t multiplier = 1, k;
72✔
1335
                long long l;
72✔
1336
                char *e;
72✔
1337

1338
                p += strspn(p, WHITESPACE);
72✔
1339

1340
                if (*p == 0) {
72✔
1341
                        if (!something)
20✔
1342
                                return -EINVAL;
24✔
1343

1344
                        break;
19✔
1345
                }
1346

1347
                if (*p == '-') /* Don't allow "-0" */
52✔
1348
                        return -ERANGE;
1349

1350
                errno = 0;
47✔
1351
                l = strtoll(p, &e, 10);
47✔
1352
                if (errno > 0)
47✔
UNCOV
1353
                        return -errno;
×
1354
                if (l < 0)
47✔
1355
                        return -ERANGE;
1356

1357
                if (*e == '.') {
47✔
1358
                        p = e + 1;
31✔
1359
                        p += strspn(p, DIGITS);
31✔
1360
                } else if (e == p)
16✔
1361
                        return -EINVAL;
1362
                else
1363
                        p = e;
1364

1365
                s = extract_nsec_multiplier(p + strspn(p, WHITESPACE), &multiplier);
43✔
1366
                if (s == p && *s != '\0')
43✔
1367
                        /* Don't allow '12.34.56', but accept '12.34 .56' or '12.34s.56' */
1368
                        return -EINVAL;
1369

1370
                p = s;
35✔
1371

1372
                if ((nsec_t) l >= NSEC_INFINITY / multiplier)
35✔
1373
                        return -ERANGE;
1374

1375
                k = (nsec_t) l * multiplier;
34✔
1376
                if (k >= NSEC_INFINITY - nsec)
34✔
1377
                        return -ERANGE;
1378

1379
                nsec += k;
34✔
1380

1381
                something = true;
34✔
1382

1383
                if (*e == '.') {
34✔
1384
                        nsec_t m = multiplier / 10;
24✔
1385
                        const char *b;
24✔
1386

1387
                        for (b = e + 1; *b >= '0' && *b <= '9'; b++, m /= 10) {
56✔
1388
                                k = (nsec_t) (*b - '0') * m;
32✔
1389
                                if (k >= NSEC_INFINITY - nsec)
32✔
1390
                                        return -ERANGE;
1391

1392
                                nsec += k;
32✔
1393
                        }
1394

1395
                        /* Don't allow "0.-0", "3.+1", "3. 1", "3.sec" or "3.hoge" */
1396
                        if (b == e + 1)
24✔
1397
                                return -EINVAL;
1398
                }
1399
        }
1400

1401
        *ret = nsec;
19✔
1402

1403
        return 0;
19✔
1404
}
1405

UNCOV
1406
static int get_timezones_from_zone1970_tab(char ***ret) {
×
UNCOV
1407
        _cleanup_fclose_ FILE *f = NULL;
×
UNCOV
1408
        _cleanup_strv_free_ char **zones = NULL;
×
UNCOV
1409
        int r;
×
1410

1411
        assert(ret);
×
1412

1413
        f = fopen("/usr/share/zoneinfo/zone1970.tab", "re");
×
1414
        if (!f)
×
UNCOV
1415
                return -errno;
×
1416

UNCOV
1417
        for (;;) {
×
1418
                _cleanup_free_ char *line = NULL, *cc = NULL, *co = NULL, *tz = NULL;
×
1419

1420
                r = read_line(f, LONG_LINE_MAX, &line);
×
UNCOV
1421
                if (r < 0)
×
1422
                        return r;
1423
                if (r == 0)
×
1424
                        break;
1425

1426
                const char *p = line;
×
1427

1428
                /* Line format is:
1429
                 * 'country codes' 'coordinates' 'timezone' 'comments' */
UNCOV
1430
                r = extract_many_words(&p, NULL, 0, &cc, &co, &tz);
×
1431
                if (r < 0)
×
UNCOV
1432
                        continue;
×
1433

1434
                /* Lines that start with # are comments. */
1435
                if (*cc == '#')
×
1436
                        continue;
×
1437

UNCOV
1438
                r = strv_extend(&zones, tz);
×
UNCOV
1439
                if (r < 0)
×
1440
                        return r;
1441
        }
1442

1443
        *ret = TAKE_PTR(zones);
×
1444
        return 0;
×
1445
}
1446

1447
static int get_timezones_from_tzdata_zi(char ***ret) {
4✔
1448
        _cleanup_fclose_ FILE *f = NULL;
4✔
1449
        _cleanup_strv_free_ char **zones = NULL;
4✔
1450
        int r;
4✔
1451

1452
        assert(ret);
4✔
1453

1454
        f = fopen("/usr/share/zoneinfo/tzdata.zi", "re");
4✔
1455
        if (!f)
4✔
UNCOV
1456
                return -errno;
×
1457

1458
        for (;;) {
17,204✔
1459
                _cleanup_free_ char *line = NULL, *type = NULL, *f1 = NULL, *f2 = NULL;
17,200✔
1460

1461
                r = read_line(f, LONG_LINE_MAX, &line);
17,204✔
1462
                if (r < 0)
17,204✔
1463
                        return r;
1464
                if (r == 0)
17,204✔
1465
                        break;
1466

1467
                const char *p = line;
17,200✔
1468

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

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

1490
                r = strv_extend(&zones, tz);
2,392✔
1491
                if (r < 0)
2,392✔
1492
                        return r;
1493
        }
1494

1495
        *ret = TAKE_PTR(zones);
4✔
1496
        return 0;
4✔
1497
}
1498

1499
int get_timezones(char ***ret) {
4✔
1500
        _cleanup_strv_free_ char **zones = NULL;
4✔
1501
        int r;
4✔
1502

1503
        assert(ret);
4✔
1504

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

1515
        /* Always include UTC */
1516
        r = strv_extend(&zones, "UTC");
4✔
1517
        if (r < 0)
4✔
1518
                return r;
1519

1520
        strv_sort_uniq(zones);
4✔
1521

1522
        *ret = TAKE_PTR(zones);
4✔
1523
        return 0;
4✔
1524
}
1525

1526
int verify_timezone(const char *name, int log_level) {
1,802✔
1527
        bool slash = false;
1,802✔
1528
        const char *p, *t;
1,802✔
1529
        _cleanup_close_ int fd = -EBADF;
1,802✔
1530
        char buf[4];
1,802✔
1531
        int r;
1,802✔
1532

1533
        if (isempty(name))
3,604✔
1534
                return -EINVAL;
1535

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

1540
        if (name[0] == '/')
1,797✔
1541
                return -EINVAL;
1542

1543
        for (p = name; *p; p++) {
22,719✔
1544
                if (!ascii_isdigit(*p) &&
21,067✔
1545
                    !ascii_isalpha(*p) &&
19,949✔
1546
                    !IN_SET(*p, '-', '_', '+', '/'))
1,988✔
1547
                        return -EINVAL;
1548

1549
                if (*p == '/') {
20,923✔
1550

1551
                        if (slash)
1,376✔
1552
                                return -EINVAL;
1553

1554
                        slash = true;
1555
                } else
1556
                        slash = false;
1557
        }
1558

1559
        if (slash)
1,652✔
1560
                return -EINVAL;
1561

1562
        if (p - name >= PATH_MAX)
1,651✔
1563
                return -ENAMETOOLONG;
1564

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

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

1571
        r = fd_verify_regular(fd);
1,441✔
1572
        if (r < 0)
1,441✔
UNCOV
1573
                return log_full_errno(log_level, r, "Timezone file '%s' is not a regular file: %m", t);
×
1574

1575
        r = loop_read_exact(fd, buf, 4, false);
1,441✔
1576
        if (r < 0)
1,441✔
UNCOV
1577
                return log_full_errno(log_level, r, "Failed to read from timezone file '%s': %m", t);
×
1578

1579
        /* Magic from tzfile(5) */
1580
        if (memcmp(buf, "TZif", 4) != 0)
1,441✔
UNCOV
1581
                return log_full_errno(log_level, SYNTHETIC_ERRNO(EBADMSG),
×
1582
                                      "Timezone file '%s' has wrong magic bytes", t);
1583

1584
        return 0;
1585
}
1586

1587
bool clock_supported(clockid_t clock) {
287,064✔
1588
        struct timespec ts;
287,064✔
1589

1590
        switch (clock) {
287,064✔
1591

1592
        case CLOCK_MONOTONIC:
1593
        case CLOCK_REALTIME:
1594
        case CLOCK_BOOTTIME:
1595
                /* These three are always available in our baseline, and work in timerfd, as of kernel 3.15 */
1596
                return true;
1597

1598
        default:
1✔
1599
                /* For everything else, check properly */
1600
                return clock_gettime(clock, &ts) >= 0;
1✔
1601
        }
1602
}
1603

1604
int get_timezone(char **ret) {
51✔
1605
        _cleanup_free_ char *t = NULL;
51✔
1606
        int r;
51✔
1607

1608
        assert(ret);
51✔
1609

1610
        r = readlink_malloc("/etc/localtime", &t);
51✔
1611
        if (r == -ENOENT)
51✔
1612
                /* If the symlink does not exist, assume "UTC", like glibc does */
UNCOV
1613
                return strdup_to(ret, "UTC");
×
1614
        if (r < 0)
51✔
1615
                return r; /* Return EINVAL if not a symlink */
1616

1617
        const char *e = PATH_STARTSWITH_SET(t, "/usr/share/zoneinfo/", "../usr/share/zoneinfo/");
51✔
1618
        if (!e)
51✔
1619
                return -EINVAL;
1620
        if (!timezone_is_valid(e, LOG_DEBUG))
51✔
1621
                return -EINVAL;
1622

1623
        return strdup_to(ret, e);
51✔
1624
}
1625

1626
int mktime_or_timegm_usec(
7,827✔
1627
                struct tm *tm, /* input + normalized output */
1628
                bool utc,
1629
                usec_t *ret) {
1630

1631
        time_t t;
7,827✔
1632

1633
        assert(tm);
7,827✔
1634

1635
        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,827✔
1636
                return -ERANGE;
1637
        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,825✔
1638
                return -ERANGE;
1639

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

1645
        t = utc ? timegm(tm) : mktime(tm);
7,825✔
1646
        if (t < 0) /* Refuse negative times and errors */
7,825✔
1647
                return -ERANGE;
1648
        if ((usec_t) t >= USEC_INFINITY / USEC_PER_SEC) /* Never return USEC_INFINITY by accident (or overflow) */
7,823✔
1649
                return -ERANGE;
1650

1651
        if (ret)
7,823✔
1652
                *ret = (usec_t) t * USEC_PER_SEC;
2,160✔
1653
        return 0;
1654
}
1655

1656
int localtime_or_gmtime_usec(
106,599✔
1657
                usec_t t,
1658
                bool utc,
1659
                struct tm *ret) {
1660

1661
        t /= USEC_PER_SEC; /* Round down */
106,599✔
1662
        if (t > (usec_t) TIME_T_MAX)
106,599✔
1663
                return -ERANGE;
1664
        time_t sec = (time_t) t;
106,599✔
1665

1666
        struct tm buf = {};
106,599✔
1667
        if (!(utc ? gmtime_r(&sec, &buf) : localtime_r(&sec, &buf)))
106,599✔
1668
                return -EINVAL;
1669

1670
        if (ret)
106,599✔
1671
                *ret = buf;
106,599✔
1672

1673
        return 0;
1674
}
1675

1676
static uint32_t sysconf_clock_ticks_cached(void) {
812✔
1677
        static thread_local uint32_t hz = 0;
812✔
1678
        long r;
812✔
1679

1680
        if (hz == 0) {
812✔
1681
                r = sysconf(_SC_CLK_TCK);
80✔
1682

1683
                assert(r > 0);
80✔
1684
                hz = r;
80✔
1685
        }
1686

1687
        return hz;
812✔
1688
}
1689

1690
uint32_t usec_to_jiffies(usec_t u) {
9✔
1691
        uint32_t hz = sysconf_clock_ticks_cached();
9✔
1692
        return DIV_ROUND_UP(u, USEC_PER_SEC / hz);
9✔
1693
}
1694

1695
usec_t jiffies_to_usec(uint32_t j) {
803✔
1696
        uint32_t hz = sysconf_clock_ticks_cached();
803✔
1697
        return DIV_ROUND_UP(j * USEC_PER_SEC, hz);
803✔
1698
}
1699

1700
usec_t usec_shift_clock(usec_t x, clockid_t from, clockid_t to) {
668✔
1701
        usec_t a, b;
668✔
1702

1703
        if (x == USEC_INFINITY)
668✔
1704
                return USEC_INFINITY;
1705
        if (map_clock_id(from) == map_clock_id(to))
527✔
1706
                return x;
1707

1708
        a = now(from);
8✔
1709
        b = now(to);
8✔
1710

1711
        if (x > a)
8✔
1712
                /* x lies in the future */
1713
                return usec_add(b, usec_sub_unsigned(x, a));
12✔
1714
        else
1715
                /* x lies in the past */
1716
                return usec_sub_unsigned(b, usec_sub_unsigned(a, x));
6✔
1717
}
1718

1719
bool in_utc_timezone(void) {
676✔
1720
        tzset();
676✔
1721

1722
        return timezone == 0 && daylight == 0;
676✔
1723
}
1724

1725
int time_change_fd(void) {
430✔
1726

1727
        /* We only care for the cancellation event, hence we set the timeout to the latest possible value. */
1728
        static const struct itimerspec its = {
430✔
1729
                .it_value.tv_sec = TIME_T_MAX,
1730
        };
1731

1732
        _cleanup_close_ int fd = -EBADF;
860✔
1733

1734
        assert_cc(sizeof(time_t) == sizeof(TIME_T_MAX));
430✔
1735

1736
        /* Uses TFD_TIMER_CANCEL_ON_SET to get notifications whenever CLOCK_REALTIME makes a jump relative to
1737
         * CLOCK_MONOTONIC. */
1738

1739
        fd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK|TFD_CLOEXEC);
430✔
1740
        if (fd < 0)
430✔
UNCOV
1741
                return -errno;
×
1742

1743
        if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its, NULL) >= 0)
430✔
1744
                return TAKE_FD(fd);
430✔
1745

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

1756
#if SIZEOF_TIME_T == 8 && ULONG_MAX < UINT64_MAX
1757
        if (ERRNO_IS_NOT_SUPPORTED(errno) || errno == EOVERFLOW) {
1758
                static const struct itimerspec its32 = {
1759
                        .it_value.tv_sec = INT32_MAX,
1760
                };
1761

1762
                if (timerfd_settime(fd, TFD_TIMER_ABSTIME|TFD_TIMER_CANCEL_ON_SET, &its32, NULL) >= 0)
1763
                        return TAKE_FD(fd);
1764
        }
1765
#endif
1766

UNCOV
1767
        return -errno;
×
1768
}
1769

1770
static const char* const timestamp_style_table[_TIMESTAMP_STYLE_MAX] = {
1771
        [TIMESTAMP_PRETTY] = "pretty",
1772
        [TIMESTAMP_US]     = "us",
1773
        [TIMESTAMP_UTC]    = "utc",
1774
        [TIMESTAMP_US_UTC] = "us+utc",
1775
        [TIMESTAMP_UNIX]   = "unix",
1776
};
1777

1778
/* Use the macro for enum → string to allow for aliases */
UNCOV
1779
DEFINE_STRING_TABLE_LOOKUP_TO_STRING(timestamp_style, TimestampStyle);
×
1780

1781
/* For the string → enum mapping we use the generic implementation, but also support two aliases */
1782
TimestampStyle timestamp_style_from_string(const char *s) {
6✔
1783
        TimestampStyle t;
6✔
1784

1785
        t = (TimestampStyle) string_table_lookup_from_string(timestamp_style_table, ELEMENTSOF(timestamp_style_table), s);
6✔
1786
        if (t >= 0)
6✔
1787
                return t;
1788
        if (STRPTR_IN_SET(s, "µs", "μs")) /* accept both µ symbols in unicode, i.e. micro symbol + Greek small letter mu. */
2✔
1789
                return TIMESTAMP_US;
1✔
1790
        if (STRPTR_IN_SET(s, "µs+utc", "μs+utc"))
1✔
1791
                return TIMESTAMP_US_UTC;
1✔
1792
        return t;
1793
}
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