• 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

87.3
/src/basic/pidref.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <poll.h>
4
#include <sys/wait.h>
5
#include <unistd.h>
6

7
#include "alloc-util.h"
8
#include "errno-util.h"
9
#include "fd-util.h"
10
#include "format-util.h"
11
#include "hash-funcs.h"
12
#include "io-util.h"
13
#include "log.h"
14
#include "parse-util.h"
15
#include "pidfd-util.h"
16
#include "pidref.h"
17
#include "process-util.h"
18
#include "siphash24.h"
19
#include "time-util.h"
20

21
int pidref_acquire_pidfd_id(PidRef *pidref) {
12,519✔
22
        int r;
12,519✔
23

24
        assert(pidref);
12,519✔
25

26
        if (!pidref_is_set(pidref))
12,519✔
27
                return -ESRCH;
28

29
        if (pidref_is_remote(pidref))
12,519✔
30
                return -EREMOTE;
31

32
        if (pidref->fd < 0)
12,519✔
33
                return -ENOMEDIUM;
34

35
        if (pidref->fd_id > 0)
12,513✔
36
                return 0;
37

38
        r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
5,985✔
39
        if (r < 0) {
5,985✔
40
                if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
×
41
                        log_debug_errno(r, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
×
42
                                        pidref->pid);
43
                return r;
×
44
        }
45

46
        return 0;
47
}
48

49
bool pidref_equal(PidRef *a, PidRef *b) {
7,297✔
50

51
        /* If this is the very same structure, it definitely refers to the same process */
52
        if (a == b)
7,297✔
53
                return true;
54

55
        if (!pidref_is_set(a))
7,294✔
56
                return !pidref_is_set(b);
6,270✔
57

58
        if (!pidref_is_set(b))
4,159✔
59
                return false;
60

61
        if (a->pid != b->pid)
4,149✔
62
                return false;
63

64
        if (pidref_is_remote(a)) {
4,009✔
65
                /* If one is remote and the other isn't, they are not the same */
66
                if (!pidref_is_remote(b))
×
67
                        return false;
68

69
                /* If both are remote, compare fd IDs if we have both, otherwise don't bother, and cut things short */
70
                if (a->fd_id == 0 || b->fd_id == 0)
×
71
                        return true;
72
        } else {
73
                /* If the other side is remote, then this is not the same */
74
                if (pidref_is_remote(b))
4,009✔
75
                        return false;
76

77
                /* PID1 cannot exit, hence it cannot change pidfs ids, hence no point in comparing them, we
78
                 * can shortcut things */
79
                if (a->pid == 1)
4,008✔
80
                        return true;
81

82
                /* Try to compare pidfds using their inode numbers. This way we can ensure that we
83
                 * don't spuriously consider two PidRefs equal if the pid has been reused once. Note
84
                 * that we ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to
85
                 * many reasons. */
86
                if (pidref_acquire_pidfd_id(a) < 0 || pidref_acquire_pidfd_id(b) < 0)
4,002✔
87
                        return true;
2✔
88
        }
89

90
        return a->fd_id == b->fd_id;
4,000✔
91
}
92

93
int pidref_set_pid(PidRef *pidref, pid_t pid) {
61,528✔
94
        uint64_t pidfdid = 0;
61,528✔
95
        int fd;
61,528✔
96

97
        assert(pidref);
61,528✔
98

99
        if (pid < 0)
61,528✔
100
                return -ESRCH;
61,528✔
101
        if (pid == 0) {
61,528✔
102
                pid = getpid_cached();
31,688✔
103
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
31,688✔
104
        }
105

106
        fd = pidfd_open(pid, 0);
61,528✔
107
        if (fd < 0) {
61,528✔
108
                /* Graceful fallback in case the kernel is out of fds */
109
                if (!ERRNO_IS_RESOURCE(errno))
×
110
                        return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
×
111

112
                fd = -EBADF;
113
        }
114

115
        *pidref = (PidRef) {
61,528✔
116
                .fd = fd,
117
                .pid = pid,
118
                .fd_id = pidfdid,
119
        };
120

121
        return 0;
61,528✔
122
}
123

124
int pidref_set_pid_and_pidfd_id(
56✔
125
                PidRef *pidref,
126
                pid_t pid,
127
                uint64_t pidfd_id) {
128

129
        int r;
56✔
130

131
        assert(pidref);
56✔
132

133
        _cleanup_(pidref_done) PidRef n = PIDREF_NULL;
×
134
        r = pidref_set_pid(&n, pid);
56✔
135
        if (r < 0)
56✔
136
                return r;
137

138
        if (pidfd_id > 0) {
56✔
139
                r = pidref_acquire_pidfd_id(&n);
56✔
140
                if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
112✔
141
                        return r;
142

143
                if (n.fd_id != pidfd_id)
56✔
144
                        return -ESRCH;
145
        }
146

147
        *pidref = TAKE_PIDREF(n);
56✔
148
        return 0;
56✔
149
}
150

