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

systemd / systemd / 19877670170

02 Dec 2025 04:56PM UTC coverage: 72.905% (+0.001%) from 72.904%
19877670170

push

github

web-flow
kernel-install: exit with option --json=help (#39974)

In that case, `parse_json_argument()` returns 0.

Follow-up for bdd36c003 (v255).

0 of 1 new or added line in 1 file covered. (0.0%)

367 existing lines in 34 files now uncovered.

310170 of 425446 relevant lines covered (72.9%)

1132258.26 hits per line

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

86.99
/src/basic/pidfd-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <linux/fs.h>
4
#include <linux/magic.h>
5
#include <sys/ioctl.h>
6
#include <threads.h>
7
#include <unistd.h>
8

9
#include "errno-util.h"
10
#include "fd-util.h"
11
#include "fileio.h"
12
#include "mountpoint-util.h"
13
#include "parse-util.h"
14
#include "pidfd-util.h"
15
#include "process-util.h"
16
#include "stat-util.h"
17
#include "stdio-util.h"
18
#include "string-util.h"
19
#include "unaligned.h"
20

21
static thread_local int have_pidfs = -1;
22

23
int pidfd_check_pidfs(int pid_fd) {
35,994✔
24

25
        /* NB: the passed fd *must* be acquired via pidfd_open(), i.e. must be a true pidfd! */
26

27
        if (have_pidfs >= 0)
35,994✔
28
                return have_pidfs;
35,994✔
29

30
        _cleanup_close_ int our_fd = -EBADF;
35,994✔
31
        if (pid_fd < 0) {
7,465✔
32
                our_fd = pidfd_open(getpid_cached(), /* flags = */ 0);
1✔
33
                if (our_fd < 0)
1✔
34
                        return -errno;
×
35

36
                pid_fd = our_fd;
37
        }
38

39
        return (have_pidfs = fd_is_fs_type(pid_fd, PID_FS_MAGIC));
7,465✔
40
}
41

42
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
8,485✔
43
        static bool cached_supported = true;
8,485✔
44

45
        /* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
46
         *
47
         * Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
48
         * is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
49
         * see below) */
50

51
        assert(fd >= 0);
8,485✔
52

53
        /* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
54
         * call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
55
         * anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
56
        if (have_pidfs == 0 || !cached_supported)
8,485✔
57
                return -EOPNOTSUPP;
58

59
        int nsfd = ioctl(fd, ns_type_cmd, 0);
8,485✔
60
        if (nsfd < 0) {
8,485✔
61
                /* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
62
                 * at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
UNCOV
63
                if (IN_SET(errno, ENOTTY, EINVAL)) {
×
64
                        cached_supported = false;
×
65
                        return -EOPNOTSUPP;
×
66
                }
UNCOV
67
                if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
×
68
                        return -ENOPKG;
69

UNCOV
70
                return -errno;
×
71
        }
72

73
        return nsfd;
74
}
75

76
int pidfd_get_info(int fd, struct pidfd_info *info) {
44,940✔
77
        static bool cached_supported = true;
44,940✔
78

79
        assert(fd >= 0);
44,940✔
80
        assert(info);
44,940✔
81

82
        if (have_pidfs == 0 || !cached_supported)
44,940✔
83
                return -EOPNOTSUPP;
84

85
        if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
27,929✔
86
                if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
1,442✔
87
                        cached_supported = false;
263✔
88
                        return -EOPNOTSUPP;
263✔
89
                }
90

91
                return -errno;
1,179✔
92
        }
93

94
        return 0;
95
}
96

97
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
15,752✔
98
        char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
15,752✔
99
        _cleanup_free_ char *p = NULL;
15,752✔
100
        int r;
15,752✔
101

102
        assert(fd >= 0);
15,752✔
103

104
        xsprintf(path, "/proc/self/fdinfo/%i", fd);
15,752✔
105

106
        r = get_proc_field(path, "Pid", &p);
15,752✔
107
        if (r == -ENOENT)
15,752✔
108
                return -EBADF;
109
        if (r == -ENODATA) /* not a pidfd? */
15,752✔
110
                return -ENOTTY;
111
        if (r < 0)
15,752✔
112
                return r;
113

114
        if (streq(p, "0"))
15,752✔
115
                return -EREMOTE; /* PID is in foreign PID namespace? */
116
        if (streq(p, "-1"))
15,752✔
117
                return -ESRCH;   /* refers to reaped process? */
118

119
        return parse_pid(p, ret);
14,117✔
120
}
121

