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

systemd / systemd / 20447389715

21 Dec 2025 07:31PM UTC coverage: 72.37% (-0.1%) from 72.5%
20447389715

push

github

DaanDeMeyer
mkosi: Use initrd as exitrd

Let's speed up image builds by avoiding building
an exitrd and instead reusing the initrd image for
the same purpose.

308584 of 426400 relevant lines covered (72.37%)

1134231.7 hits per line

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

88.46
/src/shared/serialize.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <unistd.h>
5

6
#include "sd-id128.h"
7

8
#include "alloc-util.h"
9
#include "env-util.h"
10
#include "escape.h"
11
#include "extract-word.h"
12
#include "fd-util.h"
13
#include "fdset.h"
14
#include "fileio.h"
15
#include "format-util.h"
16
#include "hexdecoct.h"
17
#include "image-policy.h"
18
#include "log.h"
19
#include "memfd-util.h"
20
#include "parse-util.h"
21
#include "pidref.h"
22
#include "ratelimit.h"
23
#include "serialize.h"
24
#include "set.h"
25
#include "string-util.h"
26
#include "strv.h"
27
#include "time-util.h"
28

29
int serialize_item(FILE *f, const char *key, const char *value) {
477,517✔
30
        assert(f);
477,517✔
31
        assert(key);
477,517✔
32

33
        if (!value)
477,517✔
34
                return 0;
35

36
        /* Make sure that anything we serialize we can also read back again with read_line() with a maximum line size
37
         * of LONG_LINE_MAX. This is a safety net only. All code calling us should filter this out earlier anyway. */
38
        if (strlen(key) + 1 + strlen(value) + 1 > LONG_LINE_MAX)
414,686✔
39
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key);
7✔
40

41
        fputs(key, f);
414,679✔
42
        fputc('=', f);
414,679✔
43
        fputs(value, f);
414,679✔
44
        fputc('\n', f);
414,679✔
45

46
        return 1;
414,679✔
47
}
48

49
int serialize_item_escaped(FILE *f, const char *key, const char *value) {
40,538✔
50
        _cleanup_free_ char *c = NULL;
40,538✔
51

52
        assert(f);
40,538✔
53
        assert(key);
40,538✔
54

55
        if (!value)
40,538✔
56
                return 0;
57

58
        c = xescape(value, " ");
27,625✔
59
        if (!c)
27,625✔
60
                return log_oom();
×
61

62
        return serialize_item(f, key, c);
27,625✔
63
}
64

65
int serialize_item_format(FILE *f, const char *key, const char *format, ...) {
306,033✔
66
        _cleanup_free_ char *allocated = NULL;
306,033✔
67
        char buf[256]; /* Something reasonably short that fits nicely on any stack (i.e. is considerably less
306,033✔
68
                        * than LONG_LINE_MAX (1MiB!) */
69
        const char *b;
306,033✔
70
        va_list ap;
306,033✔
71
        int k;
306,033✔
72

73
        assert(f);
306,033✔
74
        assert(key);
306,033✔
75
        assert(format);
306,033✔
76

77
        /* First, let's try to format this into a stack buffer */
78
        va_start(ap, format);
306,033✔
79
        k = vsnprintf(buf, sizeof(buf), format, ap);
306,033✔
80
        va_end(ap);
306,033✔
81

82
        if (k < 0)
306,033✔
83
                return log_warning_errno(errno, "Failed to serialize item '%s', ignoring: %m", key);
×
84
        if (strlen(key) + 1 + k + 1 > LONG_LINE_MAX) /* See above */
306,033✔
85
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key);
×
86

87
        if ((size_t) k < sizeof(buf))
306,033✔
88
                b = buf; /* Yay, it fit! */
89
        else {
90
                /* So the string didn't fit in the short buffer above, but was not above our total limit,
91
                 * hence let's format it via dynamic memory */
92

93
                va_start(ap, format);
×
94
                k = vasprintf(&allocated, format, ap);
×
95
                va_end(ap);
×
96

97
                if (k < 0)
×
98
                        return log_warning_errno(errno, "Failed to serialize item '%s', ignoring: %m", key);
×
99

100
                b = allocated;
×
101
        }
