162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2017 Hari Bathini, IBM Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include "namespaces.h" 862306a36Sopenharmony_ci#include "event.h" 962306a36Sopenharmony_ci#include "get_current_dir_name.h" 1062306a36Sopenharmony_ci#include <sys/types.h> 1162306a36Sopenharmony_ci#include <sys/stat.h> 1262306a36Sopenharmony_ci#include <fcntl.h> 1362306a36Sopenharmony_ci#include <limits.h> 1462306a36Sopenharmony_ci#include <sched.h> 1562306a36Sopenharmony_ci#include <stdlib.h> 1662306a36Sopenharmony_ci#include <stdio.h> 1762306a36Sopenharmony_ci#include <string.h> 1862306a36Sopenharmony_ci#include <unistd.h> 1962306a36Sopenharmony_ci#include <asm/bug.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/zalloc.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_cistatic const char *perf_ns__names[] = { 2462306a36Sopenharmony_ci [NET_NS_INDEX] = "net", 2562306a36Sopenharmony_ci [UTS_NS_INDEX] = "uts", 2662306a36Sopenharmony_ci [IPC_NS_INDEX] = "ipc", 2762306a36Sopenharmony_ci [PID_NS_INDEX] = "pid", 2862306a36Sopenharmony_ci [USER_NS_INDEX] = "user", 2962306a36Sopenharmony_ci [MNT_NS_INDEX] = "mnt", 3062306a36Sopenharmony_ci [CGROUP_NS_INDEX] = "cgroup", 3162306a36Sopenharmony_ci}; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ciconst char *perf_ns__name(unsigned int id) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci if (id >= ARRAY_SIZE(perf_ns__names)) 3662306a36Sopenharmony_ci return "UNKNOWN"; 3762306a36Sopenharmony_ci return perf_ns__names[id]; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistruct namespaces *namespaces__new(struct perf_record_namespaces *event) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct namespaces *namespaces; 4362306a36Sopenharmony_ci u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) * 4462306a36Sopenharmony_ci sizeof(struct perf_ns_link_info)); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci namespaces = zalloc(sizeof(struct namespaces) + link_info_size); 4762306a36Sopenharmony_ci if (!namespaces) 4862306a36Sopenharmony_ci return NULL; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci namespaces->end_time = -1; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (event) 5362306a36Sopenharmony_ci memcpy(namespaces->link_info, event->link_info, link_info_size); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci return namespaces; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_civoid namespaces__free(struct namespaces *namespaces) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci free(namespaces); 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const char *path) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci FILE *f = NULL; 6662306a36Sopenharmony_ci char *statln = NULL; 6762306a36Sopenharmony_ci size_t linesz = 0; 6862306a36Sopenharmony_ci char *nspid; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci f = fopen(path, "r"); 7162306a36Sopenharmony_ci if (f == NULL) 7262306a36Sopenharmony_ci return -1; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci while (getline(&statln, &linesz, f) != -1) { 7562306a36Sopenharmony_ci /* Use tgid if CONFIG_PID_NS is not defined. */ 7662306a36Sopenharmony_ci if (strstr(statln, "Tgid:") != NULL) { 7762306a36Sopenharmony_ci *tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10); 7862306a36Sopenharmony_ci *nstgid = *tgid; 7962306a36Sopenharmony_ci } 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (strstr(statln, "NStgid:") != NULL) { 8262306a36Sopenharmony_ci nspid = strrchr(statln, '\t'); 8362306a36Sopenharmony_ci *nstgid = (pid_t)strtol(nspid, NULL, 10); 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * If innermost tgid is not the first, process is in a different 8662306a36Sopenharmony_ci * PID namespace. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci *in_pidns = (statln + sizeof("NStgid:") - 1) != nspid; 8962306a36Sopenharmony_ci break; 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci fclose(f); 9462306a36Sopenharmony_ci free(statln); 9562306a36Sopenharmony_ci return 0; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciint nsinfo__init(struct nsinfo *nsi) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci char oldns[PATH_MAX]; 10162306a36Sopenharmony_ci char spath[PATH_MAX]; 10262306a36Sopenharmony_ci char *newns = NULL; 10362306a36Sopenharmony_ci struct stat old_stat; 10462306a36Sopenharmony_ci struct stat new_stat; 10562306a36Sopenharmony_ci int rv = -1; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) 10862306a36Sopenharmony_ci return rv; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (asprintf(&newns, "/proc/%d/ns/mnt", nsinfo__pid(nsi)) == -1) 11162306a36Sopenharmony_ci return rv; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (stat(oldns, &old_stat) < 0) 11462306a36Sopenharmony_ci goto out; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci if (stat(newns, &new_stat) < 0) 11762306a36Sopenharmony_ci goto out; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci /* Check if the mount namespaces differ, if so then indicate that we 12062306a36Sopenharmony_ci * want to switch as part of looking up dso/map data. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci if (old_stat.st_ino != new_stat.st_ino) { 12362306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->need_setns = true; 12462306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->mntns_path = newns; 12562306a36Sopenharmony_ci newns = NULL; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* If we're dealing with a process that is in a different PID namespace, 12962306a36Sopenharmony_ci * attempt to work out the innermost tgid for the process. 13062306a36Sopenharmony_ci */ 13162306a36Sopenharmony_ci if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX) 13262306a36Sopenharmony_ci goto out; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid, 13562306a36Sopenharmony_ci &RC_CHK_ACCESS(nsi)->in_pidns, spath); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ciout: 13862306a36Sopenharmony_ci free(newns); 13962306a36Sopenharmony_ci return rv; 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_cistatic struct nsinfo *nsinfo__alloc(void) 14362306a36Sopenharmony_ci{ 14462306a36Sopenharmony_ci struct nsinfo *res; 14562306a36Sopenharmony_ci RC_STRUCT(nsinfo) *nsi; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci nsi = calloc(1, sizeof(*nsi)); 14862306a36Sopenharmony_ci if (ADD_RC_CHK(res, nsi)) 14962306a36Sopenharmony_ci refcount_set(&nsi->refcnt, 1); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci return res; 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistruct nsinfo *nsinfo__new(pid_t pid) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct nsinfo *nsi; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (pid == 0) 15962306a36Sopenharmony_ci return NULL; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci nsi = nsinfo__alloc(); 16262306a36Sopenharmony_ci if (!nsi) 16362306a36Sopenharmony_ci return NULL; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->pid = pid; 16662306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->tgid = pid; 16762306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->nstgid = pid; 16862306a36Sopenharmony_ci nsinfo__clear_need_setns(nsi); 16962306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->in_pidns = false; 17062306a36Sopenharmony_ci /* Init may fail if the process exits while we're trying to look at its 17162306a36Sopenharmony_ci * proc information. In that case, save the pid but don't try to enter 17262306a36Sopenharmony_ci * the namespace. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci if (nsinfo__init(nsi) == -1) 17562306a36Sopenharmony_ci nsinfo__clear_need_setns(nsi); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return nsi; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic const char *nsinfo__mntns_path(const struct nsinfo *nsi) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci return RC_CHK_ACCESS(nsi)->mntns_path; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistruct nsinfo *nsinfo__copy(const struct nsinfo *nsi) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci struct nsinfo *nnsi; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci if (nsi == NULL) 19062306a36Sopenharmony_ci return NULL; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci nnsi = nsinfo__alloc(); 19362306a36Sopenharmony_ci if (!nnsi) 19462306a36Sopenharmony_ci return NULL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci RC_CHK_ACCESS(nnsi)->pid = nsinfo__pid(nsi); 19762306a36Sopenharmony_ci RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi); 19862306a36Sopenharmony_ci RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi); 19962306a36Sopenharmony_ci RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi); 20062306a36Sopenharmony_ci RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi); 20162306a36Sopenharmony_ci if (nsinfo__mntns_path(nsi)) { 20262306a36Sopenharmony_ci RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi)); 20362306a36Sopenharmony_ci if (!RC_CHK_ACCESS(nnsi)->mntns_path) { 20462306a36Sopenharmony_ci nsinfo__put(nnsi); 20562306a36Sopenharmony_ci return NULL; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return nnsi; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic refcount_t *nsinfo__refcnt(struct nsinfo *nsi) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci return &RC_CHK_ACCESS(nsi)->refcnt; 21562306a36Sopenharmony_ci} 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_cistatic void nsinfo__delete(struct nsinfo *nsi) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci if (nsi) { 22062306a36Sopenharmony_ci WARN_ONCE(refcount_read(nsinfo__refcnt(nsi)) != 0, "nsinfo refcnt unbalanced\n"); 22162306a36Sopenharmony_ci zfree(&RC_CHK_ACCESS(nsi)->mntns_path); 22262306a36Sopenharmony_ci RC_CHK_FREE(nsi); 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistruct nsinfo *nsinfo__get(struct nsinfo *nsi) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct nsinfo *result; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (RC_CHK_GET(result, nsi)) 23162306a36Sopenharmony_ci refcount_inc(nsinfo__refcnt(nsi)); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return result; 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_civoid nsinfo__put(struct nsinfo *nsi) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci if (nsi && refcount_dec_and_test(nsinfo__refcnt(nsi))) 23962306a36Sopenharmony_ci nsinfo__delete(nsi); 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci RC_CHK_PUT(nsi); 24262306a36Sopenharmony_ci} 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cibool nsinfo__need_setns(const struct nsinfo *nsi) 24562306a36Sopenharmony_ci{ 24662306a36Sopenharmony_ci return RC_CHK_ACCESS(nsi)->need_setns; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_civoid nsinfo__clear_need_setns(struct nsinfo *nsi) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci RC_CHK_ACCESS(nsi)->need_setns = false; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cipid_t nsinfo__tgid(const struct nsinfo *nsi) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci return RC_CHK_ACCESS(nsi)->tgid; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cipid_t nsinfo__nstgid(const struct nsinfo *nsi) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci return RC_CHK_ACCESS(nsi)->nstgid; 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_cipid_t nsinfo__pid(const struct nsinfo *nsi) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return RC_CHK_ACCESS(nsi)->pid; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cipid_t nsinfo__in_pidns(const struct nsinfo *nsi) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci return RC_CHK_ACCESS(nsi)->in_pidns; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_civoid nsinfo__mountns_enter(struct nsinfo *nsi, 27562306a36Sopenharmony_ci struct nscookie *nc) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci char curpath[PATH_MAX]; 27862306a36Sopenharmony_ci int oldns = -1; 27962306a36Sopenharmony_ci int newns = -1; 28062306a36Sopenharmony_ci char *oldcwd = NULL; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci if (nc == NULL) 28362306a36Sopenharmony_ci return; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci nc->oldns = -1; 28662306a36Sopenharmony_ci nc->newns = -1; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (!nsi || !nsinfo__need_setns(nsi)) 28962306a36Sopenharmony_ci return; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) 29262306a36Sopenharmony_ci return; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci oldcwd = get_current_dir_name(); 29562306a36Sopenharmony_ci if (!oldcwd) 29662306a36Sopenharmony_ci return; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci oldns = open(curpath, O_RDONLY); 29962306a36Sopenharmony_ci if (oldns < 0) 30062306a36Sopenharmony_ci goto errout; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci newns = open(nsinfo__mntns_path(nsi), O_RDONLY); 30362306a36Sopenharmony_ci if (newns < 0) 30462306a36Sopenharmony_ci goto errout; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci if (setns(newns, CLONE_NEWNS) < 0) 30762306a36Sopenharmony_ci goto errout; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci nc->oldcwd = oldcwd; 31062306a36Sopenharmony_ci nc->oldns = oldns; 31162306a36Sopenharmony_ci nc->newns = newns; 31262306a36Sopenharmony_ci return; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cierrout: 31562306a36Sopenharmony_ci free(oldcwd); 31662306a36Sopenharmony_ci if (oldns > -1) 31762306a36Sopenharmony_ci close(oldns); 31862306a36Sopenharmony_ci if (newns > -1) 31962306a36Sopenharmony_ci close(newns); 32062306a36Sopenharmony_ci} 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_civoid nsinfo__mountns_exit(struct nscookie *nc) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd) 32562306a36Sopenharmony_ci return; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci setns(nc->oldns, CLONE_NEWNS); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci if (nc->oldcwd) { 33062306a36Sopenharmony_ci WARN_ON_ONCE(chdir(nc->oldcwd)); 33162306a36Sopenharmony_ci zfree(&nc->oldcwd); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (nc->oldns > -1) { 33562306a36Sopenharmony_ci close(nc->oldns); 33662306a36Sopenharmony_ci nc->oldns = -1; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (nc->newns > -1) { 34062306a36Sopenharmony_ci close(nc->newns); 34162306a36Sopenharmony_ci nc->newns = -1; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cichar *nsinfo__realpath(const char *path, struct nsinfo *nsi) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci char *rpath; 34862306a36Sopenharmony_ci struct nscookie nsc; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 35162306a36Sopenharmony_ci rpath = realpath(path, NULL); 35262306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci return rpath; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ciint nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci int ret; 36062306a36Sopenharmony_ci struct nscookie nsc; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 36362306a36Sopenharmony_ci ret = stat(filename, st); 36462306a36Sopenharmony_ci nsinfo__mountns_exit(&nsc); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci return ret; 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cibool nsinfo__is_in_root_namespace(void) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci pid_t tgid = 0, nstgid = 0; 37262306a36Sopenharmony_ci bool in_pidns = false; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, "/proc/self/status"); 37562306a36Sopenharmony_ci return !in_pidns; 37662306a36Sopenharmony_ci} 377