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

systemd / systemd / 20355307520

18 Dec 2025 09:15PM UTC coverage: 72.578% (-0.1%) from 72.709%
20355307520

push

github

DaanDeMeyer
mkosi: update debian commit reference to d9f2aa170

* d9f2aa1704 Install systemd-tpm2-generator.8 only for UEFI builds
* ac1c7d8048 Drop dependencies on libcap-dev, no longer used since v259
* c36e5871ca Do not install systemd-sysv-generator.8 in upstream build
* bac0cca0e8 Install new files for upstream build
* 2855fb1302 Update changelog for 259-1 release

309322 of 426195 relevant lines covered (72.58%)

1149469.57 hits per line

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

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

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

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

18
int pidref_acquire_pidfd_id(PidRef *pidref) {
12,915✔
19
        int r;
12,915✔
20

21
        assert(pidref);
12,915✔
22

23
        if (!pidref_is_set(pidref))
12,915✔
24
                return -ESRCH;
25

26
        if (pidref_is_remote(pidref))
12,915✔
27
                return -EREMOTE;
28

29
        if (pidref->fd < 0)
12,915✔
30
                return -ENOMEDIUM;
31

32
        if (pidref->fd_id > 0)
12,908✔
33
                return 0;
34

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

43
        return 0;
44
}
45

46
bool pidref_equal(PidRef *a, PidRef *b) {
7,562✔
47

48
        /* If this is the very same structure, it definitely refers to the same process */
49
        if (a == b)
7,562✔
50
                return true;
51

52
        if (!pidref_is_set(a))
7,559✔
53
                return !pidref_is_set(b);
3,223✔
54

55
        if (!pidref_is_set(b))
4,336✔
56
                return false;
57

58
        if (a->pid != b->pid)
4,326✔
59
                return false;
60

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

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

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

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

87
        return a->fd_id == b->fd_id;
4,177✔
88
}
89

90
int pidref_set_pid(PidRef *pidref, pid_t pid) {
52,067✔
91
        uint64_t pidfdid = 0;
52,067✔
92
        int fd;
52,067✔
93

94
        assert(pidref);
52,067✔
95

96
        if (pid < 0)
52,067✔
97
                return -ESRCH;
52,067✔
98
        if (pid == 0) {
52,067✔
99
                pid = getpid_cached();
27,482✔
100
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
27,482✔
101
        }
102

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

109
                fd = -EBADF;
110
        }
111

112
        *pidref = (PidRef) {
52,067✔
113
                .fd = fd,
114
                .pid = pid,
115
                .fd_id = pidfdid,
116
        };
117

118
        return 0;
52,067✔
119
}
120

121
int pidref_set_pid_and_pidfd_id(
56✔
122
                PidRef *pidref,
123
                pid_t pid,
124
                uint64_t pidfd_id) {
125

126
        int r;
56✔
127

128
        assert(pidref);
56✔
129

130
        _cleanup_(pidref_done) PidRef n = PIDREF_NULL;
×
131
        r = pidref_set_pid(&n, pid);
56✔
132
        if (r < 0)
56✔
133
                return r;
134

135
        if (pidfd_id > 0) {
56✔
136
                r = pidref_acquire_pidfd_id(&n);
56✔
137
                if (r < 0 && !ERRNO_IS_NEG_NOT_SUPPORTED(r))
112✔
138
                        return r;
139

140
                if (n.fd_id != pidfd_id)
56✔
141
                        return -ESRCH;
142
        }
143

144
        *pidref = TAKE_PIDREF(n);
56✔
145
        return 0;
56✔
146
}
147

148
int pidref_set_pidstr(PidRef *pidref, const char *pid) {
43✔
149
        pid_t nr;
43✔
150
        int r;
43✔
151

152
        assert(pidref);
43✔
153

154
        r = parse_pid(pid, &nr);
43✔
155
        if (r < 0)
43✔
156
                return r;
43✔
157

158
        return pidref_set_pid(pidref, nr);
43✔
159
}
160

161
int pidref_set_pidfd(PidRef *pidref, int fd) {
1,081✔
162
        int r;
1,081✔
163

164
        assert(pidref);
1,081✔
165

166
        if (fd < 0)
1,081✔
167
                return -EBADF;
168

169
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
1,081✔
170
        if (fd_copy < 0) {
1,081✔
171
                pid_t pid;
×
172

173
                if (!ERRNO_IS_RESOURCE(errno))
×
174
                        return -errno;
×
175

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

181
                *pidref = PIDREF_MAKE_FROM_PID(pid);
×
182
                return 0;
×
183
        }
184

185
        return pidref_set_pidfd_consume(pidref, fd_copy);
1,081✔
186
}
187

