Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
0002 /* Copyright (C) 2017-2018 Netronome Systems, Inc. */
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 /* bpftool's major and minor version numbers are aligned on libbpf's. There is
0076  * an offset of 6 for the version number, because bpftool's version was higher
0077  * than libbpf's when we adopted this scheme. The patch number remains at 0
0078  * for now. Set BPFTOOL_VERSION to override.
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);   /* root object */
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);   /* features */
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); /* features */
0118 
0119         jsonw_end_object(json_wtr); /* root object */
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 /* Last argument MUST be NULL pointer */
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             /* nothing */;
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 /* Split command line into argument vector. */
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         /* Skip leading whitespace. */
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         /* Word begins with quote. */
0254         if (*cp == '\'' || *cp == '"') {
0255             char quote = *cp++;
0256 
0257             n_argv[n_argc++] = cp;
0258             /* Find ending quote. */
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             /* Find end of word. */
0269             cp += strcspn(cp, ws);
0270             if (*cp == '\0')
0271                 break;
0272         }
0273 
0274         /* Separate words. */
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         /* Append continuation lines if any (coming after a line ending
0347          * with '\' in the batch file).
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             /* fall through */
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         /* Allow legacy map definitions for skeleton generation.
0507          * It will still be rejected if users use LIBBPF_STRICT_ALL
0508          * mode for loading generated skeleton.
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 }