122
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
42,382✔
123
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
42,382✔
124
        int r;
42,382✔
125

126
        assert(fd >= 0);
42,382✔
127

128
        r = pidfd_get_info(fd, &info);
42,382✔
129
        if (r < 0)
42,382✔
130
                return r;
42,382✔
131

132
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
25,451✔
133

134
        if (ret)
25,451✔
135
                *ret = info.pid;
25,451✔
136
        return 0;
137
}
138

139
int pidfd_get_pid(int fd, pid_t *ret) {
42,382✔
140
        int r;
42,382✔
141

142
        /* Converts a pidfd into a pid. We try ioctl(PIDFD_GET_INFO) (kernel 6.13+) first,
143
         * /proc/self/fdinfo/ as fallback. Well known errors:
144
         *
145
         *    -EBADF   → fd invalid
146
         *    -ESRCH   → fd valid, but process is already reaped
147
         *
148
         * pidfd_get_pid_fdinfo() might additionally fail for other reasons:
149
         *
150
         *    -ENOSYS  → /proc/ not mounted
151
         *    -ENOTTY  → fd valid, but not a pidfd
152
         *    -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
153
         *               (when using PIDFD_GET_INFO this is indistinguishable from -ESRCH)
154
         */
155

156
        assert(fd >= 0);
42,382✔
157

158
        r = pidfd_get_pid_ioctl(fd, ret);
42,382✔
159
        if (r != -EOPNOTSUPP)
42,382✔
160
                return r;
161

162
        return pidfd_get_pid_fdinfo(fd, ret);
15,752✔
163
}
164

165
int pidfd_verify_pid(int pidfd, pid_t pid) {
23,177✔
166
        pid_t current_pid;
23,177✔
167
        int r;
23,177✔
168

169
        assert(pidfd >= 0);
23,177✔
170
        assert(pid > 0);
23,177✔
171

172
        r = pidfd_get_pid(pidfd, &current_pid);
23,177✔
173
        if (r < 0)
23,177✔
174
                return r;
23,177✔
175

176
        return current_pid != pid ? -ESRCH : 0;
20,363✔
177
}
178

179
int pidfd_get_ppid(int fd, pid_t *ret) {
2,487✔
180
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
2,487✔
181
        int r;
2,487✔
182

183
        assert(fd >= 0);
2,487✔
184

185
        r = pidfd_get_info(fd, &info);
2,487✔
186
        if (r < 0)
2,487✔
187
                return r;
2,487✔
188

189
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
968✔
190

191
        if (info.ppid == 0) /* See comments in pid_get_ppid() */
968✔
192
                return -EADDRNOTAVAIL;
193

194
        if (ret)
968✔
195
                *ret = info.ppid;
968✔
196
        return 0;
197
}
198

199
int pidfd_get_uid(int fd, uid_t *ret) {
69✔
200
        struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
69✔
201
        int r;
69✔
202

203
        assert(fd >= 0);
69✔
204

205
        r = pidfd_get_info(fd, &info);
69✔
206
        if (r < 0)
69✔
207
                return r;
69✔
208

209
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
66✔
210

211
        if (ret)
66✔
212
                *ret = info.ruid;
66✔
213
        return 0;
214
}
215

216
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
×
217
        struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
×
218
        int r;
×
219

220
        assert(fd >= 0);
×
221

222
        r = pidfd_get_info(fd, &info);
×
223
        if (r < 0)
×
224
                return r;
×
225

