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