iproute2/vdpa/vdpa.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2
   3#include <stdio.h>
   4#include <getopt.h>
   5#include <errno.h>
   6#include <linux/genetlink.h>
   7#include <linux/vdpa.h>
   8#include <linux/virtio_ids.h>
   9#include <linux/netlink.h>
  10#include <libmnl/libmnl.h>
  11#include "mnl_utils.h"
  12
  13#include "version.h"
  14#include "json_print.h"
  15#include "utils.h"
  16
  17#define VDPA_OPT_MGMTDEV_HANDLE         BIT(0)
  18#define VDPA_OPT_VDEV_MGMTDEV_HANDLE    BIT(1)
  19#define VDPA_OPT_VDEV_NAME              BIT(2)
  20#define VDPA_OPT_VDEV_HANDLE            BIT(3)
  21
  22struct vdpa_opts {
  23        uint64_t present; /* flags of present items */
  24        char *mdev_bus_name;
  25        char *mdev_name;
  26        const char *vdev_name;
  27        unsigned int device_id;
  28};
  29
  30struct vdpa {
  31        struct mnlu_gen_socket nlg;
  32        struct vdpa_opts opts;
  33        bool json_output;
  34        struct indent_mem *indent;
  35};
  36
  37static void pr_out_section_start(struct vdpa *vdpa, const char *name)
  38{
  39        open_json_object(NULL);
  40        open_json_object(name);
  41}
  42
  43static void pr_out_section_end(struct vdpa *vdpa)
  44{
  45        close_json_object();
  46        close_json_object();
  47}
  48
  49static void pr_out_array_start(struct vdpa *vdpa, const char *name)
  50{
  51        if (!vdpa->json_output) {
  52                print_nl();
  53                inc_indent(vdpa->indent);
  54                print_indent(vdpa->indent);
  55        }
  56        open_json_array(PRINT_ANY, name);
  57}
  58
  59static void pr_out_array_end(struct vdpa *vdpa)
  60{
  61        close_json_array(PRINT_JSON, NULL);
  62        if (!vdpa->json_output)
  63                dec_indent(vdpa->indent);
  64}
  65
  66static const enum mnl_attr_data_type vdpa_policy[VDPA_ATTR_MAX + 1] = {
  67        [VDPA_ATTR_MGMTDEV_BUS_NAME] = MNL_TYPE_NUL_STRING,
  68        [VDPA_ATTR_MGMTDEV_DEV_NAME] = MNL_TYPE_NUL_STRING,
  69        [VDPA_ATTR_DEV_NAME] = MNL_TYPE_STRING,
  70        [VDPA_ATTR_DEV_ID] = MNL_TYPE_U32,
  71        [VDPA_ATTR_DEV_VENDOR_ID] = MNL_TYPE_U32,
  72        [VDPA_ATTR_DEV_MAX_VQS] = MNL_TYPE_U32,
  73        [VDPA_ATTR_DEV_MAX_VQ_SIZE] = MNL_TYPE_U16,
  74};
  75
  76static int attr_cb(const struct nlattr *attr, void *data)
  77{
  78        const struct nlattr **tb = data;
  79        int type;
  80
  81        if (mnl_attr_type_valid(attr, VDPA_ATTR_MAX) < 0)
  82                return MNL_CB_OK;
  83
  84        type = mnl_attr_get_type(attr);
  85        if (mnl_attr_validate(attr, vdpa_policy[type]) < 0)
  86                return MNL_CB_ERROR;
  87
  88        tb[type] = attr;
  89        return MNL_CB_OK;
  90}
  91
  92static int vdpa_argv_handle(struct vdpa *vdpa, int argc, char **argv,
  93                            char **p_mdev_bus_name,
  94                            char **p_mdev_name)
  95{
  96        unsigned int slashcount;
  97        char *str;
  98
  99        if (argc <= 0 || *argv == NULL) {
 100                fprintf(stderr,
 101                        "vdpa identification (\"mgmtdev_bus_name/mgmtdev_name\") expected\n");
 102                return -EINVAL;
 103        }
 104        str = *argv;
 105        slashcount = get_str_char_count(str, '/');
 106        if (slashcount > 1) {
 107                fprintf(stderr,
 108                        "Wrong vdpa mgmtdev identification string format\n");
 109                fprintf(stderr, "Expected \"mgmtdev_bus_name/mgmtdev_name\"\n");
 110                fprintf(stderr, "Expected \"mgmtdev_name\"\n");
 111                return -EINVAL;
 112        }
 113        switch (slashcount) {
 114        case 0:
 115                *p_mdev_bus_name = NULL;
 116                *p_mdev_name = str;
 117                return 0;
 118        case 1:
 119                str_split_by_char(str, p_mdev_bus_name, p_mdev_name, '/');
 120                return 0;
 121        default:
 122                return -EINVAL;
 123        }
 124}
 125
 126static int vdpa_argv_str(struct vdpa *vdpa, int argc, char **argv,
 127                         const char **p_str)
 128{
 129        if (argc <= 0 || *argv == NULL) {
 130                fprintf(stderr, "String parameter expected\n");
 131                return -EINVAL;
 132        }
 133        *p_str = *argv;
 134        return 0;
 135}
 136
 137struct vdpa_args_metadata {
 138        uint64_t o_flag;
 139        const char *err_msg;
 140};
 141
 142static const struct vdpa_args_metadata vdpa_args_required[] = {
 143        {VDPA_OPT_VDEV_MGMTDEV_HANDLE, "management device handle not set."},
 144        {VDPA_OPT_VDEV_NAME, "device name is not set."},
 145        {VDPA_OPT_VDEV_HANDLE, "device name is not set."},
 146};
 147
 148static int vdpa_args_finding_required_validate(uint64_t o_required,
 149                                               uint64_t o_found)
 150{
 151        uint64_t o_flag;
 152        int i;
 153
 154        for (i = 0; i < ARRAY_SIZE(vdpa_args_required); i++) {
 155                o_flag = vdpa_args_required[i].o_flag;
 156                if ((o_required & o_flag) && !(o_found & o_flag)) {
 157                        fprintf(stderr, "%s\n", vdpa_args_required[i].err_msg);
 158                        return -EINVAL;
 159                }
 160        }
 161        if (o_required & ~o_found) {
 162                fprintf(stderr,
 163                        "BUG: unknown argument required but not found\n");
 164                return -EINVAL;
 165        }
 166        return 0;
 167}
 168
 169static void vdpa_opts_put(struct nlmsghdr *nlh, struct vdpa *vdpa)
 170{
 171        struct vdpa_opts *opts = &vdpa->opts;
 172
 173        if ((opts->present & VDPA_OPT_MGMTDEV_HANDLE) ||
 174            (opts->present & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
 175                if (opts->mdev_bus_name)
 176                        mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_BUS_NAME,
 177                                          opts->mdev_bus_name);
 178                mnl_attr_put_strz(nlh, VDPA_ATTR_MGMTDEV_DEV_NAME,
 179                                  opts->mdev_name);
 180        }
 181        if ((opts->present & VDPA_OPT_VDEV_NAME) ||
 182            (opts->present & VDPA_OPT_VDEV_HANDLE))
 183                mnl_attr_put_strz(nlh, VDPA_ATTR_DEV_NAME, opts->vdev_name);
 184}
 185
 186static int vdpa_argv_parse(struct vdpa *vdpa, int argc, char **argv,
 187                           uint64_t o_required)
 188{
 189        struct vdpa_opts *opts = &vdpa->opts;
 190        uint64_t o_all = o_required;
 191        uint64_t o_found = 0;
 192        int err;
 193
 194        if (o_required & VDPA_OPT_MGMTDEV_HANDLE) {
 195                err = vdpa_argv_handle(vdpa, argc, argv, &opts->mdev_bus_name,
 196                                       &opts->mdev_name);
 197                if (err)
 198                        return err;
 199
 200                NEXT_ARG_FWD();
 201                o_found |= VDPA_OPT_MGMTDEV_HANDLE;
 202        } else if (o_required & VDPA_OPT_VDEV_HANDLE) {
 203                err = vdpa_argv_str(vdpa, argc, argv, &opts->vdev_name);
 204                if (err)
 205                        return err;
 206
 207                NEXT_ARG_FWD();
 208                o_found |= VDPA_OPT_VDEV_HANDLE;
 209        }
 210
 211        while (NEXT_ARG_OK()) {
 212                if ((matches(*argv, "name") == 0) &&
 213                    (o_all & VDPA_OPT_VDEV_NAME)) {
 214                        const char *namestr;
 215
 216                        NEXT_ARG_FWD();
 217                        err = vdpa_argv_str(vdpa, argc, argv, &namestr);
 218                        if (err)
 219                                return err;
 220                        opts->vdev_name = namestr;
 221                        NEXT_ARG_FWD();
 222                        o_found |= VDPA_OPT_VDEV_NAME;
 223                } else if ((matches(*argv, "mgmtdev")  == 0) &&
 224                           (o_all & VDPA_OPT_VDEV_MGMTDEV_HANDLE)) {
 225                        NEXT_ARG_FWD();
 226                        err = vdpa_argv_handle(vdpa, argc, argv,
 227                                               &opts->mdev_bus_name,
 228                                               &opts->mdev_name);
 229                        if (err)
 230                                return err;
 231
 232                        NEXT_ARG_FWD();
 233                        o_found |= VDPA_OPT_VDEV_MGMTDEV_HANDLE;
 234                } else {
 235                        fprintf(stderr, "Unknown option \"%s\"\n", *argv);
 236                        return -EINVAL;
 237                }
 238        }
 239
 240        opts->present = o_found;
 241
 242        return vdpa_args_finding_required_validate(o_required, o_found);
 243}
 244
 245static int vdpa_argv_parse_put(struct nlmsghdr *nlh, struct vdpa *vdpa,
 246                               int argc, char **argv,
 247                               uint64_t o_required)
 248{
 249        int err;
 250
 251        err = vdpa_argv_parse(vdpa, argc, argv, o_required);
 252        if (err)
 253                return err;
 254        vdpa_opts_put(nlh, vdpa);
 255        return 0;
 256}
 257
 258static void cmd_mgmtdev_help(void)
 259{
 260        fprintf(stderr, "Usage: vdpa mgmtdev show [ DEV ]\n");
 261}
 262
 263static void pr_out_handle_start(struct vdpa *vdpa, struct nlattr **tb)
 264{
 265        const char *mdev_bus_name = NULL;
 266        const char *mdev_name;
 267        SPRINT_BUF(buf);
 268
 269        mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
 270        if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME]) {
 271                mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
 272                sprintf(buf, "%s/%s", mdev_bus_name, mdev_name);
 273        } else {
 274                sprintf(buf, "%s", mdev_name);
 275        }
 276
 277        if (vdpa->json_output)
 278                open_json_object(buf);
 279        else
 280                printf("%s: ", buf);
 281}
 282
 283static void pr_out_handle_end(struct vdpa *vdpa)
 284{
 285        if (vdpa->json_output)
 286                close_json_object();
 287        else
 288                print_nl();
 289}
 290
 291static void __pr_out_vdev_handle_start(struct vdpa *vdpa, const char *vdev_name)
 292{
 293        SPRINT_BUF(buf);
 294
 295        sprintf(buf, "%s", vdev_name);
 296        if (vdpa->json_output)
 297                open_json_object(buf);
 298        else
 299                printf("%s: ", buf);
 300}
 301
 302static void pr_out_vdev_handle_start(struct vdpa *vdpa, struct nlattr **tb)
 303{
 304        const char *vdev_name;
 305
 306        vdev_name = mnl_attr_get_str(tb[VDPA_ATTR_DEV_NAME]);
 307        __pr_out_vdev_handle_start(vdpa, vdev_name);
 308}
 309
 310static void pr_out_vdev_handle_end(struct vdpa *vdpa)
 311{
 312        if (vdpa->json_output)
 313                close_json_object();
 314        else
 315                print_nl();
 316}
 317
 318static struct str_num_map class_map[] = {
 319        { .str = "net", .num = VIRTIO_ID_NET },
 320        { .str = "block", .num = VIRTIO_ID_BLOCK },
 321        { .str = NULL, },
 322};
 323
 324static const char *parse_class(int num)
 325{
 326        const char *class;
 327
 328        class = str_map_lookup_uint(class_map, num);
 329        return class ? class : "< unknown class >";
 330}
 331
 332static void pr_out_mgmtdev_show(struct vdpa *vdpa, const struct nlmsghdr *nlh,
 333                                struct nlattr **tb)
 334{
 335        const char *class;
 336        unsigned int i;
 337
 338        pr_out_handle_start(vdpa, tb);
 339
 340        if (tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]) {
 341                uint64_t classes = mnl_attr_get_u64(tb[VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES]);
 342
 343                pr_out_array_start(vdpa, "supported_classes");
 344
 345                for (i = 1; i < 64; i++) {
 346                        if ((classes & (1ULL << i)) == 0)
 347                                continue;
 348
 349                        class = parse_class(i);
 350                        print_string(PRINT_ANY, NULL, " %s", class);
 351                }
 352                pr_out_array_end(vdpa);
 353        }
 354
 355        pr_out_handle_end(vdpa);
 356}
 357
 358static int cmd_mgmtdev_show_cb(const struct nlmsghdr *nlh, void *data)
 359{
 360        struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
 361        struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
 362        struct vdpa *vdpa = data;
 363
 364        mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
 365
 366        if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME])
 367                return MNL_CB_ERROR;
 368
 369        pr_out_mgmtdev_show(vdpa, nlh, tb);
 370
 371        return MNL_CB_OK;
 372}
 373
 374static int cmd_mgmtdev_show(struct vdpa *vdpa, int argc, char **argv)
 375{
 376        uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
 377        struct nlmsghdr *nlh;
 378        int err;
 379
 380        if (argc == 0)
 381                flags |= NLM_F_DUMP;
 382
 383        nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_MGMTDEV_GET,
 384                                          flags);
 385        if (argc > 0) {
 386                err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
 387                                          VDPA_OPT_MGMTDEV_HANDLE);
 388                if (err)
 389                        return err;
 390        }
 391
 392        pr_out_section_start(vdpa, "mgmtdev");
 393        err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_mgmtdev_show_cb, vdpa);
 394        pr_out_section_end(vdpa);
 395        return err;
 396}
 397
 398static int cmd_mgmtdev(struct vdpa *vdpa, int argc, char **argv)
 399{
 400        if (!argc || matches(*argv, "help") == 0) {
 401                cmd_mgmtdev_help();
 402                return 0;
 403        } else if (matches(*argv, "show") == 0 ||
 404                   matches(*argv, "list") == 0) {
 405                return cmd_mgmtdev_show(vdpa, argc - 1, argv + 1);
 406        }
 407        fprintf(stderr, "Command \"%s\" not found\n", *argv);
 408        return -ENOENT;
 409}
 410
 411static void cmd_dev_help(void)
 412{
 413        fprintf(stderr, "Usage: vdpa dev show [ DEV ]\n");
 414        fprintf(stderr, "       vdpa dev add name NAME mgmtdev MANAGEMENTDEV\n");
 415        fprintf(stderr, "       vdpa dev del DEV\n");
 416}
 417
 418static const char *device_type_name(uint32_t type)
 419{
 420        switch (type) {
 421        case 0x1: return "network";
 422        case 0x2: return "block";
 423        default: return "<unknown type>";
 424        }
 425}
 426
 427static void pr_out_dev(struct vdpa *vdpa, struct nlattr **tb)
 428{
 429        const char *mdev_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_DEV_NAME]);
 430        uint32_t device_id = mnl_attr_get_u32(tb[VDPA_ATTR_DEV_ID]);
 431        const char *mdev_bus_name = NULL;
 432        char mgmtdev_buf[128];
 433
 434        if (tb[VDPA_ATTR_MGMTDEV_BUS_NAME])
 435                mdev_bus_name = mnl_attr_get_str(tb[VDPA_ATTR_MGMTDEV_BUS_NAME]);
 436
 437        if (mdev_bus_name)
 438                sprintf(mgmtdev_buf, "%s/%s", mdev_bus_name, mdev_name);
 439        else
 440                sprintf(mgmtdev_buf, "%s", mdev_name);
 441        pr_out_vdev_handle_start(vdpa, tb);
 442        print_string(PRINT_ANY, "type", "type %s", device_type_name(device_id));
 443        print_string(PRINT_ANY, "mgmtdev", " mgmtdev %s", mgmtdev_buf);
 444
 445        if (tb[VDPA_ATTR_DEV_VENDOR_ID])
 446                print_uint(PRINT_ANY, "vendor_id", " vendor_id %u",
 447                           mnl_attr_get_u32(tb[VDPA_ATTR_DEV_VENDOR_ID]));
 448        if (tb[VDPA_ATTR_DEV_MAX_VQS])
 449                print_uint(PRINT_ANY, "max_vqs", " max_vqs %u",
 450                           mnl_attr_get_u32(tb[VDPA_ATTR_DEV_MAX_VQS]));
 451        if (tb[VDPA_ATTR_DEV_MAX_VQ_SIZE])
 452                print_uint(PRINT_ANY, "max_vq_size", " max_vq_size %u",
 453                           mnl_attr_get_u16(tb[VDPA_ATTR_DEV_MAX_VQ_SIZE]));
 454        pr_out_vdev_handle_end(vdpa);
 455}
 456
 457static int cmd_dev_show_cb(const struct nlmsghdr *nlh, void *data)
 458{
 459        struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh);
 460        struct nlattr *tb[VDPA_ATTR_MAX + 1] = {};
 461        struct vdpa *vdpa = data;
 462
 463        mnl_attr_parse(nlh, sizeof(*genl), attr_cb, tb);
 464        if (!tb[VDPA_ATTR_MGMTDEV_DEV_NAME] ||
 465            !tb[VDPA_ATTR_DEV_NAME] || !tb[VDPA_ATTR_DEV_ID])
 466                return MNL_CB_ERROR;
 467        pr_out_dev(vdpa, tb);
 468        return MNL_CB_OK;
 469}
 470
 471static int cmd_dev_show(struct vdpa *vdpa, int argc, char **argv)
 472{
 473        uint16_t flags = NLM_F_REQUEST | NLM_F_ACK;
 474        struct nlmsghdr *nlh;
 475        int err;
 476
 477        if (argc <= 0)
 478                flags |= NLM_F_DUMP;
 479
 480        nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_GET, flags);
 481        if (argc > 0) {
 482                err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
 483                                          VDPA_OPT_VDEV_HANDLE);
 484                if (err)
 485                        return err;
 486        }
 487
 488        pr_out_section_start(vdpa, "dev");
 489        err = mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, cmd_dev_show_cb, vdpa);
 490        pr_out_section_end(vdpa);
 491        return err;
 492}
 493
 494static int cmd_dev_add(struct vdpa *vdpa, int argc, char **argv)
 495{
 496        struct nlmsghdr *nlh;
 497        int err;
 498
 499        nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_NEW,
 500                                          NLM_F_REQUEST | NLM_F_ACK);
 501        err = vdpa_argv_parse_put(nlh, vdpa, argc, argv,
 502                                  VDPA_OPT_VDEV_MGMTDEV_HANDLE | VDPA_OPT_VDEV_NAME);
 503        if (err)
 504                return err;
 505
 506        return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
 507}
 508
 509static int cmd_dev_del(struct vdpa *vdpa,  int argc, char **argv)
 510{
 511        struct nlmsghdr *nlh;
 512        int err;
 513
 514        nlh = mnlu_gen_socket_cmd_prepare(&vdpa->nlg, VDPA_CMD_DEV_DEL,
 515                                          NLM_F_REQUEST | NLM_F_ACK);
 516        err = vdpa_argv_parse_put(nlh, vdpa, argc, argv, VDPA_OPT_VDEV_HANDLE);
 517        if (err)
 518                return err;
 519
 520        return mnlu_gen_socket_sndrcv(&vdpa->nlg, nlh, NULL, NULL);
 521}
 522
 523static int cmd_dev(struct vdpa *vdpa, int argc, char **argv)
 524{
 525        if (!argc)
 526                return cmd_dev_show(vdpa, argc - 1, argv + 1);
 527
 528        if (matches(*argv, "help") == 0) {
 529                cmd_dev_help();
 530                return 0;
 531        } else if (matches(*argv, "show") == 0 ||
 532                   matches(*argv, "list") == 0) {
 533                return cmd_dev_show(vdpa, argc - 1, argv + 1);
 534        } else if (matches(*argv, "add") == 0) {
 535                return cmd_dev_add(vdpa, argc - 1, argv + 1);
 536        } else if (matches(*argv, "del") == 0) {
 537                return cmd_dev_del(vdpa, argc - 1, argv + 1);
 538        }
 539        fprintf(stderr, "Command \"%s\" not found\n", *argv);
 540        return -ENOENT;
 541}
 542
 543static void help(void)
 544{
 545        fprintf(stderr,
 546                "Usage: vdpa [ OPTIONS ] OBJECT { COMMAND | help }\n"
 547                "where  OBJECT := { mgmtdev | dev }\n"
 548                "       OPTIONS := { -V[ersion] | -n[o-nice-names] | -j[son] | -p[retty] | -v[erbose] }\n");
 549}
 550
 551static int vdpa_cmd(struct vdpa *vdpa, int argc, char **argv)
 552{
 553        if (!argc || matches(*argv, "help") == 0) {
 554                help();
 555                return 0;
 556        } else if (matches(*argv, "mgmtdev") == 0) {
 557                return cmd_mgmtdev(vdpa, argc - 1, argv + 1);
 558        } else if (matches(*argv, "dev") == 0) {
 559                return cmd_dev(vdpa, argc - 1, argv + 1);
 560        }
 561        fprintf(stderr, "Object \"%s\" not found\n", *argv);
 562        return -ENOENT;
 563}
 564
 565static int vdpa_init(struct vdpa *vdpa)
 566{
 567        int err;
 568
 569        err = mnlu_gen_socket_open(&vdpa->nlg, VDPA_GENL_NAME,
 570                                   VDPA_GENL_VERSION);
 571        if (err) {
 572                fprintf(stderr, "Failed to connect to vdpa Netlink\n");
 573                return -errno;
 574        }
 575        new_json_obj_plain(vdpa->json_output);
 576        return 0;
 577}
 578
 579static void vdpa_fini(struct vdpa *vdpa)
 580{
 581        delete_json_obj_plain();
 582        mnlu_gen_socket_close(&vdpa->nlg);
 583}
 584
 585static struct vdpa *vdpa_alloc(void)
 586{
 587        struct vdpa *vdpa = calloc(1, sizeof(struct vdpa));
 588
 589        if (!vdpa)
 590                return NULL;
 591
 592        vdpa->indent = alloc_indent_mem();
 593        if (!vdpa->indent)
 594                goto indent_err;
 595
 596        return vdpa;
 597
 598indent_err:
 599        free(vdpa);
 600        return NULL;
 601}
 602
 603static void vdpa_free(struct vdpa *vdpa)
 604{
 605        free_indent_mem(vdpa->indent);
 606        free(vdpa);
 607}
 608
 609int main(int argc, char **argv)
 610{
 611        static const struct option long_options[] = {
 612                { "Version",            no_argument,    NULL, 'V' },
 613                { "json",               no_argument,    NULL, 'j' },
 614                { "pretty",             no_argument,    NULL, 'p' },
 615                { "help",               no_argument,    NULL, 'h' },
 616                { NULL, 0, NULL, 0 }
 617        };
 618        struct vdpa *vdpa;
 619        int opt;
 620        int err;
 621        int ret;
 622
 623        vdpa = vdpa_alloc();
 624        if (!vdpa) {
 625                fprintf(stderr, "Failed to allocate memory for vdpa\n");
 626                return EXIT_FAILURE;
 627        }
 628
 629        while ((opt = getopt_long(argc, argv, "Vjpsh", long_options, NULL)) >= 0) {
 630                switch (opt) {
 631                case 'V':
 632                        printf("vdpa utility, iproute2-%s\n", version);
 633                        ret = EXIT_SUCCESS;
 634                        goto vdpa_free;
 635                case 'j':
 636                        vdpa->json_output = true;
 637                        break;
 638                case 'p':
 639                        pretty = true;
 640                        break;
 641                case 'h':
 642                        help();
 643                        ret = EXIT_SUCCESS;
 644                        goto vdpa_free;
 645                default:
 646                        fprintf(stderr, "Unknown option.\n");
 647                        help();
 648                        ret = EXIT_FAILURE;
 649                        goto vdpa_free;
 650                }
 651        }
 652
 653        argc -= optind;
 654        argv += optind;
 655
 656        err = vdpa_init(vdpa);
 657        if (err) {
 658                ret = EXIT_FAILURE;
 659                goto vdpa_free;
 660        }
 661
 662        err = vdpa_cmd(vdpa, argc, argv);
 663        if (err) {
 664                ret = EXIT_FAILURE;
 665                goto vdpa_fini;
 666        }
 667
 668        ret = EXIT_SUCCESS;
 669
 670vdpa_fini:
 671        vdpa_fini(vdpa);
 672vdpa_free:
 673        vdpa_free(vdpa);
 674        return ret;
 675}
 676