102

103
        fputs(key, f);
306,033✔
104
        fputc('=', f);
306,033✔
105
        fputs(b, f);
306,033✔
106
        fputc('\n', f);
306,033✔
107

108
        return 1;
109
}
110

111
int serialize_fd(FILE *f, FDSet *fds, const char *key, int fd) {
54,338✔
112
        int copy;
54,338✔
113

114
        assert(f);
54,338✔
115
        assert(fds);
54,338✔
116
        assert(key);
54,338✔
117

118
        if (fd < 0)
54,338✔
119
                return 0;
120

121
        copy = fdset_put_dup(fds, fd);
7,725✔
122
        if (copy < 0)
7,725✔
123
                return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
×
124

125
        return serialize_item_format(f, key, "%i", copy);
7,725✔
126
}
127

128
int serialize_fd_many(FILE *f, FDSet *fds, const char *key, const int fd_array[], size_t n_fd_array) {
711✔
129
        _cleanup_free_ char *t = NULL;
711✔
130

131
        assert(f);
711✔
132

133
        if (n_fd_array == 0)
711✔
134
                return 0;
135

136
        assert(fd_array);
711✔
137

138
        for (size_t i = 0; i < n_fd_array; i++) {
1,876✔
139
                int copy;
1,165✔
140

141
                if (fd_array[i] < 0)
1,165✔
142
                        return -EBADF;
143

144
                copy = fdset_put_dup(fds, fd_array[i]);
1,165✔
145
                if (copy < 0)
1,165✔
146
                        return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
×
147

148
                if (strextendf_with_separator(&t, " ", "%i", copy) < 0)
1,165✔
149
                        return log_oom();
×
150
        }
151

152
        return serialize_item(f, key, t);
711✔
153
}
154

155
int serialize_usec(FILE *f, const char *key, usec_t usec) {
21,839✔
156
        assert(f);
21,839✔
157
        assert(key);
21,839✔
158

159
        if (usec == USEC_INFINITY)
21,839✔
160
                return 0;
161

162
        return serialize_item_format(f, key, USEC_FMT, usec);
4,999✔
163
}
164

165
int serialize_dual_timestamp(FILE *f, const char *key, const dual_timestamp *t) {
141,950✔
166
        assert(f);
141,950✔
167
        assert(key);
141,950✔
168
        assert(t);
141,950✔
169

170
        if (!dual_timestamp_is_set(t))
141,950✔
171
                return 0;
172

173
        return serialize_item_format(f, key, USEC_FMT " " USEC_FMT, t->realtime, t->monotonic);
56,795✔
174
}
175

176
int serialize_strv(FILE *f, const char *key, char * const *l) {
43,116✔
177
        int ret = 0, r;
43,116✔
178

179
        /* Returns the first error, or positive if anything was serialized, 0 otherwise. */
180

181
        assert(f);
43,116✔
182
        assert(key);
43,116✔
183

184
        STRV_FOREACH(i, l) {
69,415✔
185
                r = serialize_item_escaped(f, key, *i);
26,299✔
186
                if ((ret >= 0 && r < 0) ||
26,299✔
187
                    (ret == 0 && r > 0))
26,298✔
188
                        ret = r;
6,917✔
189
        }
190

191
        return ret;
43,116✔
192
}
193

194
int serialize_id128(FILE *f, const char *key, sd_id128_t id) {
18,246✔
195
        assert(f);
18,246✔
196
        assert(key);
18,246✔
197

198
        if (sd_id128_is_null(id))
18,246✔
199
                return 0;
7,934✔
200

201
        return serialize_item_format(f, key, SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(id));
10,312✔
202
}
203

