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

systemd / systemd / 12877533250

20 Jan 2025 11:16PM UTC coverage: 0.117%. Remained the same
12877533250

push

github

web-flow
pidfd: cache our own pidfd inode id, and use it at various places (#36060)

This is split out of and preparation for #35224, but makes a ton of
sense on its own

0 of 95 new or added lines in 10 files covered. (0.0%)

4667 existing lines in 34 files now uncovered.

478 of 408040 relevant lines covered (0.12%)

1.45 hits per line

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

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

3
#include <sys/ioctl.h>
4
#include <unistd.h>
5

6
#include "errno-util.h"
7
#include "fd-util.h"
8
#include "fileio.h"
9
#include "macro.h"
10
#include "memory-util.h"
11
#include "missing_magic.h"
12
#include "missing_threads.h"
13
#include "parse-util.h"
14
#include "path-util.h"
15
#include "pidfd-util.h"
16
#include "process-util.h"
17
#include "stat-util.h"
18
#include "string-util.h"
19

20
static int have_pidfs = -1;
21

NEW
22
static int pidfd_check_pidfs(int pid_fd) {
×
23

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

26
        if (have_pidfs >= 0)
×
27
                return have_pidfs;
28

NEW
29
        return (have_pidfs = fd_is_fs_type(pid_fd, PID_FS_MAGIC));
×
30
}
31

32
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
×
33
        static bool cached_supported = true;
×
34

35
        /* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
36
         *
37
         * Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
38
         * is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
39
         * see below) */
40

41
        assert(fd >= 0);
×
42

43
        /* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
44
         * call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
45
         * anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
46
        if (have_pidfs == 0 || !cached_supported)
×
47
                return -EOPNOTSUPP;
48

49
        int nsfd = ioctl(fd, ns_type_cmd);
×
50
        if (nsfd < 0) {
×
51
                /* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
52
                 * at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
53
                if (IN_SET(errno, ENOTTY, EINVAL)) {
×
54
                        cached_supported = false;
×
55
                        return -EOPNOTSUPP;
×
56
                }
57
                if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
×
58
                        return -ENOPKG;
59

60
                return -errno;
×
61
        }
62

63
        return nsfd;
64
}
65

66
static int pidfd_get_info(int fd, struct pidfd_info *info) {
×
67
        static bool cached_supported = true;
×
68

69
        assert(fd >= 0);
×
70
        assert(info);
×
71

72
        if (have_pidfs == 0 || !cached_supported)
×
73
                return -EOPNOTSUPP;
74

75
        if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
×
76
                if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
×
77
                        cached_supported = false;
×
78
                        return -EOPNOTSUPP;
×
79
                }
80

81
                return -errno;
×
82
        }
83

84
        return 0;
85
}
86

87
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
×
88
        char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
×
89
        _cleanup_free_ char *fdinfo = NULL;
×
90
        int r;
×
91

92
        assert(fd >= 0);
×
93

94
        xsprintf(path, "/proc/self/fdinfo/%i", fd);
×
95

96
        r = read_full_virtual_file(path, &fdinfo, NULL);
×
97
        if (r == -ENOENT)
×
98
                return proc_fd_enoent_errno();
×
99
        if (r < 0)
×
100
                return r;
101

102
        char *p = find_line_startswith(fdinfo, "Pid:");
×
103
        if (!p)
×
104
                return -ENOTTY; /* not a pidfd? */
105

106
        p = skip_leading_chars(p, /* bad = */ NULL);
×
107
        p[strcspn(p, WHITESPACE)] = 0;
×
108

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

114
        return parse_pid(p, ret);
×
115
}
116

117
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
×
118
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
×
119
        int r;
×
120

121
        assert(fd >= 0);
×
122

123
        r = pidfd_get_info(fd, &info);
×
124
        if (r < 0)
×
125
                return r;
×
126

127
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
×
128

129
        if (ret)
×
130
                *ret = info.pid;
×
131
        return 0;
132
}
133

