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