188
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
3,687✔
189
        pid_t pid;
3,687✔
190
        int r;
3,687✔
191

192
        assert(pidref);
3,687✔
193

194
        if (fd < 0)
3,687✔
195
                return -EBADF;
3,687✔
196

197
        r = pidfd_get_pid(fd, &pid);
3,687✔
198
        if (r < 0)
3,687✔
199
                return r;
200

201
        *pidref = (PidRef) {
3,687✔
202
                .fd = fd,
203
                .pid = pid,
204
        };
205

206
        return 0;
3,687✔
207
}
208

209
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
3,685✔
210
        int r;
3,685✔
211

212
        r = pidref_set_pidfd_take(pidref, fd);
3,685✔
213
        if (r < 0)
3,685✔
214
                safe_close(fd);
×
215

216
        return r;
3,685✔
217
}
218

219
int pidref_set_parent(PidRef *ret) {
1,241✔
220
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
1,241✔
221
        pid_t ppid;
1,241✔
222
        int r;
1,241✔
223

224
        assert(ret);
1,241✔
225

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

229
        ppid = getppid();
1,241✔
230
        for (;;) {
1,241✔
231
                r = pidref_set_pid(&parent, ppid);
1,241✔
232
                if (r < 0)
1,241✔
233
                        return r;
234

235
                if (parent.fd < 0) /* If pidfds are not available, then we are done */
1,241✔
236
                        break;
237

238
                pid_t now_ppid = getppid();
1,241✔
239
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
1,241✔
240
                        break;
241

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

247
        *ret = TAKE_PIDREF(parent);
1,241✔
248
        return 0;
1,241✔
249
}
250

251
void pidref_done(PidRef *pidref) {
215,979✔
252
        assert(pidref);
215,979✔
253

254
        *pidref = (PidRef) {
431,958✔
255
                .fd = safe_close(pidref->fd),
215,979✔
256
        };
257
}
215,979✔
258

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

264
        pidref_done(pidref);
3,277✔
265
        return mfree(pidref);
3,277✔
266
}
267

268
int pidref_copy(const PidRef *pidref, PidRef *ret) {
3,278✔
269
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
3,278✔
270

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

274
        assert(ret);
3,278✔
275

276
        if (pidref) {
3,278✔
277
                if (pidref_is_remote(pidref)) /* Propagate remote flag */
3,276✔
278
                        copy.fd = -EREMOTE;
×
279
                else if (pidref->fd >= 0) {
3,276✔
280
                        copy.fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
3,270✔
281
                        if (copy.fd < 0) {
3,270✔
282
                                if (!ERRNO_IS_RESOURCE(errno))
×
283
                                        return -errno;
×
284

285
                                copy.fd = -EBADF;
×
286
                        }
287
                }
288

289
                copy.pid = pidref->pid;
3,276✔
290
                copy.fd_id = pidref->fd_id;
3,276✔
291
        }
292

293
        *ret = TAKE_PIDREF(copy);
3,278✔
294
        return 0;
3,278✔
295
}
296

297
int pidref_dup(const PidRef *pidref, PidRef **ret) {
3,274✔
298
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
3,274✔
299
        int r;
3,274✔
300

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

304
        assert(ret);
3,274✔
305

306
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
3,274✔
307
        if (!dup_pidref)
3,274✔
308
                return -ENOMEM;
309

310
        r = pidref_copy(pidref, dup_pidref);
3,274✔
311
        if (r < 0)
3,274✔
312
                return r;
313

314
        *ret = TAKE_PTR(dup_pidref);
3,274✔
315
        return 0;
3,274✔
316
}
317

318
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
4✔
319
        _cleanup_(pidref_freep) PidRef *n = NULL;
4✔
320
        int r;
4✔
321

322
        assert(ret);
4✔
323

324
        if (pid < 0)
4✔
325
                return -ESRCH;
326

327
        n = new(PidRef, 1);
3✔
328
        if (!n)
3✔
329
                return -ENOMEM;
330

331
        *n = PIDREF_NULL;
3✔
332

333
        r = pidref_set_pid(n, pid);
3✔
334
        if (r < 0)
3✔
335
                return r;
336

337
        *ret = TAKE_PTR(n);
3✔
338
        return 0;
3✔
339
}
340

341
int pidref_kill(const PidRef *pidref, int sig) {
15,711✔
342

343
        if (!pidref)
15,711✔
344
                return -ESRCH;
345

346
        if (pidref_is_remote(pidref))
15,711✔
347
                return -EREMOTE;
348

349
        if (pidref->fd >= 0)
15,709✔
350
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,014✔
351

352
        if (pidref->pid > 0)
8,695✔
353
                return RET_NERRNO(kill(pidref->pid, sig));
15,761✔
354

355
        return -ESRCH;
356
}
357

