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

systemd / systemd / 16130628215

08 Jul 2025 12:05AM UTC coverage: 72.172% (+0.07%) from 72.1%
16130628215

push

github

yuwata
man: also use title case in systemd.service(5)

Follow-up for: 172dd81e9

301188 of 417317 relevant lines covered (72.17%)

716235.63 hits per line

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

87.5
/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) {
10,476✔
19
        int r;
10,476✔
20

21
        assert(pidref);
10,476✔
22

23
        if (!pidref_is_set(pidref))
10,476✔
24
                return -ESRCH;
25

26
        if (pidref_is_remote(pidref))
10,476✔
27
                return -EREMOTE;
28

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

32
        if (pidref->fd_id > 0)
10,469✔
33
                return 0;
34

35
        r = pidfd_get_inode_id(pidref->fd, &pidref->fd_id);
5,044✔
36
        if (r < 0) {
5,044✔
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) {
20,716✔
47

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

52
        if (!pidref_is_set(a))
20,713✔
53
                return !pidref_is_set(b);
3,021✔
54

55
        if (!pidref_is_set(b))
17,692✔
56
                return false;
57

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

61
        if (pidref_is_remote(a)) {
3,418✔
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))
3,418✔
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)
3,417✔
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)
3,296✔
84
                        return true;
2✔
85
        }
86

87
        return a->fd_id == b->fd_id;
3,294✔
88
}
89

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

94
        assert(pidref);
62,866✔
95

96
        if (pid < 0)
62,866✔
97
                return -ESRCH;
62,866✔
98
        if (pid == 0) {
62,866✔
99
                pid = getpid_cached();
39,211✔
100
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
39,211✔
101
        }
102

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

118
        return 0;
62,866✔
119
}
120

121
int pidref_set_pidstr(PidRef *pidref, const char *pid) {
19✔
122
        pid_t nr;
19✔
123
        int r;
19✔
124

125
        assert(pidref);
19✔
126

127
        r = parse_pid(pid, &nr);
19✔
128
        if (r < 0)
19✔
129
                return r;
19✔
130

131
        return pidref_set_pid(pidref, nr);
19✔
132
}
133

134
int pidref_set_pidfd(PidRef *pidref, int fd) {
621✔
135
        int r;
621✔
136

137
        assert(pidref);
621✔
138

139
        if (fd < 0)
621✔
140
                return -EBADF;
141

142
        int fd_copy = fcntl(fd, F_DUPFD_CLOEXEC, 3);
621✔
143
        if (fd_copy < 0) {
621✔
144
                pid_t pid;
×
145

146
                if (!ERRNO_IS_RESOURCE(errno))
×
147
                        return -errno;
×
148

149
                /* Graceful fallback if we are out of fds */
150
                r = pidfd_get_pid(fd, &pid);
×
151
                if (r < 0)
×
152
                        return r;
153

154
                *pidref = PIDREF_MAKE_FROM_PID(pid);
×
155
                return 0;
×
156
        }
157

158
        return pidref_set_pidfd_consume(pidref, fd_copy);
621✔
159
}
160

161
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
2,926✔
162
        pid_t pid;
2,926✔
163
        int r;
2,926✔
164

165
        assert(pidref);
2,926✔
166

167
        if (fd < 0)
2,926✔
168
                return -EBADF;
2,926✔
169

170
        r = pidfd_get_pid(fd, &pid);
2,926✔
171
        if (r < 0)
2,926✔
172
                return r;
173

174
        *pidref = (PidRef) {
2,926✔
175
                .fd = fd,
176
                .pid = pid,
177
        };
178

179
        return 0;
2,926✔
180
}
181

182
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
2,899✔
183
        int r;
2,899✔
184

185
        r = pidref_set_pidfd_take(pidref, fd);
2,899✔
186
        if (r < 0)
2,899✔
187
                safe_close(fd);
×
188

189
        return r;
2,899✔
190
}
191

192
int pidref_set_parent(PidRef *ret) {
1,378✔
193
        _cleanup_(pidref_done) PidRef parent = PIDREF_NULL;
1,378✔
194
        pid_t ppid;
1,378✔
195
        int r;
1,378✔
196

197
        assert(ret);
1,378✔
198

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

202
        ppid = getppid();
1,378✔
203
        for (;;) {
1,378✔
204
                r = pidref_set_pid(&parent, ppid);
1,378✔
205
                if (r < 0)
1,378✔
206
                        return r;
207

208
                if (parent.fd < 0) /* If pidfds are not available, then we are done */
1,378✔
209
                        break;
210

211
                pid_t now_ppid = getppid();
1,378✔
212
                if (now_ppid == ppid) /* If our ppid is still the same, then we are done */
1,378✔
213
                        break;
214

215
                /* Otherwise let's try again with the new ppid */
216
                ppid = now_ppid;
×
217
                pidref_done(&parent);
×
218
        }
219

220
        *ret = TAKE_PIDREF(parent);
1,378✔
221
        return 0;
1,378✔
222
}
223

