main.c 7.3 KB
Newer Older
1 2
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2017-2018 Netronome Systems, Inc. */
J
Jakub Kicinski 已提交
3 4 5

#include <ctype.h>
#include <errno.h>
6
#include <getopt.h>
J
Jakub Kicinski 已提交
7 8 9 10 11 12 13 14 15
#include <linux/bpf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <bpf.h>

#include "main.h"

16 17 18
#define BATCH_LINE_LEN_MAX 65536
#define BATCH_ARG_NB_MAX 4096

J
Jakub Kicinski 已提交
19 20 21 22
const char *bin_name;
static int last_argc;
static char **last_argv;
static int (*last_do_help)(int argc, char **argv);
23 24 25
json_writer_t *json_wtr;
bool pretty_output;
bool json_output;
26
bool show_pinned;
27
int bpf_flags;
28 29
struct pinned_obj_table prog_table;
struct pinned_obj_table map_table;
J
Jakub Kicinski 已提交
30

31 32 33 34 35 36 37 38
static void __noreturn clean_and_exit(int i)
{
	if (json_output)
		jsonw_destroy(&json_wtr);

	exit(i);
}

J
Jakub Kicinski 已提交
39 40 41 42
void usage(void)
{
	last_do_help(last_argc - 1, last_argv + 1);

43
	clean_and_exit(-1);
J
Jakub Kicinski 已提交
44 45 46 47
}

static int do_help(int argc, char **argv)
{
48 49 50 51 52
	if (json_output) {
		jsonw_null(json_wtr);
		return 0;
	}

J
Jakub Kicinski 已提交
53
	fprintf(stderr,
54
		"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
J
Jakub Kicinski 已提交
55
		"       %s batch file FILE\n"
56
		"       %s version\n"
J
Jakub Kicinski 已提交
57
		"\n"
58
		"       OBJECT := { prog | map | cgroup | perf | net }\n"
59 60
		"       " HELP_SPEC_OPTIONS "\n"
		"",
61
		bin_name, bin_name, bin_name);
J
Jakub Kicinski 已提交
62 63 64 65

	return 0;
}

66 67
static int do_version(int argc, char **argv)
{
68 69 70
	if (json_output) {
		jsonw_start_object(json_wtr);
		jsonw_name(json_wtr, "version");
71
		jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
72 73
		jsonw_end_object(json_wtr);
	} else {
74
		printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
75
	}
76 77 78
	return 0;
}

J
Jakub Kicinski 已提交
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
int cmd_select(const struct cmd *cmds, int argc, char **argv,
	       int (*help)(int argc, char **argv))
{
	unsigned int i;

	last_argc = argc;
	last_argv = argv;
	last_do_help = help;

	if (argc < 1 && cmds[0].func)
		return cmds[0].func(argc, argv);

	for (i = 0; cmds[i].func; i++)
		if (is_prefix(*argv, cmds[i].cmd))
			return cmds[i].func(argc - 1, argv + 1);

	help(argc - 1, argv + 1);

	return -1;
}

bool is_prefix(const char *pfx, const char *str)
{
	if (!pfx)
		return false;
	if (strlen(str) < strlen(pfx))
		return false;

	return !memcmp(str, pfx, strlen(pfx));
}

