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