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

systemd / systemd / 14013585018

22 Mar 2025 03:54PM UTC coverage: 71.951% (+0.002%) from 71.949%
14013585018

push

github

web-flow
some dbus property fixes (#36830)

4 of 4 new or added lines in 1 file covered. (100.0%)

145 existing lines in 35 files now uncovered.

296620 of 412255 relevant lines covered (71.95%)

737629.16 hits per line

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

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

3
#include "errno-util.h"
4
#include "fd-util.h"
5
#include "missing_syscall.h"
6
#include "missing_wait.h"
7
#include "parse-util.h"
8
#include "pidfd-util.h"
9
#include "pidref.h"
10
#include "process-util.h"
11
#include "signal-util.h"
12

13
int pidref_acquire_pidfd_id(PidRef *pidref) {
16,045✔
14
        int r;
16,045✔
15

16
        assert(pidref);
16,045✔
17

18
        if (!pidref_is_set(pidref))
16,045✔
19
                return -ESRCH;
20

21
        if (pidref_is_remote(pidref))
16,045✔
22
                return -EREMOTE;
23

24
        if (pidref->fd < 0)
16,045✔
25
                return -ENOMEDIUM;
26

27
        if (pidref->fd_id > 0)
15,615✔
28
                return 0;
29

30
        r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
9,724✔
31
        if (r < 0) {
9,724✔
32
                if (!ERRNO_IS_NEG_NOT_SUPPORTED(r))
2,312✔
33
                        log_debug_errno(r, "Failed to get inode number of pidfd for pid " PID_FMT ": %m",
×
34
                                        pidref->pid);
35
                return r;
2,312✔
36
        }
37

38
        return 0;
39
}
40

41
bool pidref_equal(PidRef *a, PidRef *b) {
25,772✔
42

43
        /* If this is the very same structure, it definitely refers to the same process */
44
        if (a == b)
25,772✔
45
                return true;
46

47
        if (!pidref_is_set(a))
25,769✔
48
                return !pidref_is_set(b);
8,166✔
49

50
        if (!pidref_is_set(b))
21,686✔
51
                return false;
52

53
        if (a->pid != b->pid)
21,680✔
54
                return false;
55

56
        if (pidref_is_remote(a)) {
8,184✔
57
                /* If one is remote and the other isn't, they are not the same */
58
                if (!pidref_is_remote(b))
×
59
                        return false;
60

61
                /* If both are remote, compare fd IDs if we have both, otherwise don't bother, and cut things short */
62
                if (a->fd_id == 0 || b->fd_id == 0)
×
63
                        return true;
64
        } else {
65
                /* If the other side is remote, then this is not the same */
66
                if (pidref_is_remote(b))
8,184✔
67
                        return false;
68

69
                /* PID1 cannot exit, hence it cannot change pidfs ids, hence no point in comparing them, we
70
                 * can shortcut things */
71
                if (a->pid == 1)
8,183✔
72
                        return true;
73

74
                /* Try to compare pidfds using their inode numbers. This way we can ensure that we
75
                 * don't spuriously consider two PidRefs equal if the pid has been reused once. Note
76
                 * that we ignore all errors here, not only EOPNOTSUPP, as fstat() might fail due to
77
                 * many reasons. */
78
                if (pidref_acquire_pidfd_id(a) < 0 || pidref_acquire_pidfd_id(b) < 0)
7,909✔
79
                        return true;
1,620✔
80
        }
81

82
        return a->fd_id == b->fd_id;
6,289✔
83
}
84

85
int pidref_set_pid(PidRef *pidref, pid_t pid) {
37,412✔
86
        uint64_t pidfdid = 0;
37,412✔
87
        int fd;
37,412✔
88

89
        assert(pidref);
37,412✔
90

91
        if (pid < 0)
37,412✔
92
                return -ESRCH;
37,412✔
93
        if (pid == 0) {
37,412✔
94
                pid = getpid_cached();
21,659✔
95
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
21,659✔
96
        }
97

98
        fd = pidfd_open(pid, 0);
37,412✔
99
        if (fd < 0) {
37,412✔
100
                /* Graceful fallback in case the kernel is out of fds */
UNCOV
101
                if (!ERRNO_IS_RESOURCE(errno))
×
UNCOV
102
                        return log_debug_errno(errno, "Failed to open pidfd for pid " PID_FMT ": %m", pid);
×
103

104
                fd = -EBADF;
105
        }
106

107
        *pidref = (PidRef) {
37,412✔
108
                .fd = fd,
109
                .pid = pid,
110
                .fd_id = pidfdid,
111
        };
112

113
        return 0;
37,412✔
114
}
115

116
int pidref_set_pidstr(PidRef *pidref, const char *pid) {
4✔
117
        pid_t nr;
4✔
118
        int r;
4✔
119

120
        assert(pidref);
4✔
121

122
        r = parse_pid(pid, &nr);
4✔
123
        if (r < 0)
4✔
124
                return r;
4✔
125

126
        return pidref_set_pid(pidref, nr);
4✔
127
}
128

129
int pidref_set_pidfd(PidRef *pidref, int fd) {
540✔
130
        int r;
540✔
131

132
        assert(pidref);
540✔
133

134
        if (fd < 0)
540✔
135
                return -EBADF;
136

137
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
540✔
138
        if (fd_copy < 0) {
540✔
139
                pid_t pid;
×
140

141
                if (!ERRNO_IS_RESOURCE(errno))
×
142
                        return -errno;
×
143

144
                /* Graceful fallback if we are out of fds */
145
                r = pidfd_get_pid(fd, &pid);
×
146
                if (r < 0)
×
147
                        return r;
148

149
                *pidref = PIDREF_MAKE_FROM_PID(pid);
×
150
                return 0;
×
151
        }
152

153
        return pidref_set_pidfd_consume(pidref, fd_copy);
540✔
154
}
155

156
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
4,464✔
157
        pid_t pid;
4,464✔
158
        int r;
4,464✔
159

160
        assert(pidref);
4,464✔
161

162
        if (fd < 0)
4,464✔
163
                return -EBADF;
4,464✔
164

165
        r = pidfd_get_pid(fd, &pid);
4,464✔
166
        if (r < 0)
4,464✔
167
                return r;
168

169
        *pidref = (PidRef) {
4,464✔
170
                .fd = fd,
171
                .pid = pid,
172
        };
173

174
        return 0;
4,464✔
175
}
176

