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