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