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  
clean_and_exit(int i)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  
usage(void)45  void usage(void)
46  {
47  	last_do_help(last_argc - 1, last_argv + 1);
48  
49  	clean_and_exit(-1);
50  }
51  
do_help(int argc,char ** argv)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 }\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  	{ "version",	do_version },
91  	{ 0 }
92  };
93  
94  #ifndef BPFTOOL_VERSION
95  /* bpftool's major and minor version numbers are aligned on libbpf's. There is
96   * an offset of 6 for the version number, because bpftool's version was higher
97   * than libbpf's when we adopted this scheme. The patch number remains at 0
98   * for now. Set BPFTOOL_VERSION to override.
99   */
100  #define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
101  #define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
102  #define BPFTOOL_PATCH_VERSION 0
103  #endif
104  
105  static void
print_feature(const char * feature,bool state,unsigned int * nb_features)106  print_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  
do_version(int argc,char ** argv)114  static int do_version(int argc, char **argv)
115  {
116  #ifdef HAVE_LIBBFD_SUPPORT
117  	const bool has_libbfd = true;
118  #else
119  	const bool has_libbfd = false;
120  #endif
121  #ifdef HAVE_LLVM_SUPPORT
122  	const bool has_llvm = true;
123  #else
124  	const bool has_llvm = false;
125  #endif
126  #ifdef BPFTOOL_WITHOUT_SKELETONS
127  	const bool has_skeletons = false;
128  #else
129  	const bool has_skeletons = true;
130  #endif
131  	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_VERSION
149  		jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
150  #else
151  		jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
152  			     BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
153  #endif
154  		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_VERSION
171  		printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
172  #else
173  		printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
174  		       BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
175  #endif
176  		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  
cmd_select(const struct cmd * cmds,int argc,char ** argv,int (* help)(int argc,char ** argv))187  int 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  
is_prefix(const char * pfx,const char * str)215  bool 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 */
detect_common_prefix(const char * arg,...)226  int 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  
fprint_hex(FILE * f,void * arg,unsigned int n,const char * sep)254  void 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  		else
269  			pfx = sep;
270  
271  		fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
272  	}
273  }
274  
275  /* Split command line into argument vector. */
make_args(char * line,char * n_argv[],int maxargs,int cmd_nb)276  static 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  
do_batch(int argc,char ** argv)323  static 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  	else
349  		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 ending
368  		 * 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  	}
428  err_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  
main(int argc,char ** argv)438  int 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_LIBCAP
459  	/* Libcap < 2.63 hooks before main() to compute the number of
460  	 * 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 the
463  	 * batch mode.
464  	 */
465  	errno = 0;
466  #endif
467  
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  			else
527  				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