1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <sys/ioctl.h>
#include <unistd.h>
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "macro.h"
#include "memory-util.h"
#include "missing_magic.h"
#include "parse-util.h"
#include "path-util.h"
#include "pidfd-util.h"
#include "process-util.h"
#include "stat-util.h"
#include "string-util.h"
static int have_pidfs = -1;
static int pidfd_check_pidfs(void) {
if (have_pidfs >= 0)
return have_pidfs;
_cleanup_close_ int fd = pidfd_open(getpid_cached(), 0);
if (fd < 0) {
if (ERRNO_IS_NOT_SUPPORTED(errno))
return (have_pidfs = false);
return -errno;
}
return (have_pidfs = fd_is_fs_type(fd, PID_FS_MAGIC));
}
int pidfd_get_namespace(int fd, unsigned long ns_type_cmd) {
static bool cached_supported = true;
/* Obtain the namespace fd from pidfd directly through ioctl(PIDFD_GET_*_NAMESPACE).
*
* Returns -EOPNOTSUPP if ioctl on pidfds are not supported, -ENOPKG if the requested namespace
* is disabled in kernel. (The errno used are different from what kernel returns via ioctl(),
* see below) */
assert(fd >= 0);
/* If we know ahead of time that pidfs is unavailable, shortcut things. But otherwise we don't
* call pidfd_check_pidfs() here, which is kinda extraneous and our own cache is required
* anyways (pidfs is introduced in kernel 6.9 while ioctl support there is added in 6.11). */
if (have_pidfs == 0 || !cached_supported)
return -EOPNOTSUPP;
int nsfd = ioctl(fd, ns_type_cmd);
if (nsfd < 0) {
/* Kernel returns EOPNOTSUPP if the ns type in question is disabled. Hence we need to look
* at precise errno instead of generic ERRNO_IS_(IOCTL_)NOT_SUPPORTED. */
if (IN_SET(errno, ENOTTY, EINVAL)) {
cached_supported = false;
return -EOPNOTSUPP;
}
if (errno == EOPNOTSUPP) /* Translate to something more recognizable */
return -ENOPKG;
return -errno;
}
return nsfd;
}
static int pidfd_get_info(int fd, struct pidfd_info *info) {
static bool cached_supported = true;
assert(fd >= 0);
assert(info);
if (have_pidfs == 0 || !cached_supported)
return -EOPNOTSUPP;
if (ioctl(fd, PIDFD_GET_INFO, info) < 0) {
if (ERRNO_IS_IOCTL_NOT_SUPPORTED(errno)) {
cached_supported = false;
return -EOPNOTSUPP;
}
return -errno;
}
return 0;
}
static int pidfd_get_pid_fdinfo(int fd, pid_t *ret) {
char path[STRLEN("/proc/self/fdinfo/") + DECIMAL_STR_MAX(int)];
_cleanup_free_ char *fdinfo = NULL;
int r;
assert(fd >= 0);
xsprintf(path, "/proc/self/fdinfo/%i", fd);
r = read_full_virtual_file(path, &fdinfo, NULL);
if (r == -ENOENT)
return proc_fd_enoent_errno();
if (r < 0)
return r;
char *p = find_line_startswith(fdinfo, "Pid:");
if (!p)
return -ENOTTY; /* not a pidfd? */
p = skip_leading_chars(p, /* bad = */ NULL);
p[strcspn(p, WHITESPACE)] = 0;
if (streq(p, "0"))
return -EREMOTE; /* PID is in foreign PID namespace? */
if (streq(p, "-1"))
return -ESRCH; /* refers to reaped process? */
return parse_pid(p, ret);
}
static int pidfd_get_pid_ioctl(int fd, pid_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
if (ret)
*ret = info.pid;
return 0;
}
int pidfd_get_pid(int fd, pid_t *ret) {
int r;
/* Converts a pidfd into a pid. We try ioctl(PIDFD_GET_INFO) (kernel 6.13+) first,
* /proc/self/fdinfo/ as fallback. Well known errors:
*
* -EBADF → fd invalid
* -ESRCH → fd valid, but process is already reaped
*
* pidfd_get_pid_fdinfo() might additionally fail for other reasons:
*
* -ENOSYS → /proc/ not mounted
* -ENOTTY → fd valid, but not a pidfd
* -EREMOTE → fd valid, but pid is in another namespace we cannot translate to the local one
* (when using PIDFD_GET_INFO this is indistinguishable from -ESRCH)
*/
assert(fd >= 0);
r = pidfd_get_pid_ioctl(fd, ret);
if (r != -EOPNOTSUPP)
return r;
return pidfd_get_pid_fdinfo(fd, ret);
}
int pidfd_verify_pid(int pidfd, pid_t pid) {
pid_t current_pid;
int r;
assert(pidfd >= 0);
assert(pid > 0);
r = pidfd_get_pid(pidfd, ¤t_pid);
if (r < 0)
return r;
return current_pid != pid ? -ESRCH : 0;
}
int pidfd_get_ppid(int fd, pid_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_PID };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_PID));
if (info.ppid == 0) /* See comments in pid_get_ppid() */
return -EADDRNOTAVAIL;
if (ret)
*ret = info.ppid;
return 0;
}
int pidfd_get_uid(int fd, uid_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_CREDS };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_CREDS));
if (ret)
*ret = info.ruid;
return 0;
}
int pidfd_get_cgroupid(int fd, uint64_t *ret) {
struct pidfd_info info = { .mask = PIDFD_INFO_CGROUPID };
int r;
assert(fd >= 0);
r = pidfd_get_info(fd, &info);
if (r < 0)
return r;
assert(FLAGS_SET(info.mask, PIDFD_INFO_CGROUPID));
if (ret)
*ret = info.cgroupid;
return 0;
}
int pidfd_get_inode_id(int fd, uint64_t *ret) {
int r;
assert(fd >= 0);
r = pidfd_check_pidfs();
if (r < 0)
return r;
if (r == 0)
return -EOPNOTSUPP;
struct stat st;
if (fstat(fd, &st) < 0)
return -errno;
if (ret)
*ret = (uint64_t) st.st_ino;
return 0;
}
|