18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Hari Bathini, IBM Corporation 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include "namespaces.h" 88c2ecf20Sopenharmony_ci#include "event.h" 98c2ecf20Sopenharmony_ci#include "get_current_dir_name.h" 108c2ecf20Sopenharmony_ci#include <sys/types.h> 118c2ecf20Sopenharmony_ci#include <sys/stat.h> 128c2ecf20Sopenharmony_ci#include <fcntl.h> 138c2ecf20Sopenharmony_ci#include <limits.h> 148c2ecf20Sopenharmony_ci#include <sched.h> 158c2ecf20Sopenharmony_ci#include <stdlib.h> 168c2ecf20Sopenharmony_ci#include <stdio.h> 178c2ecf20Sopenharmony_ci#include <string.h> 188c2ecf20Sopenharmony_ci#include <unistd.h> 198c2ecf20Sopenharmony_ci#include <asm/bug.h> 208c2ecf20Sopenharmony_ci#include <linux/kernel.h> 218c2ecf20Sopenharmony_ci#include <linux/zalloc.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic const char *perf_ns__names[] = { 248c2ecf20Sopenharmony_ci [NET_NS_INDEX] = "net", 258c2ecf20Sopenharmony_ci [UTS_NS_INDEX] = "uts", 268c2ecf20Sopenharmony_ci [IPC_NS_INDEX] = "ipc", 278c2ecf20Sopenharmony_ci [PID_NS_INDEX] = "pid", 288c2ecf20Sopenharmony_ci [USER_NS_INDEX] = "user", 298c2ecf20Sopenharmony_ci [MNT_NS_INDEX] = "mnt", 308c2ecf20Sopenharmony_ci [CGROUP_NS_INDEX] = "cgroup", 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ciconst char *perf_ns__name(unsigned int id) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci if (id >= ARRAY_SIZE(perf_ns__names)) 368c2ecf20Sopenharmony_ci return "UNKNOWN"; 378c2ecf20Sopenharmony_ci return perf_ns__names[id]; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistruct namespaces *namespaces__new(struct perf_record_namespaces *event) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct namespaces *namespaces; 438c2ecf20Sopenharmony_ci u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) * 448c2ecf20Sopenharmony_ci sizeof(struct perf_ns_link_info)); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci namespaces = zalloc(sizeof(struct namespaces) + link_info_size); 478c2ecf20Sopenharmony_ci if (!namespaces) 488c2ecf20Sopenharmony_ci return NULL; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci namespaces->end_time = -1; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (event) 538c2ecf20Sopenharmony_ci memcpy(namespaces->link_info, event->link_info, link_info_size); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci return namespaces; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_civoid namespaces__free(struct namespaces *namespaces) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci free(namespaces); 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ciint nsinfo__init(struct nsinfo *nsi) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci char oldns[PATH_MAX]; 668c2ecf20Sopenharmony_ci char spath[PATH_MAX]; 678c2ecf20Sopenharmony_ci char *newns = NULL; 688c2ecf20Sopenharmony_ci char *statln = NULL; 698c2ecf20Sopenharmony_ci struct stat old_stat; 708c2ecf20Sopenharmony_ci struct stat new_stat; 718c2ecf20Sopenharmony_ci FILE *f = NULL; 728c2ecf20Sopenharmony_ci size_t linesz = 0; 738c2ecf20Sopenharmony_ci int rv = -1; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) 768c2ecf20Sopenharmony_ci return rv; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1) 798c2ecf20Sopenharmony_ci return rv; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (stat(oldns, &old_stat) < 0) 828c2ecf20Sopenharmony_ci goto out; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (stat(newns, &new_stat) < 0) 858c2ecf20Sopenharmony_ci goto out; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci /* Check if the mount namespaces differ, if so then indicate that we 888c2ecf20Sopenharmony_ci * want to switch as part of looking up dso/map data. 898c2ecf20Sopenharmony_ci */ 908c2ecf20Sopenharmony_ci if (old_stat.st_ino != new_stat.st_ino) { 918c2ecf20Sopenharmony_ci nsi->need_setns = true; 928c2ecf20Sopenharmony_ci nsi->mntns_path = newns; 938c2ecf20Sopenharmony_ci newns = NULL; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* If we're dealing with a process that is in a different PID namespace, 978c2ecf20Sopenharmony_ci * attempt to work out the innermost tgid for the process. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ci if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX) 1008c2ecf20Sopenharmony_ci goto out; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci f = fopen(spath, "r"); 1038c2ecf20Sopenharmony_ci if (f == NULL) 1048c2ecf20Sopenharmony_ci goto out; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci while (getline(&statln, &linesz, f) != -1) { 1078c2ecf20Sopenharmony_ci /* Use tgid if CONFIG_PID_NS is not defined. */ 1088c2ecf20Sopenharmony_ci if (strstr(statln, "Tgid:") != NULL) { 1098c2ecf20Sopenharmony_ci nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'), 1108c2ecf20Sopenharmony_ci NULL, 10); 1118c2ecf20Sopenharmony_ci nsi->nstgid = nsi->tgid; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci if (strstr(statln, "NStgid:") != NULL) { 1158c2ecf20Sopenharmony_ci nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'), 1168c2ecf20Sopenharmony_ci NULL, 10); 1178c2ecf20Sopenharmony_ci break; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci rv = 0; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ciout: 1238c2ecf20Sopenharmony_ci if (f != NULL) 1248c2ecf20Sopenharmony_ci (void) fclose(f); 1258c2ecf20Sopenharmony_ci free(statln); 1268c2ecf20Sopenharmony_ci free(newns); 1278c2ecf20Sopenharmony_ci return rv; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistruct nsinfo *nsinfo__new(pid_t pid) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct nsinfo *nsi; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (pid == 0) 1358c2ecf20Sopenharmony_ci return NULL; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci nsi = calloc(1, sizeof(*nsi)); 1388c2ecf20Sopenharmony_ci if (nsi != NULL) { 1398c2ecf20Sopenharmony_ci nsi->pid = pid; 1408c2ecf20Sopenharmony_ci nsi->tgid = pid; 1418c2ecf20Sopenharmony_ci nsi->nstgid = pid; 1428c2ecf20Sopenharmony_ci nsi->need_setns = false; 1438c2ecf20Sopenharmony_ci /* Init may fail if the process exits while we're trying to look 1448c2ecf20Sopenharmony_ci * at its proc information. In that case, save the pid but 1458c2ecf20Sopenharmony_ci * don't try to enter the namespace. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_ci if (nsinfo__init(nsi) == -1) 1488c2ecf20Sopenharmony_ci nsi->need_setns = false; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci refcount_set(&nsi->refcnt, 1); 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return nsi; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistruct nsinfo *nsinfo__copy(struct nsinfo *nsi) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct nsinfo *nnsi; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (nsi == NULL) 1618c2ecf20Sopenharmony_ci return NULL; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci nnsi = calloc(1, sizeof(*nnsi)); 1648c2ecf20Sopenharmony_ci if (nnsi != NULL) { 1658c2ecf20Sopenharmony_ci nnsi->pid = nsi->pid; 1668c2ecf20Sopenharmony_ci nnsi->tgid = nsi->tgid; 1678c2ecf20Sopenharmony_ci nnsi->nstgid = nsi->nstgid; 1688c2ecf20Sopenharmony_ci nnsi->need_setns = nsi->need_setns; 1698c2ecf20Sopenharmony_ci if (nsi->mntns_path) { 1708c2ecf20Sopenharmony_ci nnsi->mntns_path = strdup(nsi->mntns_path); 1718c2ecf20Sopenharmony_ci if (!nnsi->mntns_path) { 1728c2ecf20Sopenharmony_ci free(nnsi); 1738c2ecf20Sopenharmony_ci return NULL; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci refcount_set(&nnsi->refcnt, 1); 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci return nnsi; 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_civoid nsinfo__delete(struct nsinfo *nsi) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci zfree(&nsi->mntns_path); 1858c2ecf20Sopenharmony_ci free(nsi); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistruct nsinfo *nsinfo__get(struct nsinfo *nsi) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci if (nsi) 1918c2ecf20Sopenharmony_ci refcount_inc(&nsi->refcnt); 1928c2ecf20Sopenharmony_ci return nsi; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_civoid nsinfo__put(struct nsinfo *nsi) 1968c2ecf20Sopenharmony_ci{ 1978c2ecf20Sopenharmony_ci if (nsi && refcount_dec_and_test(&nsi->refcnt)) 1988c2ecf20Sopenharmony_ci nsinfo__delete(nsi); 1998c2ecf20Sopenharmony_ci} 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_civoid nsinfo__mountns_enter(struct nsinfo *nsi, 2028c2ecf20Sopenharmony_ci struct nscookie *nc) 2038c2ecf20Sopenharmony_ci{ 2048c2ecf20Sopenharmony_ci char curpath[PATH_MAX]; 2058c2ecf20Sopenharmony_ci int oldns = -1; 2068c2ecf20Sopenharmony_ci int newns = -1; 2078c2ecf20Sopenharmony_ci char *oldcwd = NULL; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci if (nc == NULL) 2108c2ecf20Sopenharmony_ci return; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci nc->oldns = -1; 2138c2ecf20Sopenharmony_ci nc->newns = -1; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (!nsi || !nsi->need_setns) 2168c2ecf20Sopenharmony_ci return; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) 2198c2ecf20Sopenharmony_ci return; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci oldcwd = get_current_dir_name(); 2228c2ecf20Sopenharmony_ci if (!oldcwd) 2238c2ecf20Sopenharmony_ci return; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci oldns = open(curpath, O_RDONLY); 2268c2ecf20Sopenharmony_ci if (oldns < 0) 2278c2ecf20Sopenharmony_ci goto errout; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci newns = open(nsi->mntns_path, O_RDONLY); 2308c2ecf20Sopenharmony_ci if (newns < 0) 2318c2ecf20Sopenharmony_ci goto errout; 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci if (setns(newns, CLONE_NEWNS) < 0) 2348c2ecf20Sopenharmony_ci goto errout; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci nc->oldcwd = oldcwd; 2378c2ecf20Sopenharmony_ci nc->oldns = oldns; 2388c2ecf20Sopenharmony_ci nc->newns = newns; 2398c2ecf20Sopenharmony_ci return; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_cierrout: 2428c2ecf20Sopenharmony_ci free(oldcwd); 2438c2ecf20Sopenharmony_ci if (oldns > -1) 2448c2ecf20Sopenharmony_ci close(oldns); 2458c2ecf20Sopenharmony_ci if (newns > -1) 2468c2ecf20Sopenharmony_ci close(newns); 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_civoid nsinfo__mountns_exit(struct nscookie *nc) 2508c2ecf20Sopenharmony_ci{ 2518c2ecf20Sopenharmony_ci if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd) 2528c2ecf20Sopenharmony_ci return; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci setns(nc->oldns, CLONE_NEWNS); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci if (nc->oldcwd) { 2578c2ecf20Sopenharmony_ci WARN_ON_ONCE(chdir(nc->oldcwd)); 2588c2ecf20Sopenharmony_ci zfree(&nc->oldcwd); 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (nc->oldns > -1) { 2628c2ecf20Sopenharmony_ci close(nc->oldns); 2638c2ecf20Sopenharmony_ci nc->oldns = -1; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (nc->newns > -1) { 2678c2ecf20Sopenharmony_ci close(nc->newns); 2688c2ecf20Sopenharmony_ci nc->newns = -1; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cichar *nsinfo__realpath(const char *path, struct nsinfo *nsi) 2738c2ecf20Sopenharmony_ci{ 2748c2ecf20Sopenharmony_ci char *rpath; 2758c2ecf20Sopenharmony_ci struct nscookie nsc; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci nsinfo__mountns_enter(nsi, &nsc); 2788c2ecf20Sopenharmony_ci rpath = realpath(path, NULL); 2798c2ecf20Sopenharmony_ci nsinfo__mountns_exit(&nsc); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci return rpath; 2828c2ecf20Sopenharmony_ci} 283