namespaces.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. *
  4. * Copyright (C) 2017 Hari Bathini, IBM Corporation
  5. */
  6. #include "namespaces.h"
  7. #include "event.h"
  8. #include <sys/types.h>
  9. #include <sys/stat.h>
  10. #include <fcntl.h>
  11. #include <limits.h>
  12. #include <sched.h>
  13. #include <stdlib.h>
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <unistd.h>
  17. #include <asm/bug.h>
  18. #include <linux/kernel.h>
  19. #include <linux/zalloc.h>
  20. static const char *perf_ns__names[] = {
  21. [NET_NS_INDEX] = "net",
  22. [UTS_NS_INDEX] = "uts",
  23. [IPC_NS_INDEX] = "ipc",
  24. [PID_NS_INDEX] = "pid",
  25. [USER_NS_INDEX] = "user",
  26. [MNT_NS_INDEX] = "mnt",
  27. [CGROUP_NS_INDEX] = "cgroup",
  28. };
  29. const char *perf_ns__name(unsigned int id)
  30. {
  31. if (id >= ARRAY_SIZE(perf_ns__names))
  32. return "UNKNOWN";
  33. return perf_ns__names[id];
  34. }
  35. struct namespaces *namespaces__new(struct perf_record_namespaces *event)
  36. {
  37. struct namespaces *namespaces;
  38. u64 link_info_size = ((event ? event->nr_namespaces : NR_NAMESPACES) *
  39. sizeof(struct perf_ns_link_info));
  40. namespaces = zalloc(sizeof(struct namespaces) + link_info_size);
  41. if (!namespaces)
  42. return NULL;
  43. namespaces->end_time = -1;
  44. if (event)
  45. memcpy(namespaces->link_info, event->link_info, link_info_size);
  46. return namespaces;
  47. }
  48. void namespaces__free(struct namespaces *namespaces)
  49. {
  50. free(namespaces);
  51. }
  52. static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, bool *in_pidns, const char *path)
  53. {
  54. FILE *f = NULL;
  55. char *statln = NULL;
  56. size_t linesz = 0;
  57. char *nspid;
  58. f = fopen(path, "r");
  59. if (f == NULL)
  60. return -1;
  61. while (getline(&statln, &linesz, f) != -1) {
  62. /* Use tgid if CONFIG_PID_NS is not defined. */
  63. if (strstr(statln, "Tgid:") != NULL) {
  64. *tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10);
  65. *nstgid = *tgid;
  66. }
  67. if (strstr(statln, "NStgid:") != NULL) {
  68. nspid = strrchr(statln, '\t');
  69. *nstgid = (pid_t)strtol(nspid, NULL, 10);
  70. /*
  71. * If innermost tgid is not the first, process is in a different
  72. * PID namespace.
  73. */
  74. *in_pidns = (statln + sizeof("NStgid:") - 1) != nspid;
  75. break;
  76. }
  77. }
  78. fclose(f);
  79. free(statln);
  80. return 0;
  81. }
  82. int nsinfo__init(struct nsinfo *nsi)
  83. {
  84. char oldns[PATH_MAX];
  85. char spath[PATH_MAX];
  86. char *newns = NULL;
  87. struct stat old_stat;
  88. struct stat new_stat;
  89. int rv = -1;
  90. if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
  91. return rv;
  92. if (asprintf(&newns, "/proc/%d/ns/mnt", nsinfo__pid(nsi)) == -1)
  93. return rv;
  94. if (stat(oldns, &old_stat) < 0)
  95. goto out;
  96. if (stat(newns, &new_stat) < 0)
  97. goto out;
  98. /* Check if the mount namespaces differ, if so then indicate that we
  99. * want to switch as part of looking up dso/map data.
  100. */
  101. if (old_stat.st_ino != new_stat.st_ino) {
  102. RC_CHK_ACCESS(nsi)->need_setns = true;
  103. RC_CHK_ACCESS(nsi)->mntns_path = newns;
  104. newns = NULL;
  105. }
  106. /* If we're dealing with a process that is in a different PID namespace,
  107. * attempt to work out the innermost tgid for the process.
  108. */
  109. if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsinfo__pid(nsi)) >= PATH_MAX)
  110. goto out;
  111. rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
  112. &RC_CHK_ACCESS(nsi)->in_pidns, spath);
  113. out:
  114. free(newns);
  115. return rv;
  116. }
  117. static struct nsinfo *nsinfo__alloc(void)
  118. {
  119. struct nsinfo *res;
  120. RC_STRUCT(nsinfo) *nsi;
  121. nsi = calloc(1, sizeof(*nsi));
  122. if (ADD_RC_CHK(res, nsi))
  123. refcount_set(&nsi->refcnt, 1);
  124. return res;
  125. }
  126. struct nsinfo *nsinfo__new(pid_t pid)
  127. {
  128. struct nsinfo *nsi;
  129. if (pid == 0)
  130. return NULL;
  131. nsi = nsinfo__alloc();
  132. if (!nsi)
  133. return NULL;
  134. RC_CHK_ACCESS(nsi)->pid = pid;
  135. RC_CHK_ACCESS(nsi)->tgid = pid;
  136. RC_CHK_ACCESS(nsi)->nstgid = pid;
  137. nsinfo__clear_need_setns(nsi);
  138. RC_CHK_ACCESS(nsi)->in_pidns = false;
  139. /* Init may fail if the process exits while we're trying to look at its
  140. * proc information. In that case, save the pid but don't try to enter
  141. * the namespace.
  142. */
  143. if (nsinfo__init(nsi) == -1)
  144. nsinfo__clear_need_setns(nsi);
  145. return nsi;
  146. }
  147. static const char *nsinfo__mntns_path(const struct nsinfo *nsi)
  148. {
  149. return RC_CHK_ACCESS(nsi)->mntns_path;
  150. }
  151. struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
  152. {
  153. struct nsinfo *nnsi;
  154. if (nsi == NULL)
  155. return NULL;
  156. nnsi = nsinfo__alloc();
  157. if (!nnsi)
  158. return NULL;
  159. RC_CHK_ACCESS(nnsi)->pid = nsinfo__pid(nsi);
  160. RC_CHK_ACCESS(nnsi)->tgid = nsinfo__tgid(nsi);
  161. RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
  162. RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi);
  163. RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
  164. if (nsinfo__mntns_path(nsi)) {
  165. RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi));
  166. if (!RC_CHK_ACCESS(nnsi)->mntns_path) {
  167. nsinfo__put(nnsi);
  168. return NULL;
  169. }
  170. }
  171. return nnsi;
  172. }
  173. static refcount_t *nsinfo__refcnt(struct nsinfo *nsi)
  174. {
  175. return &RC_CHK_ACCESS(nsi)->refcnt;
  176. }
  177. static void nsinfo__delete(struct nsinfo *nsi)
  178. {
  179. if (nsi) {
  180. WARN_ONCE(refcount_read(nsinfo__refcnt(nsi)) != 0, "nsinfo refcnt unbalanced\n");
  181. zfree(&RC_CHK_ACCESS(nsi)->mntns_path);
  182. RC_CHK_FREE(nsi);
  183. }
  184. }
  185. struct nsinfo *nsinfo__get(struct nsinfo *nsi)
  186. {
  187. struct nsinfo *result;
  188. if (RC_CHK_GET(result, nsi))
  189. refcount_inc(nsinfo__refcnt(nsi));
  190. return result;
  191. }
  192. void nsinfo__put(struct nsinfo *nsi)
  193. {
  194. if (nsi && refcount_dec_and_test(nsinfo__refcnt(nsi)))
  195. nsinfo__delete(nsi);
  196. else
  197. RC_CHK_PUT(nsi);
  198. }
  199. bool nsinfo__need_setns(const struct nsinfo *nsi)
  200. {
  201. return RC_CHK_ACCESS(nsi)->need_setns;
  202. }
  203. void nsinfo__clear_need_setns(struct nsinfo *nsi)
  204. {
  205. RC_CHK_ACCESS(nsi)->need_setns = false;
  206. }
  207. pid_t nsinfo__tgid(const struct nsinfo *nsi)
  208. {
  209. return RC_CHK_ACCESS(nsi)->tgid;
  210. }
  211. pid_t nsinfo__nstgid(const struct nsinfo *nsi)
  212. {
  213. return RC_CHK_ACCESS(nsi)->nstgid;
  214. }
  215. pid_t nsinfo__pid(const struct nsinfo *nsi)
  216. {
  217. return RC_CHK_ACCESS(nsi)->pid;
  218. }
  219. bool nsinfo__in_pidns(const struct nsinfo *nsi)
  220. {
  221. return RC_CHK_ACCESS(nsi)->in_pidns;
  222. }
  223. void nsinfo__set_in_pidns(struct nsinfo *nsi)
  224. {
  225. RC_CHK_ACCESS(nsi)->in_pidns = true;
  226. }
  227. void nsinfo__mountns_enter(struct nsinfo *nsi,
  228. struct nscookie *nc)
  229. {
  230. char curpath[PATH_MAX];
  231. int oldns = -1;
  232. int newns = -1;
  233. char *oldcwd = NULL;
  234. if (nc == NULL)
  235. return;
  236. nc->oldns = -1;
  237. nc->newns = -1;
  238. if (!nsi || !nsinfo__need_setns(nsi))
  239. return;
  240. if (!getcwd(curpath, sizeof(curpath)))
  241. return;
  242. oldcwd = strdup(curpath);
  243. if (!oldcwd)
  244. return;
  245. oldns = open("/proc/self/ns/mnt", O_RDONLY);
  246. if (oldns < 0)
  247. goto errout;
  248. newns = open(nsinfo__mntns_path(nsi), O_RDONLY);
  249. if (newns < 0)
  250. goto errout;
  251. if (setns(newns, CLONE_NEWNS) < 0)
  252. goto errout;
  253. nc->oldcwd = oldcwd;
  254. nc->oldns = oldns;
  255. nc->newns = newns;
  256. return;
  257. errout:
  258. free(oldcwd);
  259. if (oldns > -1)
  260. close(oldns);
  261. if (newns > -1)
  262. close(newns);
  263. }
  264. void nsinfo__mountns_exit(struct nscookie *nc)
  265. {
  266. if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
  267. return;
  268. setns(nc->oldns, CLONE_NEWNS);
  269. if (nc->oldcwd) {
  270. WARN_ON_ONCE(chdir(nc->oldcwd));
  271. zfree(&nc->oldcwd);
  272. }
  273. if (nc->oldns > -1) {
  274. close(nc->oldns);
  275. nc->oldns = -1;
  276. }
  277. if (nc->newns > -1) {
  278. close(nc->newns);
  279. nc->newns = -1;
  280. }
  281. }
  282. char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
  283. {
  284. char *rpath;
  285. struct nscookie nsc;
  286. nsinfo__mountns_enter(nsi, &nsc);
  287. rpath = realpath(path, NULL);
  288. nsinfo__mountns_exit(&nsc);
  289. return rpath;
  290. }
  291. int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi)
  292. {
  293. int ret;
  294. struct nscookie nsc;
  295. nsinfo__mountns_enter(nsi, &nsc);
  296. ret = stat(filename, st);
  297. nsinfo__mountns_exit(&nsc);
  298. return ret;
  299. }
  300. bool nsinfo__is_in_root_namespace(void)
  301. {
  302. pid_t tgid = 0, nstgid = 0;
  303. bool in_pidns = false;
  304. nsinfo__get_nspid(&tgid, &nstgid, &in_pidns, "/proc/self/status");
  305. return !in_pidns;
  306. }