177
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
4,448✔
178
        int r;
4,448✔
179

180
        r = pidref_set_pidfd_take(pidref, fd);
4,448✔
181
        if (r < 0)
4,448✔
182
                safe_close(fd);
×
183

184
        return r;
4,448✔
185
}
186

187
int pidref_set_parent(PidRef *ret) {
945✔
188
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
945✔
189
        pid_t ppid;
945✔
190
        int r;
945✔
191

192
        assert(ret);
945✔
193

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

197
        ppid = getppid();
945✔
198
        for (;;) {
945✔
199
                r = pidref_set_pid(&parent, ppid);
945✔
200
                if (r < 0)
945✔
201
                        return r;
202

203
                if (parent.fd < 0) /* If pidfds are not available, then we are done */
945✔
204
                        break;
205

206
                pid_t now_ppid = getppid();
945✔
207
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
945✔
208
                        break;
209

210
                /* Otherwise let's try again with the new ppid */
211
                ppid = now_ppid;
×
212
                pidref_done(&parent);
×
213
        }
214

215
        *ret = TAKE_PIDREF(parent);
945✔
216
        return 0;
945✔
217
}
218

219
void pidref_done(PidRef *pidref) {
177,571✔
220
        assert(pidref);
177,571✔
221

222
        *pidref = (PidRef) {
355,142✔
223
                .fd = safe_close(pidref->fd),
177,571✔
224
        };
225
}
177,571✔
226