224
void pidref_done(PidRef *pidref) {
211,606✔
225
        assert(pidref);
211,606✔
226

227
        *pidref = (PidRef) {
423,212✔
228
                .fd = safe_close(pidref->fd),
211,606✔
229
        };
230
}
211,606✔
231

232
PidRef* pidref_free(PidRef *pidref) {
2,933✔
233
        /* Regularly, this is an embedded structure. But sometimes we want it on the heap too */
234
        if (!pidref)
2,933✔
235
                return NULL;
236

237
        pidref_done(pidref);
2,933✔
238
        return mfree(pidref);
2,933✔
239
}
240

241
int pidref_copy(const PidRef *pidref, PidRef *ret) {
2,934✔
242
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
2,934✔
243

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

247
        assert(ret);
2,934✔
248

249
        if (pidref) {
2,934✔
250
                if (pidref_is_remote(pidref)) /* Propagate remote flag */
2,932✔
251
                        copy.fd = -EREMOTE;
×
252
                else if (pidref->fd >= 0) {
2,932✔
253
                        copy.fd = fcntl(pidref->fd, F_DUPFD_CLOEXEC, 3);
2,926✔
254
                        if (copy.fd < 0) {
2,926✔
255
                                if (!ERRNO_IS_RESOURCE(errno))
×
256
                                        return -errno;
×
257

258
                                copy.fd = -EBADF;
×
259
                        }
260
                }
261

262
                copy.pid = pidref->pid;
2,932✔
263
                copy.fd_id = pidref->fd_id;
2,932✔
264
        }
265

266
        *ret = TAKE_PIDREF(copy);
2,934✔
267
        return 0;
2,934✔
268
}
269

270
int pidref_dup(const PidRef *pidref, PidRef **ret) {
2,930✔
271
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
2,930✔
272
        int r;
2,930✔
273

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

277
        assert(ret);
2,930✔
278

279
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
2,930✔
280
        if (!dup_pidref)
2,930✔
281
                return -ENOMEM;
282

283
        r = pidref_copy(pidref, dup_pidref);
2,930✔
284
        if (r < 0)
2,930✔
285
                return r;
286

287
        *ret = TAKE_PTR(dup_pidref);
2,930✔
288
        return 0;
2,930✔
289
}
290

291
int pidref_new_from_pid(pid_t pid, PidRef **ret) {
4✔
292
        _cleanup_(pidref_freep) PidRef *n = NULL;
4✔
293
        int r;
4✔
294

295
        assert(ret);
4✔
296

297
        if (pid < 0)
4✔
298
                return -ESRCH;
299

300
        n = new(PidRef, 1);
3✔
301
        if (!n)
3✔
302
                return -ENOMEM;
303

304
        *n = PIDREF_NULL;
3✔
305

306
        r = pidref_set_pid(n, pid);
3✔
307
        if (r < 0)
3✔
308
                return r;
309

310
        *ret = TAKE_PTR(n);
3✔
311
        return 0;
3✔
312
}
313

314
int pidref_kill(const PidRef *pidref, int sig) {
15,192✔
315

316
        if (!pidref)
15,192✔
317
                return -ESRCH;
318

319
        if (pidref_is_remote(pidref))
15,192✔
320
                return -EREMOTE;
321

322
        if (pidref->fd >= 0)
15,190✔
323
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,891✔
324

325
        if (pidref->pid > 0)
7,299✔
326
                return RET_NERRNO(kill(pidref->pid, sig));
13,326✔
327

328
        return -ESRCH;
329
}
330

331
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
571✔
332
        int r;
571✔
333

334
        r = pidref_kill(pidref, sig);
571✔
335
        if (r < 0)
571✔
336
                return r;
337

338
        if (!IN_SET(sig, SIGCONT, SIGKILL))
570✔
339
                (void) pidref_kill(pidref, SIGCONT);
570✔
340

341
        return 0;
342
}
343

344
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
9,200✔
345

346
        if (!pidref)
9,200✔
347
                return -ESRCH;
