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