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