204
int serialize_pidref(FILE *f, FDSet *fds, const char *key, PidRef *pidref) {
13,075✔
205
        int r;
13,075✔
206

207
        assert(f);
13,075✔
208
        assert(fds);
13,075✔
209

210
        if (!pidref_is_set(pidref))
13,075✔
211
                return 0;
212

213
        /* We always serialize the pid separately, to keep downgrades mostly working (older versions will
214
         * deserialize the pid and silently fail to deserialize the pidfd). If we also have a pidfd, we
215
         * serialize both the pid and pidfd, so that we can construct the exact same pidref after
216
         * deserialization (this doesn't work with only the pidfd, as we can't retrieve the original pid
217
         * from the pidfd anymore if the process is reaped). */
218

219
        if (pidref->fd >= 0) {
735✔
220
                int copy = fdset_put_dup(fds, pidref->fd);
735✔
221
                if (copy < 0)
735✔
222
                        return log_error_errno(copy, "Failed to add file descriptor to serialization set: %m");
×
223

224
                r = serialize_item_format(f, key, "@%i:" PID_FMT, copy, pidref->pid);
735✔
225
                if (r < 0)
735✔
226
                        return r;
227
        }
228

229
        return serialize_item_format(f, key, PID_FMT, pidref->pid);
735✔
230
}
231

232
int serialize_ratelimit(FILE *f, const char *key, const RateLimit *rl) {
39,284✔
233
        assert(rl);
39,284✔
234

235
        return serialize_item_format(f, key,
78,568✔
236
                                     USEC_FMT " " USEC_FMT " %u %u",
237
                                     rl->begin,
39,284✔
238
                                     rl->interval,
39,284✔
239
                                     rl->num,
39,284✔
240
                                     rl->burst);
39,284✔
241
}
242

243
int serialize_item_hexmem(FILE *f, const char *key, const void *p, size_t l) {
2,425✔
244
        _cleanup_free_ char *encoded = NULL;
2,425✔
245
        int r;
2,425✔
246

247
        assert(f);
2,425✔
248
        assert(key);
2,425✔
249

250
        if (!p && l > 0)
2,425✔
251
                return -EINVAL;
252

253
        if (l == 0)
2,425✔
254
                return 0;
255

256
        encoded = hexmem(p, l);
1✔
257
        if (!encoded)
1✔
258
                return log_oom_debug();
×
259

260
        r = serialize_item(f, key, encoded);
1✔
261
        if (r < 0)
1✔
262
                return r;
×
263

264
        return 1;
265
}
266

267
int serialize_item_base64mem(FILE *f, const char *key, const void *p, size_t l) {
2,425✔
268
        _cleanup_free_ char *encoded = NULL;
2,425✔
269
        ssize_t len;
2,425✔
270
        int r;
2,425✔
271

272
        assert(f);
2,425✔
273
        assert(key);
2,425✔
274

275
        if (!p && l > 0)
2,425✔
276
                return -EINVAL;
277

278
        if (l == 0)
2,425✔
279
                return 0;
280

281
        len = base64mem(p, l, &encoded);
1✔
282
        if (len <= 0)
1✔
283
                return log_oom_debug();
×
284

285
        r = serialize_item(f, key, encoded);
1✔
286
        if (r < 0)
1✔
287
                return r;
×
288

289
        return 1;
290
}
291

292
int serialize_string_set(FILE *f, const char *key, const Set *s) {
4,848✔
293
        int r;
4,848✔
294

295
        assert(f);
4,848✔
296
        assert(key);
4,848✔
297

298
        if (set_isempty(s))
4,848✔
299
                return 0;
4,848✔
300

301
        /* Serialize as individual items, as each element might contain separators and escapes */
302

303
        const char *e;
1✔
304
        SET_FOREACH(e, s) {
3✔
305
                r = serialize_item(f, key, e);
2✔
306
                if (r < 0)
2✔
307
                        return r;
×
308
        }
309

310
        return 1;
1✔
311
}
312

