1 /* 2 * This program is free software; you can redistribute it and/or modify 3 * it under the terms of the GNU General Public License, version 2, as 4 * published by the Free Software Foundation. 5 * 6 * Copyright (C) 2017 Hari Bathini, IBM Corporation 7 */ 8 9 #include "namespaces.h" 10 #include "util.h" 11 #include "event.h" 12 #include <sys/types.h> 13 #include <sys/stat.h> 14 #include <sched.h> 15 #include <stdlib.h> 16 #include <stdio.h> 17 #include <string.h> 18 #include <unistd.h> 19 20 struct namespaces *namespaces__new(struct namespaces_event *event) 21 { 22 struct namespaces *namespaces; 23 u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) * 24 sizeof(struct perf_ns_link_info)); 25 26 namespaces = zalloc(sizeof(struct namespaces) + link_info_size); 27 if (!namespaces) 28 return NULL; 29 30 namespaces->end_time = -1; 31 32 if (event) 33 memcpy(namespaces->link_info, event->link_info, link_info_size); 34 35 return namespaces; 36 } 37 38 void namespaces__free(struct namespaces *namespaces) 39 { 40 free(namespaces); 41 } 42 43 int nsinfo__init(struct nsinfo *nsi) 44 { 45 char oldns[PATH_MAX]; 46 char spath[PATH_MAX]; 47 char *newns = NULL; 48 char *statln = NULL; 49 struct stat old_stat; 50 struct stat new_stat; 51 FILE *f = NULL; 52 size_t linesz = 0; 53 int rv = -1; 54 55 if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) 56 return rv; 57 58 if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1) 59 return rv; 60 61 if (stat(oldns, &old_stat) < 0) 62 goto out; 63 64 if (stat(newns, &new_stat) < 0) 65 goto out; 66 67 /* Check if the mount namespaces differ, if so then indicate that we 68 * want to switch as part of looking up dso/map data. 69 */ 70 if (old_stat.st_ino != new_stat.st_ino) { 71 nsi->need_setns = true; 72 nsi->mntns_path = newns; 73 newns = NULL; 74 } 75 76 /* If we're dealing with a process that is in a different PID namespace, 77 * attempt to work out the innermost tgid for the process. 78 */ 79 if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX) 80 goto out; 81 82 f = fopen(spath, "r"); 83 if (f == NULL) 84 goto out; 85 86 while (getline(&statln, &linesz, f) != -1) { 87 /* Use tgid if CONFIG_PID_NS is not defined. */ 88 if (strstr(statln, "Tgid:") != NULL) { 89 nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'), 90 NULL, 10); 91 nsi->nstgid = nsi->tgid; 92 } 93 94 if (strstr(statln, "NStgid:") != NULL) { 95 nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'), 96 NULL, 10); 97 break; 98 } 99 } 100 rv = 0; 101 102 out: 103 if (f != NULL) 104 (void) fclose(f); 105 free(statln); 106 free(newns); 107 return rv; 108 } 109 110 struct nsinfo *nsinfo__new(pid_t pid) 111 { 112 struct nsinfo *nsi; 113 114 if (pid == 0) 115 return NULL; 116 117 nsi = calloc(1, sizeof(*nsi)); 118 if (nsi != NULL) { 119 nsi->pid = pid; 120 nsi->tgid = pid; 121 nsi->nstgid = pid; 122 nsi->need_setns = false; 123 /* Init may fail if the process exits while we're trying to look 124 * at its proc information. In that case, save the pid but 125 * don't try to enter the namespace. 126 */ 127 if (nsinfo__init(nsi) == -1) 128 nsi->need_setns = false; 129 130 refcount_set(&nsi->refcnt, 1); 131 } 132 133 return nsi; 134 } 135 136 struct nsinfo *nsinfo__copy(struct nsinfo *nsi) 137 { 138 struct nsinfo *nnsi; 139 140 nnsi = calloc(1, sizeof(*nnsi)); 141 if (nnsi != NULL) { 142 nnsi->pid = nsi->pid; 143 nnsi->tgid = nsi->tgid; 144 nnsi->nstgid = nsi->nstgid; 145 nnsi->need_setns = nsi->need_setns; 146 if (nsi->mntns_path) { 147 nnsi->mntns_path = strdup(nsi->mntns_path); 148 if (!nnsi->mntns_path) { 149 free(nnsi); 150 return NULL; 151 } 152 } 153 refcount_set(&nnsi->refcnt, 1); 154 } 155 156 return nnsi; 157 } 158 159 void nsinfo__delete(struct nsinfo *nsi) 160 { 161 zfree(&nsi->mntns_path); 162 free(nsi); 163 } 164 165 struct nsinfo *nsinfo__get(struct nsinfo *nsi) 166 { 167 if (nsi) 168 refcount_inc(&nsi->refcnt); 169 return nsi; 170 } 171 172 void nsinfo__put(struct nsinfo *nsi) 173 { 174 if (nsi && refcount_dec_and_test(&nsi->refcnt)) 175 nsinfo__delete(nsi); 176 } 177 178 void nsinfo__mountns_enter(struct nsinfo *nsi, 179 struct nscookie *nc) 180 { 181 char curpath[PATH_MAX]; 182 int oldns = -1; 183 int newns = -1; 184 185 if (nc == NULL) 186 return; 187 188 nc->oldns = -1; 189 nc->newns = -1; 190 191 if (!nsi || !nsi->need_setns) 192 return; 193 194 if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) 195 return; 196 197 oldns = open(curpath, O_RDONLY); 198 if (oldns < 0) 199 return; 200 201 newns = open(nsi->mntns_path, O_RDONLY); 202 if (newns < 0) 203 goto errout; 204 205 if (setns(newns, CLONE_NEWNS) < 0) 206 goto errout; 207 208 nc->oldns = oldns; 209 nc->newns = newns; 210 return; 211 212 errout: 213 if (oldns > -1) 214 close(oldns); 215 if (newns > -1) 216 close(newns); 217 } 218 219 void nsinfo__mountns_exit(struct nscookie *nc) 220 { 221 if (nc == NULL || nc->oldns == -1 || nc->newns == -1) 222 return; 223 224 setns(nc->oldns, CLONE_NEWNS); 225 226 if (nc->oldns > -1) { 227 close(nc->oldns); 228 nc->oldns = -1; 229 } 230 231 if (nc->newns > -1) { 232 close(nc->newns); 233 nc->newns = -1; 234 } 235 } 236