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

systemd / systemd / 14424536636

13 Apr 2025 12:15AM UTC coverage: 72.03% (+0.02%) from 72.01%
14424536636

push

github

web-flow
locale-util,kbd-util: several cleanups (#37090)

49 of 51 new or added lines in 5 files covered. (96.08%)

2391 existing lines in 44 files now uncovered.

297332 of 412788 relevant lines covered (72.03%)

683891.99 hits per line

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

88.36
/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) {
7,990✔
14
        int r;
7,990✔
15

16
        assert(pidref);
7,990✔
17

18
        if (!pidref_is_set(pidref))
7,990✔
19
                return -ESRCH;
20

21
        if (pidref_is_remote(pidref))
7,990✔
22
                return -EREMOTE;
23

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

27
        if (pidref->fd_id > 0)
7,288✔
28
                return 0;
29

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

38
        return 0;
39
}
40

41
bool pidref_equal(PidRef *a, PidRef *b) {
20,023✔
42

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

47
        if (!pidref_is_set(a))
20,020✔
48
                return !pidref_is_set(b);
5,842✔
49

50
        if (!pidref_is_set(b))
17,099✔
51
                return false;
52

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

56
        if (pidref_is_remote(a)) {
3,622✔
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))
3,622✔
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)
3,621✔
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)
3,503✔
79
                        return true;
1,553✔
80
        }
81

82
        return a->fd_id == b->fd_id;
1,950✔
83
}
84

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

89
        assert(pidref);
59,935✔
90

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

98
        fd = pidfd_open(pid, 0);
59,935✔
99
        if (fd < 0) {
59,935✔
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) {
59,935✔
108
                .fd = fd,
109
                .pid = pid,
110
                .fd_id = pidfdid,
111
        };
112

113
        return 0;
59,935✔
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) {
590✔
130
        int r;
590✔
131

132
        assert(pidref);
590✔
133

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

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

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

160
        assert(pidref);
2,835✔
161

162
        if (fd < 0)
2,835✔
163
                return -EBADF;
2,835✔
164

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

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

174
        return 0;
2,835✔
175
}
176

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

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

184
        return r;
2,806✔
185
}
186

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

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

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

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

219
void pidref_done(PidRef *pidref) {
211,439✔
220
        assert(pidref);
211,439✔
221

222
        *pidref = (PidRef) {
422,878✔
223
                .fd = safe_close(pidref->fd),
211,439✔
224
        };
225
}
211,439✔
226

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

232
        pidref_done(pidref);
2,862✔
233
        return mfree(pidref);
2,862✔
234
}
235

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

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

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

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

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

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

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

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

282
        *ret = TAKE_PTR(dup_pidref);
2,859✔
283
        return 0;
2,859✔
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) {
15,345✔
310

311
        if (!pidref)
15,345✔
312
                return -ESRCH;
313

314
        if (pidref_is_remote(pidref))
15,345✔
315
                return -EREMOTE;
316

317
        if (pidref->fd >= 0)
15,343✔
318
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,986✔
319

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

323
        return -ESRCH;
324
}
325

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

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

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

336
        return 0;
337
}
338

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

341
        if (!pidref)
9,122✔
342
                return -ESRCH;
343

344
        if (pidref_is_remote(pidref))
9,122✔
345
                return -EREMOTE;
346

347
        if (pidref->fd >= 0) {
9,122✔
348
                siginfo_t si;
9,122✔
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);
9,122✔
354
                si.si_signo = sig;
9,122✔
355
                si.si_code = SI_QUEUE;
9,122✔
356
                si.si_pid = getpid_cached();
9,122✔
357
                si.si_uid = getuid();
9,122✔
358
                si.si_value.sival_int = value;
9,122✔
359

360
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
9,122✔
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) {
65,013✔
370
        int r;
65,013✔
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))
65,013✔
377
                return -ESRCH;
378

379
        if (pidref_is_remote(pidref))
65,011✔
380
                return -EREMOTE;
381

382
        if (pidref->pid == 1)
65,010✔
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)
50,808✔
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);
33,385✔
389
        if (r < 0)
33,385✔
390
                return r;
1,848✔
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) {
38,529✔
396
        if (!pidref_is_set(pidref))
38,529✔
397
                return false;
38,529✔
398

399
        if (pidref_is_remote(pidref))
38,527✔
400
                return false;
401

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

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

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

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

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

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

423
        if (!pidref_is_set(pidref))
13,897✔
424
                return -ESRCH;
13,897✔
425

426
        if (pidref_is_remote(pidref))
13,897✔
427
                return -EREMOTE;
428

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

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

440
        if (ret)
13,896✔
441
                *ret = si;
9,898✔
442

443
        return 0;
444
}
445

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

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

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

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

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

467
        assert(a);
83,039✔
468
        assert(b);
83,039✔
469

470
        r = CMP(pidref_is_set(a), pidref_is_set(b));
249,117✔
471
        if (r != 0)
83,039✔
472
                return r;
×
473

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

478
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
166,078✔
479
        if (r != 0)
83,039✔
480
                return r;
×
481

482
        r = CMP(a->pid, b->pid);
83,039✔
483
        if (r != 0)
67,740✔
484
                return r;
18,331✔
485

486
        if (a->fd_id != 0 && b->fd_id != 0)
64,708✔
487
                return CMP(a->fd_id, b->fd_id);
173✔
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