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 <ctype.h>
  28 #include <locale.h>
  29 #include <signal.h>
  30 #include <stdarg.h>
  31 #include <stdlib.h>
  32 #include <fcntl.h>
  33 #include <string.h>
  34 #include <stropts.h>
  35 #include <sys/stat.h>
  36 #include <errno.h>
  37 #include <strings.h>
  38 #include <getopt.h>
  39 #include <unistd.h>
  40 #include <priv.h>
  41 #include <termios.h>
  42 #include <pwd.h>
  43 #include <auth_attr.h>
  44 #include <auth_list.h>
  45 #include <libintl.h>
  46 #include <libdevinfo.h>
  47 #include <libdlpi.h>
  48 #include <libdladm.h>
  49 #include <libdllink.h>
  50 #include <libdlstat.h>
  51 #include <libdlaggr.h>
  52 #include <libinetutil.h>
  53 #include <bsm/adt.h>
  54 #include <bsm/adt_event.h>
  55 #include <stddef.h>
  56 #include <ofmt.h>
  57 
  58 typedef struct link_chain_s {
  59         datalink_id_t           lc_linkid;
  60         boolean_t               lc_visited;
  61         dladm_stat_chain_t      *lc_statchain[DLADM_STAT_NUM_STATS];
  62         struct link_chain_s     *lc_next;
  63 } link_chain_t;
  64 
  65 typedef void *  (*stats2str_t)(const char *, void *,
  66                     char, boolean_t);
  67 
  68 typedef struct show_state {
  69         link_chain_t    *ls_linkchain;
  70         boolean_t       ls_stattype[DLADM_STAT_NUM_STATS];
  71         stats2str_t     ls_stats2str[DLADM_STAT_NUM_STATS];
  72         ofmt_handle_t   ls_ofmt;
  73         char            ls_unit;
  74         boolean_t       ls_parsable;
  75 } show_state_t;
  76 
  77 typedef struct show_history_state_s {
  78         boolean_t       hs_plot;
  79         boolean_t       hs_parsable;
  80         boolean_t       hs_printheader;
  81         boolean_t       hs_first;
  82         boolean_t       hs_showall;
  83         ofmt_handle_t   hs_ofmt;
  84 } show_history_state_t;
  85 
  86 /*
  87  * callback functions for printing output and error diagnostics.
  88  */
  89 static ofmt_cb_t print_default_cb;
  90 
  91 static void dlstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
  92 
  93 typedef void cmdfunc_t(int, char **, const char *);
  94 
  95 static cmdfunc_t do_show, do_show_history, do_show_phys, do_show_link;
  96 static cmdfunc_t do_show_aggr;
  97 
  98 static void     die(const char *, ...);
  99 static void     die_optdup(int);
 100 static void     die_opterr(int, int, const char *);
 101 static void     die_dlerr(dladm_status_t, const char *, ...);
 102 static void     warn(const char *, ...);
 103 
 104 typedef struct  cmd {
 105         char            *c_name;
 106         cmdfunc_t       *c_fn;
 107         const char      *c_usage;
 108 } cmd_t;
 109 
 110 static cmd_t    cmds[] = {
 111         { "",           do_show,
 112             "dlstat [-r | -t] [-i <interval>] [link]\n"
 113             "       dlstat [-a | -A] [-i <interval>] [-p] [ -o field[,...]]\n"
 114             "              [-u R|K|M|G|T|P] [link]"},
 115         { "show-phys", do_show_phys,
 116             "dlstat show-phys [-r | -t] [-i interval] [-a]\n"
 117             "                 [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
 118             "[link]"},
 119         { "show-link", do_show_link,
 120             "dlstat show-link [-r [-F] | -t] [-i interval] [-a]\n"
 121             "                 [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
 122             "[link]\n"
 123             "       dlstat show-link -h [-a] [-d] [-F <format>]\n"
 124             "                 [-s <DD/MM/YYYY,HH:MM:SS>] "
 125             "[-e <DD/MM/YYYY,HH:MM:SS>]\n"
 126             "                 -f <logfile> [<link>]" },
 127         { "show-aggr", do_show_aggr,
 128             "dlstat show-aggr [-r | -t] [-i interval] [-p]\n"
 129             "                 [ -o field[,...]] [-u R|K|M|G|T|P] "
 130             " [link]" }
 131 };
 132 
 133 #define MAXSTATLEN 15
 134 
 135 /*
 136  * dlstat : total stat fields
 137  */
 138 typedef struct total_fields_buf_s {
 139         char t_linkname[MAXLINKNAMELEN];
 140         char t_ipackets[MAXSTATLEN];
 141         char t_rbytes[MAXSTATLEN];
 142         char t_opackets[MAXSTATLEN];
 143         char t_obytes[MAXSTATLEN];
 144 } total_fields_buf_t;
 145 
 146 static ofmt_field_t total_s_fields[] = {
 147 { "LINK",       15,
 148     offsetof(total_fields_buf_t, t_linkname),   print_default_cb},
 149 { "IPKTS",      8,
 150     offsetof(total_fields_buf_t, t_ipackets),   print_default_cb},
 151 { "RBYTES",     8,
 152     offsetof(total_fields_buf_t, t_rbytes),     print_default_cb},
 153 { "OPKTS",      8,
 154     offsetof(total_fields_buf_t, t_opackets),   print_default_cb},
 155 { "OBYTES",     8,
 156     offsetof(total_fields_buf_t, t_obytes),     print_default_cb},
 157 { NULL,         0,      0,              NULL}};
 158 
 159 /*
 160  * dlstat show-phys: both Rx and Tx stat fields
 161  */
 162 typedef struct ring_fields_buf_s {
 163         char r_linkname[MAXLINKNAMELEN];
 164         char r_type[MAXSTATLEN];
 165         char r_id[MAXSTATLEN];
 166         char r_index[MAXSTATLEN];
 167         char r_packets[MAXSTATLEN];
 168         char r_bytes[MAXSTATLEN];
 169 } ring_fields_buf_t;
 170 
 171 static ofmt_field_t ring_s_fields[] = {
 172 { "LINK",       15,
 173     offsetof(ring_fields_buf_t, r_linkname),    print_default_cb},
 174 { "TYPE",       5,
 175     offsetof(ring_fields_buf_t, r_type),        print_default_cb},
 176 { "ID",         7,
 177     offsetof(ring_fields_buf_t, r_id),          print_default_cb},
 178 { "INDEX",      6,
 179     offsetof(ring_fields_buf_t, r_index),       print_default_cb},
 180 { "PKTS",       8,
 181     offsetof(ring_fields_buf_t, r_packets),     print_default_cb},
 182 { "BYTES",      8,
 183     offsetof(ring_fields_buf_t, r_bytes),       print_default_cb},
 184 { NULL,         0,              0,              NULL}};
 185 
 186 /*
 187  * dlstat show-phys -r: Rx Ring stat fields
 188  */
 189 typedef struct rx_ring_fields_buf_s {
 190         char rr_linkname[MAXLINKNAMELEN];
 191         char rr_type[MAXSTATLEN];
 192         char rr_id[MAXSTATLEN];
 193         char rr_index[MAXSTATLEN];
 194         char rr_ipackets[MAXSTATLEN];
 195         char rr_rbytes[MAXSTATLEN];
 196 } rx_ring_fields_buf_t;
 197 
 198 static ofmt_field_t rx_ring_s_fields[] = {
 199 { "LINK",       15,
 200     offsetof(rx_ring_fields_buf_t, rr_linkname),        print_default_cb},
 201 { "TYPE",       5,
 202     offsetof(rx_ring_fields_buf_t, rr_type),            print_default_cb},
 203 { "ID",         7,
 204     offsetof(rx_ring_fields_buf_t, rr_id),              print_default_cb},
 205 { "INDEX",      6,
 206     offsetof(rx_ring_fields_buf_t, rr_index),           print_default_cb},
 207 { "IPKTS",      8,
 208     offsetof(rx_ring_fields_buf_t, rr_ipackets),        print_default_cb},
 209 { "RBYTES",     8,
 210     offsetof(rx_ring_fields_buf_t, rr_rbytes),          print_default_cb},
 211 { NULL,         0,              0,              NULL}};
 212 
 213 /*
 214  * dlstat show-phys -t: Tx Ring stat fields
 215  */
 216 typedef struct tx_ring_fields_buf_s {
 217         char tr_linkname[MAXLINKNAMELEN];
 218         char tr_type[MAXSTATLEN];
 219         char tr_id[MAXSTATLEN];
 220         char tr_index[MAXSTATLEN];
 221         char tr_opackets[MAXSTATLEN];
 222         char tr_obytes[MAXSTATLEN];
 223 } tx_ring_fields_buf_t;
 224 
 225 static ofmt_field_t tx_ring_s_fields[] = {
 226 { "LINK",       15,
 227     offsetof(tx_ring_fields_buf_t, tr_linkname),        print_default_cb},
 228 { "TYPE",       5,
 229     offsetof(tx_ring_fields_buf_t, tr_type),            print_default_cb},
 230 { "ID",         7,
 231     offsetof(tx_ring_fields_buf_t, tr_id),              print_default_cb},
 232 { "INDEX",      6,
 233     offsetof(tx_ring_fields_buf_t, tr_index),           print_default_cb},
 234 { "OPKTS",      8,
 235     offsetof(tx_ring_fields_buf_t, tr_opackets),        print_default_cb},
 236 { "OBYTES",     8,
 237     offsetof(tx_ring_fields_buf_t, tr_obytes),          print_default_cb},
 238 { NULL,         0,              0,              NULL}};
 239 
 240 /*
 241  * dlstat show-link: both Rx and Tx lane fields
 242  */
 243 typedef struct lane_fields_buf_s {
 244         char l_linkname[MAXLINKNAMELEN];
 245         char l_type[MAXSTATLEN];
 246         char l_id[MAXSTATLEN];
 247         char l_index[MAXSTATLEN];
 248         char l_packets[MAXSTATLEN];
 249         char l_bytes[MAXSTATLEN];
 250 } lane_fields_buf_t;
 251 
 252 static ofmt_field_t lane_s_fields[] = {
 253 { "LINK",       15,
 254     offsetof(lane_fields_buf_t, l_linkname),    print_default_cb},
 255 { "TYPE",       5,
 256     offsetof(lane_fields_buf_t, l_type),        print_default_cb},
 257 { "ID",         7,
 258     offsetof(lane_fields_buf_t, l_id),          print_default_cb},
 259 { "INDEX",      6,
 260     offsetof(lane_fields_buf_t, l_index),       print_default_cb},
 261 { "PKTS",       8,
 262     offsetof(lane_fields_buf_t, l_packets),     print_default_cb},
 263 { "BYTES",      8,
 264     offsetof(lane_fields_buf_t, l_bytes),       print_default_cb},
 265 { NULL,         0,              0,              NULL}};
 266 
 267 /*
 268  * dlstat show-link -r, dlstat -r: Rx Lane stat fields
 269  */
 270 typedef struct rx_lane_fields_buf_s {
 271         char rl_linkname[MAXLINKNAMELEN];
 272         char rl_type[MAXSTATLEN];
 273         char rl_id[MAXSTATLEN];
 274         char rl_index[MAXSTATLEN];
 275         char rl_ipackets[MAXSTATLEN];
 276         char rl_rbytes[MAXSTATLEN];
 277         char rl_intrs[MAXSTATLEN];
 278         char rl_polls[MAXSTATLEN];
 279         char rl_sdrops[MAXSTATLEN];
 280         char rl_chl10[MAXSTATLEN];
 281         char rl_ch10_50[MAXSTATLEN];
 282         char rl_chg50[MAXSTATLEN];
 283 } rx_lane_fields_buf_t;
 284 
 285 static ofmt_field_t rx_lane_s_fields[] = {
 286 { "LINK",       10,
 287     offsetof(rx_lane_fields_buf_t, rl_linkname),        print_default_cb},
 288 { "TYPE",       5,
 289     offsetof(rx_lane_fields_buf_t, rl_type),            print_default_cb},
 290 { "ID",         7,
 291     offsetof(rx_lane_fields_buf_t, rl_id),              print_default_cb},
 292 { "INDEX",      6,
 293     offsetof(rx_lane_fields_buf_t, rl_index),           print_default_cb},
 294 { "IPKTS",      8,
 295     offsetof(rx_lane_fields_buf_t, rl_ipackets),        print_default_cb},
 296 { "RBYTES",     8,
 297     offsetof(rx_lane_fields_buf_t, rl_rbytes),          print_default_cb},
 298 { "INTRS",      8,
 299     offsetof(rx_lane_fields_buf_t, rl_intrs),           print_default_cb},
 300 { "POLLS",      8,
 301     offsetof(rx_lane_fields_buf_t, rl_polls),           print_default_cb},
 302 { "SDROPS",     8,
 303     offsetof(rx_lane_fields_buf_t, rl_sdrops),          print_default_cb},
 304 { "CH<10",   8,
 305     offsetof(rx_lane_fields_buf_t, rl_chl10),           print_default_cb},
 306 { "CH10-50",    8,
 307     offsetof(rx_lane_fields_buf_t, rl_ch10_50),         print_default_cb},
 308 { "CH>50",   8,
 309     offsetof(rx_lane_fields_buf_t, rl_chg50),           print_default_cb},
 310 { NULL,         0,              0,              NULL}};
 311 
 312 /*
 313  * dlstat show-link -r -F: Rx fanout stat fields
 314  */
 315 typedef struct rx_fanout_lane_fields_buf_s {
 316         char rfl_linkname[MAXLINKNAMELEN];
 317         char rfl_type[MAXSTATLEN];
 318         char rfl_id[MAXSTATLEN];
 319         char rfl_index[MAXSTATLEN];
 320         char rfl_fout[MAXSTATLEN];
 321         char rfl_ipackets[MAXSTATLEN];
 322         char rfl_rbytes[MAXSTATLEN];
 323 } rx_fanout_lane_fields_buf_t;
 324 
 325 static ofmt_field_t rx_fanout_lane_s_fields[] = {
 326 { "LINK",       15,
 327     offsetof(rx_fanout_lane_fields_buf_t, rfl_linkname), print_default_cb},
 328 { "TYPE",       5,
 329     offsetof(rx_fanout_lane_fields_buf_t, rfl_type),    print_default_cb},
 330 { "ID",         7,
 331     offsetof(rx_fanout_lane_fields_buf_t, rfl_id),      print_default_cb},
 332 { "INDEX",      6,
 333     offsetof(rx_fanout_lane_fields_buf_t, rfl_index),   print_default_cb},
 334 { "FOUT",       6,
 335     offsetof(rx_fanout_lane_fields_buf_t, rfl_fout),    print_default_cb},
 336 { "IPKTS",      8,
 337     offsetof(rx_fanout_lane_fields_buf_t, rfl_ipackets), print_default_cb},
 338 { "RBYTES",     8,
 339     offsetof(rx_fanout_lane_fields_buf_t, rfl_rbytes),  print_default_cb},
 340 { NULL,         0,              0,              NULL}};
 341 
 342 /*
 343  * dlstat show-link -t: Tx Lane stat fields
 344  */
 345 typedef struct tx_lane_fields_buf_s {
 346         char tl_linkname[MAXLINKNAMELEN];
 347         char tl_index[MAXSTATLEN];
 348         char tl_type[MAXSTATLEN];
 349         char tl_id[MAXSTATLEN];
 350         char tl_opackets[MAXSTATLEN];
 351         char tl_obytes[MAXSTATLEN];
 352         char tl_blockcnt[MAXSTATLEN];
 353         char tl_unblockcnt[MAXSTATLEN];
 354         char tl_sdrops[MAXSTATLEN];
 355 } tx_lane_fields_buf_t;
 356 
 357 static ofmt_field_t tx_lane_s_fields[] = {
 358 { "LINK",       15,
 359     offsetof(tx_lane_fields_buf_t, tl_linkname),        print_default_cb},
 360 { "TYPE",       5,
 361     offsetof(tx_lane_fields_buf_t, tl_type),            print_default_cb},
 362 { "ID",         7,
 363     offsetof(tx_lane_fields_buf_t, tl_id),              print_default_cb},
 364 { "INDEX",      6,
 365     offsetof(tx_lane_fields_buf_t, tl_index),           print_default_cb},
 366 { "OPKTS",      8,
 367     offsetof(tx_lane_fields_buf_t, tl_opackets),        print_default_cb},
 368 { "OBYTES",     8,
 369     offsetof(tx_lane_fields_buf_t, tl_obytes),          print_default_cb},
 370 { "BLKCNT",     8,
 371     offsetof(tx_lane_fields_buf_t, tl_blockcnt),        print_default_cb},
 372 { "UBLKCNT",    8,
 373     offsetof(tx_lane_fields_buf_t, tl_unblockcnt),      print_default_cb},
 374 { "SDROPS",     8,
 375     offsetof(tx_lane_fields_buf_t, tl_sdrops),          print_default_cb},
 376 { NULL,         0,              0,              NULL}};
 377 
 378 /*
 379  * dlstat show-aggr: aggr port stat fields
 380  */
 381 typedef struct aggr_port_fields_buf_s {
 382         char ap_linkname[MAXLINKNAMELEN];
 383         char ap_portname[MAXLINKNAMELEN];
 384         char ap_ipackets[MAXSTATLEN];
 385         char ap_rbytes[MAXSTATLEN];
 386         char ap_opackets[MAXSTATLEN];
 387         char ap_obytes[MAXSTATLEN];
 388 } aggr_port_fields_buf_t;
 389 
 390 static ofmt_field_t aggr_port_s_fields[] = {
 391 { "LINK",       15,
 392     offsetof(aggr_port_fields_buf_t, ap_linkname),      print_default_cb},
 393 { "PORT",       15,
 394     offsetof(aggr_port_fields_buf_t, ap_portname),      print_default_cb},
 395 { "IPKTS",      8,
 396     offsetof(aggr_port_fields_buf_t, ap_ipackets),      print_default_cb},
 397 { "RBYTES",     8,
 398     offsetof(aggr_port_fields_buf_t, ap_rbytes),        print_default_cb},
 399 { "OPKTS",      8,
 400     offsetof(aggr_port_fields_buf_t, ap_opackets),      print_default_cb},
 401 { "OBYTES",     8,
 402     offsetof(aggr_port_fields_buf_t, ap_obytes),        print_default_cb},
 403 { NULL,         0,              0,              NULL}};
 404 
 405 /*
 406  * structures for 'dlstat show-link -h'
 407  */
 408 typedef struct  history_fields_buf_s {
 409         char    h_link[12];
 410         char    h_duration[10];
 411         char    h_ipackets[9];
 412         char    h_rbytes[10];
 413         char    h_opackets[9];
 414         char    h_obytes[10];
 415         char    h_bandwidth[14];
 416 } history_fields_buf_t;
 417 
 418 static ofmt_field_t history_fields[] = {
 419 { "LINK",       13,
 420         offsetof(history_fields_buf_t, h_link), print_default_cb},
 421 { "DURATION",   11,
 422         offsetof(history_fields_buf_t, h_duration), print_default_cb},
 423 { "IPKTS",      10,
 424         offsetof(history_fields_buf_t, h_ipackets), print_default_cb},
 425 { "RBYTES",     11,
 426         offsetof(history_fields_buf_t, h_rbytes), print_default_cb},
 427 { "OPKTS",      10,
 428         offsetof(history_fields_buf_t, h_opackets), print_default_cb},
 429 { "OBYTES",     11,
 430         offsetof(history_fields_buf_t, h_obytes), print_default_cb},
 431 { "BANDWIDTH",  15,
 432         offsetof(history_fields_buf_t, h_bandwidth), print_default_cb},
 433 { NULL,         0, 0, NULL}};
 434 
 435 /*
 436  * structures for 'dlstat show-link -h link'
 437  */
 438 typedef struct  history_l_fields_buf_s {
 439         char    hl_link[12];
 440         char    hl_stime[13];
 441         char    hl_etime[13];
 442         char    hl_rbytes[8];
 443         char    hl_obytes[8];
 444         char    hl_bandwidth[14];
 445 } history_l_fields_buf_t;
 446 
 447 static ofmt_field_t history_l_fields[] = {
 448 /* name,        field width,    offset */
 449 { "LINK",       13,
 450         offsetof(history_l_fields_buf_t, hl_link), print_default_cb},
 451 { "START",      14,
 452         offsetof(history_l_fields_buf_t, hl_stime), print_default_cb},
 453 { "END",        14,
 454         offsetof(history_l_fields_buf_t, hl_etime), print_default_cb},
 455 { "RBYTES",     9,
 456         offsetof(history_l_fields_buf_t, hl_rbytes), print_default_cb},
 457 { "OBYTES",     9,
 458         offsetof(history_l_fields_buf_t, hl_obytes), print_default_cb},
 459 { "BANDWIDTH",  15,
 460         offsetof(history_l_fields_buf_t, hl_bandwidth), print_default_cb},
 461 { NULL,         0, 0, NULL}}
 462 ;
 463 
 464 static char *progname;
 465 
 466 /*
 467  * Handle to libdladm.  Opened in main() before the sub-command
 468  * specific function is called.
 469  */
 470 static dladm_handle_t handle = NULL;
 471 
 472 static void
 473 usage(void)
 474 {
 475         int     i;
 476         cmd_t   *cmdp;
 477 
 478         (void) fprintf(stderr, gettext("usage: "));
 479         for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
 480                 cmdp = &cmds[i];
 481                 if (cmdp->c_usage != NULL)
 482                         (void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
 483         }
 484 
 485         /* close dladm handle if it was opened */
 486         if (handle != NULL)
 487                 dladm_close(handle);
 488 
 489         exit(1);
 490 }
 491 
 492 int
 493 main(int argc, char *argv[])
 494 {
 495         int             i;
 496         cmd_t           *cmdp;
 497         dladm_status_t  status;
 498 
 499         (void) setlocale(LC_ALL, "");
 500 #if !defined(TEXT_DOMAIN)
 501 #define TEXT_DOMAIN "SYS_TEST"
 502 #endif
 503         (void) textdomain(TEXT_DOMAIN);
 504 
 505         progname = argv[0];
 506 
 507         /* Open the libdladm handle */
 508         if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
 509                 die_dlerr(status, "could not open /dev/dld");
 510 
 511         if (argc == 1) {
 512                 do_show(argc - 1, NULL, cmds[0].c_usage);
 513                 goto done;
 514         }
 515 
 516         for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
 517                 cmdp = &cmds[i];
 518                 if (strcmp(argv[1], cmdp->c_name) == 0) {
 519                         cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
 520                         goto done;
 521                 }
 522         }
 523 
 524         do_show(argc, &argv[0], cmds[0].c_usage);
 525 
 526 done:
 527         dladm_close(handle);
 528         return (0);
 529 }
 530 
 531 /*ARGSUSED*/
 532 static int
 533 show_history_date(dladm_usage_t *history, void *arg)
 534 {
 535         show_history_state_t    *state = arg;
 536         time_t                  stime;
 537         char                    timebuf[20];
 538         dladm_status_t          status;
 539         uint32_t                flags;
 540 
 541         /*
 542          * Only show history information for existing links unless '-a'
 543          * is specified.
 544          */
 545         if (!state->hs_showall) {
 546                 if ((status = dladm_name2info(handle, history->du_name,
 547                     NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
 548                         return (status);
 549                 }
 550                 if ((flags & DLADM_OPT_ACTIVE) == 0)
 551                         return (DLADM_STATUS_LINKINVAL);
 552         }
 553 
 554         stime = history->du_stime;
 555         (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
 556             localtime(&stime));
 557         (void) printf("%s\n", timebuf);
 558 
 559         return (DLADM_STATUS_OK);
 560 }
 561 
 562 static int
 563 show_history_time(dladm_usage_t *history, void *arg)
 564 {
 565         show_history_state_t    *state = arg;
 566         char                    buf[DLADM_STRSIZE];
 567         history_l_fields_buf_t  ubuf;
 568         time_t                  time;
 569         double                  bw;
 570         dladm_status_t          status;
 571         uint32_t                flags;
 572 
 573         /*
 574          * Only show history information for existing links unless '-a'
 575          * is specified.
 576          */
 577         if (!state->hs_showall) {
 578                 if ((status = dladm_name2info(handle, history->du_name,
 579                     NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
 580                         return (status);
 581                 }
 582                 if ((flags & DLADM_OPT_ACTIVE) == 0)
 583                         return (DLADM_STATUS_LINKINVAL);
 584         }
 585 
 586         if (state->hs_plot) {
 587                 if (!state->hs_printheader) {
 588                         if (state->hs_first) {
 589                                 (void) printf("# Time");
 590                                 state->hs_first = B_FALSE;
 591                         }
 592                         (void) printf(" %s", history->du_name);
 593                         if (history->du_last) {
 594                                 (void) printf("\n");
 595                                 state->hs_first = B_TRUE;
 596                                 state->hs_printheader = B_TRUE;
 597                         }
 598                 } else {
 599                         if (state->hs_first) {
 600                                 time = history->du_etime;
 601                                 (void) strftime(buf, sizeof (buf), "%T",
 602                                     localtime(&time));
 603                                 state->hs_first = B_FALSE;
 604                                 (void) printf("%s", buf);
 605                         }
 606                         bw = (double)history->du_bandwidth/1000;
 607                         (void) printf(" %.2f", bw);
 608                         if (history->du_last) {
 609                                 (void) printf("\n");
 610                                 state->hs_first = B_TRUE;
 611                         }
 612                 }
 613                 return (DLADM_STATUS_OK);
 614         }
 615 
 616         bzero(&ubuf, sizeof (ubuf));
 617 
 618         (void) snprintf(ubuf.hl_link, sizeof (ubuf.hl_link), "%s",
 619             history->du_name);
 620         time = history->du_stime;
 621         (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
 622         (void) snprintf(ubuf.hl_stime, sizeof (ubuf.hl_stime), "%s",
 623             buf);
 624         time = history->du_etime;
 625         (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
 626         (void) snprintf(ubuf.hl_etime, sizeof (ubuf.hl_etime), "%s",
 627             buf);
 628         (void) snprintf(ubuf.hl_rbytes, sizeof (ubuf.hl_rbytes),
 629             "%llu", history->du_rbytes);
 630         (void) snprintf(ubuf.hl_obytes, sizeof (ubuf.hl_obytes),
 631             "%llu", history->du_obytes);
 632         (void) snprintf(ubuf.hl_bandwidth, sizeof (ubuf.hl_bandwidth),
 633             "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
 634 
 635         ofmt_print(state->hs_ofmt, &ubuf);
 636         return (DLADM_STATUS_OK);
 637 }
 638 
 639 static int
 640 show_history_res(dladm_usage_t *history, void *arg)
 641 {
 642         show_history_state_t    *state = arg;
 643         char                    buf[DLADM_STRSIZE];
 644         history_fields_buf_t    ubuf;
 645         dladm_status_t          status;
 646         uint32_t                flags;
 647 
 648         /*
 649          * Only show history information for existing links unless '-a'
 650          * is specified.
 651          */
 652         if (!state->hs_showall) {
 653                 if ((status = dladm_name2info(handle, history->du_name,
 654                     NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
 655                         return (status);
 656                 }
 657                 if ((flags & DLADM_OPT_ACTIVE) == 0)
 658                         return (DLADM_STATUS_LINKINVAL);
 659         }
 660 
 661         bzero(&ubuf, sizeof (ubuf));
 662 
 663         (void) snprintf(ubuf.h_link, sizeof (ubuf.h_link), "%s",
 664             history->du_name);
 665         (void) snprintf(ubuf.h_duration, sizeof (ubuf.h_duration),
 666             "%llu", history->du_duration);
 667         (void) snprintf(ubuf.h_ipackets, sizeof (ubuf.h_ipackets),
 668             "%llu", history->du_ipackets);
 669         (void) snprintf(ubuf.h_rbytes, sizeof (ubuf.h_rbytes),
 670             "%llu", history->du_rbytes);
 671         (void) snprintf(ubuf.h_opackets, sizeof (ubuf.h_opackets),
 672             "%llu", history->du_opackets);
 673         (void) snprintf(ubuf.h_obytes, sizeof (ubuf.h_obytes),
 674             "%llu", history->du_obytes);
 675         (void) snprintf(ubuf.h_bandwidth, sizeof (ubuf.h_bandwidth),
 676             "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
 677 
 678         ofmt_print(state->hs_ofmt, &ubuf);
 679 
 680         return (DLADM_STATUS_OK);
 681 }
 682 
 683 static boolean_t
 684 valid_formatspec(char *formatspec_str)
 685 {
 686         return (strcmp(formatspec_str, "gnuplot") == 0);
 687 }
 688 
 689 /*ARGSUSED*/
 690 static void
 691 do_show_history(int argc, char *argv[], const char *use)
 692 {
 693         char                    *file = NULL;
 694         int                     opt;
 695         dladm_status_t          status;
 696         boolean_t               d_arg = B_FALSE;
 697         char                    *stime = NULL;
 698         char                    *etime = NULL;
 699         char                    *resource = NULL;
 700         show_history_state_t    state;
 701         boolean_t               o_arg = B_FALSE;
 702         boolean_t               F_arg = B_FALSE;
 703         char                    *fields_str = NULL;
 704         char                    *formatspec_str = NULL;
 705         char                    *all_l_fields =
 706             "link,start,end,rbytes,obytes,bandwidth";
 707         ofmt_handle_t           ofmt;
 708         ofmt_status_t           oferr;
 709         uint_t                  ofmtflags = 0;
 710 
 711         bzero(&state, sizeof (show_history_state_t));
 712         state.hs_parsable = B_FALSE;
 713         state.hs_printheader = B_FALSE;
 714         state.hs_plot = B_FALSE;
 715         state.hs_first = B_TRUE;
 716 
 717         while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
 718                 switch (opt) {
 719                 case 'd':
 720                         d_arg = B_TRUE;
 721                         break;
 722                 case 'a':
 723                         state.hs_showall = B_TRUE;
 724                         break;
 725                 case 'f':
 726                         file = optarg;
 727                         break;
 728                 case 's':
 729                         stime = optarg;
 730                         break;
 731                 case 'e':
 732                         etime = optarg;
 733                         break;
 734                 case 'o':
 735                         o_arg = B_TRUE;
 736                         fields_str = optarg;
 737                         break;
 738                 case 'F':
 739                         state.hs_plot = F_arg = B_TRUE;
 740                         formatspec_str = optarg;
 741                         break;
 742                 default:
 743                         die_opterr(optopt, opt, use);
 744                         break;
 745                 }
 746         }
 747 
 748         if (file == NULL)
 749                 die("show-link -h requires a file");
 750 
 751         if (optind == (argc-1)) {
 752                 uint32_t        flags;
 753 
 754                 resource = argv[optind];
 755                 if (!state.hs_showall &&
 756                     (((status = dladm_name2info(handle, resource, NULL, &flags,
 757                     NULL, NULL)) != DLADM_STATUS_OK) ||
 758                     ((flags & DLADM_OPT_ACTIVE) == 0))) {
 759                         die("invalid link: '%s'", resource);
 760                 }
 761         }
 762 
 763         if (F_arg && d_arg)
 764                 die("incompatible -d and -F options");
 765 
 766         if (F_arg && !valid_formatspec(formatspec_str))
 767                 die("Format specifier %s not supported", formatspec_str);
 768 
 769         if (state.hs_parsable)
 770                 ofmtflags |= OFMT_PARSABLE;
 771 
 772         if (resource == NULL && stime == NULL && etime == NULL) {
 773                 oferr = ofmt_open(fields_str, history_fields, ofmtflags, 0,
 774                     &ofmt);
 775         } else {
 776                 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
 777                         fields_str = all_l_fields;
 778                 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 0,
 779                     &ofmt);
 780 
 781         }
 782         dlstat_ofmt_check(oferr, state.hs_parsable, ofmt);
 783         state.hs_ofmt = ofmt;
 784 
 785         if (d_arg) {
 786                 /* Print log dates */
 787                 status = dladm_usage_dates(show_history_date,
 788                     DLADM_LOGTYPE_LINK, file, resource, &state);
 789         } else if (resource == NULL && stime == NULL && etime == NULL &&
 790             !F_arg) {
 791                 /* Print summary */
 792                 status = dladm_usage_summary(show_history_res,
 793                     DLADM_LOGTYPE_LINK, file, &state);
 794         } else if (resource != NULL) {
 795                 /* Print log entries for named resource */
 796                 status = dladm_walk_usage_res(show_history_time,
 797                     DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state);
 798         } else {
 799                 /* Print time and information for each link */
 800                 status = dladm_walk_usage_time(show_history_time,
 801                     DLADM_LOGTYPE_LINK, file, stime, etime, &state);
 802         }
 803 
 804         if (status != DLADM_STATUS_OK)
 805                 die_dlerr(status, "show-link -h");
 806         ofmt_close(ofmt);
 807 }
 808 
 809 boolean_t
 810 dlstat_unit(char *oarg, char *unit)
 811 {
 812         if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
 813             (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
 814             (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
 815                 *unit = oarg[0];
 816                 return (B_TRUE);
 817         }
 818 
 819         return (B_FALSE);
 820 }
 821 
 822 void
 823 map_to_units(char *buf, uint_t bufsize, double num, char unit,
 824     boolean_t parsable)
 825 {
 826         if (parsable) {
 827                 (void) snprintf(buf, bufsize, "%.0lf", num);
 828                 return;
 829         }
 830 
 831         if (unit == '\0') {
 832                 int index;
 833 
 834                 for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
 835                         ;
 836 
 837                 switch (index) {
 838                         case 0:
 839                                 unit = '\0';
 840                                 break;
 841                         case 1:
 842                                 unit = 'K';
 843                                 break;
 844                         case 2:
 845                                 unit = 'M';
 846                                 break;
 847                         case 3:
 848                                 unit = 'G';
 849                                 break;
 850                         case 4:
 851                                 unit = 'T';
 852                                 break;
 853                         case 5:
 854                                 /* Largest unit supported */
 855                         default:
 856                                 unit = 'P';
 857                                 break;
 858                 }
 859         } else  {
 860                 switch (unit) {
 861                         case 'R':
 862                                 /* Already raw numbers */
 863                                 unit = '\0';
 864                                 break;
 865                         case 'K':
 866                                 num /= 1000;
 867                                 break;
 868                         case 'M':
 869                                 num /= (1000*1000);
 870                                 break;
 871                         case 'G':
 872                                 num /= (1000*1000*1000);
 873                                 break;
 874                         case 'T':
 875                                 num /= (1000.0*1000.0*1000.0*1000.0);
 876                                 break;
 877                         case 'P':
 878                                 /* Largest unit supported */
 879                         default:
 880                                 num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
 881                                 break;
 882                 }
 883         }
 884 
 885         if (unit == '\0')
 886                 (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
 887         else
 888                 (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
 889 }
 890 
 891 link_chain_t *
 892 get_link_prev_stat(datalink_id_t linkid, void *arg)
 893 {
 894         show_state_t    *state = (show_state_t *)arg;
 895         link_chain_t    *link_curr = NULL;
 896 
 897         /* Scan prev linkid list and look for entry matching this entry */
 898         for (link_curr = state->ls_linkchain; link_curr;
 899             link_curr = link_curr->lc_next) {
 900                 if (link_curr->lc_linkid == linkid)
 901                         break;
 902         }
 903                                 /* New link, add it */
 904         if (link_curr == NULL) {
 905                 link_curr = (link_chain_t *)malloc(sizeof (link_chain_t));
 906                 if (link_curr == NULL)
 907                         goto done;
 908                 link_curr->lc_linkid = linkid;
 909                 bzero(&link_curr->lc_statchain,
 910                     sizeof (link_curr->lc_statchain));
 911                 link_curr->lc_next = state->ls_linkchain;
 912                 state->ls_linkchain = link_curr;
 913         }
 914 done:
 915         return (link_curr);
 916 }
 917 
 918 /*
 919  * Number of links may change while dlstat with -i is executing.
 920  * Free memory allocated for links that are no longer there.
 921  * Prepare for next iteration by marking visited = false for existing stat
 922  * entries.
 923  */
 924 static void
 925 cleanup_removed_links(show_state_t *state)
 926 {
 927         link_chain_t    *lcurr;
 928         link_chain_t    *lprev;
 929         link_chain_t    *tofree;
 930         int             i;
 931 
 932         /* Delete all nodes from the list that have lc_visited marked false */
 933         lcurr = state->ls_linkchain;
 934         while (lcurr != NULL) {
 935                 if (lcurr->lc_visited) {
 936                         lcurr->lc_visited = B_FALSE;
 937                         lprev = lcurr;
 938                         lcurr = lcurr->lc_next;
 939                         continue;
 940                 }
 941                                 /* Is it head of the list? */
 942                 if (lcurr == state->ls_linkchain)
 943                         state->ls_linkchain = lcurr->lc_next;
 944                 else
 945                         lprev->lc_next = lcurr->lc_next;
 946                                 /* lprev remains the same */
 947                 tofree = lcurr;
 948                 lcurr = lcurr->lc_next;
 949 
 950                                 /* Free stats memory for the removed link */
 951                 for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
 952                         if (state->ls_stattype[i])
 953                                 dladm_link_stat_free(tofree->lc_statchain[i]);
 954                 }
 955                 free(tofree);
 956         }
 957 }
 958 
 959 void *
 960 print_total_stats(const char *linkname, void *statentry, char unit,
 961     boolean_t parsable)
 962 {
 963         total_stat_entry_t      *sentry = statentry;
 964         total_stat_t            *link_stats = &sentry->tse_stats;
 965         total_fields_buf_t      *buf;
 966 
 967         buf = malloc(sizeof (total_fields_buf_t));
 968         if (buf == NULL)
 969                 goto done;
 970 
 971         (void) snprintf(buf->t_linkname, sizeof (buf->t_linkname), "%s",
 972             linkname);
 973 
 974         map_to_units(buf->t_ipackets, sizeof (buf->t_ipackets),
 975             link_stats->ts_ipackets, unit, parsable);
 976 
 977         map_to_units(buf->t_rbytes, sizeof (buf->t_rbytes),
 978             link_stats->ts_rbytes, unit, parsable);
 979 
 980         map_to_units(buf->t_opackets, sizeof (buf->t_opackets),
 981             link_stats->ts_opackets, unit, parsable);
 982 
 983         map_to_units(buf->t_obytes, sizeof (buf->t_obytes),
 984             link_stats->ts_obytes, unit, parsable);
 985 
 986 done:
 987         return (buf);
 988 }
 989 
 990 void *
 991 print_rx_generic_ring_stats(const char *linkname, void *statentry, char unit,
 992     boolean_t parsable)
 993 {
 994         ring_stat_entry_t       *sentry = statentry;
 995         ring_stat_t             *link_stats = &sentry->re_stats;
 996         ring_fields_buf_t       *buf;
 997 
 998         buf = malloc(sizeof (ring_fields_buf_t));
 999         if (buf == NULL)
1000                 goto done;
1001 
1002         (void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
1003             linkname);
1004 
1005         (void) snprintf(buf->r_type, sizeof (buf->r_type), "rx");
1006 
1007         if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1008                 (void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
1009         } else {
1010                 (void) snprintf(buf->r_index, sizeof (buf->r_index),
1011                     "%llu", sentry->re_index);
1012         }
1013 
1014         map_to_units(buf->r_packets, sizeof (buf->r_packets),
1015             link_stats->r_packets, unit, parsable);
1016 
1017         map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
1018             link_stats->r_bytes, unit, parsable);
1019 
1020 done:
1021         return (buf);
1022 }
1023 
1024 void *
1025 print_tx_generic_ring_stats(const char *linkname, void *statentry, char unit,
1026     boolean_t parsable)
1027 {
1028         ring_stat_entry_t       *sentry = statentry;
1029         ring_stat_t             *link_stats = &sentry->re_stats;
1030         ring_fields_buf_t       *buf;
1031 
1032         buf = malloc(sizeof (ring_fields_buf_t));
1033         if (buf == NULL)
1034                 goto done;
1035 
1036         (void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
1037             linkname);
1038 
1039         (void) snprintf(buf->r_type, sizeof (buf->r_type), "tx");
1040 
1041         if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1042                 (void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
1043         } else {
1044                 (void) snprintf(buf->r_index, sizeof (buf->r_index),
1045                     "%llu", sentry->re_index);
1046         }
1047 
1048         map_to_units(buf->r_packets, sizeof (buf->r_packets),
1049             link_stats->r_packets, unit, parsable);
1050 
1051         map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
1052             link_stats->r_bytes, unit, parsable);
1053 
1054 done:
1055         return (buf);
1056 }
1057 
1058 void *
1059 print_rx_ring_stats(const char *linkname, void *statentry, char unit,
1060     boolean_t parsable)
1061 {
1062         ring_stat_entry_t       *sentry = statentry;
1063         ring_stat_t             *link_stats = &sentry->re_stats;
1064         rx_ring_fields_buf_t    *buf;
1065 
1066         buf = malloc(sizeof (rx_ring_fields_buf_t));
1067         if (buf == NULL)
1068                 goto done;
1069 
1070         (void) snprintf(buf->rr_linkname, sizeof (buf->rr_linkname), "%s",
1071             linkname);
1072 
1073         (void) snprintf(buf->rr_type, sizeof (buf->rr_type), "rx");
1074 
1075         if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1076                 (void) snprintf(buf->rr_index, sizeof (buf->rr_index), "--");
1077         } else {
1078                 (void) snprintf(buf->rr_index, sizeof (buf->rr_index),
1079                     "%llu", sentry->re_index);
1080         }
1081 
1082         map_to_units(buf->rr_ipackets, sizeof (buf->rr_ipackets),
1083             link_stats->r_packets, unit, parsable);
1084 
1085         map_to_units(buf->rr_rbytes, sizeof (buf->rr_rbytes),
1086             link_stats->r_bytes, unit, parsable);
1087 
1088 done:
1089         return (buf);
1090 }
1091 
1092 void *
1093 print_tx_ring_stats(const char *linkname, void *statentry, char unit,
1094     boolean_t parsable)
1095 {
1096         ring_stat_entry_t       *sentry = statentry;
1097         ring_stat_t             *link_stats = &sentry->re_stats;
1098         tx_ring_fields_buf_t    *buf;
1099 
1100         buf = malloc(sizeof (tx_ring_fields_buf_t));
1101         if (buf == NULL)
1102                 goto done;
1103 
1104         (void) snprintf(buf->tr_linkname, sizeof (buf->tr_linkname), "%s",
1105             linkname);
1106 
1107         (void) snprintf(buf->tr_type, sizeof (buf->tr_type), "tx");
1108 
1109         if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
1110                 (void) snprintf(buf->tr_index, sizeof (buf->tr_index), "--");
1111         } else {
1112                 (void) snprintf(buf->tr_index, sizeof (buf->tr_index),
1113                     "%llu", sentry->re_index);
1114         }
1115 
1116         map_to_units(buf->tr_opackets, sizeof (buf->tr_opackets),
1117             link_stats->r_packets, unit, parsable);
1118 
1119         map_to_units(buf->tr_obytes, sizeof (buf->tr_obytes),
1120             link_stats->r_bytes, unit, parsable);
1121 
1122 done:
1123         return (buf);
1124 }
1125 
1126 void *
1127 print_rx_generic_lane_stats(const char *linkname, void *statentry, char unit,
1128     boolean_t parsable)
1129 {
1130         rx_lane_stat_entry_t    *sentry = statentry;
1131         rx_lane_stat_t          *link_stats = &sentry->rle_stats;
1132         lane_fields_buf_t       *buf;
1133 
1134         if (sentry->rle_id == L_DFNCT)
1135                 return (NULL);
1136 
1137         buf = malloc(sizeof (lane_fields_buf_t));
1138         if (buf == NULL)
1139                 goto done;
1140 
1141         (void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
1142             linkname);
1143 
1144         (void) snprintf(buf->l_type, sizeof (buf->l_type), "rx");
1145 
1146         if (sentry->rle_id == L_HWLANE)
1147                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
1148         else if (sentry->rle_id == L_SWLANE)
1149                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
1150         else if (sentry->rle_id == L_LOCAL)
1151                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "local");
1152         else if (sentry->rle_id == L_BCAST)
1153                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
1154         else
1155                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
1156 
1157         if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
1158                 (void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
1159         } else {
1160                 (void) snprintf(buf->l_index, sizeof (buf->l_index),
1161                     "%llu", sentry->rle_index);
1162         }
1163 
1164         map_to_units(buf->l_packets, sizeof (buf->l_packets),
1165             link_stats->rl_ipackets, unit, parsable);
1166 
1167         map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
1168             link_stats->rl_rbytes, unit, parsable);
1169 
1170 done:
1171         return (buf);
1172 }
1173 
1174 void *
1175 print_tx_generic_lane_stats(const char *linkname, void *statentry, char unit,
1176     boolean_t parsable)
1177 {
1178         tx_lane_stat_entry_t    *sentry = statentry;
1179         tx_lane_stat_t          *link_stats = &sentry->tle_stats;
1180         lane_fields_buf_t       *buf;
1181 
1182         if (sentry->tle_id == L_DFNCT)
1183                 return (NULL);
1184 
1185         buf = malloc(sizeof (lane_fields_buf_t));
1186         if (buf == NULL)
1187                 goto done;
1188 
1189         (void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
1190             linkname);
1191 
1192         (void) snprintf(buf->l_type, sizeof (buf->l_type), "tx");
1193 
1194         if (sentry->tle_id == L_HWLANE)
1195                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
1196         else if (sentry->tle_id == L_SWLANE)
1197                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
1198         else if (sentry->tle_id == L_BCAST)
1199                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
1200         else
1201                 (void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
1202 
1203         if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
1204                 (void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
1205         } else {
1206                 (void) snprintf(buf->l_index, sizeof (buf->l_index),
1207                     "%llu", sentry->tle_index);
1208         }
1209         map_to_units(buf->l_packets, sizeof (buf->l_packets),
1210             link_stats->tl_opackets, unit, parsable);
1211 
1212         map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
1213             link_stats->tl_obytes, unit, parsable);
1214 
1215 done:
1216         return (buf);
1217 }
1218 
1219 void *
1220 print_rx_lane_stats(const char *linkname, void *statentry, char unit,
1221     boolean_t parsable)
1222 {
1223         rx_lane_stat_entry_t    *sentry = statentry;
1224         rx_lane_stat_t          *link_stats = &sentry->rle_stats;
1225         rx_lane_fields_buf_t    *buf;
1226 
1227         if (sentry->rle_id == L_DFNCT)
1228                 return (NULL);
1229 
1230         buf = malloc(sizeof (rx_lane_fields_buf_t));
1231         if (buf == NULL)
1232                 goto done;
1233 
1234         (void) snprintf(buf->rl_linkname, sizeof (buf->rl_linkname), "%s",
1235             linkname);
1236 
1237         (void) snprintf(buf->rl_type, sizeof (buf->rl_type), "rx");
1238 
1239         if (sentry->rle_id == L_HWLANE)
1240                 (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "hw");
1241         else if (sentry->rle_id == L_SWLANE)
1242                 (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "sw");
1243         else if (sentry->rle_id == L_LOCAL)
1244                 (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "local");
1245         else if (sentry->rle_id == L_BCAST)
1246                 (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "bcast");
1247         else
1248                 (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "--");
1249 
1250         if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
1251                 (void) snprintf(buf->rl_index, sizeof (buf->rl_index), "--");
1252         } else {
1253                 (void) snprintf(buf->rl_index, sizeof (buf->rl_index),
1254                     "%llu", sentry->rle_index);
1255         }
1256 
1257         map_to_units(buf->rl_ipackets, sizeof (buf->rl_ipackets),
1258             link_stats->rl_ipackets, unit, parsable);
1259 
1260         map_to_units(buf->rl_rbytes, sizeof (buf->rl_rbytes),
1261             link_stats->rl_rbytes, unit, parsable);
1262 
1263         map_to_units(buf->rl_intrs, sizeof (buf->rl_intrs),
1264             link_stats->rl_intrs, unit, parsable);
1265 
1266         map_to_units(buf->rl_polls, sizeof (buf->rl_polls),
1267             link_stats->rl_polls, unit, parsable);
1268 
1269         map_to_units(buf->rl_sdrops, sizeof (buf->rl_sdrops),
1270             link_stats->rl_sdrops, unit, parsable);
1271 
1272         map_to_units(buf->rl_chl10, sizeof (buf->rl_chl10),
1273             link_stats->rl_chl10, unit, parsable);
1274 
1275         map_to_units(buf->rl_ch10_50, sizeof (buf->rl_ch10_50),
1276             link_stats->rl_ch10_50, unit, parsable);
1277 
1278         map_to_units(buf->rl_chg50, sizeof (buf->rl_chg50),
1279             link_stats->rl_chg50, unit, parsable);
1280 
1281 done:
1282         return (buf);
1283 }
1284 
1285 void *
1286 print_tx_lane_stats(const char *linkname, void *statentry, char unit,
1287     boolean_t parsable)
1288 {
1289         tx_lane_stat_entry_t    *sentry = statentry;
1290         tx_lane_stat_t          *link_stats = &sentry->tle_stats;
1291         tx_lane_fields_buf_t    *buf = NULL;
1292 
1293         if (sentry->tle_id == L_DFNCT)
1294                 return (NULL);
1295 
1296         buf = malloc(sizeof (tx_lane_fields_buf_t));
1297         if (buf == NULL)
1298                 goto done;
1299 
1300         (void) snprintf(buf->tl_linkname, sizeof (buf->tl_linkname), "%s",
1301             linkname);
1302 
1303         (void) snprintf(buf->tl_type, sizeof (buf->tl_type), "tx");
1304 
1305         if (sentry->tle_id == L_HWLANE)
1306                 (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "hw");
1307         else if (sentry->tle_id == L_SWLANE)
1308                 (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "sw");
1309         else if (sentry->tle_id == L_BCAST)
1310                 (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "bcast");
1311         else
1312                 (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "--");
1313 
1314         if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
1315                 (void) snprintf(buf->tl_index, sizeof (buf->tl_index), "--");
1316         } else {
1317                 (void) snprintf(buf->tl_index, sizeof (buf->tl_index),
1318                     "%llu", sentry->tle_index);
1319         }
1320 
1321         map_to_units(buf->tl_opackets, sizeof (buf->tl_opackets),
1322             link_stats->tl_opackets, unit, parsable);
1323 
1324         map_to_units(buf->tl_obytes, sizeof (buf->tl_obytes),
1325             link_stats->tl_obytes, unit, parsable);
1326 
1327         map_to_units(buf->tl_blockcnt, sizeof (buf->tl_blockcnt),
1328             link_stats->tl_blockcnt, unit, parsable);
1329 
1330         map_to_units(buf->tl_unblockcnt, sizeof (buf->tl_unblockcnt),
1331             link_stats->tl_unblockcnt, unit, parsable);
1332 
1333         map_to_units(buf->tl_sdrops, sizeof (buf->tl_sdrops),
1334             link_stats->tl_sdrops, unit, parsable);
1335 
1336 done:
1337         return (buf);
1338 }
1339 
1340 void *
1341 print_fanout_stats(const char *linkname, void *statentry, char unit,
1342     boolean_t parsable)
1343 {
1344         fanout_stat_entry_t             *sentry = statentry;
1345         fanout_stat_t                   *link_stats = &sentry->fe_stats;
1346         rx_fanout_lane_fields_buf_t     *buf;
1347 
1348         buf = malloc(sizeof (rx_fanout_lane_fields_buf_t));
1349         if (buf == NULL)
1350                 goto done;
1351 
1352         (void) snprintf(buf->rfl_linkname, sizeof (buf->rfl_linkname), "%s",
1353             linkname);
1354 
1355         (void) snprintf(buf->rfl_type, sizeof (buf->rfl_type), "rx");
1356 
1357         if (sentry->fe_id == L_HWLANE)
1358                 (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "hw");
1359         else if (sentry->fe_id == L_SWLANE)
1360                 (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "sw");
1361         else if (sentry->fe_id == L_LCLSWLANE)
1362                 (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "lcl/sw");
1363         else if (sentry->fe_id == L_LOCAL)
1364                 (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "local");
1365         else if (sentry->fe_id == L_BCAST)
1366                 (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "bcast");
1367         else
1368                 (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "--");
1369 
1370         if (sentry->fe_index == DLSTAT_INVALID_ENTRY) {
1371                 (void) snprintf(buf->rfl_index, sizeof (buf->rfl_index), "--");
1372         } else {
1373                 (void) snprintf(buf->rfl_index, sizeof (buf->rfl_index),
1374                     "%llu", sentry->fe_index);
1375         }
1376 
1377         if (sentry->fe_foutindex == DLSTAT_INVALID_ENTRY)
1378                 (void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "--");
1379         else {
1380                 (void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "%llu",
1381                     sentry->fe_foutindex);
1382         }
1383 
1384         map_to_units(buf->rfl_ipackets, sizeof (buf->rfl_ipackets),
1385             link_stats->f_ipackets, unit, parsable);
1386 
1387         map_to_units(buf->rfl_rbytes, sizeof (buf->rfl_rbytes),
1388             link_stats->f_rbytes, unit, parsable);
1389 
1390 done:
1391         return (buf);
1392 }
1393 
1394 void *
1395 print_aggr_port_stats(const char *linkname, void *statentry, char unit,
1396     boolean_t parsable)
1397 {
1398         aggr_port_stat_entry_t  *sentry = statentry;
1399         aggr_port_stat_t        *link_stats = &sentry->ape_stats;
1400         aggr_port_fields_buf_t  *buf;
1401         char                    portname[MAXLINKNAMELEN];
1402 
1403         buf = malloc(sizeof (aggr_port_fields_buf_t));
1404         if (buf == NULL)
1405                 goto done;
1406 
1407         (void) snprintf(buf->ap_linkname, sizeof (buf->ap_linkname), "%s",
1408             linkname);
1409 
1410         if (dladm_datalink_id2info(handle, sentry->ape_portlinkid, NULL,
1411             NULL, NULL, portname, DLPI_LINKNAME_MAX)
1412             != DLADM_STATUS_OK) {
1413                 (void) snprintf(buf->ap_portname,
1414                     sizeof (buf->ap_portname), "--");
1415         } else {
1416                 (void) snprintf(buf->ap_portname,
1417                     sizeof (buf->ap_portname), "%s", portname);
1418         }
1419 
1420         map_to_units(buf->ap_ipackets, sizeof (buf->ap_ipackets),
1421             link_stats->ap_ipackets, unit, parsable);
1422 
1423         map_to_units(buf->ap_rbytes, sizeof (buf->ap_rbytes),
1424             link_stats->ap_rbytes, unit, parsable);
1425 
1426         map_to_units(buf->ap_opackets, sizeof (buf->ap_opackets),
1427             link_stats->ap_opackets, unit, parsable);
1428 
1429         map_to_units(buf->ap_obytes, sizeof (buf->ap_obytes),
1430             link_stats->ap_obytes, unit, parsable);
1431 
1432 done:
1433         return (buf);
1434 }
1435 
1436 dladm_stat_chain_t *
1437 query_link_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg,
1438     dladm_stat_type_t stattype)
1439 {
1440         link_chain_t            *link_node;
1441         dladm_stat_chain_t      *curr_stat;
1442         dladm_stat_chain_t      *prev_stat = NULL;
1443         dladm_stat_chain_t      *diff_stat = NULL;
1444 
1445         /*  Get prev iteration stat for this link */
1446         link_node = get_link_prev_stat(linkid, arg);
1447         if (link_node == NULL)
1448                 goto done;
1449 
1450         link_node->lc_visited = B_TRUE;
1451         prev_stat = link_node->lc_statchain[stattype];
1452 
1453         /* Query library for current stats */
1454         curr_stat = dladm_link_stat_query(dh, linkid, stattype);
1455         if (curr_stat == NULL)
1456                 goto done;
1457 
1458         /* current stats - prev iteration stats */
1459         diff_stat = dladm_link_stat_diffchain(curr_stat, prev_stat, stattype);
1460 
1461         /* Free prev stats */
1462         dladm_link_stat_free(prev_stat);
1463 
1464         /* Prev <- curr stats */
1465         link_node->lc_statchain[stattype] = curr_stat;
1466 
1467 done:
1468         return (diff_stat);
1469 }
1470 
1471 void
1472 walk_dlstat_stats(show_state_t *state, const char *linkname,
1473     dladm_stat_type_t stattype, dladm_stat_chain_t *diff_stat)
1474 {
1475         dladm_stat_chain_t  *curr;
1476 
1477         /* Unpack invidual stat entry and call library consumer's callback */
1478         for (curr = diff_stat; curr != NULL; curr = curr->dc_next) {
1479                 void    *fields_buf;
1480 
1481                 /* Format the raw numbers for printing */
1482                 fields_buf = state->ls_stats2str[stattype](linkname,
1483                     curr->dc_statentry, state->ls_unit, state->ls_parsable);
1484                 /* Print the stats */
1485                 if (fields_buf != NULL)
1486                         ofmt_print(state->ls_ofmt, fields_buf);
1487                 free(fields_buf);
1488         }
1489 }
1490 
1491 static int
1492 show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1493 {
1494         show_state_t            *state = arg;
1495         int                     i;
1496         dladm_stat_chain_t      *diff_stat;
1497         char                    linkname[DLPI_LINKNAME_MAX];
1498 
1499         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1500             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1501                 goto done;
1502         }
1503 
1504         for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
1505                 if (state->ls_stattype[i]) {
1506                         /*
1507                          * Query library for stats
1508                          * Stats are returned as chain of raw numbers
1509                          */
1510                         diff_stat = query_link_stats(handle, linkid, arg, i);
1511                         walk_dlstat_stats(state, linkname, i, diff_stat);
1512                         dladm_link_stat_free(diff_stat);
1513                 }
1514         }
1515 done:
1516         return (DLADM_WALK_CONTINUE);
1517 }
1518 
1519 void
1520 show_link_stats(datalink_id_t linkid, show_state_t state, uint32_t interval)
1521 {
1522         for (;;) {
1523                 if (linkid == DATALINK_ALL_LINKID) {
1524                         (void) dladm_walk_datalink_id(show_queried_stats,
1525                             handle, &state, DATALINK_CLASS_ALL,
1526                             DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1527                 } else {
1528                         (void) show_queried_stats(handle, linkid, &state);
1529                 }
1530 
1531                 if (interval == 0)
1532                         break;
1533 
1534                 cleanup_removed_links(&state);
1535                 (void) sleep(interval);
1536         }
1537 }
1538 
1539 void
1540 print_all_stats(dladm_handle_t dh, datalink_id_t linkid,
1541     dladm_stat_chain_t *stat_chain)
1542 {
1543         dladm_stat_chain_t      *curr;
1544         name_value_stat_entry_t *stat_entry;
1545         name_value_stat_t       *curr_stat;
1546         boolean_t               stat_printed = B_FALSE;
1547         char                    linkname[MAXLINKNAMELEN];
1548         char                    prev_linkname[MAXLINKNAMELEN];
1549 
1550         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1551             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK)
1552                 return;
1553 
1554         for (curr = stat_chain; curr != NULL; curr = curr->dc_next) {
1555                 stat_entry = curr->dc_statentry;
1556                 /*
1557                  * Print header
1558                  * If link name is already printed in previous iteration,
1559                  * don't print again
1560                  */
1561                 if (strcmp(prev_linkname, linkname) != 0)
1562                         printf("%s \n", linkname);
1563                 printf("  %s \n", stat_entry->nve_header);
1564 
1565                 /* Print stat fields */
1566                 for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
1567                     curr_stat = curr_stat->nv_nextstat) {
1568                         printf("\t%15s", curr_stat->nv_statname);
1569                         printf("\t\t%15llu\n", curr_stat->nv_statval);
1570                 }
1571 
1572                 strncpy(prev_linkname, linkname, MAXLINKNAMELEN);
1573                 stat_printed = B_TRUE;
1574         }
1575         if (stat_printed)
1576                 printf("---------------------------------------------------\n");
1577 }
1578 
1579 static int
1580 dump_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
1581 {
1582         boolean_t               *stattype = arg;
1583         int                     i;
1584         dladm_stat_chain_t      *stat_chain;
1585 
1586         for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
1587                 if (stattype[i]) {
1588                         stat_chain = dladm_link_stat_query_all(dh, linkid, i);
1589                         print_all_stats(dh, linkid, stat_chain);
1590                         dladm_link_stat_query_all_free(stat_chain);
1591                 }
1592         }
1593 done:
1594         return (DLADM_WALK_CONTINUE);
1595 }
1596 
1597 void
1598 dump_all_link_stats(datalink_id_t linkid, boolean_t *stattype)
1599 {
1600         if (linkid == DATALINK_ALL_LINKID) {
1601                 (void) dladm_walk_datalink_id(dump_queried_stats,
1602                     handle, stattype, DATALINK_CLASS_ALL,
1603                     DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
1604         } else {
1605                 (void) dump_queried_stats(handle, linkid, stattype);
1606         }
1607 }
1608 
1609 static void
1610 do_show(int argc, char *argv[], const char *use)
1611 {
1612         int                     option;
1613         boolean_t               r_arg = B_FALSE;
1614         boolean_t               t_arg = B_FALSE;
1615         boolean_t               i_arg = B_FALSE;
1616         boolean_t               p_arg = B_FALSE;
1617         boolean_t               o_arg = B_FALSE;
1618         boolean_t               u_arg = B_FALSE;
1619         boolean_t               a_arg = B_FALSE;
1620         boolean_t               A_arg = B_FALSE;
1621         uint32_t                flags = DLADM_OPT_ACTIVE;
1622         datalink_id_t           linkid = DATALINK_ALL_LINKID;
1623         uint32_t                interval = 0;
1624         char                    unit = '\0';
1625         show_state_t            state;
1626         dladm_status_t          status;
1627         char                    *fields_str = NULL;
1628         char                    *o_fields_str = NULL;
1629 
1630         char                    *total_stat_fields =
1631             "link,ipkts,rbytes,opkts,obytes";
1632         char                    *rx_total_stat_fields =
1633             "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
1634         char                    *tx_total_stat_fields =
1635             "link,opkts,obytes,blkcnt,ublkcnt";
1636 
1637         ofmt_handle_t           ofmt;
1638         ofmt_status_t           oferr;
1639         uint_t                  ofmtflags = OFMT_RIGHTJUST;
1640         ofmt_field_t            *oftemplate;
1641 
1642         bzero(&state, sizeof (state));
1643         opterr = 0;
1644         while ((option = getopt_long(argc, argv, ":rtaApi:o:u:",
1645             NULL, NULL)) != -1) {
1646                 switch (option) {
1647                 case 'r':
1648                         if (r_arg)
1649                                 die_optdup(option);
1650 
1651                         r_arg = B_TRUE;
1652                         break;
1653                 case 't':
1654                         if (t_arg)
1655                                 die_optdup(option);
1656 
1657                         t_arg = B_TRUE;
1658                         break;
1659                 case 'a':
1660                         if (a_arg)
1661                                 die_optdup(option);
1662 
1663                         a_arg = B_TRUE;
1664                         break;
1665                 case 'A':
1666                         if (A_arg)
1667                                 die_optdup(option);
1668 
1669                         A_arg = B_TRUE;
1670                         break;
1671                 case 'i':
1672                         if (i_arg)
1673                                 die_optdup(option);
1674 
1675                         i_arg = B_TRUE;
1676                         if (!dladm_str2interval(optarg, &interval))
1677                                 die("invalid interval value '%s'", optarg);
1678                         break;
1679                 case 'p':
1680                         if (p_arg)
1681                                 die_optdup(option);
1682 
1683                         p_arg = B_TRUE;
1684                         break;
1685                 case 'o':
1686                         o_arg = B_TRUE;
1687                         o_fields_str = optarg;
1688                         break;
1689                 case 'u':
1690                         if (u_arg)
1691                                 die_optdup(option);
1692 
1693                         u_arg = B_TRUE;
1694                         if (!dlstat_unit(optarg, &unit))
1695                                 die("invalid unit value '%s',"
1696                                     "unit must be R|K|M|G|T|P", optarg);
1697                         break;
1698                 default:
1699                         die_opterr(optopt, option, use);
1700                         break;
1701                 }
1702         }
1703 
1704         if (r_arg && t_arg)
1705                 die("the options -t and -r are not compatible");
1706 
1707         if (u_arg && p_arg)
1708                 die("the options -u and -p are not compatible");
1709 
1710         if (p_arg && !o_arg)
1711                 die("-p requires -o");
1712 
1713         if (p_arg && strcasecmp(o_fields_str, "all") == 0)
1714                 die("\"-o all\" is invalid with -p");
1715 
1716         if (a_arg && A_arg)
1717                 die("the options -a and -A are not compatible");
1718 
1719         if (a_arg &&
1720             (p_arg || o_arg || u_arg || i_arg)) {
1721                 die("the option -a is not compatible with "
1722                     "-p, -o, -u, -i");
1723         }
1724 
1725         if (A_arg &&
1726             (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) {
1727                 die("the option -A is not compatible with "
1728                     "-r, -t, -p, -o, -u, -i");
1729         }
1730 
1731         /* get link name (optional last argument) */
1732         if (optind == (argc-1)) {
1733                 if (strlen(argv[optind]) >= MAXLINKNAMELEN)
1734                         die("link name too long");
1735 
1736                 if ((status = dladm_name2info(handle, argv[optind], &linkid,
1737                     NULL, NULL, NULL)) != DLADM_STATUS_OK) {
1738                         die_dlerr(status, "link %s is not valid", argv[optind]);
1739                 }
1740         } else if (optind != argc) {
1741                 if (argc != 0)
1742                         usage();
1743         }
1744 
1745         if (a_arg) {
1746                 boolean_t       stattype[DLADM_STAT_NUM_STATS];
1747 
1748                 bzero(&stattype, sizeof (stattype));
1749                 if (r_arg) {
1750                         stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
1751                 } else if (t_arg) {
1752                         stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
1753                 } else {                /* Display both Rx and Tx lanes */
1754                         stattype[DLADM_STAT_TOTAL] = B_TRUE;
1755                 }
1756 
1757                 dump_all_link_stats(linkid, stattype);
1758                 return;
1759         }
1760 
1761         if (A_arg) {
1762                 boolean_t       stattype[DLADM_STAT_NUM_STATS];
1763                 int             i;
1764 
1765                 for (i = 0; i < DLADM_STAT_NUM_STATS; i++)
1766                         stattype[i] = B_TRUE;
1767 
1768                 dump_all_link_stats(linkid, stattype);
1769                 return;
1770         }
1771 
1772         state.ls_unit = unit;
1773         state.ls_parsable = p_arg;
1774 
1775         if (state.ls_parsable)
1776                 ofmtflags |= OFMT_PARSABLE;
1777 
1778         if (r_arg) {
1779                 fields_str = rx_total_stat_fields;
1780                 oftemplate = rx_lane_s_fields;
1781                 state.ls_stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
1782                 state.ls_stats2str[DLADM_STAT_RX_LANE_TOTAL] =
1783                     print_rx_lane_stats;
1784         } else if (t_arg) {
1785                 fields_str = tx_total_stat_fields;
1786                 oftemplate = tx_lane_s_fields;
1787                 state.ls_stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
1788                 state.ls_stats2str[DLADM_STAT_TX_LANE_TOTAL] =
1789                     print_tx_lane_stats;
1790         } else {                /* Display both Rx and Tx lanes total */
1791                 fields_str = total_stat_fields;
1792                 oftemplate = total_s_fields;
1793                 state.ls_stattype[DLADM_STAT_TOTAL] = B_TRUE;
1794                 state.ls_stats2str[DLADM_STAT_TOTAL] = print_total_stats;
1795         }
1796 
1797         if (o_arg) {
1798                 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
1799                     fields_str : o_fields_str;
1800         }
1801 
1802         oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
1803         dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
1804         state.ls_ofmt = ofmt;
1805 
1806         show_link_stats(linkid, state, interval);
1807 
1808         ofmt_close(ofmt);
1809 }
1810 
1811 static void
1812 do_show_phys(int argc, char *argv[], const char *use)
1813 {
1814         int                     option;
1815         boolean_t               r_arg = B_FALSE;
1816         boolean_t               t_arg = B_FALSE;
1817         boolean_t               i_arg = B_FALSE;
1818         boolean_t               p_arg = B_FALSE;
1819         boolean_t               o_arg = B_FALSE;
1820         boolean_t               u_arg = B_FALSE;
1821         boolean_t               a_arg = B_FALSE;
1822         uint32_t                flags = DLADM_OPT_ACTIVE;
1823         datalink_id_t           linkid = DATALINK_ALL_LINKID;
1824         char                    linkname[MAXLINKNAMELEN];
1825         uint32_t                interval = 0;
1826         char                    unit = '\0';
1827         show_state_t            state;
1828         dladm_status_t          status;
1829         char                    *fields_str = NULL;
1830         char                    *o_fields_str = NULL;
1831         char                    *ring_stat_fields =
1832             "link,type,index,pkts,bytes";
1833         char                    *rx_ring_stat_fields =
1834             "link,type,index,ipkts,rbytes";
1835         char                    *tx_ring_stat_fields =
1836             "link,type,index,opkts,obytes";
1837 
1838         ofmt_handle_t           ofmt;
1839         ofmt_status_t           oferr;
1840         uint_t                  ofmtflags = OFMT_RIGHTJUST;
1841         ofmt_field_t            *oftemplate;
1842 
1843         bzero(&state, sizeof (state));
1844         opterr = 0;
1845         while ((option = getopt_long(argc, argv, ":rtapi:o:u:",
1846             NULL, NULL)) != -1) {
1847                 switch (option) {
1848                 case 'r':
1849                         if (r_arg)
1850                                 die_optdup(option);
1851 
1852                         r_arg = B_TRUE;
1853                         break;
1854                 case 't':
1855                         if (t_arg)
1856                                 die_optdup(option);
1857 
1858                         t_arg = B_TRUE;
1859                         break;
1860                 case 'a':
1861                         if (a_arg)
1862                                 die_optdup(option);
1863 
1864                         a_arg = B_TRUE;
1865                         break;
1866                 case 'i':
1867                         if (i_arg)
1868                                 die_optdup(option);
1869 
1870                         i_arg = B_TRUE;
1871                         if (!dladm_str2interval(optarg, &interval))
1872                                 die("invalid interval value '%s'", optarg);
1873                         break;
1874                 case 'p':
1875                         if (p_arg)
1876                                 die_optdup(option);
1877 
1878                         p_arg = B_TRUE;
1879                         break;
1880                 case 'o':
1881                         o_arg = B_TRUE;
1882                         o_fields_str = optarg;
1883                         break;
1884                 case 'u':
1885                         if (u_arg)
1886                                 die_optdup(option);
1887 
1888                         u_arg = B_TRUE;
1889                         if (!dlstat_unit(optarg, &unit))
1890                                 die("invalid unit value '%s',"
1891                                     "unit must be R|K|M|G|T|P", optarg);
1892                         break;
1893                 default:
1894                         die_opterr(optopt, option, use);
1895                         break;
1896                 }
1897         }
1898 
1899         if (r_arg && t_arg)
1900                 die("the options -t and -r are not compatible");
1901 
1902         if (u_arg && p_arg)
1903                 die("the options -u and -p are not compatible");
1904 
1905         if (p_arg && !o_arg)
1906                 die("-p requires -o");
1907 
1908         if (p_arg && strcasecmp(o_fields_str, "all") == 0)
1909                 die("\"-o all\" is invalid with -p");
1910 
1911         if (a_arg &&
1912             (p_arg || o_arg || u_arg || i_arg)) {
1913                 die("the option -a is not compatible with "
1914                     "-p, -o, -u, -i");
1915         }
1916 
1917 
1918         /* get link name (optional last argument) */
1919         if (optind == (argc-1)) {
1920                 if (strlen(argv[optind]) >= MAXLINKNAMELEN)
1921                         die("link name too long");
1922 
1923                 if ((status = dladm_name2info(handle, argv[optind], &linkid,
1924                     NULL, NULL, NULL)) != DLADM_STATUS_OK) {
1925                         die_dlerr(status, "link %s is not valid", argv[optind]);
1926                 }
1927         } else if (optind != argc) {
1928                 usage();
1929         }
1930 
1931         if (a_arg) {
1932                 boolean_t       stattype[DLADM_STAT_NUM_STATS];
1933 
1934                 bzero(&stattype, sizeof (stattype));
1935 
1936                 if (r_arg) {
1937                         stattype[DLADM_STAT_RX_RING] = B_TRUE;
1938                 } else if (t_arg) {
1939                         stattype[DLADM_STAT_TX_RING] = B_TRUE;
1940                 } else {                /* Display both Rx and Tx lanes */
1941                         stattype[DLADM_STAT_RX_RING] = B_TRUE;
1942                         stattype[DLADM_STAT_TX_RING] = B_TRUE;
1943                 }
1944 
1945                 dump_all_link_stats(linkid, stattype);
1946                 return;
1947         }
1948 
1949         state.ls_unit = unit;
1950         state.ls_parsable = p_arg;
1951 
1952         if (state.ls_parsable)
1953                 ofmtflags |= OFMT_PARSABLE;
1954 
1955         if (r_arg) {
1956                 fields_str = rx_ring_stat_fields;
1957                 oftemplate = rx_ring_s_fields;
1958                 state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
1959                 state.ls_stats2str[DLADM_STAT_RX_RING] = print_rx_ring_stats;
1960         } else if (t_arg) {
1961                 fields_str = tx_ring_stat_fields;
1962                 oftemplate = tx_ring_s_fields;
1963                 state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
1964                 state.ls_stats2str[DLADM_STAT_TX_RING] = print_tx_ring_stats;
1965         } else {                /* Display both Rx and Tx lanes */
1966                 fields_str = ring_stat_fields;
1967                 oftemplate = ring_s_fields;
1968                 state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
1969                 state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
1970                 state.ls_stats2str[DLADM_STAT_RX_RING] =
1971                     print_rx_generic_ring_stats;
1972                 state.ls_stats2str[DLADM_STAT_TX_RING] =
1973                     print_tx_generic_ring_stats;
1974         }
1975 
1976         if (o_arg) {
1977                 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
1978                     fields_str : o_fields_str;
1979         }
1980 
1981         oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
1982         dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
1983         state.ls_ofmt = ofmt;
1984 
1985         show_link_stats(linkid, state, interval);
1986 
1987         ofmt_close(ofmt);
1988 }
1989 
1990 static void
1991 do_show_link(int argc, char *argv[], const char *use)
1992 {
1993         int                     option;
1994         boolean_t               r_arg = B_FALSE;
1995         boolean_t               F_arg = B_FALSE;
1996         boolean_t               t_arg = B_FALSE;
1997         boolean_t               i_arg = B_FALSE;
1998         boolean_t               p_arg = B_FALSE;
1999         boolean_t               o_arg = B_FALSE;
2000         boolean_t               u_arg = B_FALSE;
2001         boolean_t               a_arg = B_FALSE;
2002         uint32_t                flags = DLADM_OPT_ACTIVE;
2003         datalink_id_t           linkid = DATALINK_ALL_LINKID;
2004         uint32_t                interval = 0;
2005         char                    unit = '\0';
2006         show_state_t            state;
2007         dladm_status_t          status;
2008         char                    *fields_str = NULL;
2009         char                    *o_fields_str = NULL;
2010 
2011         char                    *lane_stat_fields =
2012             "link,type,id,index,pkts,bytes";
2013         char                    *rx_lane_stat_fields =
2014             "link,type,id,index,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
2015         char                    *tx_lane_stat_fields =
2016             "link,type,id,index,opkts,obytes,blkcnt,ublkcnt";
2017         char                    *rx_fanout_stat_fields =
2018             "link,id,index,fout,ipkts,rbytes";
2019 
2020         ofmt_handle_t           ofmt;
2021         ofmt_status_t           oferr;
2022         uint_t                  ofmtflags = OFMT_RIGHTJUST;
2023         ofmt_field_t            *oftemplate;
2024 
2025         bzero(&state, sizeof (state));
2026         opterr = 0;
2027         while ((option = getopt_long(argc, argv, ":hrtFapi:o:u:",
2028             NULL, NULL)) != -1) {
2029                 switch (option) {
2030                 case 'h':
2031                         if (r_arg || F_arg || t_arg || i_arg || p_arg ||
2032                             o_arg || u_arg || a_arg) {
2033                                 die("the option -h is not compatible with "
2034                                     "-r, -F, -t, -i, -p, -o, -u, -a");
2035                         }
2036                         do_show_history(argc, &argv[0], use);
2037                         return;
2038                 case 'r':
2039                         if (r_arg)
2040                                 die_optdup(option);
2041 
2042                         r_arg = B_TRUE;
2043                         break;
2044                 case 'F':
2045                         if (F_arg)
2046                                 die_optdup(option);
2047 
2048                         F_arg = B_TRUE;
2049                         break;
2050                 case 't':
2051                         if (t_arg)
2052                                 die_optdup(option);
2053 
2054                         t_arg = B_TRUE;
2055                         break;
2056                 case 'a':
2057                         if (a_arg)
2058                                 die_optdup(option);
2059 
2060                         a_arg = B_TRUE;
2061                         break;
2062                 case 'i':
2063                         if (i_arg)
2064                                 die_optdup(option);
2065 
2066                         i_arg = B_TRUE;
2067                         if (!dladm_str2interval(optarg, &interval))
2068                                 die("invalid interval value '%s'", optarg);
2069                         break;
2070                 case 'p':
2071                         if (p_arg)
2072                                 die_optdup(option);
2073 
2074                         p_arg = B_TRUE;
2075                         break;
2076                 case 'o':
2077                         o_arg = B_TRUE;
2078                         o_fields_str = optarg;
2079                         break;
2080                 case 'u':
2081                         if (u_arg)
2082                                 die_optdup(option);
2083 
2084                         u_arg = B_TRUE;
2085                         if (!dlstat_unit(optarg, &unit))
2086                                 die("invalid unit value '%s',"
2087                                     "unit must be R|K|M|G|T|P", optarg);
2088                         break;
2089                 default:
2090                         die_opterr(optopt, option, use);
2091                         break;
2092                 }
2093         }
2094 
2095         if (r_arg && t_arg)
2096                 die("the options -t and -r are not compatible");
2097 
2098         if (u_arg && p_arg)
2099                 die("the options -u and -p are not compatible");
2100 
2101         if (F_arg && !r_arg)
2102                 die("-F must be used with -r");
2103 
2104         if (p_arg && !o_arg)
2105                 die("-p requires -o");
2106 
2107         if (p_arg && strcasecmp(o_fields_str, "all") == 0)
2108                 die("\"-o all\" is invalid with -p");
2109 
2110         if (a_arg &&
2111             (p_arg || o_arg || u_arg || i_arg)) {
2112                 die("the option -a is not compatible with "
2113                     "-p, -o, -u, -i");
2114         }
2115 
2116         /* get link name (optional last argument) */
2117         if (optind == (argc-1)) {
2118                 if (strlen(argv[optind]) >= MAXLINKNAMELEN)
2119                         die("link name too long");
2120 
2121                 if ((status = dladm_name2info(handle, argv[optind], &linkid,
2122                     NULL, NULL, NULL)) != DLADM_STATUS_OK) {
2123                         die_dlerr(status, "link %s is not valid", argv[optind]);
2124                 }
2125         } else if (optind != argc) {
2126                 usage();
2127         }
2128 
2129         if (a_arg) {
2130                 boolean_t       stattype[DLADM_STAT_NUM_STATS];
2131 
2132                 bzero(&stattype, sizeof (stattype));
2133 
2134                 if (r_arg) {
2135                         if (F_arg) {
2136                                 stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
2137                         } else {
2138                                 stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2139                         }
2140                 } else if (t_arg) {
2141                         stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2142                 } else {                /* Display both Rx and Tx lanes */
2143                         stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2144                         stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2145                 }
2146 
2147                 dump_all_link_stats(linkid, stattype);
2148                 return;
2149         }
2150 
2151         state.ls_unit = unit;
2152         state.ls_parsable = p_arg;
2153 
2154         if (state.ls_parsable)
2155                 ofmtflags |= OFMT_PARSABLE;
2156 
2157         if (r_arg) {
2158                 if (F_arg) {
2159                         fields_str = rx_fanout_stat_fields;
2160                         oftemplate = rx_fanout_lane_s_fields;
2161                         state.ls_stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
2162                         state.ls_stats2str[DLADM_STAT_RX_LANE_FOUT] =
2163                             print_fanout_stats;
2164                 } else {
2165                         fields_str = rx_lane_stat_fields;
2166                         oftemplate = rx_lane_s_fields;
2167                         state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2168                         state.ls_stats2str[DLADM_STAT_RX_LANE] =
2169                             print_rx_lane_stats;
2170                 }
2171         } else if (t_arg) {
2172                 fields_str = tx_lane_stat_fields;
2173                 oftemplate = tx_lane_s_fields;
2174                 state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2175                 state.ls_stats2str[DLADM_STAT_TX_LANE] = print_tx_lane_stats;
2176         } else {                /* Display both Rx and Tx lanes */
2177                 fields_str = lane_stat_fields;
2178                 oftemplate = lane_s_fields;
2179                 state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
2180                 state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
2181                 state.ls_stats2str[DLADM_STAT_RX_LANE] =
2182                     print_rx_generic_lane_stats;
2183                 state.ls_stats2str[DLADM_STAT_TX_LANE] =
2184                     print_tx_generic_lane_stats;
2185         }
2186         if (o_arg) {
2187                 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
2188                     fields_str : o_fields_str;
2189         }
2190 
2191         oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
2192         dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
2193 
2194         state.ls_ofmt = ofmt;
2195 
2196         show_link_stats(linkid, state, interval);
2197 
2198         ofmt_close(ofmt);
2199 }
2200 
2201 static void
2202 do_show_aggr(int argc, char *argv[], const char *use)
2203 {
2204         int                     option;
2205         boolean_t               r_arg = B_FALSE;
2206         boolean_t               t_arg = B_FALSE;
2207         boolean_t               i_arg = B_FALSE;
2208         boolean_t               p_arg = B_FALSE;
2209         boolean_t               o_arg = B_FALSE;
2210         boolean_t               u_arg = B_FALSE;
2211         uint32_t                flags = DLADM_OPT_ACTIVE;
2212         datalink_id_t           linkid = DATALINK_ALL_LINKID;
2213         uint32_t                interval = 0;
2214         char                    unit = '\0';
2215         show_state_t            state;
2216         dladm_status_t          status;
2217         char                    *fields_str = NULL;
2218         char                    *o_fields_str = NULL;
2219 
2220         char                    *aggr_stat_fields =
2221             "link,port,ipkts,rbytes,opkts,obytes";
2222         char                    *rx_aggr_stat_fields = "link,port,ipkts,rbytes";
2223         char                    *tx_aggr_stat_fields = "link,port,opkts,obytes";
2224 
2225         ofmt_handle_t           ofmt;
2226         ofmt_status_t           oferr;
2227         uint_t                  ofmtflags = OFMT_RIGHTJUST;
2228         ofmt_field_t            *oftemplate;
2229 
2230         bzero(&state, sizeof (state));
2231         opterr = 0;
2232         while ((option = getopt_long(argc, argv, ":rtpi:o:u:",
2233             NULL, NULL)) != -1) {
2234                 switch (option) {
2235                 case 'r':
2236                         if (r_arg)
2237                                 die_optdup(option);
2238 
2239                         r_arg = B_TRUE;
2240                         break;
2241                 case 't':
2242                         if (t_arg)
2243                                 die_optdup(option);
2244 
2245                         t_arg = B_TRUE;
2246                         break;
2247                 case 'i':
2248                         if (i_arg)
2249                                 die_optdup(option);
2250 
2251                         i_arg = B_TRUE;
2252                         if (!dladm_str2interval(optarg, &interval))
2253                                 die("invalid interval value '%s'", optarg);
2254                         break;
2255                 case 'p':
2256                         if (p_arg)
2257                                 die_optdup(option);
2258 
2259                         p_arg = B_TRUE;
2260                         break;
2261                 case 'o':
2262                         o_arg = B_TRUE;
2263                         o_fields_str = optarg;
2264                         break;
2265                 case 'u':
2266                         if (u_arg)
2267                                 die_optdup(option);
2268 
2269                         u_arg = B_TRUE;
2270                         if (!dlstat_unit(optarg, &unit))
2271                                 die("invalid unit value '%s',"
2272                                     "unit must be R|K|M|G|T|P", optarg);
2273                         break;
2274                 default:
2275                         die_opterr(optopt, option, use);
2276                         break;
2277                 }
2278         }
2279 
2280         if (r_arg && t_arg)
2281                 die("the options -t and -r are not compatible");
2282 
2283         if (u_arg && p_arg)
2284                 die("the options -u and -p are not compatible");
2285 
2286         if (p_arg && !o_arg)
2287                 die("-p requires -o");
2288 
2289         if (p_arg && strcasecmp(o_fields_str, "all") == 0)
2290                 die("\"-o all\" is invalid with -p");
2291 
2292 
2293         /* get link name (optional last argument) */
2294         if (optind == (argc-1)) {
2295                 if (strlen(argv[optind]) >= MAXLINKNAMELEN)
2296                         die("link name too long");
2297 
2298                 if ((status = dladm_name2info(handle, argv[optind], &linkid,
2299                     NULL, NULL, NULL)) != DLADM_STATUS_OK) {
2300                         die_dlerr(status, "link %s is not valid", argv[optind]);
2301                 }
2302         } else if (optind != argc) {
2303                 usage();
2304         }
2305 
2306         state.ls_unit = unit;
2307         state.ls_parsable = p_arg;
2308 
2309         if (state.ls_parsable)
2310                 ofmtflags |= OFMT_PARSABLE;
2311 
2312         oftemplate = aggr_port_s_fields;
2313         state.ls_stattype[DLADM_STAT_AGGR_PORT] = B_TRUE;
2314         state.ls_stats2str[DLADM_STAT_AGGR_PORT] = print_aggr_port_stats;
2315 
2316         if (r_arg)
2317                 fields_str = rx_aggr_stat_fields;
2318         else if (t_arg)
2319                 fields_str = tx_aggr_stat_fields;
2320         else
2321                 fields_str = aggr_stat_fields;
2322 
2323         if (o_arg) {
2324                 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
2325                     fields_str : o_fields_str;
2326         }
2327 
2328         oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
2329         dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
2330         state.ls_ofmt = ofmt;
2331 
2332         show_link_stats(linkid, state, interval);
2333 
2334         ofmt_close(ofmt);
2335 }
2336 
2337 /* PRINTFLIKE1 */
2338 static void
2339 warn(const char *format, ...)
2340 {
2341         va_list alist;
2342 
2343         format = gettext(format);
2344         (void) fprintf(stderr, "%s: warning: ", progname);
2345 
2346         va_start(alist, format);
2347         (void) vfprintf(stderr, format, alist);
2348         va_end(alist);
2349 
2350         (void) putc('\n', stderr);
2351 }
2352 
2353 /*
2354  * Also closes the dladm handle if it is not NULL.
2355  */
2356 /* PRINTFLIKE2 */
2357 static void
2358 die_dlerr(dladm_status_t err, const char *format, ...)
2359 {
2360         va_list alist;
2361         char    errmsg[DLADM_STRSIZE];
2362 
2363         format = gettext(format);
2364         (void) fprintf(stderr, "%s: ", progname);
2365 
2366         va_start(alist, format);
2367         (void) vfprintf(stderr, format, alist);
2368         va_end(alist);
2369         (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
2370 
2371         /* close dladm handle if it was opened */
2372         if (handle != NULL)
2373                 dladm_close(handle);
2374 
2375         exit(EXIT_FAILURE);
2376 }
2377 
2378 /* PRINTFLIKE1 */
2379 static void
2380 die(const char *format, ...)
2381 {
2382         va_list alist;
2383 
2384         format = gettext(format);
2385         (void) fprintf(stderr, "%s: ", progname);
2386 
2387         va_start(alist, format);
2388         (void) vfprintf(stderr, format, alist);
2389         va_end(alist);
2390 
2391         (void) putc('\n', stderr);
2392 
2393         /* close dladm handle if it was opened */
2394         if (handle != NULL)
2395                 dladm_close(handle);
2396 
2397         exit(EXIT_FAILURE);
2398 }
2399 
2400 static void
2401 die_optdup(int opt)
2402 {
2403         die("the option -%c cannot be specified more than once", opt);
2404 }
2405 
2406 static void
2407 die_opterr(int opt, int opterr, const char *usage)
2408 {
2409         switch (opterr) {
2410         case ':':
2411                 die("option '-%c' requires a value\nusage: %s", opt,
2412                     gettext(usage));
2413                 break;
2414         case '?':
2415         default:
2416                 die("unrecognized option '-%c'\nusage: %s", opt,
2417                     gettext(usage));
2418                 break;
2419         }
2420 }
2421 
2422 /*
2423  * default output callback function that, when invoked,
2424  * prints string which is offset by ofmt_arg->ofmt_id within buf.
2425  */
2426 static boolean_t
2427 print_default_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
2428 {
2429         char *value;
2430 
2431         value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
2432         (void) strlcpy(buf, value, bufsize);
2433         return (B_TRUE);
2434 }
2435 
2436 static void
2437 dlstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
2438     ofmt_handle_t ofmt)
2439 {
2440         char buf[OFMT_BUFSIZE];
2441 
2442         if (oferr == OFMT_SUCCESS)
2443                 return;
2444         (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
2445         /*
2446          * All errors are considered fatal in parsable mode.
2447          * NOMEM errors are always fatal, regardless of mode.
2448          * For other errors, we print diagnostics in human-readable
2449          * mode and processs what we can.
2450          */
2451         if (parsable || oferr == OFMT_ENOFIELDS) {
2452                 ofmt_close(ofmt);
2453                 die(buf);
2454         } else {
2455                 warn(buf);
2456         }
2457 }