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