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

systemd / systemd / 22508817992

27 Feb 2026 10:57PM UTC coverage: 72.532% (-0.04%) from 72.575%
22508817992

push

github

poettering
NEWS: add various more features added in v260, and introduce more sections

315698 of 435256 relevant lines covered (72.53%)

1132137.23 hits per line

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

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

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

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

19
static thread_local int have_pidfs = -1;
20

21
int pidfd_check_pidfs(int pid_fd) {
43,569✔
22

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

25
        if (have_pidfs >= 0)
43,569✔
26
                return have_pidfs;
43,569✔
27

28
        _cleanup_close_ int our_fd = -EBADF;
43,569✔
29
        if (pid_fd < 0) {
10,040✔
30
                our_fd = pidfd_open(getpid_cached(), /* flags= */ 0);
1✔
31
                if (our_fd < 0)
1✔
32
                        return -errno;
×
33

34
                pid_fd = our_fd;
35
        }
36

37
        return (have_pidfs = fd_is_fs_type(pid_fd, PID_FS_MAGIC));
10,040✔
38
}
39

40
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
9,923✔
41
        static bool cached_supported = true;
9,923✔
42

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

49
        assert(fd >= 0);
9,923✔
50

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

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

68
                return -errno;
×
69
        }
70

71
        return nsfd;
72
}
73

74
int pidfd_get_info(int fd, struct pidfd_info *info) {
46,848✔
75
        static bool cached_supported = true;
46,848✔
76

77
        assert(fd >= 0);
46,848✔
78
        assert(info);
46,848✔
79

80
        if (have_pidfs == 0 || !cached_supported)
46,848✔
81
                return -EOPNOTSUPP;
82

83
        if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
46,848✔
84
                if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
3,087✔
85
                        cached_supported = false;
×
86
                        return -EOPNOTSUPP;
×
87
                }
88

89
                return -errno;
3,087✔
90
        }
91

92
        return 0;
93
}
94

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

100
        assert(fd >= 0);
×
101

102
        xsprintf(path, "/proc/self/fdinfo/%i", fd);
×
103

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

112
        if (streq(p, "0"))
×
113
                return -EREMOTE; /* PID is in foreign PID namespace? */
114
        if (streq(p, "-1"))
×
115
                return -ESRCH;   /* refers to reaped process? */
116

117
        return parse_pid(p, ret);
×
118
}
119

120
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
44,134✔
121
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
44,134✔
122
        int r;
44,134✔
123

124
        assert(fd >= 0);
44,134✔
125

126
        r = pidfd_get_info(fd, &info);
44,134✔
127
        if (r < 0)
44,134✔
128
                return r;
44,134✔
129

130
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
41,047✔
131

132
        if (ret)
41,047✔
133
                *ret = info.pid;
41,047✔
134
        return 0;
135
}
136

137
int pidfd_get_pid(int fd, pid_t *ret) {
44,134✔
138
        int r;
44,134✔
139

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

154
        assert(fd >= 0);
44,134✔
155

156
        r = pidfd_get_pid_ioctl(fd, ret);
44,134✔
157
        if (r != -EOPNOTSUPP)
44,134✔
158
                return r;
159

160
        return pidfd_get_pid_fdinfo(fd, ret);
×
161
}
162

163
int pidfd_verify_pid(int pidfd, pid_t pid) {
23,237✔
164
        pid_t current_pid;
23,237✔
165
        int r;
23,237✔
166

167
        assert(pidfd >= 0);
23,237✔
168
        assert(pid > 0);
23,237✔
169

170
        r = pidfd_get_pid(pidfd, &current_pid);
23,237✔
171
        if (r < 0)
23,237✔
172
                return r;
23,237✔
173

174
        return current_pid != pid ? -ESRCH : 0;
20,150✔
175
}
176

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

181
        assert(fd >= 0);
2,639✔
182

183
        r = pidfd_get_info(fd, &info);
2,639✔
184
        if (r < 0)
