1 | // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) |
2 | /* Copyright (c) 2015-2017 Daniel Borkmann */ |
3 | /* Copyright (c) 2018 Netronome Systems, Inc. */ |
4 | |
5 | #include <errno.h> |
6 | #include <limits.h> |
7 | #include <signal.h> |
8 | #include <stdio.h> |
9 | #include <string.h> |
10 | #include <unistd.h> |
11 | #include <linux/magic.h> |
12 | #include <fcntl.h> |
13 | #include <sys/vfs.h> |
14 | |
15 | #include "main.h" |
16 | |
17 | #ifndef TRACEFS_MAGIC |
18 | # define TRACEFS_MAGIC 0x74726163 |
19 | #endif |
20 | |
21 | #define _textify(x) #x |
22 | #define textify(x) _textify(x) |
23 | |
24 | FILE *trace_pipe_fd; |
25 | char *buff; |
26 | |
27 | static int validate_tracefs_mnt(const char *mnt, unsigned long magic) |
28 | { |
29 | struct statfs st_fs; |
30 | |
31 | if (statfs(mnt, &st_fs) < 0) |
32 | return -ENOENT; |
33 | if ((unsigned long)st_fs.f_type != magic) |
34 | return -ENOENT; |
35 | |
36 | return 0; |
37 | } |
38 | |
39 | static bool |
40 | find_tracefs_mnt_single(unsigned long magic, char *mnt, const char *mntpt) |
41 | { |
42 | size_t src_len; |
43 | |
44 | if (validate_tracefs_mnt(mnt: mntpt, magic)) |
45 | return false; |
46 | |
47 | src_len = strlen(mntpt); |
48 | if (src_len + 1 >= PATH_MAX) { |
49 | p_err(fmt: "tracefs mount point name too long" ); |
50 | return false; |
51 | } |
52 | |
53 | strcpy(p: mnt, q: mntpt); |
54 | return true; |
55 | } |
56 | |
57 | static bool get_tracefs_pipe(char *mnt) |
58 | { |
59 | static const char * const known_mnts[] = { |
60 | "/sys/kernel/debug/tracing" , |
61 | "/sys/kernel/tracing" , |
62 | "/tracing" , |
63 | "/trace" , |
64 | }; |
65 | const char *pipe_name = "/trace_pipe" ; |
66 | const char *fstype = "tracefs" ; |
67 | char type[100], format[32]; |
68 | const char * const *ptr; |
69 | bool found = false; |
70 | FILE *fp; |
71 | |
72 | for (ptr = known_mnts; ptr < known_mnts + ARRAY_SIZE(known_mnts); ptr++) |
73 | if (find_tracefs_mnt_single(TRACEFS_MAGIC, mnt, mntpt: *ptr)) |
74 | goto exit_found; |
75 | |
76 | fp = fopen("/proc/mounts" , "r" ); |
77 | if (!fp) |
78 | return false; |
79 | |
80 | /* Allow room for NULL terminating byte and pipe file name */ |
81 | snprintf(buf: format, size: sizeof(format), fmt: "%%*s %%%zds %%99s %%*s %%*d %%*d\\n" , |
82 | PATH_MAX - strlen(pipe_name) - 1); |
83 | while (fscanf(fp, format, mnt, type) == 2) |
84 | if (strcmp(type, fstype) == 0) { |
85 | found = true; |
86 | break; |
87 | } |
88 | fclose(fp); |
89 | |
90 | /* The string from fscanf() might be truncated, check mnt is valid */ |
91 | if (found && validate_tracefs_mnt(mnt, TRACEFS_MAGIC)) |
92 | goto exit_found; |
93 | |
94 | if (block_mount) |
95 | return false; |
96 | |
97 | p_info(fmt: "could not find tracefs, attempting to mount it now" ); |
98 | /* Most of the time, tracefs is automatically mounted by debugfs at |
99 | * /sys/kernel/debug/tracing when we try to access it. If we could not |
100 | * find it, it is likely that debugfs is not mounted. Let's give one |
101 | * attempt at mounting just tracefs at /sys/kernel/tracing. |
102 | */ |
103 | strcpy(p: mnt, q: known_mnts[1]); |
104 | if (mount_tracefs(target: mnt)) |
105 | return false; |
106 | |
107 | exit_found: |
108 | strcat(p: mnt, q: pipe_name); |
109 | return true; |
110 | } |
111 | |
112 | static void exit_tracelog(int signum) |
113 | { |
114 | fclose(trace_pipe_fd); |
115 | free(buff); |
116 | |
117 | if (json_output) { |
118 | jsonw_end_array(self: json_wtr); |
119 | jsonw_destroy(self_p: &json_wtr); |
120 | } |
121 | |
122 | exit(0); |
123 | } |
124 | |
125 | int do_tracelog(int argc, char **argv) |
126 | { |
127 | const struct sigaction act = { |
128 | .sa_handler = exit_tracelog |
129 | }; |
130 | char trace_pipe[PATH_MAX]; |
131 | size_t buff_len = 0; |
132 | |
133 | if (json_output) |
134 | jsonw_start_array(self: json_wtr); |
135 | |
136 | if (!get_tracefs_pipe(mnt: trace_pipe)) |
137 | return -1; |
138 | |
139 | trace_pipe_fd = fopen(trace_pipe, "r" ); |
140 | if (!trace_pipe_fd) { |
141 | p_err(fmt: "could not open trace pipe: %s" , strerror(errno)); |
142 | return -1; |
143 | } |
144 | |
145 | sigaction(SIGHUP, &act, NULL); |
146 | sigaction(SIGINT, &act, NULL); |
147 | sigaction(SIGTERM, &act, NULL); |
148 | while (1) { |
149 | ssize_t ret; |
150 | |
151 | ret = getline(&buff, &buff_len, trace_pipe_fd); |
152 | if (ret <= 0) { |
153 | p_err(fmt: "failed to read content from trace pipe: %s" , |
154 | strerror(errno)); |
155 | break; |
156 | } |
157 | if (json_output) |
158 | jsonw_string(self: json_wtr, value: buff); |
159 | else |
160 | printf("%s" , buff); |
161 | } |
162 | |
163 | fclose(trace_pipe_fd); |
164 | free(buff); |
165 | return -1; |
166 | } |
167 | |