xref: /openbmc/linux/tools/bpf/bpftool/main.c (revision 31af04cd)
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.h>
13 
14 #include "main.h"
15 
16 #define BATCH_LINE_LEN_MAX 65536
17 #define BATCH_ARG_NB_MAX 4096
18 
19 const char *bin_name;
20 static int last_argc;
21 static char **last_argv;
22 static int (*last_do_help)(int argc, char **argv);
23 json_writer_t *json_wtr;
24 bool pretty_output;
25 bool json_output;
26 bool show_pinned;
27 bool block_mount;
28 int bpf_flags;
29 struct pinned_obj_table prog_table;
30 struct pinned_obj_table map_table;
31 
32 static void __noreturn clean_and_exit(int i)
33 {
34 	if (json_output)
35 		jsonw_destroy(&json_wtr);
36 
37 	exit(i);
38 }
39 
40 void usage(void)
41 {
42 	last_do_help(last_argc - 1, last_argv + 1);
43 
44 	clean_and_exit(-1);
45 }
46 
47 static int do_help(int argc, char **argv)
48 {
49 	if (json_output) {
50 		jsonw_null(json_wtr);
51 		return 0;
52 	}
53 
54 	fprintf(stderr,
55 		"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
56 		"       %s batch file FILE\n"
57 		"       %s version\n"
58 		"\n"
59 		"       OBJECT := { prog | map | cgroup | perf | net }\n"
60 		"       " HELP_SPEC_OPTIONS "\n"
61 		"",
62 		bin_name, bin_name, bin_name);
63 
64 	return 0;
65 }
66 
67 static int do_version(int argc, char **argv)
68 {
69 	if (json_output) {
70 		jsonw_start_object(json_wtr);
71 		jsonw_name(json_wtr, "version");
72 		jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
73 		jsonw_end_object(json_wtr);
74 	} else {
75 		printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
76 	}
77 	return 0;
78 }
79 
80 int cmd_select(const struct cmd *cmds, int argc, char **argv,
81 	       int (*help)(int argc, char **argv))
82 {
83 	unsigned int i;
84 
85 	last_argc = argc;
86 	last_argv = argv;
87 	last_do_help = help;
88 
89 	if (argc < 1 && cmds[0].func)
90 		return cmds[0].func(argc, argv);
91 
92 	for (i = 0; cmds[i].func; i++)
93 		if (is_prefix(*argv, cmds[i].cmd))
94 			return cmds[i].func(argc - 1, argv + 1);
95 
96 	help(argc - 1, argv + 1);
97 
98 	return -1;
99 }
100 
101 bool is_prefix(const char *pfx, const char *str)
102 {
103 	if (!pfx)
104 		return false;
105 	if (strlen(str) < strlen(pfx))
106 		return false;
107 
108 	return !memcmp(str, pfx, strlen(pfx));
109 }
110 
111 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
112 {
113 	unsigned char *data = arg;
114 	unsigned int i;
115 
116 	for (i = 0; i < n; i++) {
117 		const char *pfx = "";
118 
119 		if (!i)
120 			/* nothing */;
121 		else if (!(i % 16))
122 			fprintf(f, "\n");
123 		else if (!(i % 8))
124 			fprintf(f, "  ");
125 		else
126 			pfx = sep;
127 
128 		fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
129 	}
130 }
131 
132 /* Split command line into argument vector. */
133 static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
134 {
135 	static const char ws[] = " \t\r\n";
136 	char *cp = line;
137 	int n_argc = 0;
138 
139 	while (*cp) {
140 		/* Skip leading whitespace. */
141 		cp += strspn(cp, ws);
142 
143 		if (*cp == '\0')
144 			break;
145 
146 		if (n_argc >= (maxargs - 1)) {
147 			p_err("too many arguments to command %d", cmd_nb);
148 			return -1;
149 		}
150 
151 		/* Word begins with quote. */
152 		if (*cp == '\'' || *cp == '"') {
153 			char quote = *cp++;
154 
155 			n_argv[n_argc++] = cp;
156 			/* Find ending quote. */
157 			cp = strchr(cp, quote);
158 			if (!cp) {
159 				p_err("unterminated quoted string in command %d",
160 				      cmd_nb);
161 				return -1;
162 			}
163 		} else {
164 			n_argv[n_argc++] = cp;
165 
166 			/* Find end of word. */
167 			cp += strcspn(cp, ws);
168 			if (*cp == '\0')
169 				break;
170 		}
171 
172 		/* Separate words. */
173 		*cp++ = 0;
174 	}
175 	n_argv[n_argc] = NULL;
176 
177 	return n_argc;
178 }
179 
180 static int do_batch(int argc, char **argv);
181 
182 static const struct cmd cmds[] = {
183 	{ "help",	do_help },
184 	{ "batch",	do_batch },
185 	{ "prog",	do_prog },
186 	{ "map",	do_map },
187 	{ "cgroup",	do_cgroup },
188 	{ "perf",	do_perf },
189 	{ "net",	do_net },
190 	{ "version",	do_version },
191 	{ 0 }
192 };
193 
194 static int do_batch(int argc, char **argv)
195 {
196 	char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
197 	char *n_argv[BATCH_ARG_NB_MAX];
198 	unsigned int lines = 0;
199 	int n_argc;
200 	FILE *fp;
201 	char *cp;
202 	int err;
203 	int i;
204 
205 	if (argc < 2) {
206 		p_err("too few parameters for batch");
207 		return -1;
208 	} else if (!is_prefix(*argv, "file")) {
209 		p_err("expected 'file', got: %s", *argv);
210 		return -1;
211 	} else if (argc > 2) {
212 		p_err("too many parameters for batch");
213 		return -1;
214 	}
215 	NEXT_ARG();
216 
217 	if (!strcmp(*argv, "-"))
218 		fp = stdin;
219 	else
220 		fp = fopen(*argv, "r");
221 	if (!fp) {
222 		p_err("Can't open file (%s): %s", *argv, strerror(errno));
223 		return -1;
224 	}
225 
226 	if (json_output)
227 		jsonw_start_array(json_wtr);
228 	while (fgets(buf, sizeof(buf), fp)) {
229 		cp = strchr(buf, '#');
230 		if (cp)
231 			*cp = '\0';
232 
233 		if (strlen(buf) == sizeof(buf) - 1) {
234 			errno = E2BIG;
235 			break;
236 		}
237 
238 		/* Append continuation lines if any (coming after a line ending
239 		 * with '\' in the batch file).
240 		 */
241 		while ((cp = strstr(buf, "\\\n")) != NULL) {
242 			if (!fgets(contline, sizeof(contline), fp) ||
243 			    strlen(contline) == 0) {
244 				p_err("missing continuation line on command %d",
245 				      lines);
246 				err = -1;
247 				goto err_close;
248 			}
249 
250 			cp = strchr(contline, '#');
251 			if (cp)
252 				*cp = '\0';
253 
254 			if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
255 				p_err("command %d is too long", lines);
256 				err = -1;
257 				goto err_close;
258 			}
259 			buf[strlen(buf) - 2] = '\0';
260 			strcat(buf, contline);
261 		}
262 
263 		n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
264 		if (!n_argc)
265 			continue;
266 		if (n_argc < 0)
267 			goto err_close;
268 
269 		if (json_output) {
270 			jsonw_start_object(json_wtr);
271 			jsonw_name(json_wtr, "command");
272 			jsonw_start_array(json_wtr);
273 			for (i = 0; i < n_argc; i++)
274 				jsonw_string(json_wtr, n_argv[i]);
275 			jsonw_end_array(json_wtr);
276 			jsonw_name(json_wtr, "output");
277 		}
278 
279 		err = cmd_select(cmds, n_argc, n_argv, do_help);
280 
281 		if (json_output)
282 			jsonw_end_object(json_wtr);
283 
284 		if (err)
285 			goto err_close;
286 
287 		lines++;
288 	}
289 
290 	if (errno && errno != ENOENT) {
291 		p_err("reading batch file failed: %s", strerror(errno));
292 		err = -1;
293 	} else {
294 		if (!json_output)
295 			printf("processed %d commands\n", lines);
296 		err = 0;
297 	}
298 err_close:
299 	if (fp != stdin)
300 		fclose(fp);
301 
302 	if (json_output)
303 		jsonw_end_array(json_wtr);
304 
305 	return err;
306 }
307 
308 int main(int argc, char **argv)
309 {
310 	static const struct option options[] = {
311 		{ "json",	no_argument,	NULL,	'j' },
312 		{ "help",	no_argument,	NULL,	'h' },
313 		{ "pretty",	no_argument,	NULL,	'p' },
314 		{ "version",	no_argument,	NULL,	'V' },
315 		{ "bpffs",	no_argument,	NULL,	'f' },
316 		{ "mapcompat",	no_argument,	NULL,	'm' },
317 		{ "nomount",	no_argument,	NULL,	'n' },
318 		{ 0 }
319 	};
320 	int opt, ret;
321 
322 	last_do_help = do_help;
323 	pretty_output = false;
324 	json_output = false;
325 	show_pinned = false;
326 	block_mount = false;
327 	bin_name = argv[0];
328 
329 	hash_init(prog_table.table);
330 	hash_init(map_table.table);
331 
332 	opterr = 0;
333 	while ((opt = getopt_long(argc, argv, "Vhpjfmn",
334 				  options, NULL)) >= 0) {
335 		switch (opt) {
336 		case 'V':
337 			return do_version(argc, argv);
338 		case 'h':
339 			return do_help(argc, argv);
340 		case 'p':
341 			pretty_output = true;
342 			/* fall through */
343 		case 'j':
344 			if (!json_output) {
345 				json_wtr = jsonw_new(stdout);
346 				if (!json_wtr) {
347 					p_err("failed to create JSON writer");
348 					return -1;
349 				}
350 				json_output = true;
351 			}
352 			jsonw_pretty(json_wtr, pretty_output);
353 			break;
354 		case 'f':
355 			show_pinned = true;
356 			break;
357 		case 'm':
358 			bpf_flags = MAPS_RELAX_COMPAT;
359 			break;
360 		case 'n':
361 			block_mount = true;
362 			break;
363 		default:
364 			p_err("unrecognized option '%s'", argv[optind - 1]);
365 			if (json_output)
366 				clean_and_exit(-1);
367 			else
368 				usage();
369 		}
370 	}
371 
372 	argc -= optind;
373 	argv += optind;
374 	if (argc < 0)
375 		usage();
376 
377 	ret = cmd_select(cmds, argc, argv, do_help);
378 
379 	if (json_output)
380 		jsonw_destroy(&json_wtr);
381 
382 	if (show_pinned) {
383 		delete_pinned_obj_table(&prog_table);
384 		delete_pinned_obj_table(&map_table);
385 	}
386 
387 	return ret;
388 }
389