xref: /linux/tools/bpf/bpftool/main.c (revision b7f6400849162b918020c5d10d5b7f378afbf470)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
3 
4 #include <ctype.h>
5 #include <errno.h>
6 #include <getopt.h>
7 #include <linux/bpf.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <bpf/bpf.h>
13 #include <bpf/btf.h>
14 #include <bpf/hashmap.h>
15 #include <bpf/libbpf.h>
16 
17 #include "main.h"
18 
19 #define BATCH_LINE_LEN_MAX 65536
20 #define BATCH_ARG_NB_MAX 4096
21 
22 const char *bin_name;
23 static int last_argc;
24 static char **last_argv;
25 static int (*last_do_help)(int argc, char **argv);
26 json_writer_t *json_wtr;
27 bool pretty_output;
28 bool json_output;
29 bool show_pinned;
30 bool block_mount;
31 bool verifier_logs;
32 bool relaxed_maps;
33 bool use_loader;
34 struct btf *base_btf;
35 struct hashmap *refs_table;
36 
37 static void __noreturn clean_and_exit(int i)
38 {
39 	if (json_output)
40 		jsonw_destroy(&json_wtr);
41 
42 	exit(i);
43 }
44 
45 void usage(void)
46 {
47 	last_do_help(last_argc - 1, last_argv + 1);
48 
49 	clean_and_exit(-1);
50 }
51 
52 static int do_help(int argc, char **argv)
53 {
54 	if (json_output) {
55 		jsonw_null(json_wtr);
56 		return 0;
57 	}
58 
59 	fprintf(stderr,
60 		"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
61 		"       %s batch file FILE\n"
62 		"       %s version\n"
63 		"\n"
64 		"       OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter | token }\n"
65 		"       " HELP_SPEC_OPTIONS " |\n"
66 		"                    {-V|--version} }\n"
67 		"",
68 		bin_name, bin_name, bin_name);
69 
70 	return 0;
71 }
72 
73 static int do_batch(int argc, char **argv);
74 static int do_version(int argc, char **argv);
75 
76 static const struct cmd commands[] = {
77 	{ "help",	do_help },
78 	{ "batch",	do_batch },
79 	{ "prog",	do_prog },
80 	{ "map",	do_map },
81 	{ "link",	do_link },
82 	{ "cgroup",	do_cgroup },
83 	{ "perf",	do_perf },
84 	{ "net",	do_net },
85 	{ "feature",	do_feature },
86 	{ "btf",	do_btf },
87 	{ "gen",	do_gen },
88 	{ "struct_ops",	do_struct_ops },
89 	{ "iter",	do_iter },
90 	{ "token",	do_token },
91 	{ "version",	do_version },
92 	{ 0 }
93 };
94 
95 #ifndef BPFTOOL_VERSION
96 /* bpftool's major and minor version numbers are aligned on libbpf's. There is
97  * an offset of 6 for the version number, because bpftool's version was higher
98  * than libbpf's when we adopted this scheme. The patch number remains at 0
99  * for now. Set BPFTOOL_VERSION to override.
100  */
101 #define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
102 #define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
103 #define BPFTOOL_PATCH_VERSION 0
104 #endif
105 
106 static void
107 print_feature(const char *feature, bool state, unsigned int *nb_features)
108 {
109 	if (state) {
110 		printf("%s %s", *nb_features ? "," : "", feature);
111 		*nb_features = *nb_features + 1;
112 	}
113 }
114 
115 static int do_version(int argc, char **argv)
116 {
117 #ifdef HAVE_LIBBFD_SUPPORT
118 	const bool has_libbfd = true;
119 #else
120 	const bool has_libbfd = false;
121 #endif
122 #ifdef HAVE_LLVM_SUPPORT
123 	const bool has_llvm = true;
124 #else
125 	const bool has_llvm = false;
126 #endif
127 #ifdef BPFTOOL_WITHOUT_SKELETONS
128 	const bool has_skeletons = false;
129 #else
130 	const bool has_skeletons = true;
131 #endif
132 	bool bootstrap = false;
133 	int i;
134 
135 	for (i = 0; commands[i].cmd; i++) {
136 		if (!strcmp(commands[i].cmd, "prog")) {
137 			/* Assume we run a bootstrap version if "bpftool prog"
138 			 * is not available.
139 			 */
140 			bootstrap = !commands[i].func;
141 			break;
142 		}
143 	}
144 
145 	if (json_output) {
146 		jsonw_start_object(json_wtr);	/* root object */
147 
148 		jsonw_name(json_wtr, "version");
149 #ifdef BPFTOOL_VERSION
150 		jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
151 #else
152 		jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
153 			     BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
154 #endif
155 		jsonw_name(json_wtr, "libbpf_version");
156 		jsonw_printf(json_wtr, "\"%u.%u\"",
157 			     libbpf_major_version(), libbpf_minor_version());
158 
159 		jsonw_name(json_wtr, "features");
160 		jsonw_start_object(json_wtr);	/* features */
161 		jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
162 		jsonw_bool_field(json_wtr, "llvm", has_llvm);
163 		jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
164 		jsonw_bool_field(json_wtr, "bootstrap", bootstrap);
165 		jsonw_end_object(json_wtr);	/* features */
166 
167 		jsonw_end_object(json_wtr);	/* root object */
168 	} else {
169 		unsigned int nb_features = 0;
170 
171 #ifdef BPFTOOL_VERSION
172 		printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
173 #else
174 		printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
175 		       BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
176 #endif
177 		printf("using libbpf %s\n", libbpf_version_string());
178 		printf("features:");
179 		print_feature("libbfd", has_libbfd, &nb_features);
180 		print_feature("llvm", has_llvm, &nb_features);
181 		print_feature("skeletons", has_skeletons, &nb_features);
182 		print_feature("bootstrap", bootstrap, &nb_features);
183 		printf("\n");
184 	}
185 	return 0;
186 }
187 
188 int cmd_select(const struct cmd *cmds, int argc, char **argv,
189 	       int (*help)(int argc, char **argv))
190 {
191 	unsigned int i;
192 
193 	last_argc = argc;
194 	last_argv = argv;
195 	last_do_help = help;
196 
197 	if (argc < 1 && cmds[0].func)
198 		return cmds[0].func(argc, argv);
199 
200 	for (i = 0; cmds[i].cmd; i++) {
201 		if (is_prefix(*argv, cmds[i].cmd)) {
202 			if (!cmds[i].func) {
203 				p_err("command '%s' is not supported in bootstrap mode",
204 				      cmds[i].cmd);
205 				return -1;
206 			}
207 			return cmds[i].func(argc - 1, argv + 1);
208 		}
209 	}
210 
211 	help(argc - 1, argv + 1);
212 
213 	return -1;
214 }
215 
216 bool is_prefix(const char *pfx, const char *str)
217 {
218 	if (!pfx)
219 		return false;
220 	if (strlen(str) < strlen(pfx))
221 		return false;
222 
223 	return !memcmp(str, pfx, strlen(pfx));
224 }
225 
226 /* Last argument MUST be NULL pointer */
227 int detect_common_prefix(const char *arg, ...)
228 {
229 	unsigned int count = 0;
230 	const char *ref;
231 	char msg[256];
232 	va_list ap;
233 
234 	snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
235 	va_start(ap, arg);
236 	while ((ref = va_arg(ap, const char *))) {
237 		if (!is_prefix(arg, ref))
238 			continue;
239 		count++;
240 		if (count > 1)
241 			strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
242 		strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
243 	}
244 	va_end(ap);
245 	strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
246 
247 	if (count >= 2) {
248 		p_err("%s", msg);
249 		return -1;
250 	}
251 
252 	return 0;
253 }
254 
255 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
256 {
257 	unsigned char *data = arg;
258 	unsigned int i;
259 
260 	for (i = 0; i < n; i++) {
261 		const char *pfx = "";
262 
263 		if (!i)
264 			/* nothing */;
265 		else if (!(i % 16))
266 			fprintf(f, "\n");
267 		else if (!(i % 8))
268 			fprintf(f, "  ");
269 		else
270 			pfx = sep;
271 
272 		fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
273 	}
274 }
275 
276 /* Split command line into argument vector. */
277 static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
278 {
279 	static const char ws[] = " \t\r\n";
280 	char *cp = line;
281 	int n_argc = 0;
282 
283 	while (*cp) {
284 		/* Skip leading whitespace. */
285 		cp += strspn(cp, ws);
286 
287 		if (*cp == '\0')
288 			break;
289 
290 		if (n_argc >= (maxargs - 1)) {
291 			p_err("too many arguments to command %d", cmd_nb);
292 			return -1;
293 		}
294 
295 		/* Word begins with quote. */
296 		if (*cp == '\'' || *cp == '"') {
297 			char quote = *cp++;
298 
299 			n_argv[n_argc++] = cp;
300 			/* Find ending quote. */
301 			cp = strchr(cp, quote);
302 			if (!cp) {
303 				p_err("unterminated quoted string in command %d",
304 				      cmd_nb);
305 				return -1;
306 			}
307 		} else {
308 			n_argv[n_argc++] = cp;
309 
310 			/* Find end of word. */
311 			cp += strcspn(cp, ws);
312 			if (*cp == '\0')
313 				break;
314 		}
315 
316 		/* Separate words. */
317 		*cp++ = 0;
318 	}
319 	n_argv[n_argc] = NULL;
320 
321 	return n_argc;
322 }
323 
324 static int do_batch(int argc, char **argv)
325 {
326 	char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
327 	char *n_argv[BATCH_ARG_NB_MAX];
328 	unsigned int lines = 0;
329 	int n_argc;
330 	FILE *fp;
331 	char *cp;
332 	int err = 0;
333 	int i;
334 
335 	if (argc < 2) {
336 		p_err("too few parameters for batch");
337 		return -1;
338 	} else if (argc > 2) {
339 		p_err("too many parameters for batch");
340 		return -1;
341 	} else if (!is_prefix(*argv, "file")) {
342 		p_err("expected 'file', got: %s", *argv);
343 		return -1;
344 	}
345 	NEXT_ARG();
346 
347 	if (!strcmp(*argv, "-"))
348 		fp = stdin;
349 	else
350 		fp = fopen(*argv, "r");
351 	if (!fp) {
352 		p_err("Can't open file (%s): %s", *argv, strerror(errno));
353 		return -1;
354 	}
355 
356 	if (json_output)
357 		jsonw_start_array(json_wtr);
358 	while (fgets(buf, sizeof(buf), fp)) {
359 		cp = strchr(buf, '#');
360 		if (cp)
361 			*cp = '\0';
362 
363 		if (strlen(buf) == sizeof(buf) - 1) {
364 			errno = E2BIG;
365 			break;
366 		}
367 
368 		/* Append continuation lines if any (coming after a line ending
369 		 * with '\' in the batch file).
370 		 */
371 		while ((cp = strstr(buf, "\\\n")) != NULL) {
372 			if (!fgets(contline, sizeof(contline), fp) ||
373 			    strlen(contline) == 0) {
374 				p_err("missing continuation line on command %u",
375 				      lines);
376 				err = -1;
377 				goto err_close;
378 			}
379 
380 			cp = strchr(contline, '#');
381 			if (cp)
382 				*cp = '\0';
383 
384 			if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
385 				p_err("command %u is too long", lines);
386 				err = -1;
387 				goto err_close;
388 			}
389 			buf[strlen(buf) - 2] = '\0';
390 			strcat(buf, contline);
391 		}
392 
393 		n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
394 		if (!n_argc)
395 			continue;
396 		if (n_argc < 0) {
397 			err = n_argc;
398 			goto err_close;
399 		}
400 
401 		if (json_output) {
402 			jsonw_start_object(json_wtr);
403 			jsonw_name(json_wtr, "command");
404 			jsonw_start_array(json_wtr);
405 			for (i = 0; i < n_argc; i++)
406 				jsonw_string(json_wtr, n_argv[i]);
407 			jsonw_end_array(json_wtr);
408 			jsonw_name(json_wtr, "output");
409 		}
410 
411 		err = cmd_select(commands, n_argc, n_argv, do_help);
412 
413 		if (json_output)
414 			jsonw_end_object(json_wtr);
415 
416 		if (err)
417 			goto err_close;
418 
419 		lines++;
420 	}
421 
422 	if (errno && errno != ENOENT) {
423 		p_err("reading batch file failed: %s", strerror(errno));
424 		err = -1;
425 	} else {
426 		if (!json_output)
427 			printf("processed %u commands\n", lines);
428 	}
429 err_close:
430 	if (fp != stdin)
431 		fclose(fp);
432 
433 	if (json_output)
434 		jsonw_end_array(json_wtr);
435 
436 	return err;
437 }
438 
439 int main(int argc, char **argv)
440 {
441 	static const struct option options[] = {
442 		{ "json",	no_argument,	NULL,	'j' },
443 		{ "help",	no_argument,	NULL,	'h' },
444 		{ "pretty",	no_argument,	NULL,	'p' },
445 		{ "version",	no_argument,	NULL,	'V' },
446 		{ "bpffs",	no_argument,	NULL,	'f' },
447 		{ "mapcompat",	no_argument,	NULL,	'm' },
448 		{ "nomount",	no_argument,	NULL,	'n' },
449 		{ "debug",	no_argument,	NULL,	'd' },
450 		{ "use-loader",	no_argument,	NULL,	'L' },
451 		{ "base-btf",	required_argument, NULL, 'B' },
452 		{ 0 }
453 	};
454 	bool version_requested = false;
455 	int opt, ret;
456 
457 	setlinebuf(stdout);
458 
459 #ifdef USE_LIBCAP
460 	/* Libcap < 2.63 hooks before main() to compute the number of
461 	 * capabilities of the running kernel, and doing so it calls prctl()
462 	 * which may fail and set errno to non-zero.
463 	 * Let's reset errno to make sure this does not interfere with the
464 	 * batch mode.
465 	 */
466 	errno = 0;
467 #endif
468 
469 	last_do_help = do_help;
470 	pretty_output = false;
471 	json_output = false;
472 	show_pinned = false;
473 	block_mount = false;
474 	bin_name = "bpftool";
475 
476 	opterr = 0;
477 	while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
478 				  options, NULL)) >= 0) {
479 		switch (opt) {
480 		case 'V':
481 			version_requested = true;
482 			break;
483 		case 'h':
484 			return do_help(argc, argv);
485 		case 'p':
486 			pretty_output = true;
487 			/* fall through */
488 		case 'j':
489 			if (!json_output) {
490 				json_wtr = jsonw_new(stdout);
491 				if (!json_wtr) {
492 					p_err("failed to create JSON writer");
493 					return -1;
494 				}
495 				json_output = true;
496 			}
497 			jsonw_pretty(json_wtr, pretty_output);
498 			break;
499 		case 'f':
500 			show_pinned = true;
501 			break;
502 		case 'm':
503 			relaxed_maps = true;
504 			break;
505 		case 'n':
506 			block_mount = true;
507 			break;
508 		case 'd':
509 			libbpf_set_print(print_all_levels);
510 			verifier_logs = true;
511 			break;
512 		case 'B':
513 			base_btf = btf__parse(optarg, NULL);
514 			if (!base_btf) {
515 				p_err("failed to parse base BTF at '%s': %d\n",
516 				      optarg, -errno);
517 				return -1;
518 			}
519 			break;
520 		case 'L':
521 			use_loader = true;
522 			break;
523 		default:
524 			p_err("unrecognized option '%s'", argv[optind - 1]);
525 			if (json_output)
526 				clean_and_exit(-1);
527 			else
528 				usage();
529 		}
530 	}
531 
532 	argc -= optind;
533 	argv += optind;
534 	if (argc < 0)
535 		usage();
536 
537 	if (version_requested)
538 		ret = do_version(argc, argv);
539 	else
540 		ret = cmd_select(commands, argc, argv, do_help);
541 
542 	if (json_output)
543 		jsonw_destroy(&json_wtr);
544 
545 	btf__free(base_btf);
546 
547 	return ret;
548 }
549