brintos

brintos / linux-shallow public Read only

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