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

systemd / systemd / 19996191435

06 Dec 2025 12:24AM UTC coverage: 72.546% (-0.2%) from 72.765%
19996191435

push

github

web-flow
Add 82-net-auto-link-local.{hwdb,rules} to build system and add BMC USB-to-USB links to hwdb (#40006)

Using systems with ADLINK COM-HPC-ALT, ASRock Rack ALTRAD8UD-1L2T and
AMPONED8-2T/BCM boards, there's an issue due to the internal network
connection between the BMC and host, which runs over USB (i.e. Linux
running on the BMC configures a USB gadget ethernet device, with a link
local address).

With the default configuration on Ubuntu (I'm using 25.10),
NetworkManager repeatedly tries to get an address for the interface
using DHCP, resulting in an "Activation of network connection failed"
notification every minute or two.

Add 82-net-auto-link-local.hwdb and 82-net-auto-link-local.rules to the
build system and update 82-net-auto-link-local.hwdb to add the USB
device vendor/product pairs I've seen on the various systems I have with
Ampere Altra and AmpereOne CPUs.

308773 of 425625 relevant lines covered (72.55%)

1159320.34 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,795✔
19
        int r;
12,795✔
20

21
        assert(pidref);
12,795✔
22

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

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

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

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

35
        r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
6,129✔
36
        if (r < 0) {
6,129✔
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,539✔
47

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

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

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

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

61
        if (pidref_is_remote(a)) {
4,143✔
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,143✔
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,142✔
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,136✔
84
                        return true;
2✔
85
        }
86

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

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

94
        assert(pidref);
51,524✔
95

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

103
        fd = pidfd_open(pid, 0);
51,524✔
104
        if (fd < 0) {
51,524✔
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) {
51,524✔
113
                .fd = fd,
114
                .pid = pid,
115
                .fd_id = pidfdid,
116
        };
117

118
        return 0;
51,524✔
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,107✔
162
        int r;
1,107✔
163

164
        assert(pidref);
1,107✔
165

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

169
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
1,107✔
170
        if (fd_copy < 0) {
1,107✔
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,107✔
186
}
187

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

192
        assert(pidref);
3,587✔
193

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

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

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

206
        return 0;
3,587✔
207
}
208

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

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

216
        return r;
3,585✔
217
}
218

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

224
        assert(ret);
1,265✔
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,265✔
230
        for (;;) {
1,265✔
231
                r = pidref_set_pid(&parent, ppid);
1,265✔
232
                if (r < 0)
1,265✔
233
                        return r;
234

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

238
                pid_t now_ppid = getppid();
1,265✔
239
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
1,265✔
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,265✔
248
        return 0;
1,265✔
249
}
250

251
void pidref_done(PidRef *pidref) {
205,371✔
252
        assert(pidref);
205,371✔
253

254
        *pidref = (PidRef) {
410,742✔
255
                .fd = safe_close(pidref->fd),
205,371✔
256
        };
257
}
205,371✔
258

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

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

268
int pidref_copy(const PidRef *pidref, PidRef *ret) {
3,156✔
269
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
3,156✔
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,156✔
275

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

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

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

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

297
int pidref_dup(const PidRef *pidref, PidRef **ret) {
3,152✔
298
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
3,152✔
299
        int r;
3,152✔
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,152✔
305

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

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

314
        *ret = TAKE_PTR(dup_pidref);
3,152✔
315
        return 0;
3,152✔
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,762✔
342

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

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

349
        if (pidref->fd >= 0)
15,760✔
350
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
6,874✔
351

352
        if (pidref->pid > 0)
8,886✔
353
                return RET_NERRNO(kill(pidref->pid, sig));
16,428✔
354

355
        return -ESRCH;
356
}
357

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

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

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

368
        return 0;
369
}
370

371
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
9,212✔
372

373
        if (!pidref)
9,212✔
374
                return -ESRCH;
375

376
        if (pidref_is_remote(pidref))
9,212✔
377
                return -EREMOTE;
378

379
        if (pidref->fd >= 0) {
9,212✔
380
                siginfo_t si;
9,212✔
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);
9,212✔
386
                si.si_signo = sig;
9,212✔
387
                si.si_code = SI_QUEUE;
9,212✔
388
                si.si_pid = getpid_cached();
9,212✔
389
                si.si_uid = getuid();
9,212✔
390
                si.si_value.sival_int = value;
9,212✔
391

392
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
9,212✔
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) {
44,800✔
402
        int r;
44,800✔
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))
44,800✔
409
                return -ESRCH;
410

411
        if (pidref_is_remote(pidref))
44,798✔
412
                return -EREMOTE;
413

414
        if (pidref->pid == 1)
44,797✔
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)
43,409✔
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,244✔
421
        if (r < 0)
23,244✔
422
                return r;
2,804✔
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,051✔
428
        if (!pidref_is_set(pidref))
34,051✔
429
                return false;
34,051✔
430

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

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

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

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

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

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

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

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

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

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

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

472
        if (ret)
10,842✔
473
                *ret = si;
7,448✔
474

475
        return 0;
476
}
477

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

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

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

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

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

499
        assert(a);
84,876✔
500
        assert(b);
84,876✔
501

502
        r = CMP(pidref_is_set(a), pidref_is_set(b));
254,628✔
503
        if (r != 0)
84,876✔
504
                return r;
×
505

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

510
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
169,752✔
511
        if (r != 0)
84,876✔
512
                return r;
×
513

514
        r = CMP(a->pid, b->pid);
84,876✔
515
        if (r != 0)
68,729✔
516
                return r;
19,226✔
517

518
        if (a->fd_id != 0 && b->fd_id != 0)
65,650✔
519
                return CMP(a->fd_id, b->fd_id);
665✔
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