2,639✔
185
                return r;
2,639✔
186

187
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
2,639✔
188

189
        if (info.ppid == 0) /* See comments in pid_get_ppid() */
2,639✔
190
                return -EADDRNOTAVAIL;
191

192
        if (ret)
2,638✔
193
                *ret = info.ppid;
2,638✔
194
        return 0;
195
}
196

197
int pidfd_get_uid(int fd, uid_t *ret) {
73✔
198
        struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
73✔
199
        int r;
73✔
200

201
        assert(fd >= 0);
73✔
202

203
        r = pidfd_get_info(fd, &info);
73✔
204
        if (r < 0)
73✔
205
                return r;
73✔
206

207
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
73✔
208

209
        if (ret)
73✔
210
                *ret = info.ruid;
73✔
211
        return 0;
212
}
213

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

218
        assert(fd >= 0);
×
219

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

224
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
×
225

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

231
int pidfd_get_inode_id_impl(int fd, uint64_t *ret) {
43,568✔
232
        static thread_local bool file_handle_supported = true;
43,568✔
233
        int r;
43,568✔
234

235
        assert(fd >= 0);
43,568✔
236

237
        /* Since kernel 6.14 (b3caba8f7a34a2bbaf45ffc6ff3a49b70afeb192), we can use name_to_handle_at(). */
238
        if (file_handle_supported) {
43,568✔
239
                r = fd_to_handle_u64(fd, ret);
43,568✔
240
                if (r >= 0 || is_name_to_handle_at_fatal_error(r))
43,568✔
241
                        return r;
43,568✔
242

243
                file_handle_supported = false;
×
244
        }
245

246
#if SIZEOF_INO_T == 8
247
        struct stat st;
×
248
        if (fstat(fd, &st) < 0)
×
249
                return -errno;
×
250

251
        if (ret)
×
252
                *ret = (uint64_t) st.st_ino;
×
253
        return 0;
254

255
#elif SIZEOF_INO_T == 4
256
        /* On 32-bit systems (where sizeof(ino_t) == 4), the inode id returned by fstat() cannot be used to
257
         * reliably identify the process, nor can we communicate the origin of the id with the clients.
258
         * Hence let's just refuse to acquire pidfdid through fstat() here. All clients shall also insist on
259
         * the 64-bit id from name_to_handle_at(). */
260
        return -EOPNOTSUPP;
261
#else
262
#  error Unsupported ino_t size
263
#endif
264
}
265

266
int pidfd_get_inode_id(int fd, uint64_t *ret) {
43,567✔
267
        int r;
43,567✔
268

269
        assert(fd >= 0);
43,567✔
270

271
        r = pidfd_check_pidfs(fd);
43,567✔
272
        if (r < 0)
43,567✔
273
                return r;
274
        if (r == 0)
43,567✔
275
                return -EOPNOTSUPP;
276

277
        return pidfd_get_inode_id_impl(fd, ret);
43,567✔
278
}
279

280
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
45,666✔
281
        static thread_local uint64_t cached = 0;
45,666✔
282
        static thread_local pid_t initialized = 0; /* < 0: cached error; == 0: invalid; > 0: valid and pid that was current */
45,666✔
283
        int r;
45,666✔
284

285
        assert(ret);
45,666✔
286

287
        if (initialized == getpid_cached()) {
45,666✔
288
                *ret = cached;
9,365✔
289
                return 0;
9,365✔
290
        }
291
        if (initialized < 0)
36,301✔
292
                return initialized;
293

294
        _cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
81,967✔
295
        if (fd < 0)
36,301✔
296
                return -errno;
×
297

298
        r = pidfd_get_inode_id(fd, &cached);
36,301✔
299
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
36,301✔
300
                return (initialized = -EOPNOTSUPP);
×
301
        if (r < 0)
36,301✔
302
                return r;
303

304
        *ret = cached;
36,301✔
305
        initialized = getpid_cached();
36,301✔
306
        return 0;
36,301✔
307
}
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