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