358
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
431✔
359
        int r;
431✔
360

361
        r = pidref_kill(pidref, sig);
431✔
362
        if (r < 0)
431✔
363
                return r;
364

365
        if (!IN_SET(sig, SIGCONT, SIGKILL))
430✔
366
                (void) pidref_kill(pidref, SIGCONT);
430✔
367

368
        return 0;
369
}
370

371
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
10,222✔
372

373
        if (!pidref)
10,222✔
374
                return -ESRCH;
375

376
        if (pidref_is_remote(pidref))
10,222✔
377
                return -EREMOTE;
378

379
        if (pidref->fd >= 0) {
10,222✔
380
                siginfo_t si;
10,222✔
381

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

392
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
10,222✔
393
        }
394

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

398
        return -ESRCH;
399
}
400

401
int pidref_verify(const PidRef *pidref) {
46,511✔
402
        int r;
46,511✔
403

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

408
        if (!pidref_is_set(pidref))
46,511✔
409
                return -ESRCH;
410

411
        if (pidref_is_remote(pidref))
46,509✔
412
                return -EREMOTE;
413

414
        if (pidref->pid == 1)
46,508✔
415
                return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
416

417
        if (pidref->fd < 0)
45,003✔
418
                return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
419

420
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
23,414✔
421
        if (r < 0)
23,414✔
422
                return r;
2,875✔
423

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

427
bool pidref_is_self(PidRef *pidref) {
34,004✔
428
        if (!pidref_is_set(pidref))
34,004✔
429
                return false;
34,004✔
430

431
        if (pidref_is_remote(pidref))
34,002✔
432
                return false;
433

434
        if (pidref->pid != getpid_cached())
34,002✔
435
                return false;
436

437
        /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
438
        if (pidref->pid == 1)
694✔
439
                return true;
440

441
        /* Also compare pidfd ID if we can get it */
442
        if (pidref_acquire_pidfd_id(pidref) < 0)
694✔
443
                return true;
444

445
        uint64_t self_id;
689✔
446
        if (pidfd_get_inode_id_self_cached(&self_id) < 0)
689✔
447
                return true;
448

449
        return pidref->fd_id == self_id;
689✔
450
}
451

452
int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
10,840✔
453
        int r;
10,840✔
454

455
        if (!pidref_is_set(pidref))
10,840✔
456
                return -ESRCH;
10,840✔
457

458
        if (pidref_is_remote(pidref))
10,840✔
459
                return -EREMOTE;
460

461
        if (pidref->pid == 1 || pidref_is_self(pidref))
10,839✔
462
                return -ECHILD;
×
463

464
        siginfo_t si = {};
10,839✔
465
        if (pidref->fd >= 0)
10,839✔
466
                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
4,653✔
467
        else
468
                r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
6,186✔
469
        if (r < 0)
×
470
                return r;
471

472
        if (ret)
10,839✔
473
                *ret = si;
7,440✔
474

475
        return 0;
476
}
477

478
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
10,840✔
479
        int r;
10,840✔
480

481
        for (;;) {
10,840✔
482
                r = pidref_wait(pidref, ret, WEXITED);
10,840✔
483
                if (r != -EINTR)
10,840✔
484
                        return r;
10,840✔
485
        }
486
}
487

488
bool pidref_is_automatic(const PidRef *pidref) {
184,992✔
489
        return pidref && pid_is_automatic(pidref->pid);
184,992✔
490
}
491

492
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
92,022✔
493
        siphash24_compress_typesafe(pidref->pid, state);
92,022✔
494
}
92,022✔
495

496
int pidref_compare_func(const PidRef *a, const PidRef *b) {
92,470✔
497
        int r;
92,470✔
498

499
        assert(a);
92,470✔
500
        assert(b);
92,470✔
501

502
        r = CMP(pidref_is_set(a), pidref_is_set(b));
277,410✔
503
        if (r != 0)
92,470✔
504
                return r;
×
505

506
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
92,470✔
507
        if (r != 0)
92,470✔
508
                return r;
×
509

510
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
184,940✔
511
        if (r != 0)
92,470✔
512
                return r;
×
513

514
        r = CMP(a->pid, b->pid);
92,470✔
515
        if (r != 0)
73,413✔
516
                return r;
21,695✔
517

518
        if (a->fd_id != 0 && b->fd_id != 0)
70,775✔
519
                return CMP(a->fd_id, b->fd_id);
661✔
520

521
        return 0;
522
}
523

524
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
525

526
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
×
527
                                    PidRef, pidref_hash_func, pidref_compare_func,
528
                                    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