227
PidRef* pidref_free(PidRef *pidref) {
4,545✔
228
        /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
229
        if (!pidref)
4,545✔
230
                return NULL;
231

232
        pidref_done(pidref);
4,545✔
233
        return mfree(pidref);
4,545✔
234
}
235

236
int pidref_copy(const PidRef *pidref, PidRef *ret) {
4,546✔
237
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
4,546✔
238

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

242
        assert(ret);
4,546✔
243

244
        if (pidref) {
4,546✔
245
                if (pidref_is_remote(pidref)) /* Propagate remote flag */
4,544✔
246
                        copy.fd = -EREMOTE;
×
247
                else if (pidref->fd >= 0) {
4,544✔
248
                        copy.fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
4,538✔
249
                        if (copy.fd < 0) {
4,538✔
250
                                if (!ERRNO_IS_RESOURCE(errno))
×
251
                                        return -errno;
×
252

253
                                copy.fd = -EBADF;
×
254
                        }
255
                }
256

257
                copy.pid = pidref->pid;
4,544✔
258
                copy.fd_id = pidref->fd_id;
4,544✔
259
        }
260

261
        *ret = TAKE_PIDREF(copy);
4,546✔
262
        return 0;
4,546✔
263
}
264

265
int pidref_dup(const PidRef *pidref, PidRef **ret) {
4,542✔
266
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
4,542✔
267
        int r;
4,542✔
268

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

272
        assert(ret);
4,542✔
273

274
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
4,542✔
275
        if (!dup_pidref)
4,542✔
276
                return -ENOMEM;
277

278
        r = pidref_copy(pidref, dup_pidref);
4,542✔
279
        if (r < 0)
4,542✔
280
                return r;
281

282
        *ret = TAKE_PTR(dup_pidref);
4,542✔
283
        return 0;
4,542✔
284
}
285

286
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
4✔
287
        _cleanup_(pidref_freep) PidRef *n = NULL;
4✔
288
        int r;
4✔
289

290
        assert(ret);
4✔
291

292
        if (pid < 0)
4✔
293
                return -ESRCH;
294

295
        n = new(PidRef, 1);
3✔
296
        if (!n)
3✔
297
                return -ENOMEM;
298

299
        *n = PIDREF_NULL;
3✔
300

301
        r = pidref_set_pid(n, pid);
3✔
302
        if (r < 0)
3✔
303
                return r;
304

305
        *ret = TAKE_PTR(n);
3✔
306
        return 0;
3✔
307
}
308

309
int pidref_kill(const PidRef *pidref, int sig) {
17,001✔
310

311
        if (!pidref)
17,001✔
312
                return -ESRCH;
313

314
        if (pidref_is_remote(pidref))
17,001✔
315
                return -EREMOTE;
316

317
        if (pidref->fd >= 0)
16,999✔
318
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
9,776✔
319

320
        if (pidref->pid > 0)
7,223✔
321
                return RET_NERRNO(kill(pidref->pid, sig));
13,609✔
322

323
        return -ESRCH;
324
}
325

326
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
830✔
327
        int r;
830✔
328

329
        r = pidref_kill(pidref, sig);
830✔
330
        if (r < 0)
830✔
331
                return r;
332

333
        if (!IN_SET(sig, SIGCONT, SIGKILL))
829✔
334
                (void) pidref_kill(pidref, SIGCONT);
829✔
335

336
        return 0;
337
}
338

339
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
1✔
340

341
        if (!pidref)
1✔
342
                return -ESRCH;
343

344
        if (pidref_is_remote(pidref))
1✔
345
                return -EREMOTE;
346

347
        if (pidref->fd >= 0) {
1✔
348
                siginfo_t si;
1✔
349

350
                /* We can't use structured initialization here, since the structure contains various unions
351
                 * and these fields lie in overlapping (carefully aligned) unions that LLVM is allergic to
352
                 * allow assignments to */
353
                zero(si);
1✔
354
                si.si_signo = sig;
1✔
355
                si.si_code = SI_QUEUE;
1✔
356
                si.si_pid = getpid_cached();
1✔
357
                si.si_uid = getuid();
1✔
358
                si.si_value.sival_int = value;
1✔
359

360
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
1✔
361
        }