348

349
        if (pidref_is_remote(pidref))
9,200✔
350
                return -EREMOTE;
351

352
        if (pidref->fd >= 0) {
9,200✔
353
                siginfo_t si;
9,200✔
354

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

365
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
9,200✔
366
        }
367

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

371
        return -ESRCH;
372
}
373

374
int pidref_verify(const PidRef *pidref) {
76,717✔
375
        int r;
76,717✔
376

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

381
        if (!pidref_is_set(pidref))
76,717✔
382
                return -ESRCH;
383

384
        if (pidref_is_remote(pidref))
76,715✔
385
                return -EREMOTE;
386

387
        if (pidref->pid == 1)
76,714✔
388
                return 1; /* PID 1 can never go away, hence never be recycled to a different process → return 1 */
389

390
        if (pidref->fd < 0)
61,520✔
391
                return 0; /* If we don't have a pidfd we cannot validate it, hence we assume it's all OK → return 0 */
392

393
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
42,622✔
394
        if (r < 0)
42,622✔
395
                return r;
2,402✔
396

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

400
bool pidref_is_self(PidRef *pidref) {
37,720✔
401
        if (!pidref_is_set(pidref))
37,720✔
402
                return false;
37,720✔
403

404
        if (pidref_is_remote(pidref))
37,718✔
405
                return false;
406

407
        if (pidref->pid != getpid_cached())
37,718✔
408
                return false;
409

410
        /* PID1 cannot exit, hence no point in comparing pidfd IDs, they can never change */
411
        if (pidref->pid == 1)
676✔
412
                return true;
413

414
        /* Also compare pidfd ID if we can get it */
415
        if (pidref_acquire_pidfd_id(pidref) < 0)
676✔
416
                return true;
417

418
        uint64_t self_id;
671✔
419
        if (pidfd_get_inode_id_self_cached(&self_id) < 0)
671✔
420
                return true;
421

422
        return pidref->fd_id == self_id;
671✔
423
}
424

425
int pidref_wait(PidRef *pidref, siginfo_t *ret, int options) {
13,983✔
426
        int r;
13,983✔
427

428
        if (!pidref_is_set(pidref))
13,983✔
429
                return -ESRCH;
13,983✔
430

431
        if (pidref_is_remote(pidref))
13,983✔
432
                return -EREMOTE;
433

434
        if (pidref->pid == 1 || pidref_is_self(pidref))
13,982✔
435
                return -ECHILD;
×
436

437
        siginfo_t si = {};
13,982✔
438
        if (pidref->fd >= 0)
13,982✔
439
                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
5,387✔
440
        else
441
                r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
8,595✔
442
        if (r < 0)
×
443
                return r;
444

445
        if (ret)
13,982✔
446
                *ret = si;
9,903✔
447

448
        return 0;
449
}
450

451
int pidref_wait_for_terminate(PidRef *pidref, siginfo_t *ret) {
13,983✔
452
        int r;
13,983✔
453

454
        for (;;) {
13,983✔
455
                r = pidref_wait(pidref, ret, WEXITED);
13,983✔
456
                if (r != -EINTR)
13,983✔
457
                        return r;
13,983✔
458
        }
459
}
460

461
bool pidref_is_automatic(const PidRef *pidref) {
170,897✔
462
        return pidref && pid_is_automatic(pidref->pid);
170,897✔
463
}
464

465
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
80,826✔
466
        siphash24_compress_typesafe(pidref->pid, state);
80,826✔
467
}
80,826✔
468

469
int pidref_compare_func(const PidRef *a, const PidRef *b) {
85,422✔
470
        int r;
85,422✔
471

472
        assert(a);
85,422✔
473
        assert(b);
85,422✔
474

475
        r = CMP(pidref_is_set(a), pidref_is_set(b));
85,422✔
476
        if (r != 0)
85,422✔
477
                return r;
×
478

479
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
85,422✔
480
        if (r != 0)
85,422✔
481
                return r;
×
482

483
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
170,844✔
484
        if (r != 0)
85,422✔
485
                return r;
×
486

487
        r = CMP(a->pid, b->pid);
85,422✔
488
        if (r != 0)
67,636✔
489
                return r;
21,134✔
490

491
        if (a->fd_id != 0 && b->fd_id != 0)
64,288✔
492
                return CMP(a->fd_id, b->fd_id);
601✔
493

494
        return 0;
495
}
496

497
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
498

499
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
×
500
                                    PidRef, pidref_hash_func, pidref_compare_func,
501
                                    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