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

systemd / systemd / 21192089104

20 Jan 2026 11:35PM UTC coverage: 72.524% (-0.3%) from 72.818%
21192089104

push

github

yuwata
mkdir: reset mtime *after* fchown()

Follow-up for 34c3d5747

Also, drop pointless shortcut.

1 of 2 new or added lines in 1 file covered. (50.0%)

2960 existing lines in 48 files now uncovered.

309808 of 427181 relevant lines covered (72.52%)

1236537.64 hits per line

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

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

21
int pidref_acquire_pidfd_id(PidRef *pidref) {
12,901✔
22
        int r;
12,901✔
23

24
        assert(pidref);
12,901✔
25

26
        if (!pidref_is_set(pidref))
12,901✔
27
                return -ESRCH;
28

29
        if (pidref_is_remote(pidref))
12,901✔
30
                return -EREMOTE;
31

32
        if (pidref->fd < 0)
12,901✔
33
                return -ENOMEDIUM;
34

35
        if (pidref->fd_id > 0)
12,894✔
36
                return 0;
37

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

46
        return 0;
47
}
48

49
bool pidref_equal(PidRef *a, PidRef *b) {
7,571✔
50

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

55
        if (!pidref_is_set(a))
7,568✔
56
                return !pidref_is_set(b);
6,454✔
57

58
        if (!pidref_is_set(b))
4,341✔
59
                return false;
60

61
        if (a->pid != b->pid)
4,331✔
62
                return false;
63

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

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

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

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

90
        return a->fd_id == b->fd_id;
4,187✔
91
}
92

93
int pidref_set_pid(PidRef *pidref, pid_t pid) {
64,743✔
94
        uint64_t pidfdid = 0;
64,743✔
95
        int fd;
64,743✔
96

97
        assert(pidref);
64,743✔
98

99
        if (pid < 0)
64,743✔
100
                return -ESRCH;
64,743✔
101
        if (pid == 0) {
64,743✔
102
                pid = getpid_cached();
33,840✔
103
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
33,840✔
104
        }
105

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

112
                fd = -EBADF;
113
        }
114

115
        *pidref = (PidRef) {
64,743✔
116
                .fd = fd,
117
                .pid = pid,
118
                .fd_id = pidfdid,
119
        };
120

121
        return 0;
64,743✔
122
}
123

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

129
        int r;
56✔
130

131
        assert(pidref);
56✔
132

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

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

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

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

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

155
        assert(pidref);
43✔
156

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

161
        return pidref_set_pid(pidref, nr);
43✔
162
}
163

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

167
        assert(pidref);
1,083✔
168

169
        if (fd < 0)
1,083✔
170
                return -EBADF;
171

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

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

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

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

188
        return pidref_set_pidfd_consume(pidref, fd_copy);
1,083✔
189
}
190

191
int pidref_set_pidfd_take(PidRef *pidref, int fd) {
3,691✔
192
        pid_t pid;
3,691✔
193
        int r;
3,691✔
194

195
        assert(pidref);
3,691✔
196

197
        if (fd < 0)
3,691✔
198
                return -EBADF;
3,691✔
199

200
        r = pidfd_get_pid(fd, &pid);
3,691✔
201
        if (r < 0)
3,691✔
202
                return r;
203

204
        *pidref = (PidRef) {
3,691✔
205
                .fd = fd,
206
                .pid = pid,
207
        };
208

209
        return 0;
3,691✔
210
}
211

212
int pidref_set_pidfd_consume(PidRef *pidref, int fd) {
3,689✔
213
        int r;
3,689✔
214

215
        r = pidref_set_pidfd_take(pidref, fd);
3,689✔
216
        if (r < 0)
3,689✔
217
                safe_close(fd);
×
218

219
        return r;
3,689✔
220
}
221

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

227
        assert(ret);
1,257✔
228

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

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

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

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

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

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

