linux/tools/bpf/bpftool/main.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2017 Netronome Systems, Inc.
   3 *
   4 * This software is dual licensed under the GNU General License Version 2,
   5 * June 1991 as shown in the file COPYING in the top-level directory of this
   6 * source tree or the BSD 2-Clause License provided below.  You have the
   7 * option to license this software under the complete terms of either license.
   8 *
   9 * The BSD 2-Clause License:
  10 *
  11 *     Redistribution and use in source and binary forms, with or
  12 *     without modification, are permitted provided that the following
  13 *     conditions are met:
  14 *
  15 *      1. Redistributions of source code must retain the above
  16 *         copyright notice, this list of conditions and the following
  17 *         disclaimer.
  18 *
  19 *      2. Redistributions in binary form must reproduce the above
  20 *         copyright notice, this list of conditions and the following
  21 *         disclaimer in the documentation and/or other materials
  22 *         provided with the distribution.
  23 *
  24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  25 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  26 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  27 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
  28 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  29 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  30 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  31 * SOFTWARE.
  32 */
  33
  34/* Author: Jakub Kicinski <kubakici@wp.pl> */
  35
  36#include <bfd.h>
  37#include <ctype.h>
  38#include <errno.h>
  39#include <getopt.h>
  40#include <linux/bpf.h>
  41#include <stdio.h>
  42#include <stdlib.h>
  43#include <string.h>
  44
  45#include <bpf.h>
  46
  47#include "main.h"
  48
  49#define BATCH_LINE_LEN_MAX 65536
  50#define BATCH_ARG_NB_MAX 4096
  51
  52const char *bin_name;
  53static int last_argc;
  54static char **last_argv;
  55static int (*last_do_help)(int argc, char **argv);
  56json_writer_t *json_wtr;
  57bool pretty_output;
  58bool json_output;
  59bool show_pinned;
  60struct pinned_obj_table prog_table;
  61struct pinned_obj_table map_table;
  62
  63static void __noreturn clean_and_exit(int i)
  64{
  65        if (json_output)
  66                jsonw_destroy(&json_wtr);
  67
  68        exit(i);
  69}
  70
  71void usage(void)
  72{
  73        last_do_help(last_argc - 1, last_argv + 1);
  74
  75        clean_and_exit(-1);
  76}
  77
  78static int do_help(int argc, char **argv)
  79{
  80        if (json_output) {
  81                jsonw_null(json_wtr);
  82                return 0;
  83        }
  84
  85        fprintf(stderr,
  86                "Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
  87                "       %s batch file FILE\n"
  88                "       %s version\n"
  89                "\n"
  90                "       OBJECT := { prog | map | cgroup }\n"
  91                "       " HELP_SPEC_OPTIONS "\n"
  92                "",
  93                bin_name, bin_name, bin_name);
  94
  95        return 0;
  96}
  97
  98static int do_version(int argc, char **argv)
  99{
 100        if (json_output) {
 101                jsonw_start_object(json_wtr);
 102                jsonw_name(json_wtr, "version");
 103                jsonw_printf(json_wtr, "\"%s\"", BPFTOOL_VERSION);
 104                jsonw_end_object(json_wtr);
 105        } else {
 106                printf("%s v%s\n", bin_name, BPFTOOL_VERSION);
 107        }
 108        return 0;
 109}
 110
 111int cmd_select(const struct cmd *cmds, int argc, char **argv,
 112               int (*help)(int argc, char **argv))
 113{
 114        unsigned int i;
 115
 116        last_argc = argc;
 117        last_argv = argv;
 118        last_do_help = help;
 119
 120        if (argc < 1 && cmds[0].func)
 121                return cmds[0].func(argc, argv);
 122
 123        for (i = 0; cmds[i].func; i++)
 124                if (is_prefix(*argv, cmds[i].cmd))
 125                        return cmds[i].func(argc - 1, argv + 1);
 126
 127        help(argc - 1, argv + 1);
 128
 129        return -1;
 130}
 131
 132bool is_prefix(const char *pfx, const char *str)
 133{
 134        if (!pfx)
 135                return false;
 136        if (strlen(str) < strlen(pfx))
 137                return false;
 138
 139        return !memcmp(str, pfx, strlen(pfx));
 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        { "version",    do_version },
 220        { 0 }
 221};
 222
 223static int do_batch(int argc, char **argv)
 224{
 225        char buf[BATCH_LINE_LEN_MAX], contline[BATCH_LINE_LEN_MAX];
 226        char *n_argv[BATCH_ARG_NB_MAX];
 227        unsigned int lines = 0;
 228        int n_argc;
 229        FILE *fp;
 230        char *cp;
 231        int err;
 232        int i;
 233
 234        if (argc < 2) {
 235                p_err("too few parameters for batch");
 236                return -1;
 237        } else if (!is_prefix(*argv, "file")) {
 238                p_err("expected 'file', got: %s", *argv);
 239                return -1;
 240        } else if (argc > 2) {
 241                p_err("too many parameters for batch");
 242                return -1;
 243        }
 244        NEXT_ARG();
 245
 246        if (!strcmp(*argv, "-"))
 247                fp = stdin;
 248        else
 249                fp = fopen(*argv, "r");
 250        if (!fp) {
 251                p_err("Can't open file (%s): %s", *argv, strerror(errno));
 252                return -1;
 253        }
 254
 255        if (json_output)
 256                jsonw_start_array(json_wtr);
 257        while (fgets(buf, sizeof(buf), fp)) {
 258                cp = strchr(buf, '#');
 259                if (cp)
 260                        *cp = '\0';
 261
 262                if (strlen(buf) == sizeof(buf) - 1) {
 263                        errno = E2BIG;
 264                        break;
 265                }
 266
 267                /* Append continuation lines if any (coming after a line ending
 268                 * with '\' in the batch file).
 269                 */
 270                while ((cp = strstr(buf, "\\\n")) != NULL) {
 271                        if (!fgets(contline, sizeof(contline), fp) ||
 272                            strlen(contline) == 0) {
 273                                p_err("missing continuation line on command %d",
 274                                      lines);
 275                                err = -1;
 276                                goto err_close;
 277                        }
 278
 279                        cp = strchr(contline, '#');
 280                        if (cp)
 281                                *cp = '\0';
 282
 283                        if (strlen(buf) + strlen(contline) + 1 > sizeof(buf)) {
 284                                p_err("command %d is too long", lines);
 285                                err = -1;
 286                                goto err_close;
 287                        }
 288                        buf[strlen(buf) - 2] = '\0';
 289                        strcat(buf, contline);
 290                }
 291
 292                n_argc = make_args(buf, n_argv, BATCH_ARG_NB_MAX, lines);
 293                if (!n_argc)
 294                        continue;
 295                if (n_argc < 0)
 296                        goto err_close;
 297
 298                if (json_output) {
 299                        jsonw_start_object(json_wtr);
 300                        jsonw_name(json_wtr, "command");
 301                        jsonw_start_array(json_wtr);
 302                        for (i = 0; i < n_argc; i++)
 303                                jsonw_string(json_wtr, n_argv[i]);
 304                        jsonw_end_array(json_wtr);
 305                        jsonw_name(json_wtr, "output");
 306                }
 307
 308                err = cmd_select(cmds, n_argc, n_argv, do_help);
 309
 310                if (json_output)
 311                        jsonw_end_object(json_wtr);
 312
 313                if (err)
 314                        goto err_close;
 315
 316                lines++;
 317        }
 318
 319        if (errno && errno != ENOENT) {
 320                p_err("reading batch file failed: %s", strerror(errno));
 321                err = -1;
 322        } else {
 323                p_info("processed %d commands", lines);
 324                err = 0;
 325        }
 326err_close:
 327        if (fp != stdin)
 328                fclose(fp);
 329
 330        if (json_output)
 331                jsonw_end_array(json_wtr);
 332
 333        return err;
 334}
 335
 336int main(int argc, char **argv)
 337{
 338        static const struct option options[] = {
 339                { "json",       no_argument,    NULL,   'j' },
 340                { "help",       no_argument,    NULL,   'h' },
 341                { "pretty",     no_argument,    NULL,   'p' },
 342                { "version",    no_argument,    NULL,   'V' },
 343                { "bpffs",      no_argument,    NULL,   'f' },
 344                { 0 }
 345        };
 346        int opt, ret;
 347
 348        last_do_help = do_help;
 349        pretty_output = false;
 350        json_output = false;
 351        show_pinned = false;
 352        bin_name = argv[0];
 353
 354        hash_init(prog_table.table);
 355        hash_init(map_table.table);
 356
 357        opterr = 0;
 358        while ((opt = getopt_long(argc, argv, "Vhpjf",
 359                                  options, NULL)) >= 0) {
 360                switch (opt) {
 361                case 'V':
 362                        return do_version(argc, argv);
 363                case 'h':
 364                        return do_help(argc, argv);
 365                case 'p':
 366                        pretty_output = true;
 367                        /* fall through */
 368                case 'j':
 369                        if (!json_output) {
 370                                json_wtr = jsonw_new(stdout);
 371                                if (!json_wtr) {
 372                                        p_err("failed to create JSON writer");
 373                                        return -1;
 374                                }
 375                                json_output = true;
 376                        }
 377                        jsonw_pretty(json_wtr, pretty_output);
 378                        break;
 379                case 'f':
 380                        show_pinned = true;
 381                        break;
 382                default:
 383                        p_err("unrecognized option '%s'", argv[optind - 1]);
 384                        if (json_output)
 385                                clean_and_exit(-1);
 386                        else
 387                                usage();
 388                }
 389        }
 390
 391        argc -= optind;
 392        argv += optind;
 393        if (argc < 0)
 394                usage();
 395
 396        bfd_init();
 397
 398        ret = cmd_select(cmds, argc, argv, do_help);
 399
 400        if (json_output)
 401                jsonw_destroy(&json_wtr);
 402
 403        if (show_pinned) {
 404                delete_pinned_obj_table(&prog_table);
 405                delete_pinned_obj_table(&map_table);
 406        }
 407
 408        return ret;
 409}
 410