377 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *4 * Copyright (C) 2017 Hari Bathini, IBM Corporation5 */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 23static 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 33const 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 40struct 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 58void namespaces__free(struct namespaces *namespaces)59{60 free(namespaces);61}62 63static 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 different86 * 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 98int 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(oldns, PATH_MAX, "/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 we120 * 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(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX)132 goto out;133 134 rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,135 &RC_CHK_ACCESS(nsi)->in_pidns, spath);136 137out:138 free(newns);139 return rv;140}141 142static 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(&nsi->refcnt, 1);150 151 return res;152}153 154struct 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 its171 * proc information. In that case, save the pid but don't try to enter172 * the namespace.173 */174 if (nsinfo__init(nsi) == -1)175 nsinfo__clear_need_setns(nsi);176 177 return nsi;178}179 180static const char *nsinfo__mntns_path(const struct nsinfo *nsi)181{182 return RC_CHK_ACCESS(nsi)->mntns_path;183}184 185struct 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(nnsi);205 return NULL;206 }207 }208 209 return nnsi;210}211 212static refcount_t *nsinfo__refcnt(struct nsinfo *nsi)213{214 return &RC_CHK_ACCESS(nsi)->refcnt;215}216 217static 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 226struct nsinfo *nsinfo__get(struct nsinfo *nsi)227{228 struct nsinfo *result;229 230 if (RC_CHK_GET(result, nsi))231 refcount_inc(nsinfo__refcnt(nsi));232 233 return result;234}235 236void nsinfo__put(struct nsinfo *nsi)237{238 if (nsi && refcount_dec_and_test(nsinfo__refcnt(nsi)))239 nsinfo__delete(nsi);240 else241 RC_CHK_PUT(nsi);242}243 244bool nsinfo__need_setns(const struct nsinfo *nsi)245{246 return RC_CHK_ACCESS(nsi)->need_setns;247}248 249void nsinfo__clear_need_setns(struct nsinfo *nsi)250{251 RC_CHK_ACCESS(nsi)->need_setns = false;252}253 254pid_t nsinfo__tgid(const struct nsinfo *nsi)255{256 return RC_CHK_ACCESS(nsi)->tgid;257}258 259pid_t nsinfo__nstgid(const struct nsinfo *nsi)260{261 return RC_CHK_ACCESS(nsi)->nstgid;262}263 264pid_t nsinfo__pid(const struct nsinfo *nsi)265{266 return RC_CHK_ACCESS(nsi)->pid;267}268 269pid_t nsinfo__in_pidns(const struct nsinfo *nsi)270{271 return RC_CHK_ACCESS(nsi)->in_pidns;272}273 274void nsinfo__mountns_enter(struct nsinfo *nsi,275 struct nscookie *nc)276{277 char curpath[PATH_MAX];278 int oldns = -1;279 int newns = -1;280 char *oldcwd = NULL;281 282 if (nc == NULL)283 return;284 285 nc->oldns = -1;286 nc->newns = -1;287 288 if (!nsi || !nsinfo__need_setns(nsi))289 return;290 291 if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)292 return;293 294 oldcwd = get_current_dir_name();295 if (!oldcwd)296 return;297 298 oldns = open(curpath, O_RDONLY);299 if (oldns < 0)300 goto errout;301 302 newns = open(nsinfo__mntns_path(nsi), O_RDONLY);303 if (newns < 0)304 goto errout;305 306 if (setns(newns, CLONE_NEWNS) < 0)307 goto errout;308 309 nc->oldcwd = oldcwd;310 nc->oldns = oldns;311 nc->newns = newns;312 return;313 314errout:315 free(oldcwd);316 if (oldns > -1)317 close(oldns);318 if (newns > -1)319 close(newns);320}321 322void nsinfo__mountns_exit(struct nscookie *nc)323{324 if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)325 return;326 327 setns(nc->oldns, CLONE_NEWNS);328 329 if (nc->oldcwd) {330 WARN_ON_ONCE(chdir(nc->oldcwd));331 zfree(&nc->oldcwd);332 }333 334 if (nc->oldns > -1) {335 close(nc->oldns);336 nc->oldns = -1;337 }338 339 if (nc->newns > -1) {340 close(nc->newns);341 nc->newns = -1;342 }343}344 345char *nsinfo__realpath(const char *path, struct nsinfo *nsi)346{347 char *rpath;348 struct nscookie nsc;349 350 nsinfo__mountns_enter(nsi, &nsc);351 rpath = realpath(path, NULL);352 nsinfo__mountns_exit(&nsc);353 354 return rpath;355}356 357int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi)358{359 int ret;360 struct nscookie nsc;361 362 nsinfo__mountns_enter(nsi, &nsc);363 ret = stat(filename, st);364 nsinfo__mountns_exit(&nsc);365 366 return ret;367}368 369bool nsinfo__is_in_root_namespace(void)370{371 pid_t tgid = 0, nstgid = 0;372 bool in_pidns = false;373 374 nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, "/proc/self/status");375 return !in_pidns;376}377