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 | token }\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 { "token", do_token }, 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, "\"%u.%u\"", 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, "skeletons", has_skeletons); 164 jsonw_bool_field(json_wtr, "bootstrap", bootstrap); 165 jsonw_end_object(json_wtr); /* features */ 166 167 jsonw_end_object(json_wtr); /* root object */ 168 } else { 169 unsigned int nb_features = 0; 170 171 #ifdef BPFTOOL_VERSION 172 printf("%s v%s\n", bin_name, BPFTOOL_VERSION); 173 #else 174 printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION, 175 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION); 176 #endif 177 printf("using libbpf %s\n", libbpf_version_string()); 178 printf("features:"); 179 print_feature("libbfd", has_libbfd, &nb_features); 180 print_feature("llvm", has_llvm, &nb_features); 181 print_feature("skeletons", has_skeletons, &nb_features); 182 print_feature("bootstrap", bootstrap, &nb_features); 183 printf("\n"); 184 } 185 return 0; 186 } 187 188 int cmd_select(const struct cmd *cmds, int argc, char **argv, 189 int (*help)(int argc, char **argv)) 190 { 191 unsigned int i; 192 193 last_argc = argc; 194 last_argv = argv; 195 last_do_help = help; 196 197 if (argc < 1 && cmds[0].func) 198 return cmds[0].func(argc, argv); 199 200 for (i = 0; cmds[i].cmd; i++) { 201 if (is_prefix(*argv, cmds[i].cmd)) { 202 if (!cmds[i].func) { 203 p_err("command '%s' is not supported in bootstrap mode", 204 cmds[i].cmd); 205 return -1; 206 } 207 return cmds[i].func(argc - 1, argv + 1); 208 } 209 } 210 211 help(argc - 1, argv + 1); 212 213 return -1; 214 } 215 216 bool is_prefix(const char *pfx, const char *str) 217 { 218 if (!pfx) 219 return false; 220 if (strlen(str) < strlen(pfx)) 221 return false; 222 223 return !memcmp(str, pfx, strlen(pfx)); 224 } 225 226 /* Last argument MUST be NULL pointer */ 227 int detect_common_prefix(const char *arg, ...) 228 { 229 unsigned int count = 0; 230 const char *ref; 231 char msg[256]; 232 va_list ap; 233 234 snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg); 235 va_start(ap, arg); 236 while ((ref = va_arg(ap, const char *))) { 237 if (!is_prefix(arg, ref)) 238 continue; 239 count++; 240 if (count > 1) 241 strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1); 242 strncat(msg, ref, sizeof(msg) - strlen(msg) - 1); 243 } 244 va_end(ap); 245 strncat(msg, "'", sizeof(msg) - strlen(msg) - 1); 246 247 if (count >= 2) { 248 p_err("%s", msg); 249 return -1; 250 } 251 252 return 0; 253 } 254 255 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) 256 { 257 unsigned char *data = arg; 258 unsigned int i; 259 260 for (i = 0; i < n; i++) { 261 const char *pfx = ""; 262 263 if (!i) 264 /* nothing */; 265 else if (!(i % 16)) 266 fprintf(f, "\n"); 267 else if (!(i % 8)) 268 fprintf(f, " "); 269 else 270 pfx = sep; 271 272 fprintf(f, "%s%02hhx", i ? pfx : "", data[i]); 273 } 274 } 275 276 /* Split command line into argument vector. */ 277 static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb) 278 { 279 static const char ws[] = " \t\r\n"; 280 char *cp = line; 281 int n_argc = 0; 282 283 while (*cp) { 284 /* Skip leading whitespace. */ 285 cp += strspn(cp, ws); 286 287 if (*cp == '\0') 288 break; 289 290 if (n_argc >= (maxargs - 1)) { 291 p_err("too many arguments to command %d", cmd_nb); 292 return -1; 293 } 294 295 /* Word begins with quote. */ 296 if (*cp == '\'' || *cp == '"') { 297 char quote = *cp++; 298 299 n_argv[n_argc++] = cp; 300 /* Find ending quote. */ 301 cp = strchr(cp, quote); 302 if (!cp) { 303 p_err("unterminated quoted string in command %d", 304 cmd_nb); 305 return -1; 306 } 307 } else { 308 n_argv[n_argc++] = cp; 309 310 /* Find end of word. */ 311 cp += strcspn(cp, ws); 312 if (*cp == '\0') 313 break; 314 } 315 316 /* Separate words. */ 317 *cp++ = 0; 318 } 319 n_argv[n_argc] = NULL; 320 321 return n_argc; 322 } 323 324 static int do_batch(int argc, char **argv) 325 { 326 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX]; 327 char *n_argv[BATCH_ARG_NB_MAX]; 328 unsigned int lines = 0; 329 int n_argc; 330 FILE *fp; 331 char *cp; 332 int err = 0; 333 int i; 334 335 if (argc < 2) { 336 p_err("too few parameters for batch"); 337 return -1; 338 } else if (argc > 2) { 339 p_err("too many parameters for batch"); 340 return -1; 341 } else if (!is_prefix(*argv, "file")) { 342 p_err("expected 'file', got: %s", *argv); 343 return -1; 344 } 345 NEXT_ARG(); 346 347 if (!strcmp(*argv, "-")) 348 fp = stdin; 349 else 350 fp = fopen(*argv, "r"); 351 if (!fp) { 352 p_err("Can't open file (%s): %s", *argv, strerror(errno)); 353 return -1; 354 } 355 356 if (json_output) 357 jsonw_start_array(json_wtr); 358 while (fgets(buf, sizeof(buf), fp)) { 359 cp = strchr(buf, '#'); 360 if (cp) 361 *cp = '\0'; 362 363 if (strlen(buf) == sizeof(buf) - 1) { 364 errno = E2BIG; 365 break; 366 } 367 368 /* Append continuation lines if any (coming after a line ending 369 * with '\' in the batch file). 370 */ 371 while ((cp = strstr(buf, "\\\n")) != NULL) { 372 if (!fgets(contline, sizeof(contline), fp) || 373 strlen(contline) == 0) { 374 p_err("missing continuation line on command %u", 375 lines); 376 err = -1; 377 goto err_close; 378 } 379 380 cp = strchr(contline, '#'); 381 if (cp) 382 *cp = '\0'; 383 384 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) { 385 p_err("command %u is too long", lines); 386 err = -1; 387 goto err_close; 388 } 389 buf[strlen(buf) - 2] = '\0'; 390 strcat(buf, contline); 391 } 392 393 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines); 394 if (!n_argc) 395 continue; 396 if (n_argc < 0) { 397 err = n_argc; 398 goto err_close; 399 } 400 401 if (json_output) { 402 jsonw_start_object(json_wtr); 403 jsonw_name(json_wtr, "command"); 404 jsonw_start_array(json_wtr); 405 for (i = 0; i < n_argc; i++) 406 jsonw_string(json_wtr, n_argv[i]); 407 jsonw_end_array(json_wtr); 408 jsonw_name(json_wtr, "output"); 409 } 410 411 err = cmd_select(commands, n_argc, n_argv, do_help); 412 413 if (json_output) 414 jsonw_end_object(json_wtr); 415 416 if (err) 417 goto err_close; 418 419 lines++; 420 } 421 422 if (errno && errno != ENOENT) { 423 p_err("reading batch file failed: %s", strerror(errno)); 424 err = -1; 425 } else { 426 if (!json_output) 427 printf("processed %u commands\n", lines); 428 } 429 err_close: 430 if (fp != stdin) 431 fclose(fp); 432 433 if (json_output) 434 jsonw_end_array(json_wtr); 435 436 return err; 437 } 438 439 int main(int argc, char **argv) 440 { 441 static const struct option options[] = { 442 { "json", no_argument, NULL, 'j' }, 443 { "help", no_argument, NULL, 'h' }, 444 { "pretty", no_argument, NULL, 'p' }, 445 { "version", no_argument, NULL, 'V' }, 446 { "bpffs", no_argument, NULL, 'f' }, 447 { "mapcompat", no_argument, NULL, 'm' }, 448 { "nomount", no_argument, NULL, 'n' }, 449 { "debug", no_argument, NULL, 'd' }, 450 { "use-loader", no_argument, NULL, 'L' }, 451 { "base-btf", required_argument, NULL, 'B' }, 452 { 0 } 453 }; 454 bool version_requested = false; 455 int opt, ret; 456 457 setlinebuf(stdout); 458 459 #ifdef USE_LIBCAP 460 /* Libcap < 2.63 hooks before main() to compute the number of 461 * capabilities of the running kernel, and doing so it calls prctl() 462 * which may fail and set errno to non-zero. 463 * Let's reset errno to make sure this does not interfere with the 464 * batch mode. 465 */ 466 errno = 0; 467 #endif 468 469 last_do_help = do_help; 470 pretty_output = false; 471 json_output = false; 472 show_pinned = false; 473 block_mount = false; 474 bin_name = "bpftool"; 475 476 opterr = 0; 477 while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l", 478 options, NULL)) >= 0) { 479 switch (opt) { 480 case 'V': 481 version_requested = true; 482 break; 483 case 'h': 484 return do_help(argc, argv); 485 case 'p': 486 pretty_output = true; 487 /* fall through */ 488 case 'j': 489 if (!json_output) { 490 json_wtr = jsonw_new(stdout); 491 if (!json_wtr) { 492 p_err("failed to create JSON writer"); 493 return -1; 494 } 495 json_output = true; 496 } 497 jsonw_pretty(json_wtr, pretty_output); 498 break; 499 case 'f': 500 show_pinned = true; 501 break; 502 case 'm': 503 relaxed_maps = true; 504 break; 505 case 'n': 506 block_mount = true; 507 break; 508 case 'd': 509 libbpf_set_print(print_all_levels); 510 verifier_logs = true; 511 break; 512 case 'B': 513 base_btf = btf__parse(optarg, NULL); 514 if (!base_btf) { 515 p_err("failed to parse base BTF at '%s': %d\n", 516 optarg, -errno); 517 return -1; 518 } 519 break; 520 case 'L': 521 use_loader = true; 522 break; 523 default: 524 p_err("unrecognized option '%s'", argv[optind - 1]); 525 if (json_output) 526 clean_and_exit(-1); 527 else 528 usage(); 529 } 530 } 531 532 argc -= optind; 533 argv += optind; 534 if (argc < 0) 535 usage(); 536 537 if (version_requested) 538 ret = do_version(argc, argv); 539 else 540 ret = cmd_select(commands, argc, argv, do_help); 541 542 if (json_output) 543 jsonw_destroy(&json_wtr); 544 545 btf__free(base_btf); 546 547 return ret; 548 } 549