313
int serialize_image_policy(FILE *f, const char *key, const ImagePolicy *p) {
7,269✔
314
        _cleanup_free_ char *policy = NULL;
7,269✔
315
        int r;
7,269✔
316

317
        assert(f);
7,269✔
318
        assert(key);
7,269✔
319

320
        if (!p)
7,269✔
321
                return 0;
322

323
        r = image_policy_to_string(p, /* simplify= */ false, &policy);
2✔
324
        if (r < 0)
2✔
325
                return r;
326

327
        r = serialize_item(f, key, policy);
2✔
328
        if (r < 0)
2✔
329
                return r;
×
330

331
        return 1;
332
}
333

334
int serialize_bool(FILE *f, const char *key, bool b) {
191,202✔
335
        return serialize_item(f, key, yes_no(b));
354,175✔
336
}
337

338
int serialize_bool_elide(FILE *f, const char *key, bool b) {
123,628✔
339
        return b ? serialize_item(f, key, yes_no(b)) : 0;
123,628✔
340
}
341

342
int deserialize_read_line(FILE *f, char **ret) {
1,761,104✔
343
        _cleanup_free_ char *line = NULL;
1,761,104✔
344
        int r;
1,761,104✔
345

346
        assert(f);
1,761,104✔
347
        assert(ret);
1,761,104✔
348

349
        r = read_stripped_line(f, LONG_LINE_MAX, &line);
1,761,104✔
350
        if (r < 0)
1,761,104✔
351
                return log_error_errno(r, "Failed to read serialization line: %m");
×
352
        if (r == 0) { /* eof */
1,761,104✔
353
                *ret = NULL;
31✔
354
                return 0;
31✔
355
        }
356

357
        if (isempty(line)) { /* End marker */
1,761,073✔
358
                *ret = NULL;
63,282✔
359
                return 0;
63,282✔
360
        }
361

362
        *ret = TAKE_PTR(line);
1,697,791✔
363
        return 1;
1,697,791✔
364
}
365

366
int deserialize_fd(FDSet *fds, const char *value) {
36,733✔
367
        _cleanup_close_ int our_fd = -EBADF;
36,733✔
368
        int parsed_fd;
36,733✔
369

370
        assert(value);
36,733✔
371

372
        parsed_fd = parse_fd(value);
36,733✔
373
        if (parsed_fd < 0)
36,733✔
374
                return log_debug_errno(parsed_fd, "Failed to parse file descriptor serialization: %s", value);
×
375

376
        our_fd = fdset_remove(fds, parsed_fd); /* Take possession of the fd */
36,733✔
377
        if (our_fd < 0)
36,733✔
378
                return log_debug_errno(our_fd, "Failed to acquire fd from serialization fds: %m");
×
379

380
        return TAKE_FD(our_fd);
381
}
382

383
int deserialize_fd_many(FDSet *fds, const char *value, size_t n, int *ret) {
2,016✔
384
        int r, *fd_array = NULL;
2,016✔
385
        size_t m = 0;
2,016✔
386

387
        assert(value);
2,016✔
388

389
        fd_array = new(int, n);
2,016✔
390
        if (!fd_array)
2,016✔
391
                return -ENOMEM;
2,016✔
392

393
        CLEANUP_ARRAY(fd_array, m, close_many_and_free);
4,032✔
394

395
        for (;;) {
10,782✔
396
                _cleanup_free_ char *w = NULL;
4,383✔
397
                int fd;
6,399✔
398

399
                r = extract_first_word(&value, &w, NULL, 0);
6,399✔
400
                if (r < 0)
6,399✔
401
                        return r;
402
                if (r == 0) {
6,399✔
403
                        if (m < n) /* Too few */
2,016✔
404
                                return -EINVAL;
405

406
                        break;
2,016✔
407
                }
408

409
                if (m >= n) /* Too many */
4,383✔
410
                        return -EINVAL;
411

412
                fd = deserialize_fd(fds, w);
4,383✔
413
                if (fd < 0)
4,383✔
414
                        return fd;
415

416
                fd_array[m++] = fd;
4,383✔
417
        }
418

419
        memcpy(ret, fd_array, m * sizeof(int));
2,016✔
420
        fd_array = mfree(fd_array);
2,016✔
421

422
        return 0;
2,016✔
423
}
424