151
int pidref_set_pidstr(PidRef *pidref, const char *pid) {
43✔
152
        pid_t nr;
43✔
153
        int r;
43✔
154

155
        assert(pidref);
43✔
156

157
        r = parse_pid(pid, &nr);
43✔
158
        if (r < 0)
43✔
159
                return r;
43✔
160

161
        return pidref_set_pid(pidref, nr);
43✔
162
}
163

164
int pidref_set_pidfd(PidRef *pidref, int fd) {
1,081✔
165
        int r;
1,081✔
166

167
        assert(pidref);
1,081✔
168

169
        if (fd < 0)
1,081✔
170
                return -EBADF;
171

172
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
1,081✔
173
        if (fd_copy < 0) {
1,081✔
174
                pid_t pid;
×
175

176
                if (!ERRNO_IS_RESOURCE(errno))
×
177
                        return -errno;
×
178

179
                /* Graceful fallback if we are out of fds */
180
                r = pidfd_get_pid(fd, &pid);
×
181
                if (r < 0)
×
182
                        return r;
183

184
                *pidref = PIDREF_MAKE_FROM_PID(pid);
×
185
                return 0;
×
186
        }
187

188
        return pidref_set_pidfd_consume(pidref, fd_copy);
1,081✔
189
}
190

191
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
3,549✔
192
        pid_t pid;
3,549✔
193
        int r;
3,549✔
194

195
        assert(pidref);
3,549✔
196

197
        if (fd < 0)
3,549✔
198
                return -EBADF;
3,549✔
199

200
        r = pidfd_get_pid(fd, &pid);
3,549✔
201
        if (r < 0)
3,549✔
202
                return r;
203

204
        *pidref = (PidRef) {
3,549✔
205
                .fd = fd,
206
                .pid = pid,
207
        };
208

209
        return 0;
3,549✔
210
}
211

212
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
3,547✔
213
        int r;
3,547✔
214

215
        r = pidref_set_pidfd_take(pidref, fd);
3,547✔
216
        if (r < 0)
3,547✔
217
                safe_close(fd);
×
218

219
        return r;
3,547✔
220
}
221

222
int pidref_set_parent(PidRef *ret) {
1,265✔
223
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
1,265✔
224
        pid_t ppid;
1,265✔
225
        int r;
1,265✔
226

227
        assert(ret);
1,265✔
228

229
        /* Acquires a pidref to our parent process. Deals with the fact that parent processes might exit, and
230
         * we get reparented to other processes, with our old parent's PID already being recycled. */
231

232
        ppid = getppid();
1,265✔
233
        for (;;) {
1,265✔
234
                r = pidref_set_pid(&parent, ppid);
1,265✔
235
                if (r < 0)
1,265✔
236
                        return r;
237

238
                if (parent.fd < 0) /* If pidfds are not available, then we are done */
1,265✔
239
                        break;
240

241
                pid_t now_ppid = getppid();
1,265✔
242
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
1,265✔
243
                        break;
244

245
                /* Otherwise let's try again with the new ppid */
246
                ppid = now_ppid;
×
247
                pidref_done(&parent);
×
248
        }
249

250
        *ret = TAKE_PIDREF(parent);
1,265✔
251
        return 0;
1,265✔
252
}
253

254
void pidref_done(PidRef *pidref) {
201,184✔
255
        assert(pidref);
201,184✔
256

257
        *pidref = (PidRef) {
402,368✔
258
                .fd = safe_close(pidref->fd),
201,184✔
259
        };
260
}
201,184✔
261

262
PidRef* pidref_free(PidRef *pidref) {
3,343✔
263
        /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
264
        if (!pidref)
3,343✔
265
                return NULL;
266

267
        pidref_done(pidref);
3,343✔
268
        return mfree(pidref);
3,343✔
269
}
270