226
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
×
227

228
        if (ret)
×
229
                *ret = info.cgroupid;
×
230
        return 0;
231
}
232

233
int pidfd_get_inode_id_impl(int fd, uint64_t *ret) {
35,993✔
234
        static thread_local bool file_handle_supported = true;
35,993✔
235
        int r;
35,993✔
236

237
        assert(fd >= 0);
35,993✔
238

239
        if (file_handle_supported) {
35,993✔
240
                union {
32,071✔
241
                        struct file_handle file_handle;
242
                        uint8_t space[offsetof(struct file_handle, f_handle) + sizeof(uint64_t)];
243
                } fh = {
32,071✔
244
                        .file_handle.handle_bytes = sizeof(uint64_t),
245
                        .file_handle.handle_type = FILEID_KERNFS,
246
                };
247
                int mnt_id;
32,071✔
248

249
                r = RET_NERRNO(name_to_handle_at(fd, "", &fh.file_handle, &mnt_id, AT_EMPTY_PATH));
32,071✔
250
                if (r >= 0) {
2,725✔
251
                        if (ret)
29,346✔
252
                                /* Note, "struct file_handle" is 32bit aligned usually, but we need to read a 64bit value from it */
253
                                *ret = unaligned_read_ne64(fh.file_handle.f_handle);
29,346✔
254
                        return 0;
29,346✔
255
                }
256
                assert(r != -EOVERFLOW);
2,725✔
257
                if (is_name_to_handle_at_fatal_error(r))
2,725✔
258
                        return r;
259

260
                file_handle_supported = false;
2,725✔
261
        }
262

263
#if SIZEOF_INO_T == 8
264
        struct stat st;
6,647✔
265
        if (fstat(fd, &st) < 0)
6,647✔
266
                return -errno;
×
267

268
        if (ret)
6,647✔
269
                *ret = (uint64_t) st.st_ino;
6,646✔
270
        return 0;
271

272
#elif SIZEOF_INO_T == 4
273
        /* On 32-bit systems (where sizeof(ino_t) == 4), the inode id returned by fstat() cannot be used to
274
         * reliably identify the process, nor can we communicate the origin of the id with the clients.
275
         * Hence let's just refuse to acquire pidfdid through fstat() here. All clients shall also insist on
276
         * the 64-bit id from name_to_handle_at(). */
277
        return -EOPNOTSUPP;
278
#else
279
#  error Unsupported ino_t size
280
#endif
281
}
282

283
int pidfd_get_inode_id(int fd, uint64_t *ret) {
35,992✔
284
        int r;
35,992✔
285

286
        assert(fd >= 0);
35,992✔
287

288
        r = pidfd_check_pidfs(fd);
35,992✔
289
        if (r < 0)
35,992✔
290
                return r;
291
        if (r == 0)
35,992✔
292
                return -EOPNOTSUPP;
293

294
        return pidfd_get_inode_id_impl(fd, ret);
35,992✔
295
}
296

297
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
35,540✔
298
        static thread_local uint64_t cached = 0;
35,540✔
299
        static thread_local pid_t initialized = 0; /* < 0: cached error; == 0: invalid; > 0: valid and pid that was current */
35,540✔
300
        int r;
35,540✔
301

302
        assert(ret);
35,540✔
303

304
        if (initialized == getpid_cached()) {
35,540✔
305
                *ret = cached;
5,743✔
306
                return 0;
5,743✔
307
        }
308
        if (initialized < 0)
29,797✔
309
                return initialized;
310

311
        _cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
65,337✔
312
        if (fd < 0)
29,797✔
313
                return -errno;
×
314

315
        r = pidfd_get_inode_id(fd, &cached);
29,797✔
316
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
29,797✔
317
                return (initialized = -EOPNOTSUPP);
×
318
        if (r < 0)
29,797✔
319
                return r;
320

321
        *ret = cached;
29,797✔
322
        initialized = getpid_cached();
29,797✔
323
        return 0;
29,797✔
324
}
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