254
void pidref_done(PidRef *pidref) {
203,970✔
255
        assert(pidref);
203,970✔
256

257
        *pidref = (PidRef) {
407,940✔
258
                .fd = safe_close(pidref->fd),
203,970✔
259
        };
260
}
203,970✔
261

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

267
        pidref_done(pidref);
3,482✔
268
        return mfree(pidref);
3,482✔
269
}
270

271
int pidref_copy(const PidRef *pidref, PidRef *ret) {
3,483✔
272
        _cleanup_(pidref_done) PidRef copy = PIDREF_NULL;
3,483✔
273

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

277
        assert(ret);
3,483✔
278

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

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

292
                copy.pid = pidref->pid;
3,481✔
293
                copy.fd_id = pidref->fd_id;
3,481✔
294
        }
295

296
        *ret = TAKE_PIDREF(copy);
3,483✔
297
        return 0;
3,483✔
298
}
299

300
int pidref_dup(const PidRef *pidref, PidRef **ret) {
3,479✔
301
        _cleanup_(pidref_freep) PidRef *dup_pidref = NULL;
3,479✔
302
        int r;
3,479✔
303

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

307
        assert(ret);
3,479✔
308

309
        dup_pidref = newdup(PidRef, &PIDREF_NULL, 1);
3,479✔
310
        if (!dup_pidref)
3,479✔
311
                return -ENOMEM;
312

313
        r = pidref_copy(pidref, dup_pidref);
3,479✔
314
        if (r < 0)
3,479✔
315
                return r;
316

317
        *ret = TAKE_PTR(dup_pidref);
3,479✔
318
        return 0;
3,479✔
319
}
320

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

325
        assert(ret);
4✔
326

327
        if (pid < 0)
4✔
328
                return -ESRCH;
329

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

334
        *n = PIDREF_NULL;
3✔
335

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

340
        *ret = TAKE_PTR(n);
3✔
341
        return 0;
3✔
342
}
343

344
int pidref_kill(const PidRef *pidref, int sig) {
15,981✔
345

346
        if (!pidref)
15,981✔
347
                return -ESRCH;
348

349
        if (pidref_is_remote(pidref))
15,981✔
350
                return -EREMOTE;
351

352
        if (pidref->fd >= 0)
15,979✔
353
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,242✔
354

355
        if (pidref->pid > 0)
8,737✔
356
                return RET_NERRNO(kill(pidref->pid, sig));
15,941✔
357

358
        return -ESRCH;
359
}
360

361
int pidref_kill_and_sigcont(const PidRef *pidref, int sig) {
430✔
362
        int r;
430✔
363

364
        r = pidref_kill(pidref, sig);
430✔
365
        if (r < 0)
430✔
366
                return r;
367

368
        if (!IN_SET(sig, SIGCONT, SIGKILL))
429✔
369
                (void) pidref_kill(pidref, SIGCONT);
429✔
370

371
        return 0;
372
}
373

374
int pidref_sigqueue(const PidRef *pidref, int sig, int value) {
10,233✔
375

376
        if (!pidref)
10,233✔
377
                return -ESRCH;
378

379
        if (pidref_is_remote(pidref))
10,233✔
380
                return -EREMOTE;
381

382
        if (pidref->fd >= 0) {
10,233✔
383
                siginfo_t si;
10,233✔
384

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

395
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, &si, 0));
10,233✔
396
        }
397

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

401
        return -ESRCH;
402
}
403