425
int deserialize_strv(const char *value, char ***l) {
81,399✔
426
        ssize_t unescaped_len;
81,399✔
427
        char *unescaped;
81,399✔
428

429
        assert(l);
81,399✔
430
        assert(value);
81,399✔
431

432
        unescaped_len = cunescape(value, 0, &unescaped);
81,399✔
433
        if (unescaped_len < 0)
81,399✔
434
                return unescaped_len;
×
435

436
        return strv_consume(l, unescaped);
81,399✔
437
}
438

439
int deserialize_usec(const char *value, usec_t *ret) {
13,316✔
440
        int r;
13,316✔
441

442
        assert(value);
13,316✔
443
        assert(ret);
13,316✔
444

445
        r = safe_atou64(value, ret);
13,316✔
446
        if (r < 0)
13,316✔
447
                return log_debug_errno(r, "Failed to parse usec value \"%s\": %m", value);
×
448

449
        return 0;
450
}
451

452
int deserialize_dual_timestamp(const char *value, dual_timestamp *ret) {
40,695✔
453
        uint64_t a, b;
40,695✔
454
        int r, pos;
40,695✔
455

456
        assert(value);
40,695✔
457
        assert(ret);
40,695✔
458

459
        pos = strspn(value, WHITESPACE);
40,695✔
460
        if (value[pos] == '-')
40,695✔
461
                return -EINVAL;
40,695✔
462
        pos += strspn(value + pos, DIGITS);
40,694✔
463
        pos += strspn(value + pos, WHITESPACE);
40,694✔
464
        if (value[pos] == '-')
40,694✔
465
                return -EINVAL;
466

467
        r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
40,693✔
468
        if (r != 2)
40,693✔
469
                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
1✔
470
                                       "Failed to parse dual timestamp value \"%s\".",
471
                                       value);
472

473
        if (value[pos] != '\0')
40,692✔
474
                /* trailing garbage */
475
                return -EINVAL;
476

477
        *ret = (dual_timestamp) {
40,691✔
478
                .realtime = a,
479
                .monotonic = b,
480
        };
481

482
        return 0;
40,691✔
483
}
484

485
int deserialize_environment(const char *value, char ***list) {
577✔
486
        _cleanup_free_ char *unescaped = NULL;
577✔
487
        ssize_t l;
577✔
488
        int r;
577✔
489

490
        assert(value);
577✔
491
        assert(list);
577✔
492

493
        /* Changes the *environment strv inline. */
494

495
        l = cunescape(value, 0, &unescaped);
577✔
496
        if (l < 0)
577✔
497
                return log_error_errno(l, "Failed to unescape: %m");
2✔
498

499
        r = strv_env_replace_consume(list, TAKE_PTR(unescaped));
575✔
500
        if (r < 0)
575✔
501
                return log_error_errno(r, "Failed to append environment variable: %m");
×
502

503
        return 0;
504
}
505

