1 | // SPDX-License-Identifier: GPL-2.0-only |
---|---|
2 | /* |
3 | * |
4 | * Copyright (C) 2017 Hari Bathini, IBM Corporation |
5 | */ |
6 | |
7 | #include "namespaces.h" |
8 | #include "event.h" |
9 | #include "get_current_dir_name.h" |
10 | #include <sys/types.h> |
11 | #include <sys/stat.h> |
12 | #include <fcntl.h> |
13 | #include <limits.h> |
14 | #include <sched.h> |
15 | #include <stdlib.h> |
16 | #include <stdio.h> |
17 | #include <string.h> |
18 | #include <unistd.h> |
19 | #include <asm/bug.h> |
20 | #include <linux/kernel.h> |
21 | #include <linux/zalloc.h> |
22 | |
23 | static const char *perf_ns__names[] = { |
24 | [NET_NS_INDEX] = "net", |
25 | [UTS_NS_INDEX] = "uts", |
26 | [IPC_NS_INDEX] = "ipc", |
27 | [PID_NS_INDEX] = "pid", |
28 | [USER_NS_INDEX] = "user", |
29 | [MNT_NS_INDEX] = "mnt", |
30 | [CGROUP_NS_INDEX] = "cgroup", |
31 | }; |
32 | |
33 | const char *perf_ns__name(unsigned int id) |
34 | { |
35 | if (id >= ARRAY_SIZE(perf_ns__names)) |
36 | return "UNKNOWN"; |
37 | return perf_ns__names[id]; |
38 | } |
39 | |
40 | struct namespaces *namespaces__new(struct perf_record_namespaces *event) |
41 | { |
42 | struct namespaces *namespaces; |
43 | u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) * |
44 | sizeof(struct perf_ns_link_info)); |
45 | |
46 | namespaces = zalloc(sizeof(struct namespaces) + link_info_size); |
47 | if (!namespaces) |
48 | return NULL; |
49 | |
50 | namespaces->end_time = -1; |
51 | |
52 | if (event) |
53 | memcpy(namespaces->link_info, event->link_info, link_info_size); |
54 | |
55 | return namespaces; |
56 | } |
57 | |
58 | void namespaces__free(struct namespaces *namespaces) |
59 | { |
60 | free(namespaces); |
61 | } |
62 | |
63 | static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const char *path) |
64 | { |
65 | FILE *f = NULL; |
66 | char *statln = NULL; |
67 | size_t linesz = 0; |
68 | char *nspid; |
69 | |
70 | f = fopen(path, "r"); |
71 | if (f == NULL) |
72 | return -1; |
73 | |
74 | while (getline(&statln, &linesz, f) != -1) { |
75 | /* Use tgid if CONFIG_PID_NS is not defined. */ |
76 | if (strstr(statln, "Tgid:") != NULL) { |
77 | *tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10); |
78 | *nstgid = *tgid; |
79 | } |
80 | |
81 | if (strstr(statln, "NStgid:") != NULL) { |
82 | nspid = strrchr(statln, '\t'); |
83 | *nstgid = (pid_t)strtol(nspid, NULL, 10); |
84 | /* |
85 | * If innermost tgid is not the first, process is in a different |
86 | * PID namespace. |
87 | */ |
88 | *in_pidns = (statln + sizeof("NStgid:") - 1) != nspid; |
89 | break; |
90 | } |
91 | } |
92 | |
93 | fclose(f); |
94 | free(statln); |
95 | return 0; |
96 | } |
97 | |
98 | int nsinfo__init(struct nsinfo *nsi) |
99 | { |
100 | char oldns[PATH_MAX]; |
101 | char spath[PATH_MAX]; |
102 | char *newns = NULL; |
103 | struct stat old_stat; |
104 | struct stat new_stat; |
105 | int rv = -1; |
106 | |
107 | if (snprintf(buf: oldns, PATH_MAX, fmt: "/proc/self/ns/mnt") >= PATH_MAX) |
108 | return rv; |
109 | |
110 | if (asprintf(&newns, "/proc/%d/ns/mnt", nsinfo__pid(nsi)) == -1) |
111 | return rv; |
112 | |
113 | if (stat(oldns, &old_stat) < 0) |
114 | goto out; |
115 | |
116 | if (stat(newns, &new_stat) < 0) |
117 | goto out; |
118 | |
119 | /* Check if the mount namespaces differ, if so then indicate that we |
120 | * want to switch as part of looking up dso/map data. |
121 | */ |
122 | if (old_stat.st_ino != new_stat.st_ino) { |
123 | RC_CHK_ACCESS(nsi)->need_setns = true; |
124 | RC_CHK_ACCESS(nsi)->mntns_path = newns; |
125 | newns = NULL; |
126 | } |
127 | |
128 | /* If we're dealing with a process that is in a different PID namespace, |
129 | * attempt to work out the innermost tgid for the process. |
130 | */ |
131 | if (snprintf(buf: spath, PATH_MAX, fmt: "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX) |
132 | goto out; |
133 | |
134 | rv = nsinfo__get_nspid(tgid: &RC_CHK_ACCESS(nsi)->tgid, nstgid: &RC_CHK_ACCESS(nsi)->nstgid, |
135 | in_pidns: &RC_CHK_ACCESS(nsi)->in_pidns, path: spath); |
136 | |
137 | out: |
138 | free(newns); |
139 | return rv; |
140 | } |
141 | |
142 | static struct nsinfo *nsinfo__alloc(void) |
143 | { |
144 | struct nsinfo *res; |
145 | RC_STRUCT(nsinfo) *nsi; |
146 | |
147 | nsi = calloc(1, sizeof(*nsi)); |
148 | if (ADD_RC_CHK(res, nsi)) |
149 | refcount_set(r: &nsi->refcnt, n: 1); |
150 | |
151 | return res; |
152 | } |
153 | |
154 | struct nsinfo *nsinfo__new(pid_t pid) |
155 | { |
156 | struct nsinfo *nsi; |
157 | |
158 | if (pid == 0) |
159 | return NULL; |
160 | |
161 | nsi = nsinfo__alloc(); |
162 | if (!nsi) |
163 | return NULL; |
164 | |
165 | RC_CHK_ACCESS(nsi)->pid = pid; |
166 | RC_CHK_ACCESS(nsi)->tgid = pid; |
167 | RC_CHK_ACCESS(nsi)->nstgid = pid; |
168 | nsinfo__clear_need_setns(nsi); |
169 | RC_CHK_ACCESS(nsi)->in_pidns = false; |
170 | /* Init may fail if the process exits while we're trying to look at its |
171 | * proc information. In that case, save the pid but don't try to enter |
172 | * the namespace. |
173 | */ |
174 | if (nsinfo__init(nsi) == -1) |
175 | nsinfo__clear_need_setns(nsi); |
176 | |
177 | return nsi; |
178 | } |
179 | |
180 | static const char *nsinfo__mntns_path(const struct nsinfo *nsi) |
181 | { |
182 | return RC_CHK_ACCESS(nsi)->mntns_path; |
183 | } |
184 | |
185 | struct nsinfo *nsinfo__copy(const struct nsinfo *nsi) |
186 | { |
187 | struct nsinfo *nnsi; |
188 | |
189 | if (nsi == NULL) |
190 | return NULL; |
191 | |
192 | nnsi = nsinfo__alloc(); |
193 | if (!nnsi) |
194 | return NULL; |
195 | |
196 | RC_CHK_ACCESS(nnsi)->pid = nsinfo__pid(nsi); |
197 | RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi); |
198 | RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi); |
199 | RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi); |
200 | RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi); |
201 | if (nsinfo__mntns_path(nsi)) { |
202 | RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi)); |
203 | if (!RC_CHK_ACCESS(nnsi)->mntns_path) { |
204 | nsinfo__put(nsi: nnsi); |
205 | return NULL; |
206 | } |
207 | } |
208 | |
209 | return nnsi; |
210 | } |
211 | |
212 | static refcount_t *nsinfo__refcnt(struct nsinfo *nsi) |
213 | { |
214 | return &RC_CHK_ACCESS(nsi)->refcnt; |
215 | } |
216 | |
217 | static void nsinfo__delete(struct nsinfo *nsi) |
218 | { |
219 | if (nsi) { |
220 | WARN_ONCE(refcount_read(nsinfo__refcnt(nsi)) != 0, "nsinfo refcnt unbalanced\n"); |
221 | zfree(&RC_CHK_ACCESS(nsi)->mntns_path); |
222 | RC_CHK_FREE(nsi); |
223 | } |
224 | } |
225 | |
226 | struct nsinfo *nsinfo__get(struct nsinfo *nsi) |
227 | { |
228 | struct nsinfo *result; |
229 | |
230 | if (RC_CHK_GET(result, nsi)) |
231 | refcount_inc(r: nsinfo__refcnt(nsi)); |
232 | |
233 | return result; |
234 | } |
235 | |
236 | void nsinfo__put(struct nsinfo *nsi) |
237 | { |
238 | if (nsi && refcount_dec_and_test(r: nsinfo__refcnt(nsi))) |
239 | nsinfo__delete(nsi); |
240 | else |
241 | RC_CHK_PUT(nsi); |
242 | } |
243 | |
244 | bool nsinfo__need_setns(const struct nsinfo *nsi) |
245 | { |
246 | return RC_CHK_ACCESS(nsi)->need_setns; |
247 | } |
248 | |
249 | void nsinfo__clear_need_setns(struct nsinfo *nsi) |
250 | { |
251 | RC_CHK_ACCESS(nsi)->need_setns = false; |
252 | } |
253 | |
254 | pid_t nsinfo__tgid(const struct nsinfo *nsi) |
255 | { |
256 | return RC_CHK_ACCESS(nsi)->tgid; |
257 | } |
258 | |
259 | pid_t nsinfo__nstgid(const struct nsinfo *nsi) |
260 | { |
261 | return RC_CHK_ACCESS(nsi)->nstgid; |
262 | } |
263 | |
264 | pid_t nsinfo__pid(const struct nsinfo *nsi) |
265 | { |
266 | return RC_CHK_ACCESS(nsi)->pid; |
267 | } |
268 | |
269 | bool nsinfo__in_pidns(const struct nsinfo *nsi) |
270 | { |
271 | return RC_CHK_ACCESS(nsi)->in_pidns; |
272 | } |
273 | |
274 | void nsinfo__set_in_pidns(struct nsinfo *nsi) |
275 | { |
276 | RC_CHK_ACCESS(nsi)->in_pidns = true; |
277 | } |
278 | |
279 | void nsinfo__mountns_enter(struct nsinfo *nsi, |
280 | struct nscookie *nc) |
281 | { |
282 | char curpath[PATH_MAX]; |
283 | int oldns = -1; |
284 | int newns = -1; |
285 | char *oldcwd = NULL; |
286 | |
287 | if (nc == NULL) |
288 | return; |
289 | |
290 | nc->oldns = -1; |
291 | nc->newns = -1; |
292 | |
293 | if (!nsi || !nsinfo__need_setns(nsi)) |
294 | return; |
295 | |
296 | if (snprintf(buf: curpath, PATH_MAX, fmt: "/proc/self/ns/mnt") >= PATH_MAX) |
297 | return; |
298 | |
299 | oldcwd = get_current_dir_name(); |
300 | if (!oldcwd) |
301 | return; |
302 | |
303 | oldns = open(curpath, O_RDONLY); |
304 | if (oldns < 0) |
305 | goto errout; |
306 | |
307 | newns = open(nsinfo__mntns_path(nsi), O_RDONLY); |
308 | if (newns < 0) |
309 | goto errout; |
310 | |
311 | if (setns(fd: newns, CLONE_NEWNS) < 0) |
312 | goto errout; |
313 | |
314 | nc->oldcwd = oldcwd; |
315 | nc->oldns = oldns; |
316 | nc->newns = newns; |
317 | return; |
318 | |
319 | errout: |
320 | free(oldcwd); |
321 | if (oldns > -1) |
322 | close(oldns); |
323 | if (newns > -1) |
324 | close(newns); |
325 | } |
326 | |
327 | void nsinfo__mountns_exit(struct nscookie *nc) |
328 | { |
329 | if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd) |
330 | return; |
331 | |
332 | setns(fd: nc->oldns, CLONE_NEWNS); |
333 | |
334 | if (nc->oldcwd) { |
335 | WARN_ON_ONCE(chdir(nc->oldcwd)); |
336 | zfree(&nc->oldcwd); |
337 | } |
338 | |
339 | if (nc->oldns > -1) { |
340 | close(nc->oldns); |
341 | nc->oldns = -1; |
342 | } |
343 | |
344 | if (nc->newns > -1) { |
345 | close(nc->newns); |
346 | nc->newns = -1; |
347 | } |
348 | } |
349 | |
350 | char *nsinfo__realpath(const char *path, struct nsinfo *nsi) |
351 | { |
352 | char *rpath; |
353 | struct nscookie nsc; |
354 | |
355 | nsinfo__mountns_enter(nsi, nc: &nsc); |
356 | rpath = realpath(path, NULL); |
357 | nsinfo__mountns_exit(nc: &nsc); |
358 | |
359 | return rpath; |
360 | } |
361 | |
362 | int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi) |
363 | { |
364 | int ret; |
365 | struct nscookie nsc; |
366 | |
367 | nsinfo__mountns_enter(nsi, nc: &nsc); |
368 | ret = stat(filename, st); |
369 | nsinfo__mountns_exit(nc: &nsc); |
370 | |
371 | return ret; |
372 | } |
373 | |
374 | bool nsinfo__is_in_root_namespace(void) |
375 | { |
376 | pid_t tgid = 0, nstgid = 0; |
377 | bool in_pidns = false; |
378 | |
379 | nsinfo__get_nspid(tgid: &tgid, nstgid: &nstgid, in_pidns: &in_pidns, path: "/proc/self/status"); |
380 | return !in_pidns; |
381 | } |
382 |
Definitions
- perf_ns__names
- perf_ns__name
- namespaces__new
- namespaces__free
- nsinfo__get_nspid
- nsinfo__init
- nsinfo__alloc
- nsinfo__new
- nsinfo__mntns_path
- nsinfo__copy
- nsinfo__refcnt
- nsinfo__delete
- nsinfo__get
- nsinfo__put
- nsinfo__need_setns
- nsinfo__clear_need_setns
- nsinfo__tgid
- nsinfo__nstgid
- nsinfo__pid
- nsinfo__in_pidns
- nsinfo__set_in_pidns
- nsinfo__mountns_enter
- nsinfo__mountns_exit
- nsinfo__realpath
- nsinfo__stat
Improve your Profiling and Debugging skills
Find out more