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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

91.09
/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 "fiber-ops.h"
11
#include "format-util.h"
12
#include "hash-funcs.h"
13
#include "io-util.h"
14
#include "log.h"
15
#include "parse-util.h"
16
#include "pidfd-util.h"
17
#include "pidref.h"
18
#include "process-util.h"
19
#include "siphash24.h"
20
#include "time-util.h"
21

22
int pidref_acquire_pidfd_id(PidRef *pidref) {
21,445✔
23
        int r;
21,445✔
24

25
        assert(pidref);
21,445✔
26

27
        if (!pidref_is_set(pidref))
21,445✔
28
                return -ESRCH;
29

30
        if (pidref_is_remote(pidref))
21,445✔
31
                return -EREMOTE;
32

33
        if (pidref->fd < 0)
21,445✔
34
                return -ENOMEDIUM;
35

36
        if (pidref->fd_id > 0)
21,031✔
37
                return 0;
38

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

47
        return 0;
48
}
49

50
bool pidref_equal(PidRef *a, PidRef *b) {
14,587✔
51

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

56
        if (!pidref_is_set(a))
14,584✔
57
                return !pidref_is_set(b);
13,534✔
58

59
        if (!pidref_is_set(b))
7,817✔
60
                return false;
61

62
        if (a->pid != b->pid)
7,797✔
63
                return false;
64

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

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

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

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

91
        return a->fd_id == b->fd_id;
7,544✔
92
}
93

94
int pidref_set_pid(PidRef *pidref, pid_t pid) {
70,192✔
95
        uint64_t pidfdid = 0;
70,192✔
96
        int fd;
70,192✔
97

98
        assert(pidref);
70,192✔
99

100
        if (pid < 0)
70,192✔
101
                return -ESRCH;
70,192✔
102
        if (pid == 0) {
70,192✔
103
                pid = getpid_cached();
36,431✔
104
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
36,431✔
105
        }
106

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

113
                fd = -EBADF;
114
        }
115

116
        *pidref = (PidRef) {
70,187✔
117
                .fd = fd,
118
                .pid = pid,
119
                .fd_id = pidfdid,
120
        };
121

122
        return 0;
70,187✔
123
}
124

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

130
        int r;
56✔
131

132
        assert(pidref);
56✔
133

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

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

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

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

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

156
        assert(pidref);
55✔
157

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

162
        return pidref_set_pid(pidref, nr);
54✔
163
}
164

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

168
        assert(pidref);
1,413✔
169

170
        if (fd < 0)
1,413✔
171
                return -EBADF;
172

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

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

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

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

189
        return pidref_set_pidfd_consume(pidref, fd_copy);
1,413✔
190
}
191

192
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
5,911✔
193
        pid_t pid;
5,911✔
194
        int r;
5,911✔
195

196
        assert(pidref);
5,911✔
197

198
        if (fd < 0)
5,911✔
199
                return -EBADF;
5,911✔
200

201
        r = pidfd_get_pid(fd, &pid);
5,911✔
202
        if (r < 0)
5,911✔
203
                return r;
204

205
        *pidref = (PidRef) {
5,910✔
206
                .fd = fd,
207
                .pid = pid,
208
        };
209

210
        return 0;
5,910✔
211
}
212

213
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
5,909✔
214
        int r;
5,909✔
215

216
        r = pidref_set_pidfd_take(pidref, fd);
5,909✔
217
        if (r < 0)
5,909✔
218
                safe_close(fd);
1✔
219

220
        return r;
5,909✔
221
}
222

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

228
        assert(ret);
1,336✔
229

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

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

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

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

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

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

255
void pidref_done(PidRef *pidref) {
296,135✔
256
        assert(pidref);
296,135✔
257

258
        *pidref = (PidRef) {
592,270✔
259
                .fd = safe_close(pidref->fd),
296,135✔
260
        };
261
}
296,135✔
262

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

268
        pidref_done(pidref);
8,323✔
269
        return mfree(pidref);
8,323✔
270
}
271

272
int pidref_copy(const PidRef *pidref, PidRef *ret) {
8,323✔
273
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
8,323✔
274

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

278
        assert(ret);
8,323✔
279

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

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

293
                copy.pid = pidref->pid;
8,321✔
294
                copy.fd_id = pidref->fd_id;
8,321✔
295
        }
296

297
        *ret = TAKE_PIDREF(copy);
8,323✔
298
        return 0;
8,323✔
299
}
300

301
int pidref_dup(const PidRef *pidref, PidRef **ret) {
8,319✔
302
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
8,319✔
303
        int r;
8,319✔
304

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

308
        assert(ret);
8,319✔
309

310
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
8,319✔
311
        if (!dup_pidref)
8,319✔
312
                return -ENOMEM;
313

314
        r = pidref_copy(pidref, dup_pidref);
8,319✔
315
        if (r < 0)
8,319✔
316
                return r;
317

318
        *ret = TAKE_PTR(dup_pidref);
8,319✔
319
        return 0;
8,319✔
320
}
321

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

326
        assert(ret);
5✔
327

328
        if (pid < 0)
5✔
329
                return -ESRCH;
330

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

335
        *n = PIDREF_NULL;
4✔
336

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

341
        *ret = TAKE_PTR(n);
4✔
342
        return 0;
4✔
343
}
344

345
int pidref_kill(const PidRef *pidref, int sig) {
23,047✔
346

347
        if (!pidref)
23,047✔
348
                return -ESRCH;
349

350
        if (pidref_is_remote(pidref))
23,047✔
351
                return -EREMOTE;
352

353
        if (pidref->fd >= 0)
23,045✔
354
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
13,708✔
355

356
        if (pidref->pid > 0)
9,337✔
357
                return RET_NERRNO(kill(pidref->pid, sig));
9,337✔
358

359
        return -ESRCH;
360
}
361

