Commit | Line | Data |
---|---|---|
f2d9627b | 1 | #include <ctype.h> |
cd0cfad7 | 2 | #include <errno.h> |
2d729f6a | 3 | #include <limits.h> |
cd0cfad7 BP |
4 | #include <stdbool.h> |
5 | #include <stdio.h> | |
f2d9627b | 6 | #include <stdlib.h> |
cd0cfad7 BP |
7 | #include <string.h> |
8 | #include <sys/vfs.h> | |
3a351127 ACM |
9 | #include <sys/types.h> |
10 | #include <sys/stat.h> | |
11 | #include <fcntl.h> | |
12 | #include <unistd.h> | |
73ca85ad | 13 | #include <sys/mount.h> |
4299a549 | 14 | |
cd0cfad7 | 15 | #include "fs.h" |
607bfbd7 | 16 | #include "debug-internal.h" |
4299a549 | 17 | |
b86b0d35 JO |
18 | #define _STR(x) #x |
19 | #define STR(x) _STR(x) | |
20 | ||
41e3a1fe JO |
21 | #ifndef SYSFS_MAGIC |
22 | #define SYSFS_MAGIC 0x62656572 | |
23 | #endif | |
24 | ||
25 | #ifndef PROC_SUPER_MAGIC | |
26 | #define PROC_SUPER_MAGIC 0x9fa0 | |
27 | #endif | |
28 | ||
8ccfabdb JO |
29 | #ifndef DEBUGFS_MAGIC |
30 | #define DEBUGFS_MAGIC 0x64626720 | |
31 | #endif | |
32 | ||
c495afb4 JO |
33 | #ifndef TRACEFS_MAGIC |
34 | #define TRACEFS_MAGIC 0x74726163 | |
35 | #endif | |
36 | ||
4299a549 JO |
37 | static const char * const sysfs__fs_known_mountpoints[] = { |
38 | "/sys", | |
39 | 0, | |
40 | }; | |
41 | ||
a9862418 JO |
42 | static const char * const procfs__known_mountpoints[] = { |
43 | "/proc", | |
44 | 0, | |
45 | }; | |
46 | ||
8ccfabdb JO |
47 | #ifndef DEBUGFS_DEFAULT_PATH |
48 | #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug" | |
49 | #endif | |
50 | ||
51 | static const char * const debugfs__known_mountpoints[] = { | |
52 | DEBUGFS_DEFAULT_PATH, | |
53 | "/debug", | |
54 | 0, | |
55 | }; | |
56 | ||
c495afb4 JO |
57 | |
58 | #ifndef TRACEFS_DEFAULT_PATH | |
59 | #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing" | |
60 | #endif | |
61 | ||
62 | static const char * const tracefs__known_mountpoints[] = { | |
63 | TRACEFS_DEFAULT_PATH, | |
64 | "/sys/kernel/debug/tracing", | |
65 | "/tracing", | |
66 | "/trace", | |
67 | 0, | |
68 | }; | |
69 | ||
4299a549 JO |
70 | struct fs { |
71 | const char *name; | |
72 | const char * const *mounts; | |
ccb5597f | 73 | char path[PATH_MAX]; |
4299a549 JO |
74 | bool found; |
75 | long magic; | |
76 | }; | |
77 | ||
78 | enum { | |
8ccfabdb JO |
79 | FS__SYSFS = 0, |
80 | FS__PROCFS = 1, | |
81 | FS__DEBUGFS = 2, | |
c495afb4 | 82 | FS__TRACEFS = 3, |
4299a549 JO |
83 | }; |
84 | ||
c495afb4 JO |
85 | #ifndef TRACEFS_MAGIC |
86 | #define TRACEFS_MAGIC 0x74726163 | |
87 | #endif | |
88 | ||
4299a549 JO |
89 | static struct fs fs__entries[] = { |
90 | [FS__SYSFS] = { | |
91 | .name = "sysfs", | |
92 | .mounts = sysfs__fs_known_mountpoints, | |
93 | .magic = SYSFS_MAGIC, | |
94 | }, | |
a9862418 JO |
95 | [FS__PROCFS] = { |
96 | .name = "proc", | |
97 | .mounts = procfs__known_mountpoints, | |
98 | .magic = PROC_SUPER_MAGIC, | |
99 | }, | |
8ccfabdb JO |
100 | [FS__DEBUGFS] = { |
101 | .name = "debugfs", | |
102 | .mounts = debugfs__known_mountpoints, | |
103 | .magic = DEBUGFS_MAGIC, | |
104 | }, | |
c495afb4 JO |
105 | [FS__TRACEFS] = { |
106 | .name = "tracefs", | |
107 | .mounts = tracefs__known_mountpoints, | |
108 | .magic = TRACEFS_MAGIC, | |
109 | }, | |
4299a549 JO |
110 | }; |
111 | ||
112 | static bool fs__read_mounts(struct fs *fs) | |
113 | { | |
114 | bool found = false; | |
115 | char type[100]; | |
116 | FILE *fp; | |
117 | ||
118 | fp = fopen("/proc/mounts", "r"); | |
119 | if (fp == NULL) | |
120 | return NULL; | |
121 | ||
122 | while (!found && | |
123 | fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n", | |
124 | fs->path, type) == 2) { | |
125 | ||
126 | if (strcmp(type, fs->name) == 0) | |
127 | found = true; | |
128 | } | |
129 | ||
130 | fclose(fp); | |
131 | return fs->found = found; | |
132 | } | |
133 | ||
134 | static int fs__valid_mount(const char *fs, long magic) | |
135 | { | |
136 | struct statfs st_fs; | |
137 | ||
138 | if (statfs(fs, &st_fs) < 0) | |
139 | return -ENOENT; | |
db1806ed | 140 | else if ((long)st_fs.f_type != magic) |
4299a549 JO |
141 | return -ENOENT; |
142 | ||
143 | return 0; | |
144 | } | |
145 | ||
146 | static bool fs__check_mounts(struct fs *fs) | |
147 | { | |
148 | const char * const *ptr; | |
149 | ||
150 | ptr = fs->mounts; | |
151 | while (*ptr) { | |
152 | if (fs__valid_mount(*ptr, fs->magic) == 0) { | |
153 | fs->found = true; | |
154 | strcpy(fs->path, *ptr); | |
155 | return true; | |
156 | } | |
157 | ptr++; | |
158 | } | |
159 | ||
160 | return false; | |
161 | } | |
162 | ||
f2d9627b CS |
163 | static void mem_toupper(char *f, size_t len) |
164 | { | |
165 | while (len) { | |
166 | *f = toupper(*f); | |
167 | f++; | |
168 | len--; | |
169 | } | |
170 | } | |
171 | ||
172 | /* | |
173 | * Check for "NAME_PATH" environment variable to override fs location (for | |
174 | * testing). This matches the recommendation in Documentation/sysfs-rules.txt | |
175 | * for SYSFS_PATH. | |
176 | */ | |
177 | static bool fs__env_override(struct fs *fs) | |
178 | { | |
179 | char *override_path; | |
180 | size_t name_len = strlen(fs->name); | |
181 | /* name + "_PATH" + '\0' */ | |
182 | char upper_name[name_len + 5 + 1]; | |
183 | memcpy(upper_name, fs->name, name_len); | |
184 | mem_toupper(upper_name, name_len); | |
185 | strcpy(&upper_name[name_len], "_PATH"); | |
186 | ||
187 | override_path = getenv(upper_name); | |
188 | if (!override_path) | |
189 | return false; | |
190 | ||
191 | fs->found = true; | |
192 | strncpy(fs->path, override_path, sizeof(fs->path)); | |
193 | return true; | |
194 | } | |
195 | ||
4299a549 JO |
196 | static const char *fs__get_mountpoint(struct fs *fs) |
197 | { | |
f2d9627b CS |
198 | if (fs__env_override(fs)) |
199 | return fs->path; | |
200 | ||
4299a549 JO |
201 | if (fs__check_mounts(fs)) |
202 | return fs->path; | |
203 | ||
f2d9627b CS |
204 | if (fs__read_mounts(fs)) |
205 | return fs->path; | |
206 | ||
207 | return NULL; | |
4299a549 JO |
208 | } |
209 | ||
cf38fada | 210 | static const char *fs__mountpoint(int idx) |
4299a549 JO |
211 | { |
212 | struct fs *fs = &fs__entries[idx]; | |
213 | ||
214 | if (fs->found) | |
215 | return (const char *)fs->path; | |
216 | ||
217 | return fs__get_mountpoint(fs); | |
218 | } | |
219 | ||
73ca85ad JO |
220 | static const char *mount_overload(struct fs *fs) |
221 | { | |
222 | size_t name_len = strlen(fs->name); | |
223 | /* "PERF_" + name + "_ENVIRONMENT" + '\0' */ | |
224 | char upper_name[5 + name_len + 12 + 1]; | |
225 | ||
226 | snprintf(upper_name, name_len, "PERF_%s_ENVIRONMENT", fs->name); | |
227 | mem_toupper(upper_name, name_len); | |
228 | ||
229 | return getenv(upper_name) ?: *fs->mounts; | |
230 | } | |
231 | ||
232 | static const char *fs__mount(int idx) | |
233 | { | |
234 | struct fs *fs = &fs__entries[idx]; | |
235 | const char *mountpoint; | |
236 | ||
237 | if (fs__mountpoint(idx)) | |
238 | return (const char *)fs->path; | |
239 | ||
240 | mountpoint = mount_overload(fs); | |
241 | ||
242 | if (mount(NULL, mountpoint, fs->name, 0, NULL) < 0) | |
243 | return NULL; | |
244 | ||
245 | return fs__check_mounts(fs) ? fs->path : NULL; | |
246 | } | |
247 | ||
709adcb3 JO |
248 | #define FS(name, idx) \ |
249 | const char *name##__mountpoint(void) \ | |
250 | { \ | |
251 | return fs__mountpoint(idx); \ | |
252 | } \ | |
253 | \ | |
254 | const char *name##__mount(void) \ | |
255 | { \ | |
256 | return fs__mount(idx); \ | |
257 | } \ | |
258 | \ | |
259 | bool name##__configured(void) \ | |
260 | { \ | |
261 | return name##__mountpoint() != NULL; \ | |
4299a549 JO |
262 | } |
263 | ||
73ca85ad JO |
264 | FS(sysfs, FS__SYSFS); |
265 | FS(procfs, FS__PROCFS); | |
266 | FS(debugfs, FS__DEBUGFS); | |
267 | FS(tracefs, FS__TRACEFS); | |
3a351127 ACM |
268 | |
269 | int filename__read_int(const char *filename, int *value) | |
270 | { | |
271 | char line[64]; | |
272 | int fd = open(filename, O_RDONLY), err = -1; | |
273 | ||
274 | if (fd < 0) | |
275 | return -1; | |
276 | ||
277 | if (read(fd, line, sizeof(line)) > 0) { | |
278 | *value = atoi(line); | |
279 | err = 0; | |
280 | } | |
281 | ||
282 | close(fd); | |
283 | return err; | |
284 | } | |
42e3c4a1 | 285 | |
db49120a JO |
286 | /* |
287 | * Parses @value out of @filename with strtoull. | |
288 | * By using 0 for base, the strtoull detects the | |
289 | * base automatically (see man strtoull). | |
290 | */ | |
2d729f6a ACM |
291 | int filename__read_ull(const char *filename, unsigned long long *value) |
292 | { | |
293 | char line[64]; | |
294 | int fd = open(filename, O_RDONLY), err = -1; | |
295 | ||
296 | if (fd < 0) | |
297 | return -1; | |
298 | ||
299 | if (read(fd, line, sizeof(line)) > 0) { | |
db49120a | 300 | *value = strtoull(line, NULL, 0); |
2d729f6a ACM |
301 | if (*value != ULLONG_MAX) |
302 | err = 0; | |
303 | } | |
304 | ||
305 | close(fd); | |
306 | return err; | |
307 | } | |
308 | ||
607bfbd7 JO |
309 | #define STRERR_BUFSIZE 128 /* For the buffer size of strerror_r */ |
310 | ||
311 | int filename__read_str(const char *filename, char **buf, size_t *sizep) | |
312 | { | |
313 | size_t size = 0, alloc_size = 0; | |
314 | void *bf = NULL, *nbf; | |
315 | int fd, n, err = 0; | |
316 | char sbuf[STRERR_BUFSIZE]; | |
317 | ||
318 | fd = open(filename, O_RDONLY); | |
319 | if (fd < 0) | |
320 | return -errno; | |
321 | ||
322 | do { | |
323 | if (size == alloc_size) { | |
324 | alloc_size += BUFSIZ; | |
325 | nbf = realloc(bf, alloc_size); | |
326 | if (!nbf) { | |
327 | err = -ENOMEM; | |
328 | break; | |
329 | } | |
330 | ||
331 | bf = nbf; | |
332 | } | |
333 | ||
334 | n = read(fd, bf + size, alloc_size - size); | |
335 | if (n < 0) { | |
336 | if (size) { | |
337 | pr_warning("read failed %d: %s\n", errno, | |
338 | strerror_r(errno, sbuf, sizeof(sbuf))); | |
339 | err = 0; | |
340 | } else | |
341 | err = -errno; | |
342 | ||
343 | break; | |
344 | } | |
345 | ||
346 | size += n; | |
347 | } while (n > 0); | |
348 | ||
349 | if (!err) { | |
350 | *sizep = size; | |
351 | *buf = bf; | |
352 | } else | |
353 | free(bf); | |
354 | ||
355 | close(fd); | |
356 | return err; | |
357 | } | |
4bd112df ACM |
358 | |
359 | int procfs__read_str(const char *entry, char **buf, size_t *sizep) | |
360 | { | |
361 | char path[PATH_MAX]; | |
362 | const char *procfs = procfs__mountpoint(); | |
363 | ||
364 | if (!procfs) | |
365 | return -1; | |
366 | ||
367 | snprintf(path, sizeof(path), "%s/%s", procfs, entry); | |
368 | ||
369 | return filename__read_str(path, buf, sizep); | |
370 | } | |
607bfbd7 | 371 | |
2d729f6a ACM |
372 | int sysfs__read_ull(const char *entry, unsigned long long *value) |
373 | { | |
374 | char path[PATH_MAX]; | |
375 | const char *sysfs = sysfs__mountpoint(); | |
376 | ||
377 | if (!sysfs) | |
378 | return -1; | |
379 | ||
380 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry); | |
381 | ||
382 | return filename__read_ull(path, value); | |
383 | } | |
384 | ||
385 | int sysfs__read_int(const char *entry, int *value) | |
386 | { | |
387 | char path[PATH_MAX]; | |
388 | const char *sysfs = sysfs__mountpoint(); | |
389 | ||
390 | if (!sysfs) | |
391 | return -1; | |
392 | ||
393 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry); | |
394 | ||
395 | return filename__read_int(path, value); | |
396 | } | |
397 | ||
51c0396c JO |
398 | int sysfs__read_str(const char *entry, char **buf, size_t *sizep) |
399 | { | |
400 | char path[PATH_MAX]; | |
401 | const char *sysfs = sysfs__mountpoint(); | |
402 | ||
403 | if (!sysfs) | |
404 | return -1; | |
405 | ||
406 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry); | |
407 | ||
408 | return filename__read_str(path, buf, sizep); | |
409 | } | |
410 | ||
42e3c4a1 ACM |
411 | int sysctl__read_int(const char *sysctl, int *value) |
412 | { | |
413 | char path[PATH_MAX]; | |
414 | const char *procfs = procfs__mountpoint(); | |
415 | ||
416 | if (!procfs) | |
417 | return -1; | |
418 | ||
419 | snprintf(path, sizeof(path), "%s/sys/%s", procfs, sysctl); | |
420 | ||
421 | return filename__read_int(path, value); | |
422 | } |