110
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
J
Jakub Kicinski 已提交
111 112 113 114 115 116 117 118 119 120
{
	unsigned char *data = arg;
	unsigned int i;

	for (i = 0; i < n; i++) {
		const char *pfx = "";

		if (!i)
			/* nothing */;
		else if (!(i % 16))
121
			fprintf(f, "\n");
J
Jakub Kicinski 已提交
122
		else if (!(i % 8))
123
			fprintf(f, "  ");
J
Jakub Kicinski 已提交
124 125 126
		else
			pfx = sep;

127
		fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
J
Jakub Kicinski 已提交
128 129 130
	}
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
/* Split command line into argument vector. */
static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
{
	static const char ws[] = " \t\r\n";
	char *cp = line;
	int n_argc = 0;

	while (*cp) {
		/* Skip leading whitespace. */
		cp += strspn(cp, ws);

		if (*cp == '\0')
			break;

		if (n_argc >= (maxargs - 1)) {
			p_err("too many arguments to command %d", cmd_nb);
			return -1;
		}

		/* Word begins with quote. */
		if (*cp == '\'' || *cp == '"') {
			char quote = *cp++;

			n_argv[n_argc++] = cp;
			/* Find ending quote. */
			cp = strchr(cp, quote);
			if (!cp) {
				p_err("unterminated quoted string in command %d",
				      cmd_nb);
				return -1;
			}
		} else {
			n_argv[n_argc++] = cp;

			/* Find end of word. */
			cp += strcspn(cp, ws);
			if (*cp == '\0')
				break;
		}

		/* Separate words. */
		*cp++ = 0;
	}
	n_argv[n_argc] = NULL;

	return n_argc;
}

J
Jakub Kicinski 已提交
179 180 181 182 183 184 185
static int do_batch(int argc, char **argv);

static const struct cmd cmds[] = {
	{ "help",	do_help },
	{ "batch",	do_batch },
	{ "prog",	do_prog },
	{ "map",	do_map },
186
	{ "cgroup",	do_cgroup },
187
	{ "perf",	do_perf },
188
	{ "net",	do_net },
189
	{ "version",	do_version },
J
Jakub Kicinski 已提交
190 191 192 193 194
	{ 0 }
};

static int do_batch(int argc, char **argv)
{
195 196
	char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
	char *n_argv[BATCH_ARG_NB_MAX];
J
Jakub Kicinski 已提交
197 198 199
	unsigned int lines = 0;
	int n_argc;
	FILE *fp;
200
	char *cp;
J
Jakub Kicinski 已提交
201
	int err;
202
	int i;
J
Jakub Kicinski 已提交
203 204

	if (argc < 2) {
205
		p_err("too few parameters for batch");
J
Jakub Kicinski 已提交
206 207
		return -1;
	} else if (!is_prefix(*argv, "file")) {
208
		p_err("expected 'file', got: %s", *argv);
J
Jakub Kicinski 已提交
209 210
		return -1;
	} else if (argc > 2) {
211
		p_err("too many parameters for batch");
J
Jakub Kicinski 已提交
212 213 214 215
		return -1;
	}
	NEXT_ARG();

216 217 218 219
	if (!strcmp(*argv, "-"))
		fp = stdin;
	else
		fp = fopen(*argv, "r");
J
Jakub Kicinski 已提交
220
	if (!fp) {
221
		p_err("Can't open file (%s): %s", *argv, strerror(errno));
J
Jakub Kicinski 已提交
222 223 224
		return -1;
	}

225 226
	if (json_output)
		jsonw_start_array(json_wtr);
J
Jakub Kicinski 已提交
227
	while (fgets(buf, sizeof(buf), fp)) {
228 229 230 231
		cp = strchr(buf, '#');
		if (cp)
			*cp = '\0';

J
Jakub Kicinski 已提交
232 233 234 235 236
		if (strlen(buf) == sizeof(buf) - 1) {
			errno = E2BIG;
			break;
		}

237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
		/* Append continuation lines if any (coming after a line ending
		 * with '\' in the batch file).
		 */
		while ((cp = strstr(buf, "\\\n")) != NULL) {
			if (!fgets(contline, sizeof(contline), fp) ||
			    strlen(contline) == 0) {
				p_err("missing continuation line on command %d",
				      lines);
				err = -1;
				goto err_close;
			}

			cp = strchr(contline, '#');
			if (cp)
				*cp = '\0';

			if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
				p_err("command %d is too long", lines);
				err = -1;
				goto err_close;
			}
			buf[strlen(buf) - 2] = '\0';
			strcat(buf, contline);
		}

262
		n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
J
Jakub Kicinski 已提交
263 264
		if (!n_argc)
			continue;
265 266
		if (n_argc < 0)
			goto err_close;
J
Jakub Kicinski 已提交
267

268 269 270 271 272 273 274 275 276 277
		if (json_output) {
			jsonw_start_object(json_wtr);
			jsonw_name(json_wtr, "command");
			jsonw_start_array(json_wtr);
			for (i = 0; i < n_argc; i++)
				jsonw_string(json_wtr, n_argv[i]);
			jsonw_end_array(json_wtr);
			jsonw_name(json_wtr, "output");
		}

J
Jakub Kicinski 已提交
278
		err = cmd_select(cmds, n_argc, n_argv, do_help);
279 280 281 282

		if (json_output)
			jsonw_end_object(json_wtr);

J
Jakub Kicinski 已提交
283 284 285 286 287 288 289
		if (err)
			goto err_close;

		lines++;
	}

	if (errno && errno != ENOENT) {
290
		p_err("reading batch file failed: %s", strerror(errno));
J
Jakub Kicinski 已提交
291 292
		err = -1;
	} else {
293 294
		if (!json_output)
			printf("processed %d commands\n", lines);
J
Jakub Kicinski 已提交
295 296 297
		err = 0;
	}
err_close:
298 299
	if (fp != stdin)
		fclose(fp);
J
Jakub Kicinski 已提交
300

301 302 303
	if (json_output)
		jsonw_end_array(json_wtr);

J
Jakub Kicinski 已提交
304 305 306 307 308
	return err;
}

