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