summaryrefslogtreecommitdiff
path: root/usr/src/cmd/dlstat/dlstat.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/dlstat/dlstat.c')
-rw-r--r--usr/src/cmd/dlstat/dlstat.c2457
1 files changed, 2457 insertions, 0 deletions
diff --git a/usr/src/cmd/dlstat/dlstat.c b/usr/src/cmd/dlstat/dlstat.c
new file mode 100644
index 0000000000..a931ba82ff
--- /dev/null
+++ b/usr/src/cmd/dlstat/dlstat.c
@@ -0,0 +1,2457 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <strings.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <priv.h>
+#include <termios.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <auth_list.h>
+#include <libintl.h>
+#include <libdevinfo.h>
+#include <libdlpi.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlstat.h>
+#include <libdlaggr.h>
+#include <libinetutil.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <stddef.h>
+#include <ofmt.h>
+
+typedef struct link_chain_s {
+ datalink_id_t lc_linkid;
+ boolean_t lc_visited;
+ dladm_stat_chain_t *lc_statchain[DLADM_STAT_NUM_STATS];
+ struct link_chain_s *lc_next;
+} link_chain_t;
+
+typedef void * (*stats2str_t)(const char *, void *,
+ char, boolean_t);
+
+typedef struct show_state {
+ link_chain_t *ls_linkchain;
+ boolean_t ls_stattype[DLADM_STAT_NUM_STATS];
+ stats2str_t ls_stats2str[DLADM_STAT_NUM_STATS];
+ ofmt_handle_t ls_ofmt;
+ char ls_unit;
+ boolean_t ls_parsable;
+} show_state_t;
+
+typedef struct show_history_state_s {
+ boolean_t hs_plot;
+ boolean_t hs_parsable;
+ boolean_t hs_printheader;
+ boolean_t hs_first;
+ boolean_t hs_showall;
+ ofmt_handle_t hs_ofmt;
+} show_history_state_t;
+
+/*
+ * callback functions for printing output and error diagnostics.
+ */
+static ofmt_cb_t print_default_cb;
+
+static void dlstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
+
+typedef void cmdfunc_t(int, char **, const char *);
+
+static cmdfunc_t do_show, do_show_history, do_show_phys, do_show_link;
+static cmdfunc_t do_show_aggr;
+
+static void die(const char *, ...);
+static void die_optdup(int);
+static void die_opterr(int, int, const char *);
+static void die_dlerr(dladm_status_t, const char *, ...);
+static void warn(const char *, ...);
+
+typedef struct cmd {
+ char *c_name;
+ cmdfunc_t *c_fn;
+ const char *c_usage;
+} cmd_t;
+
+static cmd_t cmds[] = {
+ { "", do_show,
+ "dlstat [-r | -t] [-i <interval>] [link]\n"
+ " dlstat [-a | -A] [-i <interval>] [-p] [ -o field[,...]]\n"
+ " [-u R|K|M|G|T|P] [link]"},
+ { "show-phys", do_show_phys,
+ "dlstat show-phys [-r | -t] [-i interval] [-a]\n"
+ " [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
+ "[link]"},
+ { "show-link", do_show_link,
+ "dlstat show-link [-r [-F] | -t] [-i interval] [-a]\n"
+ " [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
+ "[link]\n"
+ " dlstat show-link -h [-a] [-d] [-F <format>]\n"
+ " [-s <DD/MM/YYYY,HH:MM:SS>] "
+ "[-e <DD/MM/YYYY,HH:MM:SS>]\n"
+ " -f <logfile> [<link>]" },
+ { "show-aggr", do_show_aggr,
+ "dlstat show-aggr [-r | -t] [-i interval] [-p]\n"
+ " [ -o field[,...]] [-u R|K|M|G|T|P] "
+ " [link]" }
+};
+
+#define MAXSTATLEN 15
+
+/*
+ * dlstat : total stat fields
+ */
+typedef struct total_fields_buf_s {
+ char t_linkname[MAXLINKNAMELEN];
+ char t_ipackets[MAXSTATLEN];
+ char t_rbytes[MAXSTATLEN];
+ char t_opackets[MAXSTATLEN];
+ char t_obytes[MAXSTATLEN];
+} total_fields_buf_t;
+
+static ofmt_field_t total_s_fields[] = {
+{ "LINK", 15,
+ offsetof(total_fields_buf_t, t_linkname), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(total_fields_buf_t, t_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(total_fields_buf_t, t_rbytes), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(total_fields_buf_t, t_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(total_fields_buf_t, t_obytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-phys: both Rx and Tx stat fields
+ */
+typedef struct ring_fields_buf_s {
+ char r_linkname[MAXLINKNAMELEN];
+ char r_type[MAXSTATLEN];
+ char r_id[MAXSTATLEN];
+ char r_index[MAXSTATLEN];
+ char r_packets[MAXSTATLEN];
+ char r_bytes[MAXSTATLEN];
+} ring_fields_buf_t;
+
+static ofmt_field_t ring_s_fields[] = {
+{ "LINK", 15,
+ offsetof(ring_fields_buf_t, r_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(ring_fields_buf_t, r_type), print_default_cb},
+{ "ID", 7,
+ offsetof(ring_fields_buf_t, r_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(ring_fields_buf_t, r_index), print_default_cb},
+{ "PKTS", 8,
+ offsetof(ring_fields_buf_t, r_packets), print_default_cb},
+{ "BYTES", 8,
+ offsetof(ring_fields_buf_t, r_bytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-phys -r: Rx Ring stat fields
+ */
+typedef struct rx_ring_fields_buf_s {
+ char rr_linkname[MAXLINKNAMELEN];
+ char rr_type[MAXSTATLEN];
+ char rr_id[MAXSTATLEN];
+ char rr_index[MAXSTATLEN];
+ char rr_ipackets[MAXSTATLEN];
+ char rr_rbytes[MAXSTATLEN];
+} rx_ring_fields_buf_t;
+
+static ofmt_field_t rx_ring_s_fields[] = {
+{ "LINK", 15,
+ offsetof(rx_ring_fields_buf_t, rr_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(rx_ring_fields_buf_t, rr_type), print_default_cb},
+{ "ID", 7,
+ offsetof(rx_ring_fields_buf_t, rr_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(rx_ring_fields_buf_t, rr_index), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(rx_ring_fields_buf_t, rr_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(rx_ring_fields_buf_t, rr_rbytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-phys -t: Tx Ring stat fields
+ */
+typedef struct tx_ring_fields_buf_s {
+ char tr_linkname[MAXLINKNAMELEN];
+ char tr_type[MAXSTATLEN];
+ char tr_id[MAXSTATLEN];
+ char tr_index[MAXSTATLEN];
+ char tr_opackets[MAXSTATLEN];
+ char tr_obytes[MAXSTATLEN];
+} tx_ring_fields_buf_t;
+
+static ofmt_field_t tx_ring_s_fields[] = {
+{ "LINK", 15,
+ offsetof(tx_ring_fields_buf_t, tr_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(tx_ring_fields_buf_t, tr_type), print_default_cb},
+{ "ID", 7,
+ offsetof(tx_ring_fields_buf_t, tr_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(tx_ring_fields_buf_t, tr_index), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(tx_ring_fields_buf_t, tr_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(tx_ring_fields_buf_t, tr_obytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link: both Rx and Tx lane fields
+ */
+typedef struct lane_fields_buf_s {
+ char l_linkname[MAXLINKNAMELEN];
+ char l_type[MAXSTATLEN];
+ char l_id[MAXSTATLEN];
+ char l_index[MAXSTATLEN];
+ char l_packets[MAXSTATLEN];
+ char l_bytes[MAXSTATLEN];
+} lane_fields_buf_t;
+
+static ofmt_field_t lane_s_fields[] = {
+{ "LINK", 15,
+ offsetof(lane_fields_buf_t, l_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(lane_fields_buf_t, l_type), print_default_cb},
+{ "ID", 7,
+ offsetof(lane_fields_buf_t, l_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(lane_fields_buf_t, l_index), print_default_cb},
+{ "PKTS", 8,
+ offsetof(lane_fields_buf_t, l_packets), print_default_cb},
+{ "BYTES", 8,
+ offsetof(lane_fields_buf_t, l_bytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link -r, dlstat -r: Rx Lane stat fields
+ */
+typedef struct rx_lane_fields_buf_s {
+ char rl_linkname[MAXLINKNAMELEN];
+ char rl_type[MAXSTATLEN];
+ char rl_id[MAXSTATLEN];
+ char rl_index[MAXSTATLEN];
+ char rl_ipackets[MAXSTATLEN];
+ char rl_rbytes[MAXSTATLEN];
+ char rl_intrs[MAXSTATLEN];
+ char rl_polls[MAXSTATLEN];
+ char rl_sdrops[MAXSTATLEN];
+ char rl_chl10[MAXSTATLEN];
+ char rl_ch10_50[MAXSTATLEN];
+ char rl_chg50[MAXSTATLEN];
+} rx_lane_fields_buf_t;
+
+static ofmt_field_t rx_lane_s_fields[] = {
+{ "LINK", 10,
+ offsetof(rx_lane_fields_buf_t, rl_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(rx_lane_fields_buf_t, rl_type), print_default_cb},
+{ "ID", 7,
+ offsetof(rx_lane_fields_buf_t, rl_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(rx_lane_fields_buf_t, rl_index), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(rx_lane_fields_buf_t, rl_rbytes), print_default_cb},
+{ "INTRS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_intrs), print_default_cb},
+{ "POLLS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_polls), print_default_cb},
+{ "SDROPS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_sdrops), print_default_cb},
+{ "CH<10", 8,
+ offsetof(rx_lane_fields_buf_t, rl_chl10), print_default_cb},
+{ "CH10-50", 8,
+ offsetof(rx_lane_fields_buf_t, rl_ch10_50), print_default_cb},
+{ "CH>50", 8,
+ offsetof(rx_lane_fields_buf_t, rl_chg50), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link -r -F: Rx fanout stat fields
+ */
+typedef struct rx_fanout_lane_fields_buf_s {
+ char rfl_linkname[MAXLINKNAMELEN];
+ char rfl_type[MAXSTATLEN];
+ char rfl_id[MAXSTATLEN];
+ char rfl_index[MAXSTATLEN];
+ char rfl_fout[MAXSTATLEN];
+ char rfl_ipackets[MAXSTATLEN];
+ char rfl_rbytes[MAXSTATLEN];
+} rx_fanout_lane_fields_buf_t;
+
+static ofmt_field_t rx_fanout_lane_s_fields[] = {
+{ "LINK", 15,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_type), print_default_cb},
+{ "ID", 7,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_index), print_default_cb},
+{ "FOUT", 6,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_fout), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_rbytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link -t: Tx Lane stat fields
+ */
+typedef struct tx_lane_fields_buf_s {
+ char tl_linkname[MAXLINKNAMELEN];
+ char tl_index[MAXSTATLEN];
+ char tl_type[MAXSTATLEN];
+ char tl_id[MAXSTATLEN];
+ char tl_opackets[MAXSTATLEN];
+ char tl_obytes[MAXSTATLEN];
+ char tl_blockcnt[MAXSTATLEN];
+ char tl_unblockcnt[MAXSTATLEN];
+ char tl_sdrops[MAXSTATLEN];
+} tx_lane_fields_buf_t;
+
+static ofmt_field_t tx_lane_s_fields[] = {
+{ "LINK", 15,
+ offsetof(tx_lane_fields_buf_t, tl_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(tx_lane_fields_buf_t, tl_type), print_default_cb},
+{ "ID", 7,
+ offsetof(tx_lane_fields_buf_t, tl_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(tx_lane_fields_buf_t, tl_index), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(tx_lane_fields_buf_t, tl_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(tx_lane_fields_buf_t, tl_obytes), print_default_cb},
+{ "BLKCNT", 8,
+ offsetof(tx_lane_fields_buf_t, tl_blockcnt), print_default_cb},
+{ "UBLKCNT", 8,
+ offsetof(tx_lane_fields_buf_t, tl_unblockcnt), print_default_cb},
+{ "SDROPS", 8,
+ offsetof(tx_lane_fields_buf_t, tl_sdrops), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-aggr: aggr port stat fields
+ */
+typedef struct aggr_port_fields_buf_s {
+ char ap_linkname[MAXLINKNAMELEN];
+ char ap_portname[MAXLINKNAMELEN];
+ char ap_ipackets[MAXSTATLEN];
+ char ap_rbytes[MAXSTATLEN];
+ char ap_opackets[MAXSTATLEN];
+ char ap_obytes[MAXSTATLEN];
+} aggr_port_fields_buf_t;
+
+static ofmt_field_t aggr_port_s_fields[] = {
+{ "LINK", 15,
+ offsetof(aggr_port_fields_buf_t, ap_linkname), print_default_cb},
+{ "PORT", 15,
+ offsetof(aggr_port_fields_buf_t, ap_portname), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(aggr_port_fields_buf_t, ap_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(aggr_port_fields_buf_t, ap_rbytes), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(aggr_port_fields_buf_t, ap_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(aggr_port_fields_buf_t, ap_obytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * structures for 'dlstat show-link -h'
+ */
+typedef struct history_fields_buf_s {
+ char h_link[12];
+ char h_duration[10];
+ char h_ipackets[9];
+ char h_rbytes[10];
+ char h_opackets[9];
+ char h_obytes[10];
+ char h_bandwidth[14];
+} history_fields_buf_t;
+
+static ofmt_field_t history_fields[] = {
+{ "LINK", 13,
+ offsetof(history_fields_buf_t, h_link), print_default_cb},
+{ "DURATION", 11,
+ offsetof(history_fields_buf_t, h_duration), print_default_cb},
+{ "IPKTS", 10,
+ offsetof(history_fields_buf_t, h_ipackets), print_default_cb},
+{ "RBYTES", 11,
+ offsetof(history_fields_buf_t, h_rbytes), print_default_cb},
+{ "OPKTS", 10,
+ offsetof(history_fields_buf_t, h_opackets), print_default_cb},
+{ "OBYTES", 11,
+ offsetof(history_fields_buf_t, h_obytes), print_default_cb},
+{ "BANDWIDTH", 15,
+ offsetof(history_fields_buf_t, h_bandwidth), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * structures for 'dlstat show-link -h link'
+ */
+typedef struct history_l_fields_buf_s {
+ char hl_link[12];
+ char hl_stime[13];
+ char hl_etime[13];
+ char hl_rbytes[8];
+ char hl_obytes[8];
+ char hl_bandwidth[14];
+} history_l_fields_buf_t;
+
+static ofmt_field_t history_l_fields[] = {
+/* name, field width, offset */
+{ "LINK", 13,
+ offsetof(history_l_fields_buf_t, hl_link), print_default_cb},
+{ "START", 14,
+ offsetof(history_l_fields_buf_t, hl_stime), print_default_cb},
+{ "END", 14,
+ offsetof(history_l_fields_buf_t, hl_etime), print_default_cb},
+{ "RBYTES", 9,
+ offsetof(history_l_fields_buf_t, hl_rbytes), print_default_cb},
+{ "OBYTES", 9,
+ offsetof(history_l_fields_buf_t, hl_obytes), print_default_cb},
+{ "BANDWIDTH", 15,
+ offsetof(history_l_fields_buf_t, hl_bandwidth), print_default_cb},
+{ NULL, 0, 0, NULL}}
+;
+
+static char *progname;
+
+/*
+ * Handle to libdladm. Opened in main() before the sub-command
+ * specific function is called.
+ */
+static dladm_handle_t handle = NULL;
+
+static void
+usage(void)
+{
+ int i;
+ cmd_t *cmdp;
+
+ (void) fprintf(stderr, gettext("usage: "));
+ for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
+ cmdp = &cmds[i];
+ if (cmdp->c_usage != NULL)
+ (void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
+ }
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ cmd_t *cmdp;
+ dladm_status_t status;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ progname = argv[0];
+
+ /* Open the libdladm handle */
+ if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
+ die_dlerr(status, "could not open /dev/dld");
+
+ if (argc == 1) {
+ do_show(argc - 1, NULL, cmds[0].c_usage);
+ goto done;
+ }
+
+ for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
+ cmdp = &cmds[i];
+ if (strcmp(argv[1], cmdp->c_name) == 0) {
+ cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
+ goto done;
+ }
+ }
+
+ do_show(argc, &argv[0], cmds[0].c_usage);
+
+done:
+ dladm_close(handle);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+show_history_date(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = arg;
+ time_t stime;
+ char timebuf[20];
+ dladm_status_t status;
+ uint32_t flags;
+
+ /*
+ * Only show history information for existing links unless '-a'
+ * is specified.
+ */
+ if (!state->hs_showall) {
+ if ((status = dladm_name2info(handle, history->du_name,
+ NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_LINKINVAL);
+ }
+
+ stime = history->du_stime;
+ (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
+ localtime(&stime));
+ (void) printf("%s\n", timebuf);
+
+ return (DLADM_STATUS_OK);
+}
+
+static int
+show_history_time(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = arg;
+ char buf[DLADM_STRSIZE];
+ history_l_fields_buf_t ubuf;
+ time_t time;
+ double bw;
+ dladm_status_t status;
+ uint32_t flags;
+
+ /*
+ * Only show history information for existing links unless '-a'
+ * is specified.
+ */
+ if (!state->hs_showall) {
+ if ((status = dladm_name2info(handle, history->du_name,
+ NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_LINKINVAL);
+ }
+
+ if (state->hs_plot) {
+ if (!state->hs_printheader) {
+ if (state->hs_first) {
+ (void) printf("# Time");
+ state->hs_first = B_FALSE;
+ }
+ (void) printf(" %s", history->du_name);
+ if (history->du_last) {
+ (void) printf("\n");
+ state->hs_first = B_TRUE;
+ state->hs_printheader = B_TRUE;
+ }
+ } else {
+ if (state->hs_first) {
+ time = history->du_etime;
+ (void) strftime(buf, sizeof (buf), "%T",
+ localtime(&time));
+ state->hs_first = B_FALSE;
+ (void) printf("%s", buf);
+ }
+ bw = (double)history->du_bandwidth/1000;
+ (void) printf(" %.2f", bw);
+ if (history->du_last) {
+ (void) printf("\n");
+ state->hs_first = B_TRUE;
+ }
+ }
+ return (DLADM_STATUS_OK);
+ }
+
+ bzero(&ubuf, sizeof (ubuf));
+
+ (void) snprintf(ubuf.hl_link, sizeof (ubuf.hl_link), "%s",
+ history->du_name);
+ time = history->du_stime;
+ (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
+ (void) snprintf(ubuf.hl_stime, sizeof (ubuf.hl_stime), "%s",
+ buf);
+ time = history->du_etime;
+ (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
+ (void) snprintf(ubuf.hl_etime, sizeof (ubuf.hl_etime), "%s",
+ buf);
+ (void) snprintf(ubuf.hl_rbytes, sizeof (ubuf.hl_rbytes),
+ "%llu", history->du_rbytes);
+ (void) snprintf(ubuf.hl_obytes, sizeof (ubuf.hl_obytes),
+ "%llu", history->du_obytes);
+ (void) snprintf(ubuf.hl_bandwidth, sizeof (ubuf.hl_bandwidth),
+ "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
+
+ ofmt_print(state->hs_ofmt, &ubuf);
+ return (DLADM_STATUS_OK);
+}
+
+static int
+show_history_res(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = arg;
+ char buf[DLADM_STRSIZE];
+ history_fields_buf_t ubuf;
+ dladm_status_t status;
+ uint32_t flags;
+
+ /*
+ * Only show history information for existing links unless '-a'
+ * is specified.
+ */
+ if (!state->hs_showall) {
+ if ((status = dladm_name2info(handle, history->du_name,
+ NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_LINKINVAL);
+ }
+
+ bzero(&ubuf, sizeof (ubuf));
+
+ (void) snprintf(ubuf.h_link, sizeof (ubuf.h_link), "%s",
+ history->du_name);
+ (void) snprintf(ubuf.h_duration, sizeof (ubuf.h_duration),
+ "%llu", history->du_duration);
+ (void) snprintf(ubuf.h_ipackets, sizeof (ubuf.h_ipackets),
+ "%llu", history->du_ipackets);
+ (void) snprintf(ubuf.h_rbytes, sizeof (ubuf.h_rbytes),
+ "%llu", history->du_rbytes);
+ (void) snprintf(ubuf.h_opackets, sizeof (ubuf.h_opackets),
+ "%llu", history->du_opackets);
+ (void) snprintf(ubuf.h_obytes, sizeof (ubuf.h_obytes),
+ "%llu", history->du_obytes);
+ (void) snprintf(ubuf.h_bandwidth, sizeof (ubuf.h_bandwidth),
+ "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
+
+ ofmt_print(state->hs_ofmt, &ubuf);
+
+ return (DLADM_STATUS_OK);
+}
+
+static boolean_t
+valid_formatspec(char *formatspec_str)
+{
+ return (strcmp(formatspec_str, "gnuplot") == 0);
+}
+
+/*ARGSUSED*/
+static void
+do_show_history(int argc, char *argv[], const char *use)
+{
+ char *file = NULL;
+ int opt;
+ dladm_status_t status;
+ boolean_t d_arg = B_FALSE;
+ char *stime = NULL;
+ char *etime = NULL;
+ char *resource = NULL;
+ show_history_state_t state;
+ boolean_t o_arg = B_FALSE;
+ boolean_t F_arg = B_FALSE;
+ char *fields_str = NULL;
+ char *formatspec_str = NULL;
+ char *all_l_fields =
+ "link,start,end,rbytes,obytes,bandwidth";
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = 0;
+
+ bzero(&state, sizeof (show_history_state_t));
+ state.hs_parsable = B_FALSE;
+ state.hs_printheader = B_FALSE;
+ state.hs_plot = B_FALSE;
+ state.hs_first = B_TRUE;
+
+ while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
+ switch (opt) {
+ case 'd':
+ d_arg = B_TRUE;
+ break;
+ case 'a':
+ state.hs_showall = B_TRUE;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 's':
+ stime = optarg;
+ break;
+ case 'e':
+ etime = optarg;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ fields_str = optarg;
+ break;
+ case 'F':
+ state.hs_plot = F_arg = B_TRUE;
+ formatspec_str = optarg;
+ break;
+ default:
+ die_opterr(optopt, opt, use);
+ break;
+ }
+ }
+
+ if (file == NULL)
+ die("show-link -h requires a file");
+
+ if (optind == (argc-1)) {
+ uint32_t flags;
+
+ resource = argv[optind];
+ if (!state.hs_showall &&
+ (((status = dladm_name2info(handle, resource, NULL, &flags,
+ NULL, NULL)) != DLADM_STATUS_OK) ||
+ ((flags & DLADM_OPT_ACTIVE) == 0))) {
+ die("invalid link: '%s'", resource);
+ }
+ }
+
+ if (F_arg && d_arg)
+ die("incompatible -d and -F options");
+
+ if (F_arg && !valid_formatspec(formatspec_str))
+ die("Format specifier %s not supported", formatspec_str);
+
+ if (state.hs_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (resource == NULL && stime == NULL && etime == NULL) {
+ oferr = ofmt_open(fields_str, history_fields, ofmtflags, 0,
+ &ofmt);
+ } else {
+ if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
+ fields_str = all_l_fields;
+ oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 0,
+ &ofmt);
+
+ }
+ dlstat_ofmt_check(oferr, state.hs_parsable, ofmt);
+ state.hs_ofmt = ofmt;
+
+ if (d_arg) {
+ /* Print log dates */
+ status = dladm_usage_dates(show_history_date,
+ DLADM_LOGTYPE_LINK, file, resource, &state);
+ } else if (resource == NULL && stime == NULL && etime == NULL &&
+ !F_arg) {
+ /* Print summary */
+ status = dladm_usage_summary(show_history_res,
+ DLADM_LOGTYPE_LINK, file, &state);
+ } else if (resource != NULL) {
+ /* Print log entries for named resource */
+ status = dladm_walk_usage_res(show_history_time,
+ DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state);
+ } else {
+ /* Print time and information for each link */
+ status = dladm_walk_usage_time(show_history_time,
+ DLADM_LOGTYPE_LINK, file, stime, etime, &state);
+ }
+
+ if (status != DLADM_STATUS_OK)
+ die_dlerr(status, "show-link -h");
+ ofmt_close(ofmt);
+}
+
+boolean_t
+dlstat_unit(char *oarg, char *unit)
+{
+ if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
+ (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
+ (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
+ *unit = oarg[0];
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+void
+map_to_units(char *buf, uint_t bufsize, double num, char unit,
+ boolean_t parsable)
+{
+ if (parsable) {
+ (void) snprintf(buf, bufsize, "%.0lf", num);
+ return;
+ }
+
+ if (unit == '\0') {
+ int index;
+
+ for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
+ ;
+
+ switch (index) {
+ case 0:
+ unit = '\0';
+ break;
+ case 1:
+ unit = 'K';
+ break;
+ case 2:
+ unit = 'M';
+ break;
+ case 3:
+ unit = 'G';
+ break;
+ case 4:
+ unit = 'T';
+ break;
+ case 5:
+ /* Largest unit supported */
+ default:
+ unit = 'P';
+ break;
+ }
+ } else {
+ switch (unit) {
+ case 'R':
+ /* Already raw numbers */
+ unit = '\0';
+ break;
+ case 'K':
+ num /= 1000;
+ break;
+ case 'M':
+ num /= (1000*1000);
+ break;
+ case 'G':
+ num /= (1000*1000*1000);
+ break;
+ case 'T':
+ num /= (1000.0*1000.0*1000.0*1000.0);
+ break;
+ case 'P':
+ /* Largest unit supported */
+ default:
+ num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
+ break;
+ }
+ }
+
+ if (unit == '\0')
+ (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
+ else
+ (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
+}
+
+link_chain_t *
+get_link_prev_stat(datalink_id_t linkid, void *arg)
+{
+ show_state_t *state = (show_state_t *)arg;
+ link_chain_t *link_curr = NULL;
+
+ /* Scan prev linkid list and look for entry matching this entry */
+ for (link_curr = state->ls_linkchain; link_curr;
+ link_curr = link_curr->lc_next) {
+ if (link_curr->lc_linkid == linkid)
+ break;
+ }
+ /* New link, add it */
+ if (link_curr == NULL) {
+ link_curr = (link_chain_t *)malloc(sizeof (link_chain_t));
+ if (link_curr == NULL)
+ goto done;
+ link_curr->lc_linkid = linkid;
+ bzero(&link_curr->lc_statchain,
+ sizeof (link_curr->lc_statchain));
+ link_curr->lc_next = state->ls_linkchain;
+ state->ls_linkchain = link_curr;
+ }
+done:
+ return (link_curr);
+}
+
+/*
+ * Number of links may change while dlstat with -i is executing.
+ * Free memory allocated for links that are no longer there.
+ * Prepare for next iteration by marking visited = false for existing stat
+ * entries.
+ */
+static void
+cleanup_removed_links(show_state_t *state)
+{
+ link_chain_t *lcurr;
+ link_chain_t *lprev;
+ link_chain_t *tofree;
+ int i;
+
+ /* Delete all nodes from the list that have lc_visited marked false */
+ lcurr = state->ls_linkchain;
+ while (lcurr != NULL) {
+ if (lcurr->lc_visited) {
+ lcurr->lc_visited = B_FALSE;
+ lprev = lcurr;
+ lcurr = lcurr->lc_next;
+ continue;
+ }
+ /* Is it head of the list? */
+ if (lcurr == state->ls_linkchain)
+ state->ls_linkchain = lcurr->lc_next;
+ else
+ lprev->lc_next = lcurr->lc_next;
+ /* lprev remains the same */
+ tofree = lcurr;
+ lcurr = lcurr->lc_next;
+
+ /* Free stats memory for the removed link */
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
+ if (state->ls_stattype[i])
+ dladm_link_stat_free(tofree->lc_statchain[i]);
+ }
+ free(tofree);
+ }
+}
+
+void *
+print_total_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ total_stat_entry_t *sentry = statentry;
+ total_stat_t *link_stats = &sentry->tse_stats;
+ total_fields_buf_t *buf;
+
+ buf = malloc(sizeof (total_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->t_linkname, sizeof (buf->t_linkname), "%s",
+ linkname);
+
+ map_to_units(buf->t_ipackets, sizeof (buf->t_ipackets),
+ link_stats->ts_ipackets, unit, parsable);
+
+ map_to_units(buf->t_rbytes, sizeof (buf->t_rbytes),
+ link_stats->ts_rbytes, unit, parsable);
+
+ map_to_units(buf->t_opackets, sizeof (buf->t_opackets),
+ link_stats->ts_opackets, unit, parsable);
+
+ map_to_units(buf->t_obytes, sizeof (buf->t_obytes),
+ link_stats->ts_obytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_generic_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->r_type, sizeof (buf->r_type), "rx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
+ } else {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->r_packets, sizeof (buf->r_packets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_generic_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->r_type, sizeof (buf->r_type), "tx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
+ } else {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->r_packets, sizeof (buf->r_packets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ rx_ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (rx_ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->rr_linkname, sizeof (buf->rr_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->rr_type, sizeof (buf->rr_type), "rx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->rr_index, sizeof (buf->rr_index), "--");
+ } else {
+ (void) snprintf(buf->rr_index, sizeof (buf->rr_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->rr_ipackets, sizeof (buf->rr_ipackets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->rr_rbytes, sizeof (buf->rr_rbytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ tx_ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (tx_ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->tr_linkname, sizeof (buf->tr_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->tr_type, sizeof (buf->tr_type), "tx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->tr_index, sizeof (buf->tr_index), "--");
+ } else {
+ (void) snprintf(buf->tr_index, sizeof (buf->tr_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->tr_opackets, sizeof (buf->tr_opackets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->tr_obytes, sizeof (buf->tr_obytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_generic_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ rx_lane_stat_entry_t *sentry = statentry;
+ rx_lane_stat_t *link_stats = &sentry->rle_stats;
+ lane_fields_buf_t *buf;
+
+ if (sentry->rle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->l_type, sizeof (buf->l_type), "rx");
+
+ if (sentry->rle_id == L_HWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
+ else if (sentry->rle_id == L_SWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
+ else if (sentry->rle_id == L_LOCAL)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "local");
+ else if (sentry->rle_id == L_BCAST)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
+ else
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
+
+ if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
+ } else {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index),
+ "%llu", sentry->rle_index);
+ }
+
+ map_to_units(buf->l_packets, sizeof (buf->l_packets),
+ link_stats->rl_ipackets, unit, parsable);
+
+ map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
+ link_stats->rl_rbytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_generic_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ tx_lane_stat_entry_t *sentry = statentry;
+ tx_lane_stat_t *link_stats = &sentry->tle_stats;
+ lane_fields_buf_t *buf;
+
+ if (sentry->tle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->l_type, sizeof (buf->l_type), "tx");
+
+ if (sentry->tle_id == L_HWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
+ else if (sentry->tle_id == L_SWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
+ else if (sentry->tle_id == L_BCAST)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
+ else
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
+
+ if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
+ } else {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index),
+ "%llu", sentry->tle_index);
+ }
+ map_to_units(buf->l_packets, sizeof (buf->l_packets),
+ link_stats->tl_opackets, unit, parsable);
+
+ map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
+ link_stats->tl_obytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ rx_lane_stat_entry_t *sentry = statentry;
+ rx_lane_stat_t *link_stats = &sentry->rle_stats;
+ rx_lane_fields_buf_t *buf;
+
+ if (sentry->rle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (rx_lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->rl_linkname, sizeof (buf->rl_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->rl_type, sizeof (buf->rl_type), "rx");
+
+ if (sentry->rle_id == L_HWLANE)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "hw");
+ else if (sentry->rle_id == L_SWLANE)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "sw");
+ else if (sentry->rle_id == L_LOCAL)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "local");
+ else if (sentry->rle_id == L_BCAST)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "bcast");
+ else
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "--");
+
+ if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->rl_index, sizeof (buf->rl_index), "--");
+ } else {
+ (void) snprintf(buf->rl_index, sizeof (buf->rl_index),
+ "%llu", sentry->rle_index);
+ }
+
+ map_to_units(buf->rl_ipackets, sizeof (buf->rl_ipackets),
+ link_stats->rl_ipackets, unit, parsable);
+
+ map_to_units(buf->rl_rbytes, sizeof (buf->rl_rbytes),
+ link_stats->rl_rbytes, unit, parsable);
+
+ map_to_units(buf->rl_intrs, sizeof (buf->rl_intrs),
+ link_stats->rl_intrs, unit, parsable);
+
+ map_to_units(buf->rl_polls, sizeof (buf->rl_polls),
+ link_stats->rl_polls, unit, parsable);
+
+ map_to_units(buf->rl_sdrops, sizeof (buf->rl_sdrops),
+ link_stats->rl_sdrops, unit, parsable);
+
+ map_to_units(buf->rl_chl10, sizeof (buf->rl_chl10),
+ link_stats->rl_chl10, unit, parsable);
+
+ map_to_units(buf->rl_ch10_50, sizeof (buf->rl_ch10_50),
+ link_stats->rl_ch10_50, unit, parsable);
+
+ map_to_units(buf->rl_chg50, sizeof (buf->rl_chg50),
+ link_stats->rl_chg50, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ tx_lane_stat_entry_t *sentry = statentry;
+ tx_lane_stat_t *link_stats = &sentry->tle_stats;
+ tx_lane_fields_buf_t *buf = NULL;
+
+ if (sentry->tle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (tx_lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->tl_linkname, sizeof (buf->tl_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->tl_type, sizeof (buf->tl_type), "tx");
+
+ if (sentry->tle_id == L_HWLANE)
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "hw");
+ else if (sentry->tle_id == L_SWLANE)
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "sw");
+ else if (sentry->tle_id == L_BCAST)
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "bcast");
+ else
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "--");
+
+ if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->tl_index, sizeof (buf->tl_index), "--");
+ } else {
+ (void) snprintf(buf->tl_index, sizeof (buf->tl_index),
+ "%llu", sentry->tle_index);
+ }
+
+ map_to_units(buf->tl_opackets, sizeof (buf->tl_opackets),
+ link_stats->tl_opackets, unit, parsable);
+
+ map_to_units(buf->tl_obytes, sizeof (buf->tl_obytes),
+ link_stats->tl_obytes, unit, parsable);
+
+ map_to_units(buf->tl_blockcnt, sizeof (buf->tl_blockcnt),
+ link_stats->tl_blockcnt, unit, parsable);
+
+ map_to_units(buf->tl_unblockcnt, sizeof (buf->tl_unblockcnt),
+ link_stats->tl_unblockcnt, unit, parsable);
+
+ map_to_units(buf->tl_sdrops, sizeof (buf->tl_sdrops),
+ link_stats->tl_sdrops, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_fanout_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ fanout_stat_entry_t *sentry = statentry;
+ fanout_stat_t *link_stats = &sentry->fe_stats;
+ rx_fanout_lane_fields_buf_t *buf;
+
+ buf = malloc(sizeof (rx_fanout_lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->rfl_linkname, sizeof (buf->rfl_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->rfl_type, sizeof (buf->rfl_type), "rx");
+
+ if (sentry->fe_id == L_HWLANE)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "hw");
+ else if (sentry->fe_id == L_SWLANE)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "sw");
+ else if (sentry->fe_id == L_LCLSWLANE)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "lcl/sw");
+ else if (sentry->fe_id == L_LOCAL)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "local");
+ else if (sentry->fe_id == L_BCAST)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "bcast");
+ else
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "--");
+
+ if (sentry->fe_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->rfl_index, sizeof (buf->rfl_index), "--");
+ } else {
+ (void) snprintf(buf->rfl_index, sizeof (buf->rfl_index),
+ "%llu", sentry->fe_index);
+ }
+
+ if (sentry->fe_foutindex == DLSTAT_INVALID_ENTRY)
+ (void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "--");
+ else {
+ (void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "%llu",
+ sentry->fe_foutindex);
+ }
+
+ map_to_units(buf->rfl_ipackets, sizeof (buf->rfl_ipackets),
+ link_stats->f_ipackets, unit, parsable);
+
+ map_to_units(buf->rfl_rbytes, sizeof (buf->rfl_rbytes),
+ link_stats->f_rbytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_aggr_port_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ aggr_port_stat_entry_t *sentry = statentry;
+ aggr_port_stat_t *link_stats = &sentry->ape_stats;
+ aggr_port_fields_buf_t *buf;
+ char portname[MAXLINKNAMELEN];
+
+ buf = malloc(sizeof (aggr_port_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->ap_linkname, sizeof (buf->ap_linkname), "%s",
+ linkname);
+
+ if (dladm_datalink_id2info(handle, sentry->ape_portlinkid, NULL,
+ NULL, NULL, portname, DLPI_LINKNAME_MAX)
+ != DLADM_STATUS_OK) {
+ (void) snprintf(buf->ap_portname,
+ sizeof (buf->ap_portname), "--");
+ } else {
+ (void) snprintf(buf->ap_portname,
+ sizeof (buf->ap_portname), "%s", portname);
+ }
+
+ map_to_units(buf->ap_ipackets, sizeof (buf->ap_ipackets),
+ link_stats->ap_ipackets, unit, parsable);
+
+ map_to_units(buf->ap_rbytes, sizeof (buf->ap_rbytes),
+ link_stats->ap_rbytes, unit, parsable);
+
+ map_to_units(buf->ap_opackets, sizeof (buf->ap_opackets),
+ link_stats->ap_opackets, unit, parsable);
+
+ map_to_units(buf->ap_obytes, sizeof (buf->ap_obytes),
+ link_stats->ap_obytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+dladm_stat_chain_t *
+query_link_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg,
+ dladm_stat_type_t stattype)
+{
+ link_chain_t *link_node;
+ dladm_stat_chain_t *curr_stat;
+ dladm_stat_chain_t *prev_stat = NULL;
+ dladm_stat_chain_t *diff_stat = NULL;
+
+ /* Get prev iteration stat for this link */
+ link_node = get_link_prev_stat(linkid, arg);
+ if (link_node == NULL)
+ goto done;
+
+ link_node->lc_visited = B_TRUE;
+ prev_stat = link_node->lc_statchain[stattype];
+
+ /* Query library for current stats */
+ curr_stat = dladm_link_stat_query(dh, linkid, stattype);
+ if (curr_stat == NULL)
+ goto done;
+
+ /* current stats - prev iteration stats */
+ diff_stat = dladm_link_stat_diffchain(curr_stat, prev_stat, stattype);
+
+ /* Free prev stats */
+ dladm_link_stat_free(prev_stat);
+
+ /* Prev <- curr stats */
+ link_node->lc_statchain[stattype] = curr_stat;
+
+done:
+ return (diff_stat);
+}
+
+void
+walk_dlstat_stats(show_state_t *state, const char *linkname,
+ dladm_stat_type_t stattype, dladm_stat_chain_t *diff_stat)
+{
+ dladm_stat_chain_t *curr;
+
+ /* Unpack invidual stat entry and call library consumer's callback */
+ for (curr = diff_stat; curr != NULL; curr = curr->dc_next) {
+ void *fields_buf;
+
+ /* Format the raw numbers for printing */
+ fields_buf = state->ls_stats2str[stattype](linkname,
+ curr->dc_statentry, state->ls_unit, state->ls_parsable);
+ /* Print the stats */
+ if (fields_buf != NULL)
+ ofmt_print(state->ls_ofmt, fields_buf);
+ free(fields_buf);
+ }
+}
+
+static int
+show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
+{
+ show_state_t *state = arg;
+ int i;
+ dladm_stat_chain_t *diff_stat;
+ char linkname[DLPI_LINKNAME_MAX];
+
+ if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
+ DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
+ goto done;
+ }
+
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
+ if (state->ls_stattype[i]) {
+ /*
+ * Query library for stats
+ * Stats are returned as chain of raw numbers
+ */
+ diff_stat = query_link_stats(handle, linkid, arg, i);
+ walk_dlstat_stats(state, linkname, i, diff_stat);
+ dladm_link_stat_free(diff_stat);
+ }
+ }
+done:
+ return (DLADM_WALK_CONTINUE);
+}
+
+void
+show_link_stats(datalink_id_t linkid, show_state_t state, uint32_t interval)
+{
+ for (;;) {
+ if (linkid == DATALINK_ALL_LINKID) {
+ (void) dladm_walk_datalink_id(show_queried_stats,
+ handle, &state, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ } else {
+ (void) show_queried_stats(handle, linkid, &state);
+ }
+
+ if (interval == 0)
+ break;
+
+ cleanup_removed_links(&state);
+ (void) sleep(interval);
+ }
+}
+
+void
+print_all_stats(dladm_handle_t dh, datalink_id_t linkid,
+ dladm_stat_chain_t *stat_chain)
+{
+ dladm_stat_chain_t *curr;
+ name_value_stat_entry_t *stat_entry;
+ name_value_stat_t *curr_stat;
+ boolean_t stat_printed = B_FALSE;
+ char linkname[MAXLINKNAMELEN];
+ char prev_linkname[MAXLINKNAMELEN];
+
+ if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
+ DLPI_LINKNAME_MAX) != DLADM_STATUS_OK)
+ return;
+
+ for (curr = stat_chain; curr != NULL; curr = curr->dc_next) {
+ stat_entry = curr->dc_statentry;
+ /*
+ * Print header
+ * If link name is already printed in previous iteration,
+ * don't print again
+ */
+ if (strcmp(prev_linkname, linkname) != 0)
+ printf("%s \n", linkname);
+ printf(" %s \n", stat_entry->nve_header);
+
+ /* Print stat fields */
+ for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
+ curr_stat = curr_stat->nv_nextstat) {
+ printf("\t%15s", curr_stat->nv_statname);
+ printf("\t\t%15llu\n", curr_stat->nv_statval);
+ }
+
+ strncpy(prev_linkname, linkname, MAXLINKNAMELEN);
+ stat_printed = B_TRUE;
+ }
+ if (stat_printed)
+ printf("---------------------------------------------------\n");
+}
+
+static int
+dump_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
+{
+ boolean_t *stattype = arg;
+ int i;
+ dladm_stat_chain_t *stat_chain;
+
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
+ if (stattype[i]) {
+ stat_chain = dladm_link_stat_query_all(dh, linkid, i);
+ print_all_stats(dh, linkid, stat_chain);
+ dladm_link_stat_query_all_free(stat_chain);
+ }
+ }
+done:
+ return (DLADM_WALK_CONTINUE);
+}
+
+void
+dump_all_link_stats(datalink_id_t linkid, boolean_t *stattype)
+{
+ if (linkid == DATALINK_ALL_LINKID) {
+ (void) dladm_walk_datalink_id(dump_queried_stats,
+ handle, stattype, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ } else {
+ (void) dump_queried_stats(handle, linkid, stattype);
+ }
+}
+
+static void
+do_show(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t a_arg = B_FALSE;
+ boolean_t A_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *total_stat_fields =
+ "link,ipkts,rbytes,opkts,obytes";
+ char *rx_total_stat_fields =
+ "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
+ char *tx_total_stat_fields =
+ "link,opkts,obytes,blkcnt,ublkcnt";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtaApi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'a':
+ if (a_arg)
+ die_optdup(option);
+
+ a_arg = B_TRUE;
+ break;
+ case 'A':
+ if (A_arg)
+ die_optdup(option);
+
+ A_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (a_arg && A_arg)
+ die("the options -a and -A are not compatible");
+
+ if (a_arg &&
+ (p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -a is not compatible with "
+ "-p, -o, -u, -i");
+ }
+
+ if (A_arg &&
+ (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -A is not compatible with "
+ "-r, -t, -p, -o, -u, -i");
+ }
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ if (argc != 0)
+ usage();
+ }
+
+ if (a_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+
+ bzero(&stattype, sizeof (stattype));
+ if (r_arg) {
+ stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
+ } else if (t_arg) {
+ stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
+ } else { /* Display both Rx and Tx lanes */
+ stattype[DLADM_STAT_TOTAL] = B_TRUE;
+ }
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ if (A_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+ int i;
+
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++)
+ stattype[i] = B_TRUE;
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg) {
+ fields_str = rx_total_stat_fields;
+ oftemplate = rx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE_TOTAL] =
+ print_rx_lane_stats;
+ } else if (t_arg) {
+ fields_str = tx_total_stat_fields;
+ oftemplate = tx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TX_LANE_TOTAL] =
+ print_tx_lane_stats;
+ } else { /* Display both Rx and Tx lanes total */
+ fields_str = total_stat_fields;
+ oftemplate = total_s_fields;
+ state.ls_stattype[DLADM_STAT_TOTAL] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TOTAL] = print_total_stats;
+ }
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+static void
+do_show_phys(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t a_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ char linkname[MAXLINKNAMELEN];
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+ char *ring_stat_fields =
+ "link,type,index,pkts,bytes";
+ char *rx_ring_stat_fields =
+ "link,type,index,ipkts,rbytes";
+ char *tx_ring_stat_fields =
+ "link,type,index,opkts,obytes";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtapi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'a':
+ if (a_arg)
+ die_optdup(option);
+
+ a_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (a_arg &&
+ (p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -a is not compatible with "
+ "-p, -o, -u, -i");
+ }
+
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ usage();
+ }
+
+ if (a_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+
+ bzero(&stattype, sizeof (stattype));
+
+ if (r_arg) {
+ stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ } else if (t_arg) {
+ stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ } else { /* Display both Rx and Tx lanes */
+ stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ }
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg) {
+ fields_str = rx_ring_stat_fields;
+ oftemplate = rx_ring_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_RING] = print_rx_ring_stats;
+ } else if (t_arg) {
+ fields_str = tx_ring_stat_fields;
+ oftemplate = tx_ring_s_fields;
+ state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TX_RING] = print_tx_ring_stats;
+ } else { /* Display both Rx and Tx lanes */
+ fields_str = ring_stat_fields;
+ oftemplate = ring_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_RING] =
+ print_rx_generic_ring_stats;
+ state.ls_stats2str[DLADM_STAT_TX_RING] =
+ print_tx_generic_ring_stats;
+ }
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+static void
+do_show_link(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t F_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t a_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *lane_stat_fields =
+ "link,type,id,index,pkts,bytes";
+ char *rx_lane_stat_fields =
+ "link,type,id,index,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
+ char *tx_lane_stat_fields =
+ "link,type,id,index,opkts,obytes,blkcnt,ublkcnt";
+ char *rx_fanout_stat_fields =
+ "link,id,index,fout,ipkts,rbytes";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":hrtFapi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'h':
+ if (r_arg || F_arg || t_arg || i_arg || p_arg ||
+ o_arg || u_arg || a_arg) {
+ die("the option -h is not compatible with "
+ "-r, -F, -t, -i, -p, -o, -u, -a");
+ }
+ do_show_history(argc, &argv[0], use);
+ return;
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 'F':
+ if (F_arg)
+ die_optdup(option);
+
+ F_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'a':
+ if (a_arg)
+ die_optdup(option);
+
+ a_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (F_arg && !r_arg)
+ die("-F must be used with -r");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (a_arg &&
+ (p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -a is not compatible with "
+ "-p, -o, -u, -i");
+ }
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ usage();
+ }
+
+ if (a_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+
+ bzero(&stattype, sizeof (stattype));
+
+ if (r_arg) {
+ if (F_arg) {
+ stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
+ } else {
+ stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ }
+ } else if (t_arg) {
+ stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ } else { /* Display both Rx and Tx lanes */
+ stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ }
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg) {
+ if (F_arg) {
+ fields_str = rx_fanout_stat_fields;
+ oftemplate = rx_fanout_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE_FOUT] =
+ print_fanout_stats;
+ } else {
+ fields_str = rx_lane_stat_fields;
+ oftemplate = rx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE] =
+ print_rx_lane_stats;
+ }
+ } else if (t_arg) {
+ fields_str = tx_lane_stat_fields;
+ oftemplate = tx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TX_LANE] = print_tx_lane_stats;
+ } else { /* Display both Rx and Tx lanes */
+ fields_str = lane_stat_fields;
+ oftemplate = lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE] =
+ print_rx_generic_lane_stats;
+ state.ls_stats2str[DLADM_STAT_TX_LANE] =
+ print_tx_generic_lane_stats;
+ }
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+static void
+do_show_aggr(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *aggr_stat_fields =
+ "link,port,ipkts,rbytes,opkts,obytes";
+ char *rx_aggr_stat_fields = "link,port,ipkts,rbytes";
+ char *tx_aggr_stat_fields = "link,port,opkts,obytes";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtpi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ usage();
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ oftemplate = aggr_port_s_fields;
+ state.ls_stattype[DLADM_STAT_AGGR_PORT] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_AGGR_PORT] = print_aggr_port_stats;
+
+ if (r_arg)
+ fields_str = rx_aggr_stat_fields;
+ else if (t_arg)
+ fields_str = tx_aggr_stat_fields;
+ else
+ fields_str = aggr_stat_fields;
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+/* PRINTFLIKE1 */
+static void
+warn(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: warning: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) putc('\n', stderr);
+}
+
+/*
+ * Also closes the dladm handle if it is not NULL.
+ */
+/* PRINTFLIKE2 */
+static void
+die_dlerr(dladm_status_t err, const char *format, ...)
+{
+ va_list alist;
+ char errmsg[DLADM_STRSIZE];
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(EXIT_FAILURE);
+}
+
+/* PRINTFLIKE1 */
+static void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) putc('\n', stderr);
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(EXIT_FAILURE);
+}
+
+static void
+die_optdup(int opt)
+{
+ die("the option -%c cannot be specified more than once", opt);
+}
+
+static void
+die_opterr(int opt, int opterr, const char *usage)
+{
+ switch (opterr) {
+ case ':':
+ die("option '-%c' requires a value\nusage: %s", opt,
+ gettext(usage));
+ break;
+ case '?':
+ default:
+ die("unrecognized option '-%c'\nusage: %s", opt,
+ gettext(usage));
+ break;
+ }
+}
+
+/*
+ * default output callback function that, when invoked,
+ * prints string which is offset by ofmt_arg->ofmt_id within buf.
+ */
+static boolean_t
+print_default_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+ char *value;
+
+ value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
+ (void) strlcpy(buf, value, bufsize);
+ return (B_TRUE);
+}
+
+static void
+dlstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
+ ofmt_handle_t ofmt)
+{
+ char buf[OFMT_BUFSIZE];
+
+ if (oferr == OFMT_SUCCESS)
+ return;
+ (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
+ /*
+ * All errors are considered fatal in parsable mode.
+ * NOMEM errors are always fatal, regardless of mode.
+ * For other errors, we print diagnostics in human-readable
+ * mode and processs what we can.
+ */
+ if (parsable || oferr == OFMT_ENOFIELDS) {
+ ofmt_close(ofmt);
+ die(buf);
+ } else {
+ warn(buf);
+ }
+}