404
int pidref_verify(const PidRef *pidref) {
46,770✔
405
        int r;
46,770✔
406

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

411
        if (!pidref_is_set(pidref))
46,770✔
412
                return -ESRCH;
413

414
        if (pidref_is_remote(pidref))
46,768✔
415
                return -EREMOTE;
416

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

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

423
        r = pidfd_verify_pid(pidref->fd, pidref->pid);
23,394✔
424
        if (r < 0)
23,394✔
425
                return r;
2,895✔
426

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

430
bool pidref_is_self(PidRef *pidref) {
34,130✔
431
        if (!pidref_is_set(pidref))
34,130✔
432
                return false;
34,130✔
433

434
        if (pidref_is_remote(pidref))
34,128✔
435
                return false;
436

437
        if (pidref->pid != getpid_cached())
34,128✔
438
                return false;
439

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

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

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

452
        return pidref->fd_id == self_id;
631✔
453
}
454

455
int pidref_wait_for_terminate_full(PidRef *pidref, usec_t timeout, siginfo_t *ret_si) {
10,966✔
456
        int r;
10,966✔
457

458
        assert(timeout > 0);
10,966✔
459

460
        if (!pidref_is_set(pidref))
10,966✔
461
                return -ESRCH;
462

463
        if (pidref_is_remote(pidref))
10,966✔
464
                return -EREMOTE;
465

466
        if (pidref->pid == 1 || pidref_is_self(pidref))
10,965✔
467
                return -ECHILD;
×
468

469
        if (timeout != USEC_INFINITY && pidref->fd < 0)
10,965✔
470
                return -ENOMEDIUM;
471

472
        usec_t ts = timeout == USEC_INFINITY ? USEC_INFINITY : usec_add(now(CLOCK_MONOTONIC), timeout);
10,967✔
473

474
        for (;;) {
10,965✔
475
                if (ts != USEC_INFINITY) {
10,965✔
476
                        usec_t left = usec_sub_unsigned(ts, now(CLOCK_MONOTONIC));
2✔
477
                        if (left == 0)
2✔
478
                                return -ETIMEDOUT;
10,965✔
479

480
                        r = fd_wait_for_event(pidref->fd, POLLIN, left);
2✔
481
                        if (r == 0)
2✔
482
                                return -ETIMEDOUT;
483
                        if (r == -EINTR)
1✔
484
                                continue;
×
485
                        if (r < 0)
1✔
486
                                return r;
487
                }
488

489
                siginfo_t si = {};
10,964✔
490

491
                if (pidref->fd >= 0)
10,964✔
492
                        r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, WEXITED));
10,106✔
493
                else
494
                        r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, WEXITED));
858✔
495
                if (r >= 0) {
×
496
                        if (ret_si)
10,964✔
497
                                *ret_si = si;
7,536✔
498
                        return 0;
10,964✔
499
                }
500
                if (r != -EINTR)
×
501
                        return r;
502
        }
503
}
504

505
bool pidref_is_automatic(const PidRef *pidref) {
191,040✔
506
        return pidref && pid_is_automatic(pidref->pid);
191,040✔
507
}
508

509
void pidref_hash_func(const PidRef *pidref, struct siphash *state) {
92,233✔
510
        siphash24_compress_typesafe(pidref->pid, state);
92,233✔
511
}
92,233✔
512

513
int pidref_compare_func(const PidRef *a, const PidRef *b) {
95,494✔
514
        int r;
95,494✔
515

516
        assert(a);
95,494✔
517
        assert(b);
95,494✔
518

519
        r = CMP(pidref_is_set(a), pidref_is_set(b));
286,482✔
520
        if (r != 0)
95,494✔
521
                return r;
×
522

523
        r = CMP(pidref_is_automatic(a), pidref_is_automatic(b));
95,494✔
524
        if (r != 0)
95,494✔
525
                return r;
×
526

527
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
190,988✔
528
        if (r != 0)
95,494✔
529
                return r;
×
530

531
        r = CMP(a->pid, b->pid);
95,494✔
532
        if (r != 0)
74,217✔
533
                return r;
24,744✔
534

535
        if (a->fd_id != 0 && b->fd_id != 0)
70,750✔
536
                return CMP(a->fd_id, b->fd_id);
665✔
537

538
        return 0;
539
}
540

541
DEFINE_HASH_OPS(pidref_hash_ops, PidRef, pidref_hash_func, pidref_compare_func);
542

543
DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(pidref_hash_ops_free,
×
544
                                    PidRef, pidref_hash_func, pidref_compare_func,
545
                                    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