Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *
0004  * Copyright (C) 2017 Hari Bathini, IBM Corporation
0005  */
0006 
0007 #include "namespaces.h"
0008 #include "event.h"
0009 #include "get_current_dir_name.h"
0010 #include <sys/types.h>
0011 #include <sys/stat.h>
0012 #include <fcntl.h>
0013 #include <limits.h>
0014 #include <sched.h>
0015 #include <stdlib.h>
0016 #include <stdio.h>
0017 #include <string.h>
0018 #include <unistd.h>
0019 #include <asm/bug.h>
0020 #include <linux/kernel.h>
0021 #include <linux/zalloc.h>
0022 
0023 static const char *perf_ns__names[] = {
0024     [NET_NS_INDEX]      = "net",
0025     [UTS_NS_INDEX]      = "uts",
0026     [IPC_NS_INDEX]      = "ipc",
0027     [PID_NS_INDEX]      = "pid",
0028     [USER_NS_INDEX]     = "user",
0029     [MNT_NS_INDEX]      = "mnt",
0030     [CGROUP_NS_INDEX]   = "cgroup",
0031 };
0032 
0033 const char *perf_ns__name(unsigned int id)
0034 {
0035     if (id >= ARRAY_SIZE(perf_ns__names))
0036         return "UNKNOWN";
0037     return perf_ns__names[id];
0038 }
0039 
0040 struct namespaces *namespaces__new(struct perf_record_namespaces *event)
0041 {
0042     struct namespaces *namespaces;
0043     u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) *
0044                   sizeof(struct perf_ns_link_info));
0045 
0046     namespaces = zalloc(sizeof(struct namespaces) + link_info_size);
0047     if (!namespaces)
0048         return NULL;
0049 
0050     namespaces->end_time = -1;
0051 
0052     if (event)
0053         memcpy(namespaces->link_info, event->link_info, link_info_size);
0054 
0055     return namespaces;
0056 }
0057 
0058 void namespaces__free(struct namespaces *namespaces)
0059 {
0060     free(namespaces);
0061 }
0062 
0063 static int nsinfo__get_nspid(struct nsinfo *nsi, const char *path)
0064 {
0065     FILE *f = NULL;
0066     char *statln = NULL;
0067     size_t linesz = 0;
0068     char *nspid;
0069 
0070     f = fopen(path, "r");
0071     if (f == NULL)
0072         return -1;
0073 
0074     while (getline(&statln, &linesz, f) != -1) {
0075         /* Use tgid if CONFIG_PID_NS is not defined. */
0076         if (strstr(statln, "Tgid:") != NULL) {
0077             nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
0078                              NULL, 10);
0079             nsi->nstgid = nsinfo__tgid(nsi);
0080         }
0081 
0082         if (strstr(statln, "NStgid:") != NULL) {
0083             nspid = strrchr(statln, '\t');
0084             nsi->nstgid = (pid_t)strtol(nspid, NULL, 10);
0085             /*
0086              * If innermost tgid is not the first, process is in a different
0087              * PID namespace.
0088              */
0089             nsi->in_pidns = (statln + sizeof("NStgid:") - 1) != nspid;
0090             break;
0091         }
0092     }
0093 
0094     fclose(f);
0095     free(statln);
0096     return 0;
0097 }
0098 
0099 int nsinfo__init(struct nsinfo *nsi)
0100 {
0101     char oldns[PATH_MAX];
0102     char spath[PATH_MAX];
0103     char *newns = NULL;
0104     struct stat old_stat;
0105     struct stat new_stat;
0106     int rv = -1;
0107 
0108     if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
0109         return rv;
0110 
0111     if (asprintf(&newns, "/proc/%d/ns/mnt", nsinfo__pid(nsi)) == -1)
0112         return rv;
0113 
0114     if (stat(oldns, &old_stat) < 0)
0115         goto out;
0116 
0117     if (stat(newns, &new_stat) < 0)
0118         goto out;
0119 
0120     /* Check if the mount namespaces differ, if so then indicate that we
0121      * want to switch as part of looking up dso/map data.
0122      */
0123     if (old_stat.st_ino != new_stat.st_ino) {
0124         nsi->need_setns = true;
0125         nsi->mntns_path = newns;
0126         newns = NULL;
0127     }
0128 
0129     /* If we're dealing with a process that is in a different PID namespace,
0130      * attempt to work out the innermost tgid for the process.
0131      */
0132     if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX)
0133         goto out;
0134 
0135     rv = nsinfo__get_nspid(nsi, spath);
0136 
0137 out:
0138     free(newns);
0139     return rv;
0140 }
0141 
0142 struct nsinfo *nsinfo__new(pid_t pid)
0143 {
0144     struct nsinfo *nsi;
0145 
0146     if (pid == 0)
0147         return NULL;
0148 
0149     nsi = calloc(1, sizeof(*nsi));
0150     if (nsi != NULL) {
0151         nsi->pid = pid;
0152         nsi->tgid = pid;
0153         nsi->nstgid = pid;
0154         nsi->need_setns = false;
0155         nsi->in_pidns = false;
0156         /* Init may fail if the process exits while we're trying to look
0157          * at its proc information.  In that case, save the pid but
0158          * don't try to enter the namespace.
0159          */
0160         if (nsinfo__init(nsi) == -1)
0161             nsi->need_setns = false;
0162 
0163         refcount_set(&nsi->refcnt, 1);
0164     }
0165 
0166     return nsi;
0167 }
0168 
0169 struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
0170 {
0171     struct nsinfo *nnsi;
0172 
0173     if (nsi == NULL)
0174         return NULL;
0175 
0176     nnsi = calloc(1, sizeof(*nnsi));
0177     if (nnsi != NULL) {
0178         nnsi->pid = nsinfo__pid(nsi);
0179         nnsi->tgid = nsinfo__tgid(nsi);
0180         nnsi->nstgid = nsinfo__nstgid(nsi);
0181         nnsi->need_setns = nsinfo__need_setns(nsi);
0182         nnsi->in_pidns = nsinfo__in_pidns(nsi);
0183         if (nsi->mntns_path) {
0184             nnsi->mntns_path = strdup(nsi->mntns_path);
0185             if (!nnsi->mntns_path) {
0186                 free(nnsi);
0187                 return NULL;
0188             }
0189         }
0190         refcount_set(&nnsi->refcnt, 1);
0191     }
0192 
0193     return nnsi;
0194 }
0195 
0196 static void nsinfo__delete(struct nsinfo *nsi)
0197 {
0198     zfree(&nsi->mntns_path);
0199     free(nsi);
0200 }
0201 
0202 struct nsinfo *nsinfo__get(struct nsinfo *nsi)
0203 {
0204     if (nsi)
0205         refcount_inc(&nsi->refcnt);
0206     return nsi;
0207 }
0208 
0209 void nsinfo__put(struct nsinfo *nsi)
0210 {
0211     if (nsi && refcount_dec_and_test(&nsi->refcnt))
0212         nsinfo__delete(nsi);
0213 }
0214 
0215 bool nsinfo__need_setns(const struct nsinfo *nsi)
0216 {
0217         return nsi->need_setns;
0218 }
0219 
0220 void nsinfo__clear_need_setns(struct nsinfo *nsi)
0221 {
0222         nsi->need_setns = false;
0223 }
0224 
0225 pid_t nsinfo__tgid(const struct nsinfo  *nsi)
0226 {
0227         return nsi->tgid;
0228 }
0229 
0230 pid_t nsinfo__nstgid(const struct nsinfo  *nsi)
0231 {
0232         return nsi->nstgid;
0233 }
0234 
0235 pid_t nsinfo__pid(const struct nsinfo  *nsi)
0236 {
0237         return nsi->pid;
0238 }
0239 
0240 pid_t nsinfo__in_pidns(const struct nsinfo  *nsi)
0241 {
0242         return nsi->in_pidns;
0243 }
0244 
0245 void nsinfo__mountns_enter(struct nsinfo *nsi,
0246                   struct nscookie *nc)
0247 {
0248     char curpath[PATH_MAX];
0249     int oldns = -1;
0250     int newns = -1;
0251     char *oldcwd = NULL;
0252 
0253     if (nc == NULL)
0254         return;
0255 
0256     nc->oldns = -1;
0257     nc->newns = -1;
0258 
0259     if (!nsi || !nsi->need_setns)
0260         return;
0261 
0262     if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
0263         return;
0264 
0265     oldcwd = get_current_dir_name();
0266     if (!oldcwd)
0267         return;
0268 
0269     oldns = open(curpath, O_RDONLY);
0270     if (oldns < 0)
0271         goto errout;
0272 
0273     newns = open(nsi->mntns_path, O_RDONLY);
0274     if (newns < 0)
0275         goto errout;
0276 
0277     if (setns(newns, CLONE_NEWNS) < 0)
0278         goto errout;
0279 
0280     nc->oldcwd = oldcwd;
0281     nc->oldns = oldns;
0282     nc->newns = newns;
0283     return;
0284 
0285 errout:
0286     free(oldcwd);
0287     if (oldns > -1)
0288         close(oldns);
0289     if (newns > -1)
0290         close(newns);
0291 }
0292 
0293 void nsinfo__mountns_exit(struct nscookie *nc)
0294 {
0295     if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
0296         return;
0297 
0298     setns(nc->oldns, CLONE_NEWNS);
0299 
0300     if (nc->oldcwd) {
0301         WARN_ON_ONCE(chdir(nc->oldcwd));
0302         zfree(&nc->oldcwd);
0303     }
0304 
0305     if (nc->oldns > -1) {
0306         close(nc->oldns);
0307         nc->oldns = -1;
0308     }
0309 
0310     if (nc->newns > -1) {
0311         close(nc->newns);
0312         nc->newns = -1;
0313     }
0314 }
0315 
0316 char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
0317 {
0318     char *rpath;
0319     struct nscookie nsc;
0320 
0321     nsinfo__mountns_enter(nsi, &nsc);
0322     rpath = realpath(path, NULL);
0323     nsinfo__mountns_exit(&nsc);
0324 
0325     return rpath;
0326 }
0327 
0328 int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi)
0329 {
0330     int ret;
0331     struct nscookie nsc;
0332 
0333     nsinfo__mountns_enter(nsi, &nsc);
0334     ret = stat(filename, st);
0335     nsinfo__mountns_exit(&nsc);
0336 
0337     return ret;
0338 }
0339 
0340 bool nsinfo__is_in_root_namespace(void)
0341 {
0342     struct nsinfo nsi;
0343 
0344     memset(&nsi, 0x0, sizeof(nsi));
0345     nsinfo__get_nspid(&nsi, "/proc/self/status");
0346     return !nsi.in_pidns;
0347 }