0001
0002
0003
0004 #include <ctype.h>
0005 #include <errno.h>
0006 #include <getopt.h>
0007 #include <linux/bpf.h>
0008 #include <stdio.h>
0009 #include <stdlib.h>
0010 #include <string.h>
0011
0012 #include <bpf/bpf.h>
0013 #include <bpf/btf.h>
0014 #include <bpf/hashmap.h>
0015 #include <bpf/libbpf.h>
0016
0017 #include "main.h"
0018
0019 #define BATCH_LINE_LEN_MAX 65536
0020 #define BATCH_ARG_NB_MAX 4096
0021
0022 const char *bin_name;
0023 static int last_argc;
0024 static char **last_argv;
0025 static int (*last_do_help)(int argc, char **argv);
0026 json_writer_t *json_wtr;
0027 bool pretty_output;
0028 bool json_output;
0029 bool show_pinned;
0030 bool block_mount;
0031 bool verifier_logs;
0032 bool relaxed_maps;
0033 bool use_loader;
0034 bool legacy_libbpf;
0035 struct btf *base_btf;
0036 struct hashmap *refs_table;
0037
0038 static void __noreturn clean_and_exit(int i)
0039 {
0040 if (json_output)
0041 jsonw_destroy(&json_wtr);
0042
0043 exit(i);
0044 }
0045
0046 void usage(void)
0047 {
0048 last_do_help(last_argc - 1, last_argv + 1);
0049
0050 clean_and_exit(-1);
0051 }
0052
0053 static int do_help(int argc, char **argv)
0054 {
0055 if (json_output) {
0056 jsonw_null(json_wtr);
0057 return 0;
0058 }
0059
0060 fprintf(stderr,
0061 "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
0062 " %s batch file FILE\n"
0063 " %s version\n"
0064 "\n"
0065 " OBJECT := { prog | map | link | cgroup | perf | net | feature | btf | gen | struct_ops | iter }\n"
0066 " " HELP_SPEC_OPTIONS " |\n"
0067 " {-V|--version} }\n"
0068 "",
0069 bin_name, bin_name, bin_name);
0070
0071 return 0;
0072 }
0073
0074 #ifndef BPFTOOL_VERSION
0075
0076
0077
0078
0079
0080 #define BPFTOOL_MAJOR_VERSION (LIBBPF_MAJOR_VERSION + 6)
0081 #define BPFTOOL_MINOR_VERSION LIBBPF_MINOR_VERSION
0082 #define BPFTOOL_PATCH_VERSION 0
0083 #endif
0084
0085 static int do_version(int argc, char **argv)
0086 {
0087 #ifdef HAVE_LIBBFD_SUPPORT
0088 const bool has_libbfd = true;
0089 #else
0090 const bool has_libbfd = false;
0091 #endif
0092 #ifdef BPFTOOL_WITHOUT_SKELETONS
0093 const bool has_skeletons = false;
0094 #else
0095 const bool has_skeletons = true;
0096 #endif
0097
0098 if (json_output) {
0099 jsonw_start_object(json_wtr);
0100
0101 jsonw_name(json_wtr, "version");
0102 #ifdef BPFTOOL_VERSION
0103 jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
0104 #else
0105 jsonw_printf(json_wtr, "\"%d.%d.%d\"", BPFTOOL_MAJOR_VERSION,
0106 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
0107 #endif
0108 jsonw_name(json_wtr, "libbpf_version");
0109 jsonw_printf(json_wtr, "\"%d.%d\"",
0110 libbpf_major_version(), libbpf_minor_version());
0111
0112 jsonw_name(json_wtr, "features");
0113 jsonw_start_object(json_wtr);
0114 jsonw_bool_field(json_wtr, "libbfd", has_libbfd);
0115 jsonw_bool_field(json_wtr, "libbpf_strict", !legacy_libbpf);
0116 jsonw_bool_field(json_wtr, "skeletons", has_skeletons);
0117 jsonw_end_object(json_wtr);
0118
0119 jsonw_end_object(json_wtr);
0120 } else {
0121 unsigned int nb_features = 0;
0122
0123 #ifdef BPFTOOL_VERSION
0124 printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
0125 #else
0126 printf("%s v%d.%d.%d\n", bin_name, BPFTOOL_MAJOR_VERSION,
0127 BPFTOOL_MINOR_VERSION, BPFTOOL_PATCH_VERSION);
0128 #endif
0129 printf("using libbpf %s\n", libbpf_version_string());
0130 printf("features:");
0131 if (has_libbfd) {
0132 printf(" libbfd");
0133 nb_features++;
0134 }
0135 if (!legacy_libbpf) {
0136 printf("%s libbpf_strict", nb_features++ ? "," : "");
0137 nb_features++;
0138 }
0139 if (has_skeletons)
0140 printf("%s skeletons", nb_features++ ? "," : "");
0141 printf("\n");
0142 }
0143 return 0;
0144 }
0145
0146 int cmd_select(const struct cmd *cmds, int argc, char **argv,
0147 int (*help)(int argc, char **argv))
0148 {
0149 unsigned int i;
0150
0151 last_argc = argc;
0152 last_argv = argv;
0153 last_do_help = help;
0154
0155 if (argc < 1 && cmds[0].func)
0156 return cmds[0].func(argc, argv);
0157
0158 for (i = 0; cmds[i].cmd; i++) {
0159 if (is_prefix(*argv, cmds[i].cmd)) {
0160 if (!cmds[i].func) {
0161 p_err("command '%s' is not supported in bootstrap mode",
0162 cmds[i].cmd);
0163 return -1;
0164 }
0165 return cmds[i].func(argc - 1, argv + 1);
0166 }
0167 }
0168
0169 help(argc - 1, argv + 1);
0170
0171 return -1;
0172 }
0173
0174 bool is_prefix(const char *pfx, const char *str)
0175 {
0176 if (!pfx)
0177 return false;
0178 if (strlen(str) < strlen(pfx))
0179 return false;
0180
0181 return !memcmp(str, pfx, strlen(pfx));
0182 }
0183
0184
0185 int detect_common_prefix(const char *arg, ...)
0186 {
0187 unsigned int count = 0;
0188 const char *ref;
0189 char msg[256];
0190 va_list ap;
0191
0192 snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
0193 va_start(ap, arg);
0194 while ((ref = va_arg(ap, const char *))) {
0195 if (!is_prefix(arg, ref))
0196 continue;
0197 count++;
0198 if (count > 1)
0199 strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
0200 strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
0201 }
0202 va_end(ap);
0203 strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
0204
0205 if (count >= 2) {
0206 p_err("%s", msg);
0207 return -1;
0208 }
0209
0210 return 0;
0211 }
0212
0213 void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
0214 {
0215 unsigned char *data = arg;
0216 unsigned int i;
0217
0218 for (i = 0; i < n; i++) {
0219 const char *pfx = "";
0220
0221 if (!i)
0222 ;
0223 else if (!(i % 16))
0224 fprintf(f, "\n");
0225 else if (!(i % 8))
0226 fprintf(f, " ");
0227 else
0228 pfx = sep;
0229
0230 fprintf(f, "%s%02hhx", i ? pfx : "", data[i]);
0231 }
0232 }
0233
0234
0235 static int make_args(char *line, char *n_argv[], int maxargs, int cmd_nb)
0236 {
0237 static const char ws[] = " \t\r\n";
0238 char *cp = line;
0239 int n_argc = 0;
0240
0241 while (*cp) {
0242
0243 cp += strspn(cp, ws);
0244
0245 if (*cp == '\0')
0246 break;
0247
0248 if (n_argc >= (maxargs - 1)) {
0249 p_err("too many arguments to command %d", cmd_nb);
0250 return -1;
0251 }
0252
0253
0254 if (*cp == '\'' || *cp == '"') {
0255 char quote = *cp++;
0256
0257 n_argv[n_argc++] = cp;
0258
0259 cp = strchr(cp, quote);
0260 if (!cp) {
0261 p_err("unterminated quoted string in command %d",
0262 cmd_nb);
0263 return -1;
0264 }
0265 } else {
0266 n_argv[n_argc++] = cp;
0267
0268
0269 cp += strcspn(cp, ws);
0270 if (*cp == '\0')
0271 break;
0272 }
0273
0274
0275 *cp++ = 0;
0276 }
0277 n_argv[n_argc] = NULL;
0278
0279 return n_argc;
0280 }
0281
0282 static int do_batch(int argc, char **argv);
0283
0284 static const struct cmd cmds[] = {
0285 { "help", do_help },
0286 { "batch", do_batch },
0287 { "prog", do_prog },
0288 { "map", do_map },
0289 { "link", do_link },
0290 { "cgroup", do_cgroup },
0291 { "perf", do_perf },
0292 { "net", do_net },
0293 { "feature", do_feature },
0294 { "btf", do_btf },
0295 { "gen", do_gen },
0296 { "struct_ops", do_struct_ops },
0297 { "iter", do_iter },
0298 { "version", do_version },
0299 { 0 }
0300 };
0301
0302 static int do_batch(int argc, char **argv)
0303 {
0304 char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
0305 char *n_argv[BATCH_ARG_NB_MAX];
0306 unsigned int lines = 0;
0307 int n_argc;
0308 FILE *fp;
0309 char *cp;
0310 int err = 0;
0311 int i;
0312
0313 if (argc < 2) {
0314 p_err("too few parameters for batch");
0315 return -1;
0316 } else if (!is_prefix(*argv, "file")) {
0317 p_err("expected 'file', got: %s", *argv);
0318 return -1;
0319 } else if (argc > 2) {
0320 p_err("too many parameters for batch");
0321 return -1;
0322 }
0323 NEXT_ARG();
0324
0325 if (!strcmp(*argv, "-"))
0326 fp = stdin;
0327 else
0328 fp = fopen(*argv, "r");
0329 if (!fp) {
0330 p_err("Can't open file (%s): %s", *argv, strerror(errno));
0331 return -1;
0332 }
0333
0334 if (json_output)
0335 jsonw_start_array(json_wtr);
0336 while (fgets(buf, sizeof(buf), fp)) {
0337 cp = strchr(buf, '#');
0338 if (cp)
0339 *cp = '\0';
0340
0341 if (strlen(buf) == sizeof(buf) - 1) {
0342 errno = E2BIG;
0343 break;
0344 }
0345
0346
0347
0348
0349 while ((cp = strstr(buf, "\\\n")) != NULL) {
0350 if (!fgets(contline, sizeof(contline), fp) ||
0351 strlen(contline) == 0) {
0352 p_err("missing continuation line on command %d",
0353 lines);
0354 err = -1;
0355 goto err_close;
0356 }
0357
0358 cp = strchr(contline, '#');
0359 if (cp)
0360 *cp = '\0';
0361
0362 if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
0363 p_err("command %d is too long", lines);
0364 err = -1;
0365 goto err_close;
0366 }
0367 buf[strlen(buf) - 2] = '\0';
0368 strcat(buf, contline);
0369 }
0370
0371 n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
0372 if (!n_argc)
0373 continue;
0374 if (n_argc < 0) {
0375 err = n_argc;
0376 goto err_close;
0377 }
0378
0379 if (json_output) {
0380 jsonw_start_object(json_wtr);
0381 jsonw_name(json_wtr, "command");
0382 jsonw_start_array(json_wtr);
0383 for (i = 0; i < n_argc; i++)
0384 jsonw_string(json_wtr, n_argv[i]);
0385 jsonw_end_array(json_wtr);
0386 jsonw_name(json_wtr, "output");
0387 }
0388
0389 err = cmd_select(cmds, n_argc, n_argv, do_help);
0390
0391 if (json_output)
0392 jsonw_end_object(json_wtr);
0393
0394 if (err)
0395 goto err_close;
0396
0397 lines++;
0398 }
0399
0400 if (errno && errno != ENOENT) {
0401 p_err("reading batch file failed: %s", strerror(errno));
0402 err = -1;
0403 } else {
0404 if (!json_output)
0405 printf("processed %d commands\n", lines);
0406 }
0407 err_close:
0408 if (fp != stdin)
0409 fclose(fp);
0410
0411 if (json_output)
0412 jsonw_end_array(json_wtr);
0413
0414 return err;
0415 }
0416
0417 int main(int argc, char **argv)
0418 {
0419 static const struct option options[] = {
0420 { "json", no_argument, NULL, 'j' },
0421 { "help", no_argument, NULL, 'h' },
0422 { "pretty", no_argument, NULL, 'p' },
0423 { "version", no_argument, NULL, 'V' },
0424 { "bpffs", no_argument, NULL, 'f' },
0425 { "mapcompat", no_argument, NULL, 'm' },
0426 { "nomount", no_argument, NULL, 'n' },
0427 { "debug", no_argument, NULL, 'd' },
0428 { "use-loader", no_argument, NULL, 'L' },
0429 { "base-btf", required_argument, NULL, 'B' },
0430 { "legacy", no_argument, NULL, 'l' },
0431 { 0 }
0432 };
0433 bool version_requested = false;
0434 int opt, ret;
0435
0436 setlinebuf(stdout);
0437
0438 last_do_help = do_help;
0439 pretty_output = false;
0440 json_output = false;
0441 show_pinned = false;
0442 block_mount = false;
0443 bin_name = argv[0];
0444
0445 opterr = 0;
0446 while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
0447 options, NULL)) >= 0) {
0448 switch (opt) {
0449 case 'V':
0450 version_requested = true;
0451 break;
0452 case 'h':
0453 return do_help(argc, argv);
0454 case 'p':
0455 pretty_output = true;
0456
0457 case 'j':
0458 if (!json_output) {
0459 json_wtr = jsonw_new(stdout);
0460 if (!json_wtr) {
0461 p_err("failed to create JSON writer");
0462 return -1;
0463 }
0464 json_output = true;
0465 }
0466 jsonw_pretty(json_wtr, pretty_output);
0467 break;
0468 case 'f':
0469 show_pinned = true;
0470 break;
0471 case 'm':
0472 relaxed_maps = true;
0473 break;
0474 case 'n':
0475 block_mount = true;
0476 break;
0477 case 'd':
0478 libbpf_set_print(print_all_levels);
0479 verifier_logs = true;
0480 break;
0481 case 'B':
0482 base_btf = btf__parse(optarg, NULL);
0483 if (libbpf_get_error(base_btf)) {
0484 p_err("failed to parse base BTF at '%s': %ld\n",
0485 optarg, libbpf_get_error(base_btf));
0486 base_btf = NULL;
0487 return -1;
0488 }
0489 break;
0490 case 'L':
0491 use_loader = true;
0492 break;
0493 case 'l':
0494 legacy_libbpf = true;
0495 break;
0496 default:
0497 p_err("unrecognized option '%s'", argv[optind - 1]);
0498 if (json_output)
0499 clean_and_exit(-1);
0500 else
0501 usage();
0502 }
0503 }
0504
0505 if (!legacy_libbpf) {
0506
0507
0508
0509
0510 libbpf_set_strict_mode(LIBBPF_STRICT_ALL & ~LIBBPF_STRICT_MAP_DEFINITIONS);
0511 }
0512
0513 argc -= optind;
0514 argv += optind;
0515 if (argc < 0)
0516 usage();
0517
0518 if (version_requested)
0519 return do_version(argc, argv);
0520
0521 ret = cmd_select(cmds, argc, argv, do_help);
0522
0523 if (json_output)
0524 jsonw_destroy(&json_wtr);
0525
0526 btf__free(base_btf);
0527
0528 return ret;
0529 }