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/libbpf.h> 14 #include <bpf/btf.h> 15 16 #include "main.h" 17 18 #define BATCH_LINE_LEN_MAX 65536 19 #define BATCH_ARG_NB_MAX 4096 20 21 const char *bin_name; 22 static int last_argc; 23 static char **last_argv; 24 static int (*last_do_help)(int argc, char **argv); 25 json_writer_t *json_wtr; 26 bool pretty_output; 27 bool json_output; 28 bool show_pinned; 29 bool block_mount; 30 bool verifier_logs; 31 bool relaxed_maps; 32 bool use_loader; 33 struct btf *base_btf; 34 struct pinned_obj_table prog_table; 35 struct pinned_obj_table map_table; 36 struct pinned_obj_table link_table; 37 struct obj_refs_table refs_table; 38 39 static void __noreturn clean_and_exit(int i) 40 { 41 if (json_output) 42 jsonw_destroy(&json_wtr); 43 44 exit(i); 45 } 46 47 void usage(void) 48 { 49 last_do_help(last_argc - 1, last_argv + 1); 50 51 clean_and_exit(-1); 52 } 53 54 static int do_help(int argc, char **argv) 55 { 56 if (json_output) { 57 jsonw_null(json_wtr); 58 return 0; 59 } 60 61 fprintf(stderr, 62 "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n" 63 " %s batch file FILE\n" 64 " %s version\n" 65 "\n" 66 " OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter }\n" 67 " " HELP_SPEC_OPTIONS " |\n" 68 " {-V|--version} }\n" 69 "", 70 bin_name, bin_name, bin_name); 71 72 return 0; 73 } 74 75 static int do_version(int argc, char **argv) 76 { 77 #ifdef HAVE_LIBBFD_SUPPORT 78 const bool has_libbfd = true; 79 #else 80 const bool has_libbfd = false; 81 #endif 82 #ifdef BPFTOOL_WITHOUT_SKELETONS 83 const bool has_skeletons = false; 84 #else 85 const bool has_skeletons = true; 86 #endif 87 88 if (json_output) { 89 jsonw_start_object(json_wtr); /* root object */ 90 91 jsonw_name(json_wtr, "version"); 92 jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION); 93 94 jsonw_name(json_wtr, "features"); 95 jsonw_start_object(json_wtr); /* features */ 96 jsonw_bool_field(json_wtr, "libbfd", has_libbfd); 97 jsonw_bool_field(json_wtr, "skeletons", has_skeletons); 98 jsonw_end_object(json_wtr); /* features */ 99 100 jsonw_end_object(json_wtr); /* root object */ 101 } else { 102 unsigned int nb_features = 0; 103 104 printf("%s v%s\n", bin_name, BPFTOOL_VERSION); 105 printf("features:"); 106 if (has_libbfd) { 107 printf(" libbfd"); 108 nb_features++; 109 } 110 if (has_skeletons) 111 printf("%s skeletons", nb_features++ ? "," : ""); 112 printf("\n"); 113 } 114 return 0; 115 } 116 117 int cmd_select(const struct cmd *cmds, int argc, char **argv, 118 int (*help)(int argc, char **argv)) 119 { 120 unsigned int i; 121 122 last_argc = argc; 123 last_argv = argv; 124 last_do_help = help; 125 126 if (argc < 1 && cmds[0].func) 127 return cmds[0].func(argc, argv); 128 129 for (i = 0; cmds[i].cmd; i++) { 130 if (is_prefix(*argv, cmds[i].cmd)) { 131 if (!cmds[i].func) { 132 p_err("command '%s' is not supported in bootstrap mode", 133 cmds[i].cmd); 134 return -1; 135 } 136 return cmds[i].func(argc - 1, argv + 1); 137 } 138 } 139 140 help(argc - 1, argv + 1); 141 142 return -1; 143 } 144 145 bool is_prefix(const char *pfx, const char *str) 146 { 147 if (!pfx) 148 return false; 149 if (strlen(str) < strlen(pfx)) 150 return false; 151 152 return !memcmp(str, pfx, strlen(pfx)); 153 } 154 155 /* Last argument MUST be NULL pointer */ 156 int detect_common_prefix(const char *arg, ...) 157 { 158 unsigned int count = 0; 159 const char *ref; 160 char msg[256]; 161 va_list ap; 162 163 snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg); 164 va_start(ap, arg); 165 while ((ref = va_arg(ap, const char *))) { 166 if (!is_prefix(arg, ref)) 167 continue; 168 count++; 169 if (count > 1) 170 strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1); 171 strncat(msg, ref, sizeof(msg) - strlen(msg) - 1); 172 } 173 va_end(ap); 174 strncat(msg, "'", sizeof(msg) - strlen(msg) - 1); 175 176 if (count >= 2) { 177 p_err("%s", msg); 178 return -1; 179 } 180 181 return 0; 182 } 183 184 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep) 185 { 186 unsigned char *data = arg; 187 unsigned int i; 188 189 for (i = 0; i < n; i++) { 190 const char *pfx = ""; 191 192 if (!i) 193 /* nothing */; 194 else if (!(i % 16)) 195 fprintf(f, "\n"); 196 else if (!(i % 8)) 197 fprintf(f, " "); 198 else 199 pfx = sep; 200 201 fprintf(f, "%s%02hhx", i ? pfx : "", data[i]); 202 } 203 } 204 205 /* Split command line into argument vector. */ 206 static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb) 207 { 208 static const char ws[] = " \t\r\n"; 209 char *cp = line; 210 int n_argc = 0; 211 212 while (*cp) { 213 /* Skip leading whitespace. */ 214 cp += strspn(cp, ws); 215 216 if (*cp == '\0') 217 break; 218 219 if (n_argc >= (maxargs - 1)) { 220 p_err("too many arguments to command %d", cmd_nb); 221 return -1; 222 } 223 224 /* Word begins with quote. */ 225 if (*cp == '\'' || *cp == '"') { 226 char quote = *cp++; 227 228 n_argv[n_argc++] = cp; 229 /* Find ending quote. */ 230 cp = strchr(cp, quote); 231 if (!cp) { 232 p_err("unterminated quoted string in command %d", 233 cmd_nb); 234 return -1; 235 } 236 } else { 237 n_argv[n_argc++] = cp; 238 239 /* Find end of word. */ 240 cp += strcspn(cp, ws); 241 if (*cp == '\0') 242 break; 243 } 244 245 /* Separate words. */ 246 *cp++ = 0; 247 } 248 n_argv[n_argc] = NULL; 249 250 return n_argc; 251 } 252 253 static int do_batch(int argc, char **argv); 254 255 static const struct cmd cmds[] = { 256 { "help", do_help }, 257 { "batch", do_batch }, 258 { "prog", do_prog }, 259 { "map", do_map }, 260 { "link", do_link }, 261 { "cgroup", do_cgroup }, 262 { "perf", do_perf }, 263 { "net", do_net }, 264 { "feature", do_feature }, 265 { "btf", do_btf }, 266 { "gen", do_gen }, 267 { "struct_ops", do_struct_ops }, 268 { "iter", do_iter }, 269 { "version", do_version }, 270 { 0 } 271 }; 272 273 static int do_batch(int argc, char **argv) 274 { 275 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX]; 276 char *n_argv[BATCH_ARG_NB_MAX]; 277 unsigned int lines = 0; 278 int n_argc; 279 FILE *fp; 280 char *cp; 281 int err = 0; 282 int i; 283 284 if (argc < 2) { 285 p_err("too few parameters for batch"); 286 return -1; 287 } else if (!is_prefix(*argv, "file")) { 288 p_err("expected 'file', got: %s", *argv); 289 return -1; 290 } else if (argc > 2) { 291 p_err("too many parameters for batch"); 292 return -1; 293 } 294 NEXT_ARG(); 295 296 if (!strcmp(*argv, "-")) 297 fp = stdin; 298 else 299 fp = fopen(*argv, "r"); 300 if (!fp) { 301 p_err("Can't open file (%s): %s", *argv, strerror(errno)); 302 return -1; 303 } 304 305 if (json_output) 306 jsonw_start_array(json_wtr); 307 while (fgets(buf, sizeof(buf), fp)) { 308 cp = strchr(buf, '#'); 309 if (cp) 310 *cp = '\0'; 311 312 if (strlen(buf) == sizeof(buf) - 1) { 313 errno = E2BIG; 314 break; 315 } 316 317 /* Append continuation lines if any (coming after a line ending 318 * with '\' in the batch file). 319 */ 320 while ((cp = strstr(buf, "\\\n")) != NULL) { 321 if (!fgets(contline, sizeof(contline), fp) || 322 strlen(contline) == 0) { 323 p_err("missing continuation line on command %d", 324 lines); 325 err = -1; 326 goto err_close; 327 } 328 329 cp = strchr(contline, '#'); 330 if (cp) 331 *cp = '\0'; 332 333 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) { 334 p_err("command %d is too long", lines); 335 err = -1; 336 goto err_close; 337 } 338 buf[strlen(buf) - 2] = '\0'; 339 strcat(buf, contline); 340 } 341 342 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines); 343 if (!n_argc) 344 continue; 345 if (n_argc < 0) { 346 err = n_argc; 347 goto err_close; 348 } 349 350 if (json_output) { 351 jsonw_start_object(json_wtr); 352 jsonw_name(json_wtr, "command"); 353 jsonw_start_array(json_wtr); 354 for (i = 0; i < n_argc; i++) 355 jsonw_string(json_wtr, n_argv[i]); 356 jsonw_end_array(json_wtr); 357 jsonw_name(json_wtr, "output"); 358 } 359 360 err = cmd_select(cmds, n_argc, n_argv, do_help); 361 362 if (json_output) 363 jsonw_end_object(json_wtr); 364 365 if (err) 366 goto err_close; 367 368 lines++; 369 } 370 371 if (errno && errno != ENOENT) { 372 p_err("reading batch file failed: %s", strerror(errno)); 373 err = -1; 374 } else { 375 if (!json_output) 376 printf("processed %d commands\n", lines); 377 } 378 err_close: 379 if (fp != stdin) 380 fclose(fp); 381 382 if (json_output) 383 jsonw_end_array(json_wtr); 384 385 return err; 386 } 387 388 int main(int argc, char **argv) 389 { 390 static const struct option options[] = { 391 { "json", no_argument, NULL, 'j' }, 392 { "help", no_argument, NULL, 'h' }, 393 { "pretty", no_argument, NULL, 'p' }, 394 { "version", no_argument, NULL, 'V' }, 395 { "bpffs", no_argument, NULL, 'f' }, 396 { "mapcompat", no_argument, NULL, 'm' }, 397 { "nomount", no_argument, NULL, 'n' }, 398 { "debug", no_argument, NULL, 'd' }, 399 { "use-loader", no_argument, NULL, 'L' }, 400 { "base-btf", required_argument, NULL, 'B' }, 401 { 0 } 402 }; 403 int opt, ret; 404 405 last_do_help = do_help; 406 pretty_output = false; 407 json_output = false; 408 show_pinned = false; 409 block_mount = false; 410 bin_name = argv[0]; 411 412 hash_init(prog_table.table); 413 hash_init(map_table.table); 414 hash_init(link_table.table); 415 416 opterr = 0; 417 while ((opt = getopt_long(argc, argv, "VhpjfLmndB:", 418 options, NULL)) >= 0) { 419 switch (opt) { 420 case 'V': 421 return do_version(argc, argv); 422 case 'h': 423 return do_help(argc, argv); 424 case 'p': 425 pretty_output = true; 426 /* fall through */ 427 case 'j': 428 if (!json_output) { 429 json_wtr = jsonw_new(stdout); 430 if (!json_wtr) { 431 p_err("failed to create JSON writer"); 432 return -1; 433 } 434 json_output = true; 435 } 436 jsonw_pretty(json_wtr, pretty_output); 437 break; 438 case 'f': 439 show_pinned = true; 440 break; 441 case 'm': 442 relaxed_maps = true; 443 break; 444 case 'n': 445 block_mount = true; 446 break; 447 case 'd': 448 libbpf_set_print(print_all_levels); 449 verifier_logs = true; 450 break; 451 case 'B': 452 base_btf = btf__parse(optarg, NULL); 453 if (libbpf_get_error(base_btf)) { 454 p_err("failed to parse base BTF at '%s': %ld\n", 455 optarg, libbpf_get_error(base_btf)); 456 base_btf = NULL; 457 return -1; 458 } 459 break; 460 case 'L': 461 use_loader = true; 462 break; 463 default: 464 p_err("unrecognized option '%s'", argv[optind - 1]); 465 if (json_output) 466 clean_and_exit(-1); 467 else 468 usage(); 469 } 470 } 471 472 argc -= optind; 473 argv += optind; 474 if (argc < 0) 475 usage(); 476 477 ret = cmd_select(cmds, argc, argv, do_help); 478 479 if (json_output) 480 jsonw_destroy(&json_wtr); 481 482 if (show_pinned) { 483 delete_pinned_obj_table(&prog_table); 484 delete_pinned_obj_table(&map_table); 485 delete_pinned_obj_table(&link_table); 486 } 487 btf__free(base_btf); 488 489 return ret; 490 } 491