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

systemd / systemd / 25586189637

08 May 2026 11:55PM UTC coverage: 72.64% (+0.02%) from 72.624%
25586189637

push

github

bluca
man/tmpfiles: fix missing 'as' in %t details column

This was missing when the details were added in 5a8575ef013 (tmpfiles:
also add %t/%S/%C/%L specifiers, 2017-11-23).

326645 of 449679 relevant lines covered (72.64%)

1211877.11 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) {
944,874✔
30
        assert(f);
944,874✔
31
        assert(key);
944,874✔
32

33
        if (!value)
944,874✔
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)
850,417✔
39
                return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "Attempted to serialize overly long item '%s', refusing.", key);
7✔
40

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

46
        return 1;
850,410✔
47
}
48

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

52
        assert(f);
58,643✔
53
        assert(key);
58,643✔
54

55
        if (!value)
58,643✔
56
                return 0;
57

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

62
        return serialize_item(f, key, c);
32,490✔
63
}
64

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

73
        assert(f);
617,748✔
74
        assert(key);
617,748✔
75
        assert(format);
617,748✔
76

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

82
        if (k < 0)
617,748✔
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 */
617,748✔
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))
617,748✔
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);
617,748✔
104
        fputc('=', f);
617,748✔
105
        fputs(b, f);
617,748✔
106
        fputc('\n', f);
617,748✔
107

108
        return 1;
109
}
110

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

114
        assert(f);
108,139✔
115
        assert(fds);
108,139✔
116
        assert(key);
108,139✔
117

118
        if (fd < 0)
108,139✔
119
                return 0;
120

121
        copy = fdset_put_dup(fds, fd);
9,154✔
122
        if (copy < 0)
9,154✔
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);
9,154✔
126
}
127

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

131
        assert(f);
1,082✔
132

133
        if (n_fd_array == 0)
1,082✔
134
                return 0;
135

136
        assert(fd_array);
1,082✔
137

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

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

144
                copy = fdset_put_dup(fds, fd_array[i]);
1,865✔
145
                if (copy < 0)
1,865✔
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,865✔
149
                        return log_oom();
×
150
        }
151

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

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

159
        if (usec == USEC_INFINITY)
49,483✔
160
                return 0;
161

162
        return serialize_item_format(f, key, USEC_FMT, usec);
14,906✔
163
}
164

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

170
        if (!dual_timestamp_is_set(t))
359,053✔
171
                return 0;
172

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

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

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

181
        assert(f);
50,569✔
182
        assert(key);
50,569✔
183

184
        STRV_FOREACH(i, l) {
80,859✔
185
                r = serialize_item_escaped(f, key, *i);
30,290✔
186
                if ((ret >= 0 && r < 0) ||
30,290✔
187
                    (ret == 0 && r > 0))
30,289✔
188
                        ret = r;
8,360✔
189
        }
190

191
        return ret;
50,569✔
192
}
193

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

198
        if (sd_id128_is_null(id))
46,102✔
199
                return 0;
18,704✔
200

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

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

207
        assert(f);
34,612✔
208
        assert(fds);
34,612✔
209

210
        if (!pidref_is_set(pidref))
34,612✔
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) {
2,025✔
220
                int copy = fdset_put_dup(fds, pidref->fd);
2,025✔
221
                if (copy < 0)
2,025✔
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);
2,025✔
225
                if (r < 0)
2,025✔
226
                        return r;
227
        }
228

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

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

235
        return serialize_item_format(f, key,
199,600✔
236
                                     USEC_FMT " " USEC_FMT " %u %u",
237
                                     rl->begin,
99,800✔
238
                                     rl->interval,
99,800✔
239
                                     rl->num,
99,800✔
240
                                     rl->burst);
99,800✔
241
}
242

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

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

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

253
        if (l == 0)
2,829✔
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,829✔
268
        _cleanup_free_ char *encoded = NULL;
2,829✔
269
        ssize_t len;
2,829✔
270
        int r;
2,829✔
271

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

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

278
        if (l == 0)
