1 // SPDX-License-Identifier: GPL-2.0-only
4 * Copyright (C) 2017 Hari Bathini, IBM Corporation
7 #include "namespaces.h"
9 #include "get_current_dir_name.h"
10 #include <sys/types.h>
20 #include <linux/kernel.h>
21 #include <linux/zalloc.h>
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",
33 const char *perf_ns__name(unsigned int id
)
35 if (id
>= ARRAY_SIZE(perf_ns__names
))
37 return perf_ns__names
[id
];
40 struct namespaces
*namespaces__new(struct perf_record_namespaces
*event
)
42 struct namespaces
*namespaces
;
43 u64 link_info_size
= ((event
? event
->nr_namespaces
: NR_NAMESPACES
) *
44 sizeof(struct perf_ns_link_info
));
46 namespaces
= zalloc(sizeof(struct namespaces
) + link_info_size
);
50 namespaces
->end_time
= -1;
53 memcpy(namespaces
->link_info
, event
->link_info
, link_info_size
);
58 void namespaces__free(struct namespaces
*namespaces
)
63 static int nsinfo__get_nspid(pid_t
*tgid
, pid_t
*nstgid
, bool *in_pidns
, const char *path
)
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);
81 if (strstr(statln
, "NStgid:") != NULL
) {
82 nspid
= strrchr(statln
, '\t');
83 *nstgid
= (pid_t
)strtol(nspid
, NULL
, 10);
85 * If innermost tgid is not the first, process is in a different
88 *in_pidns
= (statln
+ sizeof("NStgid:") - 1) != nspid
;
98 int nsinfo__init(struct nsinfo
*nsi
)
100 char oldns
[PATH_MAX
];
101 char spath
[PATH_MAX
];
103 struct stat old_stat
;
104 struct stat new_stat
;
107 if (snprintf(oldns
, PATH_MAX
, "/proc/self/ns/mnt") >= PATH_MAX
)
110 if (asprintf(&newns
, "/proc/%d/ns/mnt", nsinfo__pid(nsi
)) == -1)
113 if (stat(oldns
, &old_stat
) < 0)
116 if (stat(newns
, &new_stat
) < 0)
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.
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
;
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.
131 if (snprintf(spath
, PATH_MAX
, "/proc/%d/status", nsinfo__pid(nsi
)) >= PATH_MAX
)
134 rv
= nsinfo__get_nspid(&RC_CHK_ACCESS(nsi
)->tgid
, &RC_CHK_ACCESS(nsi
)->nstgid
,
135 &RC_CHK_ACCESS(nsi
)->in_pidns
, spath
);
142 static struct nsinfo
*nsinfo__alloc(void)
145 RC_STRUCT(nsinfo
) *nsi
;
147 nsi
= calloc(1, sizeof(*nsi
));
148 if (ADD_RC_CHK(res
, nsi
))
149 refcount_set(&nsi
->refcnt
, 1);
154 struct nsinfo
*nsinfo__new(pid_t pid
)
161 nsi
= nsinfo__alloc();
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
174 if (nsinfo__init(nsi
) == -1)
175 nsinfo__clear_need_setns(nsi
);
180 static const char *nsinfo__mntns_path(const struct nsinfo
*nsi
)
182 return RC_CHK_ACCESS(nsi
)->mntns_path
;
185 struct nsinfo
*nsinfo__copy(const struct nsinfo
*nsi
)
192 nnsi
= nsinfo__alloc();
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
) {
212 static refcount_t
*nsinfo__refcnt(struct nsinfo
*nsi
)
214 return &RC_CHK_ACCESS(nsi
)->refcnt
;
217 static void nsinfo__delete(struct nsinfo
*nsi
)
220 WARN_ONCE(refcount_read(nsinfo__refcnt(nsi
)) != 0, "nsinfo refcnt unbalanced\n");
221 zfree(&RC_CHK_ACCESS(nsi
)->mntns_path
);
226 struct nsinfo
*nsinfo__get(struct nsinfo
*nsi
)
228 struct nsinfo
*result
;
230 if (RC_CHK_GET(result
, nsi
))
231 refcount_inc(nsinfo__refcnt(nsi
));
236 void nsinfo__put(struct nsinfo
*nsi
)
238 if (nsi
&& refcount_dec_and_test(nsinfo__refcnt(nsi
)))
244 bool nsinfo__need_setns(const struct nsinfo
*nsi
)
246 return RC_CHK_ACCESS(nsi
)->need_setns
;
249 void nsinfo__clear_need_setns(struct nsinfo
*nsi
)
251 RC_CHK_ACCESS(nsi
)->need_setns
= false;
254 pid_t
nsinfo__tgid(const struct nsinfo
*nsi
)
256 return RC_CHK_ACCESS(nsi
)->tgid
;
259 pid_t
nsinfo__nstgid(const struct nsinfo
*nsi
)
261 return RC_CHK_ACCESS(nsi
)->nstgid
;
264 pid_t
nsinfo__pid(const struct nsinfo
*nsi
)
266 return RC_CHK_ACCESS(nsi
)->pid
;
269 pid_t
nsinfo__in_pidns(const struct nsinfo
*nsi
)
271 return RC_CHK_ACCESS(nsi
)->in_pidns
;
274 void nsinfo__mountns_enter(struct nsinfo
*nsi
,
277 char curpath
[PATH_MAX
];
288 if (!nsi
|| !nsinfo__need_setns(nsi
))
291 if (snprintf(curpath
, PATH_MAX
, "/proc/self/ns/mnt") >= PATH_MAX
)
294 oldcwd
= get_current_dir_name();
298 oldns
= open(curpath
, O_RDONLY
);
302 newns
= open(nsinfo__mntns_path(nsi
), O_RDONLY
);
306 if (setns(newns
, CLONE_NEWNS
) < 0)
322 void nsinfo__mountns_exit(struct nscookie
*nc
)
324 if (nc
== NULL
|| nc
->oldns
== -1 || nc
->newns
== -1 || !nc
->oldcwd
)
327 setns(nc
->oldns
, CLONE_NEWNS
);
330 WARN_ON_ONCE(chdir(nc
->oldcwd
));
334 if (nc
->oldns
> -1) {
339 if (nc
->newns
> -1) {
345 char *nsinfo__realpath(const char *path
, struct nsinfo
*nsi
)
350 nsinfo__mountns_enter(nsi
, &nsc
);
351 rpath
= realpath(path
, NULL
);
352 nsinfo__mountns_exit(&nsc
);
357 int nsinfo__stat(const char *filename
, struct stat
*st
, struct nsinfo
*nsi
)
362 nsinfo__mountns_enter(nsi
, &nsc
);
363 ret
= stat(filename
, st
);
364 nsinfo__mountns_exit(&nsc
);
369 bool nsinfo__is_in_root_namespace(void)
371 pid_t tgid
= 0, nstgid
= 0;
372 bool in_pidns
= false;
374 nsinfo__get_nspid(&tgid
, &nstgid
, &in_pidns
, "/proc/self/status");