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