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 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 45 void usage(void) 46 { 47 last_do_help(last_argc - 1, last_argv + 1); 48 49 clean_and_exit(-1); 50 } 51 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 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 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 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 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 */ 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 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. */ 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 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 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