1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 #include <stdio.h>
  27 #include <locale.h>
  28 #include <stdarg.h>
  29 #include <stdlib.h>
  30 #include <fcntl.h>
  31 #include <string.h>
  32 #include <stropts.h>
  33 #include <errno.h>
  34 #include <strings.h>
  35 #include <getopt.h>
  36 #include <unistd.h>
  37 #include <priv.h>
  38 #include <netdb.h>
  39 #include <libintl.h>
  40 #include <libdlflow.h>
  41 #include <libdllink.h>
  42 #include <libdlstat.h>
  43 #include <sys/types.h>
  44 #include <sys/socket.h>
  45 #include <netinet/in.h>
  46 #include <arpa/inet.h>
  47 #include <sys/ethernet.h>
  48 #include <inet/ip.h>
  49 #include <inet/ip6.h>
  50 #include <stddef.h>
  51 #include <ofmt.h>
  52 
  53 typedef struct show_flow_state {
  54         dladm_status_t          fs_status;
  55         ofmt_handle_t           fs_ofmt;
  56         const char              *fs_flow;
  57         boolean_t               fs_parsable;
  58         boolean_t               fs_persist;
  59 } show_flow_state_t;
  60 
  61 typedef void cmdfunc_t(int, char **);
  62 
  63 static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow;
  64 static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop;
  65 
  66 static int      show_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
  67 static int      show_flows_onelink(dladm_handle_t, datalink_id_t, void *);
  68 
  69 static int      remove_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
  70 
  71 static int      show_flowprop(dladm_handle_t, dladm_flow_attr_t *, void *);
  72 static void     show_flowprop_one_flow(void *, const char *);
  73 static int      show_flowprop_onelink(dladm_handle_t, datalink_id_t, void *);
  74 
  75 static void     die(const char *, ...);
  76 static void     die_optdup(int);
  77 static void     die_opterr(int, int);
  78 static void     die_dlerr(dladm_status_t, const char *, ...);
  79 static void     warn(const char *, ...);
  80 static void     warn_dlerr(dladm_status_t, const char *, ...);
  81 
  82 /* callback functions for printing output */
  83 static ofmt_cb_t print_flowprop_cb, print_default_cb;
  84 static void flowadm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
  85 
  86 typedef struct  cmd {
  87         char    *c_name;
  88         void    (*c_fn)(int, char **);
  89 } cmd_t;
  90 
  91 static cmd_t    cmds[] = {
  92         { "add-flow", do_add_flow },
  93         { "remove-flow", do_remove_flow },
  94         { "show-flowprop", do_show_flowprop },
  95         { "set-flowprop", do_set_flowprop },
  96         { "reset-flowprop", do_reset_flowprop },
  97         { "show-flow", do_show_flow },
  98         { "init-flow", do_init_flow },
  99 };
 100 
 101 static const struct option longopts[] = {
 102         {"link",                required_argument,      0, 'l'},
 103         {"parsable",            no_argument,            0, 'p'},
 104         {"parseable",           no_argument,            0, 'p'},
 105         {"temporary",           no_argument,            0, 't'},
 106         {"root-dir",            required_argument,      0, 'R'},
 107         { 0, 0, 0, 0 }
 108 };
 109 
 110 static const struct option prop_longopts[] = {
 111         {"link",                required_argument,      0, 'l'},
 112         {"temporary",           no_argument,            0, 't'},
 113         {"root-dir",            required_argument,      0, 'R'},
 114         {"prop",                required_argument,      0, 'p'},
 115         {"attr",                required_argument,      0, 'a'},
 116         { 0, 0, 0, 0 }
 117 };
 118 
 119 /*
 120  * structures for 'flowadm remove-flow'
 121  */
 122 typedef struct remove_flow_state {
 123         boolean_t       fs_tempop;
 124         const char      *fs_altroot;
 125         dladm_status_t  fs_status;
 126 } remove_flow_state_t;
 127 
 128 #define PROTO_MAXSTR_LEN        7
 129 #define PORT_MAXSTR_LEN         6
 130 #define DSFIELD_MAXSTR_LEN      10
 131 #define NULL_OFMT               {NULL, 0, 0, NULL}
 132 
 133 typedef struct flow_fields_buf_s
 134 {
 135         char flow_name[MAXFLOWNAMELEN];
 136         char flow_link[MAXLINKNAMELEN];
 137         char flow_ipaddr[INET6_ADDRSTRLEN+4];
 138         char flow_proto[PROTO_MAXSTR_LEN];
 139         char flow_lport[PORT_MAXSTR_LEN];
 140         char flow_rport[PORT_MAXSTR_LEN];
 141         char flow_dsfield[DSFIELD_MAXSTR_LEN];
 142 } flow_fields_buf_t;
 143 
 144 static ofmt_field_t flow_fields[] = {
 145 /* name,        field width,    index */
 146 {  "FLOW",      12,
 147         offsetof(flow_fields_buf_t, flow_name), print_default_cb},
 148 {  "LINK",      12,
 149         offsetof(flow_fields_buf_t, flow_link), print_default_cb},
 150 {  "IPADDR",    25,
 151         offsetof(flow_fields_buf_t, flow_ipaddr), print_default_cb},
 152 {  "PROTO",     7,
 153         offsetof(flow_fields_buf_t, flow_proto), print_default_cb},
 154 {  "LPORT",     8,
 155         offsetof(flow_fields_buf_t, flow_lport), print_default_cb},
 156 {  "RPORT",     8,
 157         offsetof(flow_fields_buf_t, flow_rport), print_default_cb},
 158 {  "DSFLD",     10,
 159         offsetof(flow_fields_buf_t, flow_dsfield), print_default_cb},
 160 NULL_OFMT}
 161 ;
 162 
 163 /*
 164  * structures for 'flowadm show-flowprop'
 165  */
 166 typedef enum {
 167         FLOWPROP_FLOW,
 168         FLOWPROP_PROPERTY,
 169         FLOWPROP_VALUE,
 170         FLOWPROP_DEFAULT,
 171         FLOWPROP_POSSIBLE
 172 } flowprop_field_index_t;
 173 
 174 static ofmt_field_t flowprop_fields[] = {
 175 /* name,        fieldwidth,     index,          callback */
 176 { "FLOW",       13,     FLOWPROP_FLOW,          print_flowprop_cb},
 177 { "PROPERTY",   16,     FLOWPROP_PROPERTY,      print_flowprop_cb},
 178 { "VALUE",      15,     FLOWPROP_VALUE,         print_flowprop_cb},
 179 { "DEFAULT",    15,     FLOWPROP_DEFAULT,       print_flowprop_cb},
 180 { "POSSIBLE",   21,     FLOWPROP_POSSIBLE,      print_flowprop_cb},
 181 NULL_OFMT}
 182 ;
 183 
 184 #define MAX_PROP_LINE           512
 185 
 186 typedef struct show_flowprop_state {
 187         const char              *fs_flow;
 188         datalink_id_t           fs_linkid;
 189         char                    *fs_line;
 190         char                    **fs_propvals;
 191         dladm_arg_list_t        *fs_proplist;
 192         boolean_t               fs_parsable;
 193         boolean_t               fs_persist;
 194         boolean_t               fs_header;
 195         dladm_status_t          fs_status;
 196         dladm_status_t          fs_retstatus;
 197         ofmt_handle_t           fs_ofmt;
 198 } show_flowprop_state_t;
 199 
 200 typedef struct set_flowprop_state {
 201         const char      *fs_name;
 202         boolean_t       fs_reset;
 203         boolean_t       fs_temp;
 204         dladm_status_t  fs_status;
 205 } set_flowprop_state_t;
 206 
 207 typedef struct flowprop_args_s {
 208         show_flowprop_state_t   *fs_state;
 209         char                    *fs_propname;
 210         char                    *fs_flowname;
 211 } flowprop_args_t;
 212 
 213 static char *progname;
 214 
 215 boolean_t               t_arg = B_FALSE; /* changes are persistent */
 216 char                    *altroot = NULL;
 217 
 218 /*
 219  * Handle to libdladm.  Opened in main() before the sub-command
 220  * specific function is called.
 221  */
 222 static dladm_handle_t handle = NULL;
 223 
 224 static const char *attr_table[] =
 225         {"local_ip", "remote_ip", "transport", "local_port", "remote_port",
 226             "dsfield"};
 227 
 228 #define NATTR   (sizeof (attr_table)/sizeof (char *))
 229 
 230 static void
 231 usage(void)
 232 {
 233         (void) fprintf(stderr, gettext("usage: flowadm <subcommand>"
 234             " <args>...\n"
 235             "    add-flow       [-t] -l <link> -a <attr>=<value>[,...]\n"
 236             "\t\t   [-p <prop>=<value>,...] <flow>\n"
 237             "    remove-flow    [-t] {-l <link> | <flow>}\n"
 238             "    show-flow      [-p] [-l <link>] "
 239             "[<flow>]\n\n"
 240             "    set-flowprop   [-t] -p <prop>=<value>[,...] <flow>\n"
 241             "    reset-flowprop [-t] [-p <prop>,...] <flow>\n"
 242             "    show-flowprop  [-cP] [-l <link>] [-p <prop>,...] "
 243             "[<flow>]\n"));
 244 
 245         /* close dladm handle if it was opened */
 246         if (handle != NULL)
 247                 dladm_close(handle);
 248 
 249         exit(1);
 250 }
 251 
 252 int
 253 main(int argc, char *argv[])
 254 {
 255         int     i, arglen, cmdlen;
 256         cmd_t   *cmdp;
 257         dladm_status_t status;
 258 
 259         (void) setlocale(LC_ALL, "");
 260 #if !defined(TEXT_DOMAIN)
 261 #define TEXT_DOMAIN "SYS_TEST"
 262 #endif
 263         (void) textdomain(TEXT_DOMAIN);
 264 
 265         progname = argv[0];
 266 
 267         if (argc < 2)
 268                 usage();
 269 
 270         for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
 271                 cmdp = &cmds[i];
 272                 arglen = strlen(argv[1]);
 273                 cmdlen = strlen(cmdp->c_name);
 274                 if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name,
 275                     cmdlen) == 0)) {
 276                         /* Open the libdladm handle */
 277                         if ((status = dladm_open(&handle)) != DLADM_STATUS_OK) {
 278                                 die_dlerr(status,
 279                                     "could not open /dev/dld");
 280                         }
 281 
 282                         cmdp->c_fn(argc - 1, &argv[1]);
 283 
 284                         dladm_close(handle);
 285                         exit(EXIT_SUCCESS);
 286                 }
 287         }
 288 
 289         (void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
 290             progname, argv[1]);
 291         usage();
 292 
 293         return (0);
 294 }
 295 
 296 static const char *
 297 match_attr(char *attr)
 298 {
 299         int i;
 300 
 301         for (i = 0; i < NATTR; i++) {
 302                 if (strlen(attr) == strlen(attr_table[i]) &&
 303                     strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) {
 304                         return (attr);
 305                 }
 306         }
 307         return (NULL);
 308 }
 309 
 310 /* ARGSUSED */
 311 static void
 312 do_init_flow(int argc, char *argv[])
 313 {
 314         dladm_status_t status;
 315 
 316         status = dladm_flow_init(handle);
 317         if (status != DLADM_STATUS_OK)
 318                 die_dlerr(status, "flows initialization failed");
 319 }
 320 
 321 static void
 322 do_add_flow(int argc, char *argv[])
 323 {
 324         char                    devname[MAXLINKNAMELEN];
 325         char                    *name = NULL;
 326         uint_t                  index;
 327         datalink_id_t           linkid;
 328 
 329         char                    option;
 330         boolean_t               l_arg = B_FALSE;
 331         char                    propstr[DLADM_STRSIZE];
 332         char                    attrstr[DLADM_STRSIZE];
 333         dladm_arg_list_t        *proplist = NULL;
 334         dladm_arg_list_t        *attrlist = NULL;
 335         dladm_status_t          status;
 336 
 337         bzero(propstr, DLADM_STRSIZE);
 338         bzero(attrstr, DLADM_STRSIZE);
 339 
 340         while ((option = getopt_long(argc, argv, "tR:l:a:p:",
 341             prop_longopts, NULL)) != -1) {
 342                 switch (option) {
 343                 case 't':
 344                         t_arg = B_TRUE;
 345                         break;
 346                 case 'R':
 347                         altroot = optarg;
 348                         break;
 349                 case 'l':
 350                         if (strlcpy(devname, optarg,
 351                             MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
 352                                 die("link name too long");
 353                         }
 354                         if (dladm_name2info(handle, devname, &linkid, NULL,
 355                             NULL, NULL) != DLADM_STATUS_OK)
 356                                 die("invalid link '%s'", devname);
 357                         l_arg = B_TRUE;
 358                         break;
 359                 case 'a':
 360                         (void) strlcat(attrstr, optarg, DLADM_STRSIZE);
 361                         if (strlcat(attrstr, ",", DLADM_STRSIZE) >=
 362                             DLADM_STRSIZE)
 363                                 die("attribute list too long '%s'", attrstr);
 364                         break;
 365                 case 'p':
 366                         (void) strlcat(propstr, optarg, DLADM_STRSIZE);
 367                         if (strlcat(propstr, ",", DLADM_STRSIZE) >=
 368                             DLADM_STRSIZE)
 369                                 die("property list too long '%s'", propstr);
 370                         break;
 371                 default:
 372                         die_opterr(optopt, option);
 373                 }
 374         }
 375         if (!l_arg) {
 376                 die("link is required");
 377         }
 378 
 379         opterr = 0;
 380         index = optind;
 381 
 382         if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) {
 383                 die("flow name is required");
 384         } else {
 385                 /* get flow name; required last argument */
 386                 if (strlen(argv[index]) >= MAXFLOWNAMELEN)
 387                         die("flow name too long");
 388                 name = argv[index];
 389         }
 390 
 391         if (dladm_parse_flow_attrs(attrstr, &attrlist, B_FALSE)
 392             != DLADM_STATUS_OK)
 393                 die("invalid flow attribute specified");
 394         if (dladm_parse_flow_props(propstr, &proplist, B_FALSE)
 395             != DLADM_STATUS_OK)
 396                 die("invalid flow property specified");
 397 
 398         status = dladm_flow_add(handle, linkid, attrlist, proplist, name,
 399             t_arg, altroot);
 400         if (status != DLADM_STATUS_OK)
 401                 die_dlerr(status, "add flow failed");
 402 
 403         dladm_free_attrs(attrlist);
 404         dladm_free_props(proplist);
 405 }
 406 
 407 static void
 408 do_remove_flow(int argc, char *argv[])
 409 {
 410         char                    option;
 411         char                    *flowname = NULL;
 412         char                    linkname[MAXLINKNAMELEN];
 413         datalink_id_t           linkid = DATALINK_ALL_LINKID;
 414         boolean_t               l_arg = B_FALSE;
 415         remove_flow_state_t     state;
 416         dladm_status_t          status;
 417 
 418         bzero(&state, sizeof (state));
 419 
 420         opterr = 0;
 421         while ((option = getopt_long(argc, argv, ":tR:l:",
 422             longopts, NULL)) != -1) {
 423                 switch (option) {
 424                 case 't':
 425                         t_arg = B_TRUE;
 426                         break;
 427                 case 'R':
 428                         altroot = optarg;
 429                         break;
 430                 case 'l':
 431                         if (strlcpy(linkname, optarg,
 432                             MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
 433                                 die("link name too long");
 434                         }
 435                         if (dladm_name2info(handle, linkname, &linkid, NULL,
 436                             NULL, NULL) != DLADM_STATUS_OK) {
 437                                 die("invalid link '%s'", linkname);
 438                         }
 439                         l_arg = B_TRUE;
 440                         break;
 441                 default:
 442                         die_opterr(optopt, option);
 443                         break;
 444                 }
 445         }
 446 
 447         /* when link not specified get flow name */
 448         if (!l_arg) {
 449                 if (optind != (argc-1)) {
 450                         usage();
 451                 } else {
 452                         if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
 453                                 die("flow name too long");
 454                         flowname = argv[optind];
 455                 }
 456                 status = dladm_flow_remove(handle, flowname, t_arg, altroot);
 457         } else {
 458                 /* if link is specified then flow name should not be there */
 459                 if (optind == argc-1)
 460                         usage();
 461                 /* walk the link to find flows and remove them */
 462                 state.fs_tempop = t_arg;
 463                 state.fs_altroot = altroot;
 464                 state.fs_status = DLADM_STATUS_OK;
 465                 status = dladm_walk_flow(remove_flow, handle, linkid, &state,
 466                     B_FALSE);
 467                 /*
 468                  * check if dladm_walk_flow terminated early and see if the
 469                  * walker function as any status for us
 470                  */
 471                 if (status == DLADM_STATUS_OK)
 472                         status = state.fs_status;
 473         }
 474 
 475         if (status != DLADM_STATUS_OK)
 476                 die_dlerr(status, "remove flow failed");
 477 }
 478 
 479 /*
 480  * Walker function for removing a flow through dladm_walk_flow();
 481  */
 482 /*ARGSUSED*/
 483 static int
 484 remove_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
 485 {
 486         remove_flow_state_t     *state = (remove_flow_state_t *)arg;
 487 
 488         state->fs_status = dladm_flow_remove(handle, attr->fa_flowname,
 489             state->fs_tempop, state->fs_altroot);
 490 
 491         if (state->fs_status == DLADM_STATUS_OK)
 492                 return (DLADM_WALK_CONTINUE);
 493         else
 494                 return (DLADM_WALK_TERMINATE);
 495 }
 496 
 497 /*ARGSUSED*/
 498 static dladm_status_t
 499 print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
 500     flow_fields_buf_t *fbuf)
 501 {
 502         char            link[MAXLINKNAMELEN];
 503         dladm_status_t  status;
 504 
 505         if ((status = dladm_datalink_id2info(handle, attr->fa_linkid, NULL,
 506             NULL, NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
 507                 return (status);
 508         }
 509 
 510         (void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
 511             "%s", attr->fa_flowname);
 512         (void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
 513             "%s", link);
 514 
 515         (void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
 516             sizeof (fbuf->flow_ipaddr));
 517         (void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
 518             sizeof (fbuf->flow_proto));
 519         if ((attr->fa_flow_desc.fd_mask & FLOW_ULP_PORT_LOCAL) != 0) {
 520                 (void) dladm_flow_attr_port2str(attr, fbuf->flow_lport,
 521                     sizeof (fbuf->flow_lport));
 522         }
 523         if ((attr->fa_flow_desc.fd_mask & FLOW_ULP_PORT_REMOTE) != 0) {
 524                 (void) dladm_flow_attr_port2str(attr, fbuf->flow_rport,
 525                     sizeof (fbuf->flow_rport));
 526         }
 527         (void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
 528             sizeof (fbuf->flow_dsfield));
 529 
 530         return (DLADM_STATUS_OK);
 531 }
 532 
 533 /*
 534  * Walker function for showing flow attributes through dladm_walk_flow().
 535  */
 536 /*ARGSUSED*/
 537 static int
 538 show_flow(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
 539 {
 540         show_flow_state_t       *statep = arg;
 541         dladm_status_t          status;
 542         flow_fields_buf_t       fbuf;
 543 
 544         /*
 545          * first get all the flow attributes into fbuf;
 546          */
 547         bzero(&fbuf, sizeof (fbuf));
 548         status = print_flow(statep, attr, &fbuf);
 549 
 550         if (status != DLADM_STATUS_OK)
 551                 goto done;
 552 
 553         ofmt_print(statep->fs_ofmt, (void *)&fbuf);
 554 
 555 done:
 556         statep->fs_status = status;
 557         return (DLADM_WALK_CONTINUE);
 558 }
 559 
 560 static void
 561 show_one_flow(void *arg, const char *name)
 562 {
 563         dladm_flow_attr_t       attr;
 564 
 565         if (dladm_flow_info(handle, name, &attr) != DLADM_STATUS_OK)
 566                 die("invalid flow: '%s'", name);
 567         else
 568                 (void) show_flow(handle, &attr, arg);
 569 }
 570 
 571 /*
 572  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
 573  * dladm_walk_datalink_id(). Used for showing flow attributes for
 574  * all flows on all links.
 575  */
 576 static int
 577 show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
 578 {
 579         show_flow_state_t *state = arg;
 580 
 581         (void) dladm_walk_flow(show_flow, dh, linkid, arg, state->fs_persist);
 582 
 583         return (DLADM_WALK_CONTINUE);
 584 }
 585 
 586 static void
 587 do_show_flow(int argc, char *argv[])
 588 {
 589         char                    flowname[MAXFLOWNAMELEN];
 590         char                    linkname[MAXLINKNAMELEN];
 591         datalink_id_t           linkid = DATALINK_ALL_LINKID;
 592         int                     option;
 593         boolean_t               l_arg = B_FALSE;
 594         boolean_t               o_arg = B_FALSE;
 595         show_flow_state_t       state;
 596         char                    *fields_str = NULL;
 597         ofmt_handle_t           ofmt;
 598         ofmt_status_t           oferr;
 599         uint_t                  ofmtflags = 0;
 600 
 601         bzero(&state, sizeof (state));
 602 
 603         opterr = 0;
 604         while ((option = getopt_long(argc, argv, ":pPl:o:",
 605             longopts, NULL)) != -1) {
 606                 switch (option) {
 607                 case 'p':
 608                         state.fs_parsable = B_TRUE;
 609                         ofmtflags |= OFMT_PARSABLE;
 610                         break;
 611                 case 'P':
 612                         state.fs_persist = B_TRUE;
 613                         break;
 614                 case 'o':
 615                         if (o_arg)
 616                                 die_optdup(option);
 617 
 618                         o_arg = B_TRUE;
 619                         fields_str = optarg;
 620                         break;
 621                 case 'l':
 622                         if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
 623                             >= MAXLINKNAMELEN)
 624                                 die("link name too long\n");
 625                         if (dladm_name2info(handle, linkname, &linkid, NULL,
 626                             NULL, NULL) != DLADM_STATUS_OK)
 627                                 die("invalid link '%s'", linkname);
 628                         l_arg = B_TRUE;
 629                         break;
 630                 default:
 631                         die_opterr(optopt, option);
 632                         break;
 633                 }
 634         }
 635 
 636         /* get flow name (optional last argument */
 637         if (optind == (argc-1)) {
 638                 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
 639                     >= MAXFLOWNAMELEN)
 640                         die("flow name too long");
 641                 state.fs_flow = flowname;
 642         }
 643 
 644         oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt);
 645         flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
 646         state.fs_ofmt = ofmt;
 647 
 648         /* Show attributes of one flow */
 649         if (state.fs_flow != NULL) {
 650                 show_one_flow(&state, state.fs_flow);
 651 
 652         /* Show attributes of flows on one link */
 653         } else if (l_arg) {
 654                 (void) show_flows_onelink(handle, linkid, &state);
 655 
 656         /* Show attributes of all flows on all links */
 657         } else {
 658                 (void) dladm_walk_datalink_id(show_flows_onelink, handle,
 659                     &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
 660                     DLADM_OPT_ACTIVE);
 661         }
 662         ofmt_close(ofmt);
 663 }
 664 
 665 static dladm_status_t
 666 set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
 667     uint_t val_cnt, boolean_t reset)
 668 {
 669         dladm_status_t  status;
 670         char            *errprop;
 671 
 672         status = dladm_set_flowprop(handle, flow, prop_name, prop_val, val_cnt,
 673             DLADM_OPT_PERSIST, &errprop);
 674 
 675         if (status != DLADM_STATUS_OK) {
 676                 warn_dlerr(status, "cannot persistently %s flow "
 677                     "property '%s' on '%s'", reset? "reset": "set",
 678                     errprop, flow);
 679         }
 680         return (status);
 681 }
 682 
 683 static void
 684 set_flowprop(int argc, char **argv, boolean_t reset)
 685 {
 686         int                     i, option;
 687         char                    errmsg[DLADM_STRSIZE];
 688         const char              *flow = NULL;
 689         char                    propstr[DLADM_STRSIZE];
 690         dladm_arg_list_t        *proplist = NULL;
 691         boolean_t               temp = B_FALSE;
 692         dladm_status_t          status = DLADM_STATUS_OK;
 693 
 694         opterr = 0;
 695         bzero(propstr, DLADM_STRSIZE);
 696 
 697         while ((option = getopt_long(argc, argv, ":p:R:t",
 698             prop_longopts, NULL)) != -1) {
 699                 switch (option) {
 700                 case 'p':
 701                         (void) strlcat(propstr, optarg, DLADM_STRSIZE);
 702                         if (strlcat(propstr, ",", DLADM_STRSIZE) >=
 703                             DLADM_STRSIZE)
 704                                 die("property list too long '%s'", propstr);
 705                         break;
 706                 case 't':
 707                         temp = B_TRUE;
 708                         break;
 709                 case 'R':
 710                         status = dladm_set_rootdir(optarg);
 711                         if (status != DLADM_STATUS_OK) {
 712                                 die_dlerr(status, "invalid directory "
 713                                     "specified");
 714                         }
 715                         break;
 716                 default:
 717                         die_opterr(optopt, option);
 718                         break;
 719                 }
 720         }
 721 
 722         if (optind == (argc - 1)) {
 723                 if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
 724                         die("flow name too long");
 725                 flow = argv[optind];
 726         } else if (optind != argc) {
 727                 usage();
 728         }
 729         if (flow == NULL)
 730                 die("flow name must be specified");
 731 
 732         if (dladm_parse_flow_props(propstr, &proplist, reset)
 733             != DLADM_STATUS_OK)
 734                 die("invalid flow property specified");
 735 
 736         if (proplist == NULL) {
 737                 char *errprop;
 738 
 739                 if (!reset)
 740                         die("flow property must be specified");
 741 
 742                 status = dladm_set_flowprop(handle, flow, NULL, NULL, 0,
 743                     DLADM_OPT_ACTIVE, &errprop);
 744                 if (status != DLADM_STATUS_OK) {
 745                         warn_dlerr(status, "cannot reset flow property '%s' "
 746                             "on '%s'", errprop, flow);
 747                 }
 748                 if (!temp) {
 749                         dladm_status_t  s;
 750 
 751                         s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
 752                         if (s != DLADM_STATUS_OK)
 753                                 status = s;
 754                 }
 755                 goto done;
 756         }
 757 
 758         for (i = 0; i < proplist->al_count; i++) {
 759                 dladm_arg_info_t        *aip = &proplist->al_info[i];
 760                 char            **val;
 761                 uint_t          count;
 762                 dladm_status_t  s;
 763 
 764                 if (reset) {
 765                         val = NULL;
 766                         count = 0;
 767                 } else {
 768                         val = aip->ai_val;
 769                         count = aip->ai_count;
 770                         if (count == 0) {
 771                                 warn("no value specified for '%s'",
 772                                     aip->ai_name);
 773                                 status = DLADM_STATUS_BADARG;
 774                                 continue;
 775                         }
 776                 }
 777                 s = dladm_set_flowprop(handle, flow, aip->ai_name, val, count,
 778                     DLADM_OPT_ACTIVE, NULL);
 779                 if (s == DLADM_STATUS_OK) {
 780                         if (!temp) {
 781                                 s = set_flowprop_persist(flow,
 782                                     aip->ai_name, val, count, reset);
 783                                 if (s != DLADM_STATUS_OK)
 784                                         status = s;
 785                         }
 786                         continue;
 787                 }
 788                 status = s;
 789                 switch (s) {
 790                 case DLADM_STATUS_NOTFOUND:
 791                         warn("invalid flow property '%s'", aip->ai_name);
 792                         break;
 793                 case DLADM_STATUS_BADVAL: {
 794                         int             j;
 795                         char            *ptr, *lim;
 796                         char            **propvals = NULL;
 797                         uint_t          valcnt = DLADM_MAX_PROP_VALCNT;
 798 
 799                         ptr = malloc((sizeof (char *) +
 800                             DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
 801                             MAX_PROP_LINE);
 802 
 803                         if (ptr == NULL)
 804                                 die("insufficient memory");
 805                         propvals = (char **)(void *)ptr;
 806 
 807                         for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
 808                                 propvals[j] = ptr + sizeof (char *) *
 809                                     DLADM_MAX_PROP_VALCNT +
 810                                     j * DLADM_PROP_VAL_MAX;
 811                         }
 812                         s = dladm_get_flowprop(handle, flow,
 813                             DLADM_PROP_VAL_MODIFIABLE, aip->ai_name, propvals,
 814                             &valcnt);
 815 
 816                         ptr = errmsg;
 817                         lim = ptr + DLADM_STRSIZE;
 818                         *ptr = '\0';
 819                         for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
 820                                 ptr += snprintf(ptr, lim - ptr, "%s,",
 821                                     propvals[j]);
 822                                 if (ptr >= lim)
 823                                         break;
 824                         }
 825                         if (ptr > errmsg) {
 826                                 *(ptr - 1) = '\0';
 827                                 warn("flow property '%s' must be one of: %s",
 828                                     aip->ai_name, errmsg);
 829                         } else
 830                                 warn("%s is an invalid value for "
 831                                     "flow property %s", *val, aip->ai_name);
 832                         free(propvals);
 833                         break;
 834                 }
 835                 default:
 836                         if (reset) {
 837                                 warn_dlerr(status, "cannot reset flow property "
 838                                     "'%s' on '%s'", aip->ai_name, flow);
 839                         } else {
 840                                 warn_dlerr(status, "cannot set flow property "
 841                                     "'%s' on '%s'", aip->ai_name, flow);
 842                         }
 843                         break;
 844                 }
 845         }
 846 done:
 847         dladm_free_props(proplist);
 848         if (status != DLADM_STATUS_OK) {
 849                 dladm_close(handle);
 850                 exit(EXIT_FAILURE);
 851         }
 852 }
 853 
 854 static void
 855 do_set_flowprop(int argc, char **argv)
 856 {
 857         set_flowprop(argc, argv, B_FALSE);
 858 }
 859 
 860 static void
 861 do_reset_flowprop(int argc, char **argv)
 862 {
 863         set_flowprop(argc, argv, B_TRUE);
 864 }
 865 
 866 static void
 867 warn(const char *format, ...)
 868 {
 869         va_list alist;
 870 
 871         format = gettext(format);
 872         (void) fprintf(stderr, "%s: warning: ", progname);
 873 
 874         va_start(alist, format);
 875         (void) vfprintf(stderr, format, alist);
 876         va_end(alist);
 877 
 878         (void) putc('\n', stderr);
 879 }
 880 
 881 /* PRINTFLIKE2 */
 882 static void
 883 warn_dlerr(dladm_status_t err, const char *format, ...)
 884 {
 885         va_list alist;
 886         char    errmsg[DLADM_STRSIZE];
 887 
 888         format = gettext(format);
 889         (void) fprintf(stderr, gettext("%s: warning: "), progname);
 890 
 891         va_start(alist, format);
 892         (void) vfprintf(stderr, format, alist);
 893         va_end(alist);
 894         (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
 895 }
 896 
 897 /* PRINTFLIKE1 */
 898 static void
 899 die(const char *format, ...)
 900 {
 901         va_list alist;
 902 
 903         format = gettext(format);
 904         (void) fprintf(stderr, "%s: ", progname);
 905 
 906         va_start(alist, format);
 907         (void) vfprintf(stderr, format, alist);
 908         va_end(alist);
 909 
 910         (void) putc('\n', stderr);
 911 
 912         /* close dladm handle if it was opened */
 913         if (handle != NULL)
 914                 dladm_close(handle);
 915 
 916         exit(EXIT_FAILURE);
 917 }
 918 
 919 static void
 920 die_optdup(int opt)
 921 {
 922         die("the option -%c cannot be specified more than once", opt);
 923 }
 924 
 925 static void
 926 die_opterr(int opt, int opterr)
 927 {
 928         switch (opterr) {
 929         case ':':
 930                 die("option '-%c' requires a value", opt);
 931                 break;
 932         case '?':
 933         default:
 934                 die("unrecognized option '-%c'", opt);
 935                 break;
 936         }
 937 }
 938 
 939 /* PRINTFLIKE2 */
 940 static void
 941 die_dlerr(dladm_status_t err, const char *format, ...)
 942 {
 943         va_list alist;
 944         char    errmsg[DLADM_STRSIZE];
 945 
 946         format = gettext(format);
 947         (void) fprintf(stderr, "%s: ", progname);
 948 
 949         va_start(alist, format);
 950         (void) vfprintf(stderr, format, alist);
 951         va_end(alist);
 952         (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
 953 
 954         /* close dladm handle if it was opened */
 955         if (handle != NULL)
 956                 dladm_close(handle);
 957 
 958         exit(EXIT_FAILURE);
 959 }
 960 
 961 static void
 962 print_flowprop(const char *flowname, show_flowprop_state_t *statep,
 963     const char *propname, dladm_prop_type_t type,
 964     const char *format, char **pptr)
 965 {
 966         int             i;
 967         char            *ptr, *lim;
 968         char            buf[DLADM_STRSIZE];
 969         char            *unknown = "--", *notsup = "";
 970         char            **propvals = statep->fs_propvals;
 971         uint_t          valcnt = DLADM_MAX_PROP_VALCNT;
 972         dladm_status_t  status;
 973 
 974         status = dladm_get_flowprop(handle, flowname, type, propname, propvals,
 975             &valcnt);
 976         if (status != DLADM_STATUS_OK) {
 977                 if (status == DLADM_STATUS_TEMPONLY) {
 978                         if (type == DLADM_PROP_VAL_MODIFIABLE &&
 979                             statep->fs_persist) {
 980                                 valcnt = 1;
 981                                 propvals = &unknown;
 982                         } else {
 983                                 statep->fs_status = status;
 984                                 statep->fs_retstatus = status;
 985                                 return;
 986                         }
 987                 } else if (status == DLADM_STATUS_NOTSUP ||
 988                     statep->fs_persist) {
 989                         valcnt = 1;
 990                         if (type == DLADM_PROP_VAL_CURRENT)
 991                                 propvals = &unknown;
 992                         else
 993                                 propvals = ¬sup;
 994                 } else {
 995                         if ((statep->fs_proplist != NULL) &&
 996                             statep->fs_status == DLADM_STATUS_OK) {
 997                                 warn("invalid flow property '%s'", propname);
 998                         }
 999                         statep->fs_status = status;
1000                         statep->fs_retstatus = status;
1001                         return;
1002                 }
1003         }
1004 
1005         statep->fs_status = DLADM_STATUS_OK;
1006 
1007         ptr = buf;
1008         lim = buf + DLADM_STRSIZE;
1009         for (i = 0; i < valcnt; i++) {
1010                 if (propvals[i][0] == '\0' && !statep->fs_parsable)
1011                         ptr += snprintf(ptr, lim - ptr, "--,");
1012                 else
1013                         ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1014                 if (ptr >= lim)
1015                         break;
1016         }
1017         if (valcnt > 0)
1018                 buf[strlen(buf) - 1] = '\0';
1019 
1020         lim = statep->fs_line + MAX_PROP_LINE;
1021         if (statep->fs_parsable) {
1022                 *pptr += snprintf(*pptr, lim - *pptr,
1023                     "%s", buf);
1024         } else {
1025                 *pptr += snprintf(*pptr, lim - *pptr, format, buf);
1026         }
1027 }
1028 
1029 static boolean_t
1030 print_flowprop_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1031 {
1032         flowprop_args_t         *arg = of_arg->ofmt_cbarg;
1033         char                    *propname = arg->fs_propname;
1034         show_flowprop_state_t   *statep = arg->fs_state;
1035         char                    *ptr = statep->fs_line;
1036         char                    *lim = ptr + MAX_PROP_LINE;
1037         char                    *flowname = arg->fs_flowname;
1038 
1039         switch (of_arg->ofmt_id) {
1040         case FLOWPROP_FLOW:
1041                 (void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1042                 break;
1043         case FLOWPROP_PROPERTY:
1044                 (void) snprintf(ptr, lim - ptr, "%s", propname);
1045                 break;
1046         case FLOWPROP_VALUE:
1047                 print_flowprop(flowname, statep, propname,
1048                     statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1049                     DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1050                 /*
1051                  * If we failed to query the flow property, for example, query
1052                  * the persistent value of a non-persistable flow property,
1053                  * simply skip the output.
1054                  */
1055                 if (statep->fs_status != DLADM_STATUS_OK)
1056                         goto skip;
1057                 ptr = statep->fs_line;
1058                 break;
1059         case FLOWPROP_DEFAULT:
1060                 print_flowprop(flowname, statep, propname,
1061                     DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1062                 if (statep->fs_status != DLADM_STATUS_OK)
1063                         goto skip;
1064                 ptr = statep->fs_line;
1065                 break;
1066         case FLOWPROP_POSSIBLE:
1067                 print_flowprop(flowname, statep, propname,
1068                     DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1069                 if (statep->fs_status != DLADM_STATUS_OK)
1070                         goto skip;
1071                 ptr = statep->fs_line;
1072                 break;
1073         default:
1074                 die("invalid input");
1075                 break;
1076         }
1077         (void) strlcpy(buf, ptr, bufsize);
1078         return (B_TRUE);
1079 skip:
1080         buf[0] = '\0';
1081         return ((statep->fs_status == DLADM_STATUS_OK) ?
1082             B_TRUE : B_FALSE);
1083 }
1084 
1085 static int
1086 show_one_flowprop(void *arg, const char *propname)
1087 {
1088         show_flowprop_state_t   *statep = arg;
1089         flowprop_args_t         fs_arg;
1090 
1091         bzero(&fs_arg, sizeof (fs_arg));
1092         fs_arg.fs_state = statep;
1093         fs_arg.fs_propname = (char *)propname;
1094         fs_arg.fs_flowname = (char *)statep->fs_flow;
1095 
1096         ofmt_print(statep->fs_ofmt, (void *)&fs_arg);
1097 
1098         return (DLADM_WALK_CONTINUE);
1099 }
1100 
1101 /*ARGSUSED*/
1102 /* Walker function called by dladm_walk_flow to display flow properties */
1103 static int
1104 show_flowprop(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
1105 {
1106         show_flowprop_one_flow(arg, attr->fa_flowname);
1107         return (DLADM_WALK_CONTINUE);
1108 }
1109 
1110 /*
1111  * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1112  * usable to dladm_walk_datalink_id()
1113  */
1114 static int
1115 show_flowprop_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1116 {
1117         char    name[MAXLINKNAMELEN];
1118 
1119         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, name,
1120             sizeof (name)) != DLADM_STATUS_OK)
1121                 return (DLADM_WALK_TERMINATE);
1122 
1123         (void) dladm_walk_flow(show_flowprop, dh, linkid, arg, B_FALSE);
1124 
1125         return (DLADM_WALK_CONTINUE);
1126 }
1127 
1128 static void
1129 do_show_flowprop(int argc, char **argv)
1130 {
1131         int                     option;
1132         dladm_arg_list_t        *proplist = NULL;
1133         show_flowprop_state_t   state;
1134         char                    *fields_str = NULL;
1135         ofmt_handle_t           ofmt;
1136         ofmt_status_t           oferr;
1137         uint_t                  ofmtflags = 0;
1138 
1139         opterr = 0;
1140         state.fs_propvals = NULL;
1141         state.fs_line = NULL;
1142         state.fs_parsable = B_FALSE;
1143         state.fs_persist = B_FALSE;
1144         state.fs_header = B_TRUE;
1145         state.fs_retstatus = DLADM_STATUS_OK;
1146         state.fs_linkid = DATALINK_INVALID_LINKID;
1147         state.fs_flow = NULL;
1148 
1149         while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1150             prop_longopts, NULL)) != -1) {
1151                 switch (option) {
1152                 case 'p':
1153                         if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1154                             != DLADM_STATUS_OK)
1155                                 die("invalid flow properties specified");
1156                         break;
1157                 case 'c':
1158                         state.fs_parsable = B_TRUE;
1159                         ofmtflags |= OFMT_PARSABLE;
1160                         break;
1161                 case 'P':
1162                         state.fs_persist = B_TRUE;
1163                         break;
1164                 case 'l':
1165                         if (dladm_name2info(handle, optarg, &state.fs_linkid,
1166                             NULL, NULL, NULL) != DLADM_STATUS_OK)
1167                                 die("invalid link '%s'", optarg);
1168                         break;
1169                 case 'o':
1170                         fields_str = optarg;
1171                         break;
1172                 default:
1173                         die_opterr(optopt, option);
1174                         break;
1175                 }
1176         }
1177 
1178         if (optind == (argc - 1)) {
1179                 if (strlen(argv[optind]) >= MAXFLOWNAMELEN)
1180                         die("flow name too long");
1181                 state.fs_flow = argv[optind];
1182         } else if (optind != argc) {
1183                 usage();
1184         }
1185         state.fs_proplist = proplist;
1186         state.fs_status = DLADM_STATUS_OK;
1187 
1188         oferr = ofmt_open(fields_str, flowprop_fields, ofmtflags, 0, &ofmt);
1189         flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
1190         state.fs_ofmt = ofmt;
1191 
1192         /* Show properties for one flow */
1193         if (state.fs_flow != NULL) {
1194                 show_flowprop_one_flow(&state, state.fs_flow);
1195 
1196         /* Show properties for all flows on one link */
1197         } else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1198                 (void) show_flowprop_onelink(handle, state.fs_linkid, &state);
1199 
1200         /* Show properties for all flows on all links */
1201         } else {
1202                 (void) dladm_walk_datalink_id(show_flowprop_onelink, handle,
1203                     &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1204                     DLADM_OPT_ACTIVE);
1205         }
1206 
1207         dladm_free_props(proplist);
1208         ofmt_close(ofmt);
1209 }
1210 
1211 static void
1212 show_flowprop_one_flow(void *arg, const char *flow)
1213 {
1214         int                     i;
1215         char                    *buf;
1216         dladm_status_t          status;
1217         dladm_arg_list_t        *proplist = NULL;
1218         show_flowprop_state_t   *statep = arg;
1219         dladm_flow_attr_t       attr;
1220         const char              *savep;
1221 
1222         /*
1223          * Do not print flow props for invalid flows.
1224          */
1225         if ((status = dladm_flow_info(handle, flow, &attr)) !=
1226             DLADM_STATUS_OK) {
1227                 die("invalid flow: '%s'", flow);
1228         }
1229 
1230         savep = statep->fs_flow;
1231         statep->fs_flow = flow;
1232 
1233         proplist = statep->fs_proplist;
1234 
1235         buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1236             * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1237         if (buf == NULL)
1238                 die("insufficient memory");
1239 
1240         statep->fs_propvals = (char **)(void *)buf;
1241         for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1242                 statep->fs_propvals[i] = buf +
1243                     sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1244                     i * DLADM_PROP_VAL_MAX;
1245         }
1246         statep->fs_line = buf +
1247             (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1248 
1249         /* show only specified flow properties */
1250         if (proplist != NULL) {
1251                 for (i = 0; i < proplist->al_count; i++) {
1252                         if (show_one_flowprop(statep,
1253                             proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1254                                 break;
1255                 }
1256 
1257         /* show all flow properties */
1258         } else {
1259                 status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1260                 if (status != DLADM_STATUS_OK)
1261                         die_dlerr(status, "show-flowprop");
1262         }
1263         free(buf);
1264         statep->fs_flow = savep;
1265 }
1266 
1267 /*
1268  * default output callback function that, when invoked from dladm_print_output,
1269  * prints string which is offset by of_arg->ofmt_id within buf.
1270  */
1271 static boolean_t
1272 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1273 {
1274         char *value;
1275 
1276         value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1277         (void) strlcpy(buf, value, bufsize);
1278         return (B_TRUE);
1279 }
1280 
1281 static void
1282 flowadm_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1283     ofmt_handle_t ofmt)
1284 {
1285         char buf[OFMT_BUFSIZE];
1286 
1287         if (oferr == OFMT_SUCCESS)
1288                 return;
1289         (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1290         /*
1291          * All errors are considered fatal in parsable mode.
1292          * NOMEM errors are always fatal, regardless of mode.
1293          * For other errors, we print diagnostics in human-readable
1294          * mode and processs what we can.
1295          */
1296         if (parsable || oferr == OFMT_ENOFIELDS) {
1297                 ofmt_close(ofmt);
1298                 die(buf);
1299         } else {
1300                 warn(buf);
1301         }
1302 }