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