• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2 
3 /*
4  * mountsnoop  Trace mount and umount[2] syscalls
5  *
6  * Copyright (c) 2021 Hengqi Chen
7  * 30-May-2021   Hengqi Chen   Created this.
8  */
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12 #include <argp.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <string.h>
16 #include <time.h>
17 
18 #include <bpf/libbpf.h>
19 #include <bpf/bpf.h>
20 #include "mountsnoop.h"
21 #include "mountsnoop.skel.h"
22 #include "trace_helpers.h"
23 
24 #define PERF_BUFFER_PAGES	64
25 #define PERF_POLL_TIMEOUT_MS	100
26 #define warn(...) fprintf(stderr, __VA_ARGS__)
27 
28 /* https://www.gnu.org/software/gnulib/manual/html_node/strerrorname_005fnp.html */
29 #if !defined(__GLIBC__) || __GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 32)
strerrorname_np(int errnum)30 	const char *strerrorname_np(int errnum)
31 	{
32 		return NULL;
33 	}
34 #endif
35 
36 static volatile sig_atomic_t exiting = 0;
37 
38 static pid_t target_pid = 0;
39 static bool emit_timestamp = false;
40 static bool output_vertically = false;
41 static bool verbose = false;
42 static const char *flag_names[] = {
43 	[0] = "MS_RDONLY",
44 	[1] = "MS_NOSUID",
45 	[2] = "MS_NODEV",
46 	[3] = "MS_NOEXEC",
47 	[4] = "MS_SYNCHRONOUS",
48 	[5] = "MS_REMOUNT",
49 	[6] = "MS_MANDLOCK",
50 	[7] = "MS_DIRSYNC",
51 	[8] = "MS_NOSYMFOLLOW",
52 	[9] = "MS_NOATIME",
53 	[10] = "MS_NODIRATIME",
54 	[11] = "MS_BIND",
55 	[12] = "MS_MOVE",
56 	[13] = "MS_REC",
57 	[14] = "MS_VERBOSE",
58 	[15] = "MS_SILENT",
59 	[16] = "MS_POSIXACL",
60 	[17] = "MS_UNBINDABLE",
61 	[18] = "MS_PRIVATE",
62 	[19] = "MS_SLAVE",
63 	[20] = "MS_SHARED",
64 	[21] = "MS_RELATIME",
65 	[22] = "MS_KERNMOUNT",
66 	[23] = "MS_I_VERSION",
67 	[24] = "MS_STRICTATIME",
68 	[25] = "MS_LAZYTIME",
69 	[26] = "MS_SUBMOUNT",
70 	[27] = "MS_NOREMOTELOCK",
71 	[28] = "MS_NOSEC",
72 	[29] = "MS_BORN",
73 	[30] = "MS_ACTIVE",
74 	[31] = "MS_NOUSER",
75 };
76 static const int flag_count = sizeof(flag_names) / sizeof(flag_names[0]);
77 
78 const char *argp_program_version = "mountsnoop 0.1";
79 const char *argp_program_bug_address =
80 	"https://github.com/iovisor/bcc/tree/master/libbpf-tools";
81 const char argp_program_doc[] =
82 "Trace mount and umount syscalls.\n"
83 "\n"
84 "USAGE: mountsnoop [-h] [-t] [-p PID] [-v]\n"
85 "\n"
86 "EXAMPLES:\n"
87 "    mountsnoop         # trace mount and umount syscalls\n"
88 "    mountsnoop -d      # detailed output (one line per column value)\n"
89 "    mountsnoop -p 1216 # only trace PID 1216\n";
90 
91 static const struct argp_option opts[] = {
92 	{ "pid", 'p', "PID", 0, "Process ID to trace" },
93 	{ "timestamp", 't', NULL, 0, "Include timestamp on output" },
94 	{ "detailed", 'd', NULL, 0, "Output result in detail mode" },
95 	{ "verbose", 'v', NULL, 0, "Verbose debug output" },
96 	{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
97 	{},
98 };
99 
parse_arg(int key,char * arg,struct argp_state * state)100 static error_t parse_arg(int key, char *arg, struct argp_state *state)
101 {
102 	long pid;
103 
104 	switch (key) {
105 	case 'p':
106 		errno = 0;
107 		pid = strtol(arg, NULL, 10);
108 		if (errno || pid <= 0) {
109 			warn("Invalid PID: %s\n", arg);
110 			argp_usage(state);
111 		}
112 		target_pid = pid;
113 		break;
114 	case 't':
115 		emit_timestamp = true;
116 		break;
117 	case 'd':
118 		output_vertically = true;
119 		break;
120 	case 'v':
121 		verbose = true;
122 		break;
123 	case 'h':
124 		argp_state_help(state, stderr, ARGP_HELP_STD_HELP);
125 		break;
126 	default:
127 		return ARGP_ERR_UNKNOWN;
128 	}
129 	return 0;
130 }
131 
libbpf_print_fn(enum libbpf_print_level level,const char * format,va_list args)132 static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args)
133 {
134 	if (level == LIBBPF_DEBUG && !verbose)
135 		return 0;
136 	return vfprintf(stderr, format, args);
137 }
138 
sig_int(int signo)139 static void sig_int(int signo)
140 {
141 	exiting = 1;
142 }
143 
strflags(__u64 flags)144 static const char *strflags(__u64 flags)
145 {
146 	static char str[512];
147 	int i;
148 
149 	if (!flags)
150 		return "0x0";
151 
152 	str[0] = '\0';
153 	for (i = 0; i < flag_count; i++) {
154 		if (!((1 << i) & flags))
155 			continue;
156 		if (str[0])
157 			strcat(str, " | ");
158 		strcat(str, flag_names[i]);
159 	}
160 	return str;
161 }
162 
strerrno(int errnum)163 static const char *strerrno(int errnum)
164 {
165 	const char *errstr;
166 	static char ret[32] = {};
167 
168 	if (!errnum)
169 		return "0";
170 
171 	ret[0] = '\0';
172 	errstr = strerrorname_np(-errnum);
173 	if (!errstr) {
174 		snprintf(ret, sizeof(ret), "%d", errnum);
175 		return ret;
176 	}
177 
178 	snprintf(ret, sizeof(ret), "-%s", errstr);
179 	return ret;
180 }
181 
gen_call(const struct event * e)182 static const char *gen_call(const struct event *e)
183 {
184 	static char call[10240];
185 
186 	memset(call, 0, sizeof(call));
187 	if (e->op == UMOUNT) {
188 		snprintf(call, sizeof(call), "umount(\"%s\", %s) = %s",
189 			 e->dest, strflags(e->flags), strerrno(e->ret));
190 	} else {
191 		snprintf(call, sizeof(call), "mount(\"%s\", \"%s\", \"%s\", %s, \"%s\") = %s",
192 			 e->src, e->dest, e->fs, strflags(e->flags), e->data, strerrno(e->ret));
193 	}
194 	return call;
195 }
196 
handle_event(void * ctx,int cpu,void * data,__u32 data_sz)197 static void handle_event(void *ctx, int cpu, void *data, __u32 data_sz)
198 {
199 	const struct event *e = data;
200 	struct tm *tm;
201 	char ts[32];
202 	time_t t;
203 	const char *indent;
204 	static const char *op_name[] = {
205 		[MOUNT] = "MOUNT",
206 		[UMOUNT] = "UMOUNT",
207 	};
208 
209 	if (emit_timestamp) {
210 		time(&t);
211 		tm = localtime(&t);
212 		strftime(ts, sizeof(ts), "%H:%M:%S ", tm);
213 		printf("%s", ts);
214 		indent = "    ";
215 	} else {
216 		indent = "";
217 	}
218 	if (!output_vertically) {
219 		printf("%-16s %-7d %-7d %-11u %s\n",
220 		       e->comm, e->pid, e->tid, e->mnt_ns, gen_call(e));
221 		return;
222 	}
223 	if (emit_timestamp)
224 		printf("\n");
225 	printf("%sPID:    %d\n", indent, e->pid);
226 	printf("%sTID:    %d\n", indent, e->tid);
227 	printf("%sCOMM:   %s\n", indent, e->comm);
228 	printf("%sOP:     %s\n", indent, op_name[e->op]);
229 	printf("%sRET:    %s\n", indent, strerrno(e->ret));
230 	printf("%sLAT:    %lldus\n", indent, e->delta / 1000);
231 	printf("%sMNT_NS: %u\n", indent, e->mnt_ns);
232 	printf("%sFS:     %s\n", indent, e->fs);
233 	printf("%sSOURCE: %s\n", indent, e->src);
234 	printf("%sTARGET: %s\n", indent, e->dest);
235 	printf("%sDATA:   %s\n", indent, e->data);
236 	printf("%sFLAGS:  %s\n", indent, strflags(e->flags));
237 	printf("\n");
238 }
239 
handle_lost_events(void * ctx,int cpu,__u64 lost_cnt)240 static void handle_lost_events(void *ctx, int cpu, __u64 lost_cnt)
241 {
242 	warn("lost %llu events on CPU #%d\n", lost_cnt, cpu);
243 }
244 
main(int argc,char ** argv)245 int main(int argc, char **argv)
246 {
247 	static const struct argp argp = {
248 		.options = opts,
249 		.parser = parse_arg,
250 		.doc = argp_program_doc,
251 	};
252 	struct perf_buffer *pb = NULL;
253 	struct mountsnoop_bpf *obj;
254 	int err;
255 
256 	err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
257 	if (err)
258 		return err;
259 
260 	libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
261 	libbpf_set_print(libbpf_print_fn);
262 
263 	obj = mountsnoop_bpf__open();
264 	if (!obj) {
265 		warn("failed to open BPF object\n");
266 		return 1;
267 	}
268 
269 	obj->rodata->target_pid = target_pid;
270 
271 	err = mountsnoop_bpf__load(obj);
272 	if (err) {
273 		warn("failed to load BPF object: %d\n", err);
274 		goto cleanup;
275 	}
276 
277 	err = mountsnoop_bpf__attach(obj);
278 	if (err) {
279 		warn("failed to attach BPF programs: %d\n", err);
280 		goto cleanup;
281 	}
282 
283 	pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
284 			      handle_event, handle_lost_events, NULL, NULL);
285 	if (!pb) {
286 		err = -errno;
287 		warn("failed to open perf buffer: %d\n", err);
288 		goto cleanup;
289 	}
290 
291 	if (signal(SIGINT, sig_int) == SIG_ERR) {
292 		warn("can't set signal handler: %s\n", strerror(errno));
293 		err = 1;
294 		goto cleanup;
295 	}
296 
297 	if (!output_vertically) {
298 		if (emit_timestamp)
299 			printf("%-8s ", "TIME");
300 		printf("%-16s %-7s %-7s %-11s %s\n", "COMM", "PID", "TID", "MNT_NS", "CALL");
301 	}
302 
303 	while (!exiting) {
304 		err = perf_buffer__poll(pb, PERF_POLL_TIMEOUT_MS);
305 		if (err < 0 && err != -EINTR) {
306 			fprintf(stderr, "error polling perf buffer: %s\n", strerror(-err));
307 			goto cleanup;
308 		}
309 		/* reset err to return 0 if exiting */
310 		err = 0;
311 	}
312 
313 cleanup:
314 	perf_buffer__free(pb);
315 	mountsnoop_bpf__destroy(obj);
316 
317 	return err != 0;
318 }
319