506
int deserialize_pidref(FDSet *fds, const char *value, PidRef *ret) {
578✔
507
        const char *e;
578✔
508
        int r;
578✔
509

510
        assert(value);
578✔
511
        assert(ret);
578✔
512

513
        e = startswith(value, "@");
578✔
514
        if (e) {
578✔
515
                _cleanup_free_ char *fdstr = NULL, *pidstr = NULL;
578✔
516
                _cleanup_close_ int fd = -EBADF;
578✔
517

518
                r = extract_many_words(&e, ":", /* flags= */ 0, &fdstr, &pidstr);
578✔
519
                if (r < 0)
578✔
520
                        return log_debug_errno(r, "Failed to deserialize pidref '%s': %m", e);
×
521
                if (r == 0)
578✔
522
                        return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot deserialize pidref from empty string.");
×
523

524
                assert(r <= 2);
578✔
525

526
                fd = deserialize_fd(fds, fdstr);
578✔
527
                if (fd < 0)
578✔
528
                        return fd;
529

530
                /* The serialization format changed after 255.4. In systemd <= 255.4 only pidfd is
531
                 * serialized, but that causes problems when reconstructing pidref (see serialize_pidref for
532
                 * details). After 255.4 the pid is serialized as well even if we have a pidfd, but we still
533
                 * need to support older format as we might be upgrading from a version that still uses the
534
                 * old format. */
535
                if (pidstr) {
578✔
536
                        pid_t pid;
578✔
537

538
                        r = parse_pid(pidstr, &pid);
578✔
539
                        if (r < 0)
578✔
540
                                return log_debug_errno(r, "Failed to parse PID: %s", pidstr);
×
541

542
                        *ret = (PidRef) {
578✔
543
                                .pid = pid,
544
                                .fd = TAKE_FD(fd),
578✔
545
                        };
546
                } else
547
                        r = pidref_set_pidfd_consume(ret, TAKE_FD(fd));
×
548
        } else {
549
                pid_t pid;
×
550

551
                r = parse_pid(value, &pid);
×
552
                if (r < 0)
×
553
                        return log_debug_errno(r, "Failed to parse PID: %s", value);
×
554

555
                r = pidref_set_pid(ret, pid);
×
556
        }
557
        if (r < 0)
578✔
558
                return log_debug_errno(r, "Failed to initialize pidref: %m");
×
559

560
        return 0;
561
}
562

563
void deserialize_ratelimit(RateLimit *rl, const char *name, const char *value) {
28,548✔
564
        usec_t begin, interval;
28,548✔
565
        unsigned num, burst;
28,548✔
566

567
        assert(rl);
28,548✔
568
        assert(name);
28,548✔
569
        assert(value);
28,548✔
570

571
        if (sscanf(value, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4)
28,548✔
572
                return log_notice("Failed to parse %s, ignoring: %s", name, value);
×
573

574
        /* Preserve the counter only if the configuration didn't change. */
575
        rl->num = (interval == rl->interval && burst == rl->burst) ? num : 0;
28,548✔
576
        rl->begin = begin;
28,548✔
577
}
578

579
int open_serialization_fd(const char *ident) {
4,634✔
580
        assert(ident);
4,634✔
581

582
        int fd = memfd_new_full(ident, MFD_ALLOW_SEALING);
4,634✔
583
        if (fd < 0)
4,634✔
584
                return fd;
585

586
        log_debug("Serializing %s to memfd.", ident);
4,634✔
587
        return fd;
588
}
589

590
int open_serialization_file(const char *ident, FILE **ret) {
2,547✔
591
        _cleanup_fclose_ FILE *f = NULL;
2,547✔
592
        _cleanup_close_ int fd;
2,547✔
593

594
        assert(ret);
2,547✔
595

596
        fd = open_serialization_fd(ident);
2,547✔
597
        if (fd < 0)
2,547✔
598
                return fd;
599

600
        f = take_fdopen(&fd, "w+");
2,547✔
601
        if (!f)
2,547✔
602
                return -errno;
×
603

604
        *ret = TAKE_PTR(f);
2,547✔
605
        return 0;
2,547✔
606
}
607

608
int finish_serialization_fd(int fd) {
2,087✔
609
        assert(fd >= 0);
2,087✔
610

611
        if (lseek(fd, 0, SEEK_SET) < 0)
2,087✔
612
                return -errno;
×
613

614
        return memfd_set_sealed(fd);
2,087✔
615
}
616

617
int finish_serialization_file(FILE *f) {
2,547✔
618
        int r;
2,547✔
619

620
        assert(f);
2,547✔
621

622
        r = fflush_and_check(f);
2,547✔
623
        if (r < 0)
2,547✔
624
                return r;
625

626
        if (fseeko(f, 0, SEEK_SET) < 0)
2,547✔
627
                return -errno;
×
628

629
        int fd = fileno(f);
2,547✔
630
        if (fd < 0)
2,547✔
631
                return -EBADF;
632

633
        return memfd_set_sealed(fd);
2,547✔
634
}
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