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

systemd / systemd / 13753893467

09 Mar 2025 10:51PM UTC coverage: 71.913% (+0.1%) from 71.816%
13753893467

push

github

web-flow
meson: bump required minimum version to 0.62.0 (#36610)

- bump the requred minimum version of meson to 0.62.0,
- use install_symlink(), which is supported since meson-0.61.0,
- use more features provided by newer meson with careful
conditionalization.

Follow-up for 8442ac9c0.
Closes #35967.

295857 of 411410 relevant lines covered (71.91%)

717151.89 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) {
15,962✔
14
        int r;
15,962✔
15

16
        assert(pidref);
15,962✔
17

18
        if (!pidref_is_set(pidref))
15,962✔
19
                return -ESRCH;
20

21
        if (pidref_is_remote(pidref))
15,962✔
22
                return -EREMOTE;
23

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

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

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

38
        return 0;
39
}
40

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

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

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

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

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

56
        if (pidref_is_remote(a)) {
8,064✔
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,064✔
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,063✔
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,789✔
79
                        return true;
1,528✔
80
        }
81

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

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

89
        assert(pidref);
34,298✔
90

91
        if (pid < 0)
34,298✔
92
                return -ESRCH;
34,298✔
93
        if (pid == 0) {
34,298✔
94
                pid = getpid_cached();
18,770✔
95
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
18,770✔
96
        }
97

98
        fd = pidfd_open(pid, 0);
34,298✔
99
        if (fd < 0) {
34,298✔
100
                /* Graceful fallback in case the kernel is out of fds */
101
                if (!ERRNO_IS_RESOURCE(errno))
×
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) {
34,298✔
108
                .fd = fd,
109
                .pid = pid,
110
                .fd_id = pidfdid,
111
        };
112

113
        return 0;
34,298✔
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) {
515✔
130
        int r;
515✔
131

132
        assert(pidref);
515✔
133

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

137
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
515✔
138
        if (fd_copy < 0) {
515✔
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);
515✔
154
}
155

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

160
        assert(pidref);
4,382✔
161

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

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

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

174
        return 0;
4,382✔
175
}
176

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

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

184
        return r;
4,366✔
185
}
186

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

192
        assert(ret);
834✔
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();
834✔
198
        for (;;) {
834✔
199
                r = pidref_set_pid(&parent, ppid);
834✔
200
                if (r < 0)
834✔
201
                        return r;
202

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

206
                pid_t now_ppid = getppid();
834✔
207
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
834✔
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);
834✔
216
        return 0;
834✔
217
}
218

219
void pidref_done(PidRef *pidref) {
175,915✔
220
        assert(pidref);
175,915✔
221

222
        *pidref = (PidRef) {
351,830✔
223
                .fd = safe_close(pidref->fd),
175,915✔
224
        };
225
}
175,915✔
226

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

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

236
int pidref_copy(const PidRef *pidref, PidRef *ret) {
4,482✔
237
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
4,482✔
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,482✔
243

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

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

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

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

265
int pidref_dup(const PidRef *pidref, PidRef **ret) {
4,478✔
266
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
4,478✔
267
        int r;
4,478✔
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,478✔
273

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

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

282
        *ret = TAKE_PTR(dup_pidref);
4,478✔
283
        return 0;
4,478✔
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,006✔
310

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

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

317
        if (pidref->fd >= 0)
17,004✔
318
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
9,491✔
319

320
        if (pidref->pid > 0)
7,513✔
321
                return RET_NERRNO(kill(pidref->pid, sig));
14,270✔
322

323
        return -ESRCH;
324
}
325

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

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

333
        if (!IN_SET(sig, SIGCONT, SIGKILL))
823✔
334
                (void) pidref_kill(pidref, SIGCONT);
823✔
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) {
79,657✔
370
        int r;
79,657✔
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))
79,657✔
377
                return -ESRCH;
378

379
        if (pidref_is_remote(pidref))
79,655✔
380
                return -EREMOTE;
381

382
        if (pidref->pid == 1)
79,654✔
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)
64,541✔
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,438✔
389
        if (r < 0)
41,438✔
390
                return r;
1,699✔
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,790✔
396
        if (!pidref_is_set(pidref))
23,790✔
397
                return false;
23,790✔
398

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

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

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

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

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

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

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

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

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

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

432
        siginfo_t si = {};
4,171✔
433
        if (pidref->fd >= 0)
4,171✔
434
                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
4,171✔
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,171✔
441
                *ret = si;
3✔
442

443
        return 0;
444
}
445

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

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

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

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

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

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

470
        r = CMP(pidref_is_set(a), pidref_is_set(b));
114,087✔
471
        if (r != 0)
38,029✔
472
                return r;
×
473

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

478
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
76,058✔
479
        if (r != 0)
38,029✔
480
                return r;
×
481

482
        r = CMP(a->pid, b->pid);
38,029✔
483
        if (r != 0)
29,929✔
484
                return r;
8,811✔
485

486
        if (a->fd_id != 0 && b->fd_id != 0)
29,218✔
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