362
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
1,838✔
363
        int r;
1,838✔
364

365
        r = pidref_kill(pidref, sig);
1,838✔
366
        if (r < 0)
1,838✔
367
                return r;
368

369
        if (!IN_SET(sig, SIGCONT, SIGKILL))
1,837✔
370
                (void) pidref_kill(pidref, SIGCONT);
1,837✔
371

372
        return 0;
373
}
374

375
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
9,620✔
376

377
        if (!pidref)
9,620✔
378
                return -ESRCH;
379

380
        if (pidref_is_remote(pidref))
9,620✔
381
                return -EREMOTE;
382

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

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

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

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

402
        return -ESRCH;
403
}
404

405
int pidref_verify(const PidRef *pidref) {
75,560✔
406
        int r;
75,560✔
407

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

412
        if (!pidref_is_set(pidref))
75,560✔
413
                return -ESRCH;
414

415
        if (pidref_is_remote(pidref))
75,558✔
416
                return -EREMOTE;
417

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

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

424
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
38,756✔
425
        if (r < 0)
38,756✔
426
                return r;
3,442✔
427

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

431
bool pidref_is_self(PidRef *pidref) {
50,981✔
432
        if (!pidref_is_set(pidref))
50,981✔
433
                return false;
50,981✔
434

435
        if (pidref_is_remote(pidref))
50,979✔
436
                return false;
437

438
        if (pidref->pid != getpid_cached())
50,979✔
439
                return false;
440

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

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

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

453
        return pidref->fd_id == self_id;
641✔
454
}
455

456
int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *ret_si) {
13,351✔
457
        int r;
13,351✔
458

459
        assert(timeout > 0);
13,351✔
460

461
        if (!pidref_is_set(pidref))
13,351✔
462
                return -ESRCH;
463

464
        if (pidref_is_remote(pidref))
13,351✔
465
                return -EREMOTE;
466

467
        if (pidref->pid == 1 || pidref_is_self(pidref))
13,350✔
468
                return -ECHILD;
469

470
        if (pidref->fd < 0 && (timeout != USEC_INFINITY || fiber_ops_is_set()))
13,350✔
471
                return -ENOMEDIUM;
472

473
        usec_t ts = timeout == USEC_INFINITY ? USEC_INFINITY : usec_add(now(CLOCK_MONOTONIC), timeout);
13,350✔
474

475
        /* Poll the pidfd before waitid() if either there's a finite timeout (so we can honor it) or
476
         * we're on a fiber (so fd_wait_for_event() can suspend us instead of blocking the event loop
477
         * inside waitid()). Otherwise let waitid() block directly. The precondition above guarantees
478
         * pidref->fd >= 0 in both cases. */
479
        bool poll_first = ts != USEC_INFINITY || fiber_ops_is_set();
26,167✔
480

481
        for (;;) {
13,350✔
482
                if (poll_first) {
13,350✔
483
                        usec_t left;
533✔
484

485
                        if (ts == USEC_INFINITY)
533✔
486
                                left = USEC_INFINITY;
487
                        else {
488
                                left = usec_sub_unsigned(ts, now(CLOCK_MONOTONIC));
526✔
489
                                if (left == 0)
526✔
490
                                        return -ETIMEDOUT;
13,350✔
491
                        }
492

493
                        r = fd_wait_for_event(pidref->fd, POLLIN, left);
533✔
494
                        if (r == 0)
533✔
495
                                return -ETIMEDOUT;
496
                        if (r == -EINTR)
530✔
UNCOV
497
                                continue;
×
498
                        if (r < 0)
530✔
499
                                return r;
500
                }
501

502
                siginfo_t si = {};
13,347✔
503

504
                if (pidref->fd >= 0)
13,347✔
505
                        r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, WEXITED));
12,243✔
506
                else
507
                        r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED));
1,104✔
UNCOV
508
                if (r >= 0) {
×
509
                        if (ret_si)
13,347✔
510
                                *ret_si = si;
9,209✔
511
                        return 0;
512
                }
UNCOV
513
                if (r != -EINTR)
×
514
                        return r;
515
        }
516
}
517

518
bool pidref_is_automatic(const PidRef *pidref) {
232,598✔
519
        return pidref && pid_is_automatic(pidref->pid);
232,598✔
520
}
521

522
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
141,997✔
523
        siphash24_compress_typesafe(pidref->pid, state);
141,997✔
524
}
141,997✔
525

526
int pidref_compare_func(const PidRef *a, const PidRef *b) {
116,239✔
527
        int r;
116,239✔
528

529
        assert(a);
116,239✔
530
        assert(b);
116,239✔
531

532
        r = CMP(pidref_is_set(a), pidref_is_set(b));
348,717✔
533
        if (r != 0)
116,239✔
534
                return r;
535

536
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
116,239✔
537
        if (r != 0)
116,239✔
538
                return r;
539

540
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
232,478✔
541
        if (r != 0)
116,239✔
542
                return r;
543

544
        r = CMP(a->pid, b->pid);
116,239✔
545
        if (r != 0)
89,412✔
546
                return r;
547

548
        if (a->fd_id != 0 && b->fd_id != 0)
86,457✔
549
                return CMP(a->fd_id, b->fd_id);
771✔
550

551
        return 0;
552
}
553

554
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
555

556
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
1✔
557
                                    PidRef, pidref_hash_func, pidref_compare_func,
558
                                    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