362

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

366
        return -ESRCH;
367
}
368

369
int pidref_verify(const PidRef *pidref) {
80,500✔
370
        int r;
80,500✔
371

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

376
        if (!pidref_is_set(pidref))
80,500✔
377
                return -ESRCH;
378

379
        if (pidref_is_remote(pidref))
80,498✔
380
                return -EREMOTE;
381

382
        if (pidref->pid == 1)
80,497✔
383
                return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
384

385
        if (pidref->fd < 0)
65,462✔
386
                return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
387

388
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
41,773✔
389
        if (r < 0)
41,773✔
390
                return r;
1,729✔
391

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

395
bool pidref_is_self(PidRef *pidref) {
23,639✔
396
        if (!pidref_is_set(pidref))
23,639✔
397
                return false;
23,639✔
398

399
        if (pidref_is_remote(pidref))
23,637✔
400
                return false;
401

402
        if (pidref->pid != getpid_cached())
23,637✔
403
                return false;
404

405
        /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
406
        if (pidref->pid == 1)
859✔
407
                return true;
408

409
        /* Also compare pidfd ID if we can get it */
410
        if (pidref_acquire_pidfd_id(pidref) < 0)
859✔
411
                return true;
412

413
        uint64_t self_id;
425✔
414
        if (pidfd_get_inode_id_self_cached(&self_id) < 0)
425✔
415
                return true;
416

417
        return pidref->fd_id == self_id;
425✔
418
}
419

420
int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
4,194✔
421
        int r;
4,194✔
422

423
        if (!pidref_is_set(pidref))
4,194✔
424
                return -ESRCH;
4,194✔
425

426
        if (pidref_is_remote(pidref))
4,194✔
427
                return -EREMOTE;
428

429
        if (pidref->pid == 1 || pidref_is_self(pidref))
4,193✔
430
                return -ECHILD;
×
431

432
        siginfo_t si = {};
4,193✔
433
        if (pidref->fd >= 0)
4,193✔
434
                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
4,193✔
435
        else
436
                r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
×
437
        if (r < 0)
×
438
                return r;
439

440
        if (ret)
4,193✔
441
                *ret = si;
3✔
442

443
        return 0;
444
}
445

446
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
4,194✔
447
        int r;
4,194✔
448

449
        for (;;) {
4,194✔
450
                r = pidref_wait(pidref, ret, WEXITED);
4,194✔
451
                if (r != -EINTR)
4,194✔
452
                        return r;
4,194✔
453
        }
454
}
455

456
bool pidref_is_automatic(const PidRef *pidref) {
77,464✔
457
        return pidref && pid_is_automatic(pidref->pid);
77,464✔
458
}
459

460
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
52,463✔
461
        siphash24_compress_typesafe(pidref->pid, state);
52,463✔
462
}
52,463✔
463

464
int pidref_compare_func(const PidRef *a, const PidRef *b) {
38,706✔
465
        int r;
38,706✔
466

467
        assert(a);
38,706✔
468
        assert(b);
38,706✔
469

470
        r = CMP(pidref_is_set(a), pidref_is_set(b));
116,118✔
471
        if (r != 0)
38,706✔
472
                return r;
×
473

474
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
38,706✔
475
        if (r != 0)
38,706✔
476
                return r;
×
477

478
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
77,412✔
479
        if (r != 0)
38,706✔
480
                return r;
×
481

482
        r = CMP(a->pid, b->pid);
38,706✔
483
        if (r != 0)
30,426✔
484
                return r;
9,072✔
485

486
        if (a->fd_id != 0 && b->fd_id != 0)
29,634✔
487
                return CMP(a->fd_id, b->fd_id);
111✔
488

489
        return 0;
490
}
491

492
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
493

494
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
×
495
                                    PidRef, pidref_hash_func, pidref_compare_func,
496
                                    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