int main(int argc, char **argv)
{
309
	static const struct option options[] = {
310
		{ "json",	no_argument,	NULL,	'j' },
311
		{ "help",	no_argument,	NULL,	'h' },
312
		{ "pretty",	no_argument,	NULL,	'p' },
313
		{ "version",	no_argument,	NULL,	'V' },
314
		{ "bpffs",	no_argument,	NULL,	'f' },
315
		{ "mapcompat",	no_argument,	NULL,	'm' },
316 317
		{ 0 }
	};
318
	int opt, ret;
319 320

	last_do_help = do_help;
321 322
	pretty_output = false;
	json_output = false;
323
	show_pinned = false;
J
Jakub Kicinski 已提交
324
	bin_name = argv[0];
325

326 327 328
	hash_init(prog_table.table);
	hash_init(map_table.table);

329
	opterr = 0;
330
	while ((opt = getopt_long(argc, argv, "Vhpjfm",
331 332 333 334 335 336
				  options, NULL)) >= 0) {
		switch (opt) {
		case 'V':
			return do_version(argc, argv);
		case 'h':
			return do_help(argc, argv);
337 338 339 340
		case 'p':
			pretty_output = true;
			/* fall through */
		case 'j':
341 342 343 344 345 346 347 348 349
			if (!json_output) {
				json_wtr = jsonw_new(stdout);
				if (!json_wtr) {
					p_err("failed to create JSON writer");
					return -1;
				}
				json_output = true;
			}
			jsonw_pretty(json_wtr, pretty_output);
350
			break;
351 352 353
		case 'f':
			show_pinned = true;
			break;
354 355 356
		case 'm':
			bpf_flags = MAPS_RELAX_COMPAT;
			break;
357
		default:
358 359 360 361 362
			p_err("unrecognized option '%s'", argv[optind - 1]);
			if (json_output)
				clean_and_exit(-1);
			else
				usage();
363 364 365 366 367 368 369
		}
	}

	argc -= optind;
	argv += optind;
	if (argc < 0)
		usage();
J
Jakub Kicinski 已提交
370

371 372 373 374 375
	ret = cmd_select(cmds, argc, argv, do_help);

	if (json_output)
		jsonw_destroy(&json_wtr);

376 377 378 379
	if (show_pinned) {
		delete_pinned_obj_table(&prog_table);
		delete_pinned_obj_table(&map_table);
	}
380

381
	return ret;
J
Jakub Kicinski 已提交
382
}