271
int pidref_copy(const PidRef *pidref, PidRef *ret) {
3,344✔
272
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
3,344✔
273

274
        /* If NULL is passed we'll generate a PidRef that refers to no process. This makes it easy to
275
         * copy pidref fields that might or might not reference a process yet. */
276

277
        assert(ret);
3,344✔
278

279
        if (pidref) {
3,344✔
280
                if (pidref_is_remote(pidref)) /* Propagate remote flag */
3,342✔
281
                        copy.fd = -EREMOTE;
×
282
                else if (pidref->fd >= 0) {
3,342✔
283
                        copy.fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
3,336✔
284
                        if (copy.fd < 0) {
3,336✔
285
                                if (!ERRNO_IS_RESOURCE(errno))
×
286
                                        return -errno;
×
287

288
                                copy.fd = -EBADF;
×
289
                        }
290
                }
291

292
                copy.pid = pidref->pid;
3,342✔
293
                copy.fd_id = pidref->fd_id;
3,342✔
294
        }
295

296
        *ret = TAKE_PIDREF(copy);
3,344✔
297
        return 0;
3,344✔
298
}
299

300
int pidref_dup(const PidRef *pidref, PidRef **ret) {
3,340✔
301
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
3,340✔
302
        int r;
3,340✔
303

304
        /* Allocates a new PidRef on the heap, making it a copy of the specified pidref. This does not try to
305
         * acquire a pidfd if we don't have one yet! */
306

307
        assert(ret);
3,340✔
308

309
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
3,340✔
310
        if (!dup_pidref)
3,340✔
311
                return -ENOMEM;
312

313
        r = pidref_copy(pidref, dup_pidref);
3,340✔
314
        if (r < 0)
3,340✔
315
                return r;
316

317
        *ret = TAKE_PTR(dup_pidref);
3,340✔
318
        return 0;
3,340✔
319
}
320

321
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
4✔
322
        _cleanup_(pidref_freep) PidRef *n = NULL;
4✔
323
        int r;
4✔
324

325
        assert(ret);
4✔
326

327
        if (pid < 0)
4✔
328
                return -ESRCH;
329

330
        n = new(PidRef, 1);
3✔
331
        if (!n)
3✔
332
                return -ENOMEM;
333

334
        *n = PIDREF_NULL;
3✔
335

336
        r = pidref_set_pid(n, pid);
3✔
337
        if (r < 0)
3✔
338
                return r;
339

340
        *ret = TAKE_PTR(n);
3✔
341
        return 0;
3✔
342
}
343

344
int pidref_kill(const PidRef *pidref, int sig) {
15,178✔
345

346
        if (!pidref)
15,178✔
347
                return -ESRCH;
348

349
        if (pidref_is_remote(pidref))
15,178✔
350
                return -EREMOTE;
351

352
        if (pidref->fd >= 0)
15,176✔
353
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,105✔
354

355
        if (pidref->pid > 0)
8,071✔
356
                return RET_NERRNO(kill(pidref->pid, sig));
14,768✔
357

358
        return -ESRCH;
359
}
360

361
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
423✔
362
        int r;
423✔
363

364
        r = pidref_kill(pidref, sig);
423✔
365
        if (r < 0)
423✔
366
                return r;
367

368
        if (!IN_SET(sig, SIGCONT, SIGKILL))
422✔
369
                (void) pidref_kill(pidref, SIGCONT);
422✔
370

371
        return 0;
372
}
373

374
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
9,327✔
375

376
        if (!pidref)
9,327✔
377
                return -ESRCH;
378

379
        if (pidref_is_remote(pidref))
9,327✔
380
                return -EREMOTE;
381

382
        if (pidref->fd >= 0) {
9,327✔
383
                siginfo_t si;
9,327✔
384

385
                /* We can't use structured initialization here, since the structure contains various unions
386
                 * and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
387
                 * allow assignments to */
388
                zero(si);
9,327✔
389
                si.si_signo = sig;
9,327✔
390
                si.si_code = SI_QUEUE;
9,327✔
391
                si.si_pid = getpid_cached();
9,327✔
392
                si.si_uid = getuid();
9,327✔
393
                si.si_value.sival_int = value;
9,327✔
394

395
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
9,327✔
396
        }
397

398
        if (pidref->pid > 0)
×
399
                return RET_NERRNO(sigqueue(pidref->pid, sig, (const union sigval) { .sival_int = value }));
×
400

401
        return -ESRCH;
402
}
403

