/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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 ] [link]\n" " dlstat [-a | -A] [-i ] [-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 ]\n" " [-s
] " "[-e
]\n" " -f []" }, { "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); } }