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