404
int pidref_verify(const PidRef *pidref) {
44,019✔
405
        int r;
44,019✔
406

407
        /* This is a helper that is supposed to be called after reading information from procfs via a
408
         * PidRef. It ensures that the PID we track still matches the PIDFD we pin. If this value differs
409
         * after a procfs read, we might have read the data from a recycled PID. */
410

411
        if (!pidref_is_set(pidref))
44,019✔
412
                return -ESRCH;
413

414
        if (pidref_is_remote(pidref))
44,017✔
415
                return -EREMOTE;
416

417
        if (pidref->pid == 1)
44,016✔
418
                return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
419

420
        if (pidref->fd < 0)
42,618✔
421
                return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
422

423
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
23,131✔
424
        if (r < 0)
23,131✔
425
                return r;
2,839✔
426

427
        return 1; /* We have a pidfd and it still points to the PID we have, hence all is *really* OK → return 1 */
428
}
429

430
bool pidref_is_self(PidRef *pidref) {
33,172✔
431
        if (!pidref_is_set(pidref))
33,172✔
432
                return false;
33,172✔
433

434
        if (pidref_is_remote(pidref))
33,170✔
435
                return false;
436

437
        if (pidref->pid != getpid_cached())
33,170✔
438
                return false;
439

440
        /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
441
        if (pidref->pid == 1)
670✔
442
                return true;
443

444
        /* Also compare pidfd ID if we can get it */
445
        if (pidref_acquire_pidfd_id(pidref) < 0)
670✔
446
                return true;
447

448
        uint64_t self_id;
666✔
449
        if (pidfd_get_inode_id_self_cached(&self_id) < 0)
666✔
450
                return true;
451

452
        return pidref->fd_id == self_id;
666✔
453
}
454

455
int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *ret_si) {
10,925✔
456
        int r;
10,925✔
457

458
        assert(timeout > 0);
10,925✔
459

460
        if (!pidref_is_set(pidref))
10,925✔
461
                return -ESRCH;
462

463
        if (pidref_is_remote(pidref))
10,925✔
464
                return -EREMOTE;
465

466
        if (pidref->pid == 1 || pidref_is_self(pidref))
10,924✔
467
                return -ECHILD;
×
468

469
        if (timeout != USEC_INFINITY && pidref->fd < 0)
10,924✔
470
                return -ENOMEDIUM;
471

472
        usec_t ts = timeout == USEC_INFINITY ? USEC_INFINITY : usec_add(now(CLOCK_MONOTONIC), timeout);
10,926✔
473

474
        for (;;) {
10,924✔
475
                if (ts != USEC_INFINITY) {
10,924✔
476
                        usec_t left = usec_sub_unsigned(ts, now(CLOCK_MONOTONIC));
2✔
477
                        if (left == 0)
2✔
478
                                return -ETIMEDOUT;
10,924✔
479

480
                        r = fd_wait_for_event(pidref->fd, POLLIN, left);
2✔
481
                        if (r == 0)
2✔
482
                                return -ETIMEDOUT;
483
                        if (r == -EINTR)
1✔
484
                                continue;
×
485
                        if (r < 0)
1✔
486
                                return r;
487
                }
488

489
                siginfo_t si = {};
10,923✔
490

491
                if (pidref->fd >= 0)
10,923✔
492
                        r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, WEXITED));
10,077✔
493
                else
494
                        r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED));
846✔
495
                if (r >= 0) {
×
496
                        if (ret_si)
10,923✔
497
                                *ret_si = si;
7,521✔
498
                        return 0;
10,923✔
499
                }
500
                if (r != -EINTR)
×
501
                        return r;
502
        }
503
}
504

505
bool pidref_is_automatic(const PidRef *pidref) {
174,490✔
506
        return pidref && pid_is_automatic(pidref->pid);
174,490✔
507
}
508

509
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
86,720✔
510
        siphash24_compress_typesafe(pidref->pid, state);
86,720✔
511
}
86,720✔
512

513
int pidref_compare_func(const PidRef *a, const PidRef *b) {
87,219✔
514
        int r;
87,219✔
515

516
        assert(a);
87,219✔
517
        assert(b);
87,219✔
518

519
        r = CMP(pidref_is_set(a), pidref_is_set(b));
261,657✔
520
        if (r != 0)
87,219✔
521
                return r;
×
522

523
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
87,219✔
524
        if (r != 0)
87,219✔
525
                return r;
×
526

527
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
174,438✔
528
        if (r != 0)
87,219✔
529
                return r;
×
530

531
        r = CMP(a->pid, b->pid);
87,219✔
532
        if (r != 0)
68,871✔
533
                return r;
21,174✔
534

535
        if (a->fd_id != 0 && b->fd_id != 0)
66,045✔
536
                return CMP(a->fd_id, b->fd_id);
659✔
537

538
        return 0;
539
}
540

541
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
542

543
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
×
544
                                    PidRef, pidref_hash_func, pidref_compare_func,
545
                                    pidref_free);
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