134
int pidfd_get_pid(int fd, pid_t *ret) {
×
135
        int r;
×
136

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

151
        assert(fd >= 0);
×
152

153
        r = pidfd_get_pid_ioctl(fd, ret);
×
154
        if (r != -EOPNOTSUPP)
×
155
                return r;
156

157
        return pidfd_get_pid_fdinfo(fd, ret);
×
158
}
159

160
int pidfd_verify_pid(int pidfd, pid_t pid) {
×
161
        pid_t current_pid;
×
162
        int r;
×
163

164
        assert(pidfd >= 0);
×
165
        assert(pid > 0);
×
166

167
        r = pidfd_get_pid(pidfd, &current_pid);
×
168
        if (r < 0)
×
169
                return r;
×
170

171
        return current_pid != pid ? -ESRCH : 0;
×
172
}
173

174
int pidfd_get_ppid(int fd, pid_t *ret) {
×
175
        struct pidfd_info info = { .mask = PIDFD_INFO_PID };
×
176
        int r;
×
177

178
        assert(fd >= 0);
×
179

180
        r = pidfd_get_info(fd, &info);
×
181
        if (r < 0)
×
182
                return r;
×
183

184
        assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
×
185

186
        if (info.ppid == 0) /* See comments in pid_get_ppid() */
×
187
                return -EADDRNOTAVAIL;
188

189
        if (ret)
×
190
                *ret = info.ppid;
×
191
        return 0;
192
}
193

194
int pidfd_get_uid(int fd, uid_t *ret) {
×
195
        struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
×
196
        int r;
×
197

198
        assert(fd >= 0);
×
199

200
        r = pidfd_get_info(fd, &info);
×
201
        if (r < 0)
×
202
                return r;
×
203

204
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
×
205

206
        if (ret)
×
207
                *ret = info.ruid;
×
208
        return 0;
209
}
210

211
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
×
212
        struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
×
213
        int r;
×
214

215
        assert(fd >= 0);
×
216

217
        r = pidfd_get_info(fd, &info);
×
218
        if (r < 0)
×
219
                return r;
×
220

221
        assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
×
222

223
        if (ret)
×
224
                *ret = info.cgroupid;
×
225
        return 0;
226
}
227

228
int pidfd_get_inode_id(int fd, uint64_t *ret) {
×
229
        int r;
×
230

231
        assert(fd >= 0);
×
232

NEW
233
        r = pidfd_check_pidfs(fd);
×
234
        if (r < 0)
×
235
                return r;
×
236
        if (r == 0)
×
237
                return -EOPNOTSUPP;
238

239
        struct stat st;
×
240
        if (fstat(fd, &st) < 0)
×
241
                return -errno;
×
242

243
        if (ret)
×
244
                *ret = (uint64_t) st.st_ino;
×
245
        return 0;
246
}
247

NEW
248
int pidfd_get_inode_id_self_cached(uint64_t *ret) {
×
NEW
249
        static thread_local uint64_t cached = 0;
×
NEW
250
        static thread_local pid_t initialized = 0; /* < 0: cached error; == 0: invalid; > 0: valid and pid that was current */
×
NEW
251
        int r;
×
252

NEW
253
        assert(ret);
×
254

NEW
255
        if (initialized == getpid_cached()) {
×
NEW
256
                *ret = cached;
×
NEW
257
                return 0;
×
258
        }
NEW
259
        if (initialized < 0)
×
260
                return initialized;
261

NEW
262
        _cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
×
NEW
263
        if (fd < 0)
×
NEW
264
                return -errno;
×
265

NEW
266
        r = pidfd_get_inode_id(fd, &cached);
×
NEW
267
        if (ERRNO_IS_NEG_NOT_SUPPORTED(r))
×
NEW
268
                return (initialized = -EOPNOTSUPP);
×
NEW
269
        if (r < 0)
×
270
                return r;
271

NEW
272
        *ret = cached;
×
NEW
273
        initialized = getpid_cached();
×
NEW
274
        return 0;
×
275
}
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