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                "                    {-V|--version} }\n"
  69                "",
  70                bin_name, bin_name, bin_name);
  71
  72        return 0;
  73}
  74
  75static 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
 117int 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
 145bool 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 */
 156int 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
 184void 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. */
 206static 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
 253static int do_batch(int argc, char **argv);
 254
 255static 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
 273static 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        }
 378err_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
 388int 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