2,829✔
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) {
5,656✔
293
        int r;
5,656✔
294

295
        assert(f);
5,656✔
296
        assert(key);
5,656✔
297

298
        if (set_isempty(s))
5,656✔
299
                return 0;
5,656✔
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) {
8,481✔
314
        _cleanup_free_ char *policy = NULL;
8,481✔
315
        int r;
8,481✔
316

317
        assert(f);
8,481✔
318
        assert(key);
8,481✔
319

320
        if (!p)
8,481✔
321
                return 0;
322

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

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

331
        return 1;
332
}
333

334
int serialize_bool(FILE *f, const char *key, bool b) {
471,934✔
335
        return serialize_item(f, key, yes_no(b));
880,987✔
336
}
337

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

342
int deserialize_read_line(FILE *f, char **ret) {
2,563,534✔
343
        _cleanup_free_ char *line = NULL;
2,563,534✔
344
        int r;
2,563,534✔
345

346
        assert(f);
2,563,534✔
347
        assert(ret);
2,563,534✔
348

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

357
        if (isempty(line)) { /* End marker */
2,563,502✔
358
                *ret = NULL;
93,656✔
359
                return 0;
93,656✔
360
        }
361

362
        *ret = TAKE_PTR(line);
2,469,846✔
363
        return 1;
2,469,846✔
364
}
365

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

370
        assert(value);
42,641✔
371

372
        parsed_fd = parse_fd(value);
42,641✔
373
        if (parsed_fd < 0)
42,641✔
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 */
42,641✔
377
        if (our_fd < 0)
42,641✔
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,334✔
384
        int r, *fd_array = NULL;
2,334✔
385
        size_t m = 0;
2,334✔
386

387
        assert(value);
2,334✔
388

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

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

395
        for (;;) {
13,130✔
396
                _cleanup_free_ char *w = NULL;
5,398✔
397
                int fd;
7,732✔
398

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

406
                        break;
2,334✔
407
                }
408

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

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

416
                fd_array[m++] = fd;
5,398✔
417
        }
418

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

422
        return 0;
2,334✔
423
}
424

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

429
        assert(l);
86,127✔
430
        assert(value);
86,127✔
431

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

436
        return strv_consume(l, unescaped);
86,127✔
437
}
438

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

442
        assert(value);
38,706✔
443
        assert(ret);
38,706✔
444

445
        r = safe_atou64(value, ret);
38,706✔
446
        if (r < 0)
38,706✔
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) {
129,792✔
453
        uint64_t a, b;
129,792✔
454
        int r, pos;
129,792✔
455

456
        assert(value);
129,792✔
457
        assert(ret);
129,792✔
458

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

467
        r = sscanf(value, "%" PRIu64 "%" PRIu64 "%n", &a, &b, &pos);
129,790✔
468
        if (r != 2)
129,790✔
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')
129,789✔
474
                /* trailing garbage */
475
                return -EINVAL;
476

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

482
        return 0;
129,788✔
483
}
484

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

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

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

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

499
        r = strv_env_replace_consume(list, TAKE_PTR(unescaped));
838✔
500
        if (r < 0)
838✔
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) {
1,745✔
507
        const char *e;
1,745✔
508
        int r;
1,745✔
509

510
        assert(value);
1,745✔
511
        assert(ret);
1,745✔
512

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

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

524
                assert(r <= 2);
1,745✔
525

526
                fd = deserialize_fd(fds, fdstr);
1,745✔
527
                if (fd < 0)
1,745✔
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) {
1,745✔
536
                        pid_t pid;
1,745✔
537

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

542
                        *ret = (PidRef) {
1,745✔
543
                                .pid = pid,
544
                                .fd = TAKE_FD(fd),
1,745✔
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)
1,745✔
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) {
88,323✔
564
        usec_t begin, interval;
88,323✔
565
        unsigned num, burst;
88,323✔
566

567
        assert(rl);
88,323✔
568
        assert(name);
88,323✔
569
        assert(value);
88,323✔
570

571
        if (sscanf(value, USEC_FMT " " USEC_FMT " %u %u", &begin, &interval, &num, &burst) != 4)
88,323✔
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;
88,323✔
576
        rl->begin = begin;
88,323✔
577
}
578

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

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

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

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

594
        assert(ret);
3,065✔
595

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

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

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

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

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

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

617
int finish_serialization_file(FILE *f) {
3,065✔
618
        int r;
3,065✔
619

620
        assert(f);
3,065✔
621

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

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

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

633
        return memfd_set_sealed(fd);
3,065✔
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