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

systemd / systemd / 18765396043

23 Oct 2025 01:51PM UTC coverage: 72.284% (-0.01%) from 72.295%
18765396043

push

github

YHNdnzj
core: increment start limit counter only when we can start the unit

Otherwise, e.g. requesting to start a unit that is under stopping may
enter the failed state.

This makes
- rename .can_start() -> .test_startable(), and make it allow to return
  boolean and refuse to start units when it returns false,
- refuse earlier to start units that are in the deactivating state, so
  several redundant conditions in .start() can be dropped,
- move checks for unit states mapped to UNIT_ACTIVATING from .start() to
  .test_startable().

Fixes #39247.

13 of 15 new or added lines in 8 files covered. (86.67%)

6946 existing lines in 72 files now uncovered.

304970 of 421905 relevant lines covered (72.28%)

1105466.04 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) {
10,951✔
19
        int r;
10,951✔
20

21
        assert(pidref);
10,951✔
22

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

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

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

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

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

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

52
        if (!pidref_is_set(a))
6,328✔
53
                return !pidref_is_set(b);
2,789✔
54

55
        if (!pidref_is_set(b))
3,539✔
56
                return false;
57

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

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

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

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

94
        assert(pidref);
50,467✔
95

96
        if (pid < 0)
50,467✔
97
                return -ESRCH;
50,467✔
98
        if (pid == 0) {
50,467✔
99
                pid = getpid_cached();
26,436✔
100
                (void) pidfd_get_inode_id_self_cached(&pidfdid);
26,436✔
101
        }
102

103
        fd = pidfd_open(pid, 0);
50,467✔
104
        if (fd < 0) {
50,467✔
105
                /* Graceful fallback in case the kernel is out of fds */
UNCOV
106
                if (!ERRNO_IS_RESOURCE(errno))
×
UNCOV
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) {
50,467✔
113
                .fd = fd,
114
                .pid = pid,
115
                .fd_id = pidfdid,
116
        };
117

118
        return 0;
50,467✔
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) {
19✔
149
        pid_t nr;
19✔
150
        int r;
19✔
151

152
        assert(pidref);
19✔
153

154
        r = parse_pid(pid, &nr);
19✔
155
        if (r < 0)
19✔
156
                return r;
19✔
157

158
        return pidref_set_pid(pidref, nr);
19✔
159
}
160

161
int pidref_set_pidfd(PidRef *pidref, int fd) {
872✔
162
        int r;
872✔
163

164
        assert(pidref);
872✔
165

166
        if (fd < 0)
872✔
167
                return -EBADF;
168

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

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

192
        assert(pidref);
3,089✔
193

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

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

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

206
        return 0;
3,089✔
207
}
208

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

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

216
        return r;
3,060✔
217
}
218

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

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

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

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

251
void pidref_done(PidRef *pidref) {
201,686✔
252
        assert(pidref);
201,686✔
253

254
        *pidref = (PidRef) {
403,372✔
255
                .fd = safe_close(pidref->fd),
201,686✔
256
        };
257
}
201,686✔
258

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

264
        pidref_done(pidref);
2,682✔
265
        return mfree(pidref);
2,682✔
266
}
267

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

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

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

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

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

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

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

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

314
        *ret = TAKE_PTR(dup_pidref);
2,679✔
315
        return 0;
2,679✔
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,719✔
342

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

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

349
        if (pidref->fd >= 0)
15,717✔
350
                return RET_NERRNO(pidfd_send_signal(pidref->fd, sig, NULL, 0));
7,345✔
351

352
        if (pidref->pid > 0)
8,372✔
353
                return RET_NERRNO(kill(pidref->pid, sig));
15,381✔
354

355
        return -ESRCH;
356
}
357

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

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

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

368
        return 0;
369
}
370

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

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

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

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

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

411
        if (pidref_is_remote(pidref))
39,969✔
412
                return -EREMOTE;
413

414
        if (pidref->pid == 1)
39,968✔
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)
38,816✔
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);
19,999✔
421
        if (r < 0)
19,999✔
422
                return r;
2,368✔
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) {
37,021✔
428
        if (!pidref_is_set(pidref))
37,021✔
429
                return false;
37,021✔
430

431
        if (pidref_is_remote(pidref))
37,019✔
432
                return false;
433

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

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

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

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

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

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

455
        if (!pidref_is_set(pidref))
13,552✔
456
                return -ESRCH;
13,552✔
457

458
        if (pidref_is_remote(pidref))
13,552✔
459
                return -EREMOTE;
460

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

464
        siginfo_t si = {};
13,551✔
465
        if (pidref->fd >= 0)
13,551✔
466
                r = RET_NERRNO(waitid(P_PIDFD, pidref->fd, &si, options));
5,319✔
467
        else
468
                r = RET_NERRNO(waitid(P_PID, pidref->pid, &si, options));
8,232✔
469
        if (r < 0)
×
470
                return r;
471

472
        if (ret)
13,551✔
473
                *ret = si;
9,559✔
474

475
        return 0;
476
}
477

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

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

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

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

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

499
        assert(a);
81,109✔
500
        assert(b);
81,109✔
501

502
        r = CMP(pidref_is_set(a), pidref_is_set(b));
243,327✔
503
        if (r != 0)
81,109✔
504
                return r;
×
505

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

510
        r = CMP(pidref_is_remote(a), pidref_is_remote(b));
162,218✔
511
        if (r != 0)
81,109✔
512
                return r;
×
513

514
        r = CMP(a->pid, b->pid);
81,109✔
515
        if (r != 0)
65,390✔
516
                return r;
17,723✔
517

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