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 * Copyright 2011 Joyent, Inc. All rights reserved.
25 */
26
27 #include <stdio.h>
28 #include <locale.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <stropts.h>
34 #include <errno.h>
35 #include <strings.h>
36 #include <getopt.h>
37 #include <unistd.h>
38 #include <priv.h>
39 #include <netdb.h>
40 #include <libintl.h>
41 #include <libdlflow.h>
42 #include <libdllink.h>
43 #include <libdlstat.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48 #include <sys/ethernet.h>
49 #include <inet/ip.h>
50 #include <inet/ip6.h>
51 #include <stddef.h>
52 #include <ofmt.h>
53
54 typedef struct flow_chain_s {
55 char fc_flowname[MAXFLOWNAMELEN];
56 boolean_t fc_visited;
57 flow_stat_t *fc_stat;
58 struct flow_chain_s *fc_next;
59 } flow_chain_t;
60
61 typedef struct show_flow_state {
62 flow_chain_t *fs_flowchain;
63 ofmt_handle_t fs_ofmt;
64 char fs_unit;
65 boolean_t fs_parsable;
66 } show_flow_state_t;
67
68 typedef struct show_history_state_s {
69 boolean_t us_plot;
70 boolean_t us_parsable;
71 boolean_t us_printheader;
72 boolean_t us_first;
73 boolean_t us_showall;
74 ofmt_handle_t us_ofmt;
75 } show_history_state_t;
76
77 static void do_show_history(int, char **);
78
79 static int query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
80 static int query_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
81
82 static void die(const char *, ...);
83 static void die_optdup(int);
84 static void die_opterr(int, int, const char *);
85 static void die_dlerr(dladm_status_t, const char *, ...);
86 static void warn(const char *, ...);
87
88 /* callback functions for printing output */
89 static ofmt_cb_t print_default_cb, print_flow_stats_cb;
90 static void flowstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
91
92 #define NULL_OFMT {NULL, 0, 0, NULL}
93
94 /*
95 * structures for flowstat (printing live statistics)
96 */
97 typedef enum {
98 FLOW_S_FLOW,
99 FLOW_S_IPKTS,
100 FLOW_S_RBYTES,
101 FLOW_S_IERRORS,
102 FLOW_S_OPKTS,
103 FLOW_S_OBYTES,
104 FLOW_S_OERRORS
105 } flow_s_field_index_t;
106
107 static ofmt_field_t flow_s_fields[] = {
108 /* name, field width, index, callback */
109 { "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb},
110 { "IPKTS", 8, FLOW_S_IPKTS, print_flow_stats_cb},
111 { "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb},
112 { "IERRS", 8, FLOW_S_IERRORS, print_flow_stats_cb},
113 { "OPKTS", 8, FLOW_S_OPKTS, print_flow_stats_cb},
114 { "OBYTES", 8, FLOW_S_OBYTES, print_flow_stats_cb},
115 { "OERRS", 8, FLOW_S_OERRORS, print_flow_stats_cb},
116 NULL_OFMT}
117 ;
118
119 typedef struct flow_args_s {
120 char *flow_s_flow;
121 flow_stat_t *flow_s_stat;
122 char flow_s_unit;
123 boolean_t flow_s_parsable;
124 } flow_args_t;
125
126 /*
127 * structures for 'flowstat -h'
128 */
129 typedef struct history_fields_buf_s {
130 char history_flow[12];
131 char history_duration[10];
132 char history_ipackets[9];
133 char history_rbytes[10];
134 char history_opackets[9];
135 char history_obytes[10];
136 char history_bandwidth[14];
137 } history_fields_buf_t;
138
139 static ofmt_field_t history_fields[] = {
140 /* name, field width, offset */
141 { "FLOW", 13,
142 offsetof(history_fields_buf_t, history_flow), print_default_cb},
143 { "DURATION", 11,
144 offsetof(history_fields_buf_t, history_duration), print_default_cb},
145 { "IPACKETS", 10,
146 offsetof(history_fields_buf_t, history_ipackets), print_default_cb},
147 { "RBYTES", 11,
148 offsetof(history_fields_buf_t, history_rbytes), print_default_cb},
149 { "OPACKETS", 10,
150 offsetof(history_fields_buf_t, history_opackets), print_default_cb},
151 { "OBYTES", 11,
152 offsetof(history_fields_buf_t, history_obytes), print_default_cb},
153 { "BANDWIDTH", 15,
154 offsetof(history_fields_buf_t, history_bandwidth), print_default_cb},
155 NULL_OFMT}
156 ;
157
158 typedef struct history_l_fields_buf_s {
159 char history_l_flow[12];
160 char history_l_stime[13];
161 char history_l_etime[13];
162 char history_l_rbytes[8];
163 char history_l_obytes[8];
164 char history_l_bandwidth[14];
165 } history_l_fields_buf_t;
166
167 static ofmt_field_t history_l_fields[] = {
168 /* name, field width, offset */
169 { "FLOW", 13,
170 offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb},
171 { "START", 14,
172 offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb},
173 { "END", 14,
174 offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb},
175 { "RBYTES", 9,
176 offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb},
177 { "OBYTES", 9,
178 offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb},
179 { "BANDWIDTH", 15,
180 offsetof(history_l_fields_buf_t, history_l_bandwidth),
181 print_default_cb},
182 NULL_OFMT}
183 ;
184
185 static char *progname;
186
187 /*
188 * Handle to libdladm. Opened in main() before the sub-command
189 * specific function is called.
190 */
191 static dladm_handle_t handle = NULL;
192
193 const char *usage_ermsg = "flowstat [-r | -t] [-i interval] "
194 "[-l link] [-z zonename] [flow]\n"
195 " flowstat [-S] [-A] [-i interval] [-p] [ -o field[,...]]\n"
196 " [-u R|K|M|G|T|P] [-l link] [-z zonename] [flow]\n"
197 " flowstat -h [-a] [-d] [-F format]"
198 " [-s <DD/MM/YYYY,HH:MM:SS>]\n"
199 " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> "
200 "[<flow>]";
201
202 static void
203 usage(void)
204 {
205 (void) fprintf(stderr, "%s\n", gettext(usage_ermsg));
206
207 /* close dladm handle if it was opened */
208 if (handle != NULL)
209 dladm_close(handle);
210
211 exit(1);
212 }
213
214 boolean_t
215 flowstat_unit(char *oarg, char *unit)
216 {
217 if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
218 (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
219 (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
220 *unit = oarg[0];
221 return (B_TRUE);
222 }
223
224 return (B_FALSE);
225 }
226
227 void
228 map_to_units(char *buf, uint_t bufsize, double num, char unit,
229 boolean_t parsable)
230 {
231 if (parsable) {
232 (void) snprintf(buf, bufsize, "%.0lf", num);
233 return;
234 }
235
236 if (unit == '\0') {
237 int index;
238
239 for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
240 ;
241
242 switch (index) {
243 case 0:
244 unit = '\0';
245 break;
246 case 1:
247 unit = 'K';
248 break;
249 case 2:
250 unit = 'M';
251 break;
252 case 3:
253 unit = 'G';
254 break;
255 case 4:
256 unit = 'T';
257 break;
258 case 5:
259 /* Largest unit supported */
260 default:
261 unit = 'P';
262 break;
263 }
264 } else {
265 switch (unit) {
266 case 'R':
267 /* Already raw numbers */
268 unit = '\0';
269 break;
270 case 'K':
271 num /= 1000;
272 break;
273 case 'M':
274 num /= (1000*1000);
275 break;
276 case 'G':
277 num /= (1000*1000*1000);
278 break;
279 case 'T':
280 num /= (1000.0*1000.0*1000.0*1000.0);
281 break;
282 case 'P':
283 /* Largest unit supported */
284 default:
285 num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
286 break;
287 }
288 }
289
290 if (unit == '\0')
291 (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
292 else
293 (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
294 }
295
296 flow_chain_t *
297 get_flow_prev_stat(const char *flowname, void *arg)
298 {
299 show_flow_state_t *state = arg;
300 flow_chain_t *flow_curr = NULL;
301
302 /* Scan prev flowname list and look for entry matching this entry */
303 for (flow_curr = state->fs_flowchain; flow_curr;
304 flow_curr = flow_curr->fc_next) {
305 if (strcmp(flow_curr->fc_flowname, flowname) == 0)
306 break;
307 }
308
309 /* New flow, add it */
310 if (flow_curr == NULL) {
311 flow_curr = (flow_chain_t *)malloc(sizeof (flow_chain_t));
312 if (flow_curr == NULL)
313 goto done;
314 (void) strncpy(flow_curr->fc_flowname, flowname,
315 MAXFLOWNAMELEN);
316 flow_curr->fc_stat = NULL;
317 flow_curr->fc_next = state->fs_flowchain;
318 state->fs_flowchain = flow_curr;
319 }
320 done:
321 return (flow_curr);
322 }
323
324 /*
325 * Number of flows may change while flowstat -i is executing.
326 * Free memory allocated for flows that are no longer there.
327 * Prepare for next iteration by marking visited = false for
328 * existing stat entries.
329 */
330 static void
331 cleanup_removed_flows(show_flow_state_t *state)
332 {
333 flow_chain_t *fcurr;
334 flow_chain_t *fprev;
335 flow_chain_t *tofree;
336
337 /* Delete all nodes from the list that have fc_visited marked false */
338 fcurr = state->fs_flowchain;
339 while (fcurr != NULL) {
340 if (fcurr->fc_visited) {
341 fcurr->fc_visited = B_FALSE;
342 fprev = fcurr;
343 fcurr = fcurr->fc_next;
344 continue;
345 }
346
347 /* Is it head of the list? */
348 if (fcurr == state->fs_flowchain)
349 state->fs_flowchain = fcurr->fc_next;
350 else
351 fprev->fc_next = fcurr->fc_next;
352
353 /* fprev remains the same */
354 tofree = fcurr;
355 fcurr = fcurr->fc_next;
356
357 /* Free stats memory for the removed flow */
358 dladm_flow_stat_free(tofree->fc_stat);
359 free(tofree);
360 }
361 }
362
363 static boolean_t
364 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
365 {
366 flow_args_t *fargs = of_arg->ofmt_cbarg;
367 flow_stat_t *diff_stats = fargs->flow_s_stat;
368 char unit = fargs->flow_s_unit;
369 boolean_t parsable = fargs->flow_s_parsable;
370
371 switch (of_arg->ofmt_id) {
372 case FLOW_S_FLOW:
373 (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow);
374 break;
375 case FLOW_S_IPKTS:
376 map_to_units(buf, bufsize, diff_stats->fl_ipackets, unit,
377 parsable);
378 break;
379 case FLOW_S_RBYTES:
380 map_to_units(buf, bufsize, diff_stats->fl_rbytes, unit,
381 parsable);
382 break;
383 case FLOW_S_IERRORS:
384 map_to_units(buf, bufsize, diff_stats->fl_ierrors, unit,
385 parsable);
386 break;
387 case FLOW_S_OPKTS:
388 map_to_units(buf, bufsize, diff_stats->fl_opackets, unit,
389 parsable);
390 break;
391 case FLOW_S_OBYTES:
392 map_to_units(buf, bufsize, diff_stats->fl_obytes, unit,
393 parsable);
394 break;
395 case FLOW_S_OERRORS:
396 map_to_units(buf, bufsize, diff_stats->fl_oerrors, unit,
397 parsable);
398 break;
399 default:
400 die("invalid input");
401 break;
402 }
403 return (B_TRUE);
404 }
405
406 /* ARGSUSED */
407 static int
408 query_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
409 {
410 show_flow_state_t *state = arg;
411 flow_chain_t *flow_node;
412 flow_stat_t *curr_stat;
413 flow_stat_t *prev_stat;
414 flow_stat_t *diff_stat;
415 char *flowname = attr->fa_flowname;
416 flow_args_t fargs;
417
418 /* Get previous stats for the flow */
419 flow_node = get_flow_prev_stat(flowname, arg);
420 if (flow_node == NULL)
421 goto done;
422
423 flow_node->fc_visited = B_TRUE;
424 prev_stat = flow_node->fc_stat;
425
426 /* Query library for current stats */
427 curr_stat = dladm_flow_stat_query(flowname);
428 if (curr_stat == NULL)
429 goto done;
430
431 /* current stats - prev iteration stats */
432 diff_stat = dladm_flow_stat_diff(curr_stat, prev_stat);
433
434 /* Free prev stats */
435 dladm_flow_stat_free(prev_stat);
436
437 /* Prev <- curr stats */
438 flow_node->fc_stat = curr_stat;
439
440 if (diff_stat == NULL)
441 goto done;
442
443 /* Print stats */
444 fargs.flow_s_flow = flowname;
445 fargs.flow_s_stat = diff_stat;
446 fargs.flow_s_unit = state->fs_unit;
447 fargs.flow_s_parsable = state->fs_parsable;
448 ofmt_print(state->fs_ofmt, &fargs);
449
450 /* Free diff stats */
451 dladm_flow_stat_free(diff_stat);
452 done:
453 return (DLADM_WALK_CONTINUE);
454 }
455
456 /*
457 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
458 * dladm_walk_datalink_id(). Used for showing flow stats for
459 * all flows on all links.
460 */
461 static int
462 query_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
463 {
464 if (dladm_walk_flow(query_flow_stats, dh, linkid, arg, B_FALSE)
465 == DLADM_STATUS_OK)
466 return (DLADM_WALK_CONTINUE);
467 else
468 return (DLADM_WALK_TERMINATE);
469 }
470
471 void
472 print_all_stats(name_value_stat_entry_t *stat_entry)
473 {
474 name_value_stat_t *curr_stat;
475
476 printf("%s\n", stat_entry->nve_header);
477
478 for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
479 curr_stat = curr_stat->nv_nextstat) {
480 printf("\t%15s", curr_stat->nv_statname);
481 printf("\t%15llu\n", curr_stat->nv_statval);
482 }
483 }
484
485 /* ARGSUSED */
486 static int
487 dump_one_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
488 {
489 char *flowname = attr->fa_flowname;
490 void *stat;
491
492 stat = dladm_flow_stat_query_all(flowname);
493 if (stat == NULL)
494 goto done;
495 print_all_stats(stat);
496 dladm_flow_stat_query_all_free(stat);
497
498 done:
499 return (DLADM_WALK_CONTINUE);
500 }
501
502 /*
503 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
504 * dladm_walk_datalink_id(). Used for showing flow stats for
505 * all flows on all links.
506 */
507 static int
508 dump_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
509 {
510 if (dladm_walk_flow(dump_one_flow_stats, dh, linkid, arg, B_FALSE)
511 == DLADM_STATUS_OK)
512 return (DLADM_WALK_CONTINUE);
513 else
514 return (DLADM_WALK_TERMINATE);
515 }
516
517 static void
518 dump_all_flow_stats(dladm_flow_attr_t *attrp, void *arg, datalink_id_t linkid,
519 boolean_t flow_arg)
520 {
521 /* Show stats for named flow */
522 if (flow_arg) {
523 (void) dump_one_flow_stats(handle, attrp, arg);
524
525 /* Show stats for flows on one link */
526 } else if (linkid != DATALINK_INVALID_LINKID) {
527 (void) dladm_walk_flow(dump_one_flow_stats, handle, linkid,
528 arg, B_FALSE);
529
530 /* Show stats for all flows on all links */
531 } else {
532 (void) dladm_walk_datalink_id(dump_link_flow_stats,
533 handle, arg, DATALINK_CLASS_ALL,
534 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
535 }
536 }
537
538 int
539 main(int argc, char *argv[])
540 {
541 dladm_status_t status;
542 int option;
543 boolean_t r_arg = B_FALSE;
544 boolean_t t_arg = B_FALSE;
545 boolean_t p_arg = B_FALSE;
546 boolean_t i_arg = B_FALSE;
547 boolean_t o_arg = B_FALSE;
548 boolean_t u_arg = B_FALSE;
549 boolean_t A_arg = B_FALSE;
550 boolean_t S_arg = B_FALSE;
551 boolean_t flow_arg = B_FALSE;
552 datalink_id_t linkid = DATALINK_ALL_LINKID;
553 char linkname[MAXLINKNAMELEN];
554 char flowname[MAXFLOWNAMELEN];
555 uint32_t interval = 0;
556 char unit = '\0';
557 show_flow_state_t state;
558 char *fields_str = NULL;
559 char *o_fields_str = NULL;
560 char *zonename = NULL;
561
562 char *total_stat_fields =
563 "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs";
564 char *rx_stat_fields =
565 "flow,ipkts,rbytes,ierrs";
566 char *tx_stat_fields =
567 "flow,opkts,obytes,oerrs";
568
569 ofmt_handle_t ofmt;
570 ofmt_status_t oferr;
571 uint_t ofmtflags = OFMT_RIGHTJUST;
572
573 dladm_flow_attr_t attr;
574
575 (void) setlocale(LC_ALL, "");
576 #if !defined(TEXT_DOMAIN)
577 #define TEXT_DOMAIN "SYS_TEST"
578 #endif
579 (void) textdomain(TEXT_DOMAIN);
580
581 progname = argv[0];
582
583 /* Open the libdladm handle */
584 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
585 die_dlerr(status, "could not open /dev/dld");
586
587 linkname[0] = '\0';
588 bzero(&state, sizeof (state));
589
590 opterr = 0;
591 while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:hz:",
592 NULL, NULL)) != -1) {
593 switch (option) {
594 case 'r':
595 if (r_arg)
596 die_optdup(option);
597
598 r_arg = B_TRUE;
599 break;
600 case 't':
601 if (t_arg)
602 die_optdup(option);
603
604 t_arg = B_TRUE;
605 break;
606 case 'A':
607 if (A_arg)
608 die_optdup(option);
609
610 A_arg = B_TRUE;
611 break;
612 case 'p':
613 if (p_arg)
614 die_optdup(option);
615
616 p_arg = B_TRUE;
617 break;
618 case 'S':
619 if (S_arg)
620 die_optdup(option);
621 S_arg = B_TRUE;
622 break;
623 case 'i':
624 if (i_arg)
625 die_optdup(option);
626
627 i_arg = B_TRUE;
628 if (!dladm_str2interval(optarg, &interval))
629 die("invalid interval value '%s'", optarg);
630 break;
631 case 'o':
632 o_arg = B_TRUE;
633 o_fields_str = optarg;
634 break;
635 case 'u':
636 if (u_arg)
637 die_optdup(option);
638
639 u_arg = B_TRUE;
640 if (!flowstat_unit(optarg, &unit))
641 die("invalid unit value '%s',"
642 "unit must be R|K|M|G|T|P", optarg);
643 break;
644 case 'l':
645 if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
646 >= MAXLINKNAMELEN)
647 die("link name too long\n");
648 break;
649 case 'h':
650 if (r_arg || t_arg || p_arg || o_arg || u_arg ||
651 i_arg || S_arg || A_arg) {
652 die("the option -h is not compatible with "
653 "-r, -t, -p, -o, -u, -i, -S, -A");
654 }
655 do_show_history(argc, argv);
656 return (0);
657 break;
658 case 'z':
659 zonename = optarg;
660 break;
661 default:
662 die_opterr(optopt, option, usage_ermsg);
663 break;
664 }
665 }
666
667 if (r_arg && t_arg)
668 die("the option -t and -r are not compatible");
669
670 if (u_arg && p_arg)
671 die("the option -u and -p are not compatible");
672
673 if (p_arg && !o_arg)
674 die("-p requires -o");
675
676 if (p_arg && strcasecmp(o_fields_str, "all") == 0)
677 die("\"-o all\" is invalid with -p");
678
679 if (S_arg &&
680 (r_arg || t_arg || p_arg || o_arg || u_arg))
681 die("the option -S is not compatible with "
682 "-r, -t, -p, -o, -u");
683
684 if (A_arg &&
685 (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg))
686 die("the option -A is not compatible with "
687 "-r, -t, -p, -o, -u, -i");
688
689 if (linkname[0] != '\0') {
690 if (dladm_zname2info(handle, zonename, linkname, &linkid, NULL,
691 NULL, NULL) != DLADM_STATUS_OK)
692 die("invalid link '%s'", linkname);
693 }
694
695 /* get flow name (optional last argument) */
696 if (optind == (argc-1)) {
697 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
698 >= MAXFLOWNAMELEN)
699 die("flow name too long");
700 flow_arg = B_TRUE;
701 } else if (optind != argc) {
702 usage();
703 }
704
705 if (S_arg) {
706 dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL),
707 interval, FLOW_REPORT);
708 return (0);
709 }
710
711 if (flow_arg &&
712 dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK)
713 die("invalid flow %s", flowname);
714
715 if (A_arg) {
716 dump_all_flow_stats(&attr, &state, linkid, flow_arg);
717 return (0);
718 }
719
720 state.fs_unit = unit;
721 state.fs_parsable = p_arg;
722
723 if (state.fs_parsable)
724 ofmtflags |= OFMT_PARSABLE;
725
726 if (r_arg)
727 fields_str = rx_stat_fields;
728 else if (t_arg)
729 fields_str = tx_stat_fields;
730 else
731 fields_str = total_stat_fields;
732
733 if (o_arg) {
734 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
735 fields_str : o_fields_str;
736 }
737
738 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
739 flowstat_ofmt_check(oferr, state.fs_parsable, ofmt);
740 state.fs_ofmt = ofmt;
741
742 for (;;) {
743 /* Show stats for named flow */
744 if (flow_arg) {
745 (void) query_flow_stats(handle, &attr, &state);
746
747 /* Show stats for flows on one link */
748 } else if (linkid != DATALINK_INVALID_LINKID) {
749 (void) dladm_walk_flow(query_flow_stats, handle, linkid,
750 &state, B_FALSE);
751
752 /* Show stats for all flows on all links */
753 } else {
754 (void) dladm_walk_datalink_id(query_link_flow_stats,
755 handle, &state, DATALINK_CLASS_ALL,
756 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
757 }
758
759 if (interval == 0)
760 break;
761
762 (void) fflush(stdout);
763 cleanup_removed_flows(&state);
764 (void) sleep(interval);
765 }
766 ofmt_close(ofmt);
767
768 dladm_close(handle);
769 return (0);
770 }
771
772 /* ARGSUSED */
773 static int
774 show_history_date(dladm_usage_t *history, void *arg)
775 {
776 show_history_state_t *state = (show_history_state_t *)arg;
777 time_t stime;
778 char timebuf[20];
779 dladm_flow_attr_t attr;
780 dladm_status_t status;
781
782 /*
783 * Only show historical information for existing flows unless '-a'
784 * is specified.
785 */
786 if (!state->us_showall && ((status = dladm_flow_info(handle,
787 history->du_name, &attr)) != DLADM_STATUS_OK)) {
788 return (status);
789 }
790
791 stime = history->du_stime;
792 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
793 localtime(&stime));
794 (void) printf("%s\n", timebuf);
795
796 return (DLADM_STATUS_OK);
797 }
798
799 static int
800 show_history_time(dladm_usage_t *history, void *arg)
801 {
802 show_history_state_t *state = (show_history_state_t *)arg;
803 char buf[DLADM_STRSIZE];
804 history_l_fields_buf_t ubuf;
805 time_t time;
806 double bw;
807 dladm_flow_attr_t attr;
808 dladm_status_t status;
809
810 /*
811 * Only show historical information for existing flows unless '-a'
812 * is specified.
813 */
814 if (!state->us_showall && ((status = dladm_flow_info(handle,
815 history->du_name, &attr)) != DLADM_STATUS_OK)) {
816 return (status);
817 }
818
819 if (state->us_plot) {
820 if (!state->us_printheader) {
821 if (state->us_first) {
822 (void) printf("# Time");
823 state->us_first = B_FALSE;
824 }
825 (void) printf(" %s", history->du_name);
826 if (history->du_last) {
827 (void) printf("\n");
828 state->us_first = B_TRUE;
829 state->us_printheader = B_TRUE;
830 }
831 } else {
832 if (state->us_first) {
833 time = history->du_etime;
834 (void) strftime(buf, sizeof (buf), "%T",
835 localtime(&time));
836 state->us_first = B_FALSE;
837 (void) printf("%s", buf);
838 }
839 bw = (double)history->du_bandwidth/1000;
840 (void) printf(" %.2f", bw);
841 if (history->du_last) {
842 (void) printf("\n");
843 state->us_first = B_TRUE;
844 }
845 }
846 return (DLADM_STATUS_OK);
847 }
848
849 bzero(&ubuf, sizeof (ubuf));
850
851 (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s",
852 history->du_name);
853 time = history->du_stime;
854 (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
855 (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime),
856 "%s", buf);
857 time = history->du_etime;
858 (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
859 (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime),
860 "%s", buf);
861 (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes),
862 "%llu", history->du_rbytes);
863 (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes),
864 "%llu", history->du_obytes);
865 (void) snprintf(ubuf.history_l_bandwidth,
866 sizeof (ubuf.history_l_bandwidth), "%s Mbps",
867 dladm_bw2str(history->du_bandwidth, buf));
868
869 ofmt_print(state->us_ofmt, (void *)&ubuf);
870 return (DLADM_STATUS_OK);
871 }
872
873 static int
874 show_history_res(dladm_usage_t *history, void *arg)
875 {
876 show_history_state_t *state = (show_history_state_t *)arg;
877 char buf[DLADM_STRSIZE];
878 history_fields_buf_t ubuf;
879 dladm_flow_attr_t attr;
880 dladm_status_t status;
881
882 /*
883 * Only show historical information for existing flows unless '-a'
884 * is specified.
885 */
886 if (!state->us_showall && ((status = dladm_flow_info(handle,
887 history->du_name, &attr)) != DLADM_STATUS_OK)) {
888 return (status);
889 }
890
891 bzero(&ubuf, sizeof (ubuf));
892
893 (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s",
894 history->du_name);
895 (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration),
896 "%llu", history->du_duration);
897 (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets),
898 "%llu", history->du_ipackets);
899 (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes),
900 "%llu", history->du_rbytes);
901 (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets),
902 "%llu", history->du_opackets);
903 (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes),
904 "%llu", history->du_obytes);
905 (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth),
906 "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
907
908 ofmt_print(state->us_ofmt, (void *)&ubuf);
909
910 return (DLADM_STATUS_OK);
911 }
912
913 static boolean_t
914 valid_formatspec(char *formatspec_str)
915 {
916 return (strcmp(formatspec_str, "gnuplot") == 0);
917 }
918
919 /* ARGSUSED */
920 static void
921 do_show_history(int argc, char *argv[])
922 {
923 char *file = NULL;
924 int opt;
925 dladm_status_t status;
926 boolean_t d_arg = B_FALSE;
927 char *stime = NULL;
928 char *etime = NULL;
929 char *resource = NULL;
930 show_history_state_t state;
931 boolean_t o_arg = B_FALSE;
932 boolean_t F_arg = B_FALSE;
933 char *fields_str = NULL;
934 char *formatspec_str = NULL;
935 char *all_fields =
936 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
937 char *all_l_fields =
938 "flow,start,end,rbytes,obytes,bandwidth";
939 ofmt_handle_t ofmt;
940 ofmt_status_t oferr;
941 uint_t ofmtflags = 0;
942
943 bzero(&state, sizeof (show_history_state_t));
944 state.us_parsable = B_FALSE;
945 state.us_printheader = B_FALSE;
946 state.us_plot = B_FALSE;
947 state.us_first = B_TRUE;
948
949 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
950 switch (opt) {
951 case 'd':
952 d_arg = B_TRUE;
953 break;
954 case 'a':
955 state.us_showall = B_TRUE;
956 break;
957 case 'f':
958 file = optarg;
959 break;
960 case 's':
961 stime = optarg;
962 break;
963 case 'e':
964 etime = optarg;
965 break;
966 case 'o':
967 o_arg = B_TRUE;
968 fields_str = optarg;
969 break;
970 case 'F':
971 state.us_plot = F_arg = B_TRUE;
972 formatspec_str = optarg;
973 break;
974 default:
975 die_opterr(optopt, opt, usage_ermsg);
976 }
977 }
978
979 if (file == NULL)
980 die("-h requires a file");
981
982 if (optind == (argc-1)) {
983 dladm_flow_attr_t attr;
984
985 resource = argv[optind];
986 if (!state.us_showall &&
987 dladm_flow_info(handle, resource, &attr) !=
988 DLADM_STATUS_OK) {
989 die("invalid flow: '%s'", resource);
990 }
991 }
992
993 if (state.us_parsable)
994 ofmtflags |= OFMT_PARSABLE;
995 if (resource == NULL && stime == NULL && etime == NULL) {
996 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
997 fields_str = all_fields;
998 oferr = ofmt_open(fields_str, history_fields, ofmtflags,
999 0, &ofmt);
1000 } else {
1001 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
1002 fields_str = all_l_fields;
1003 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags,
1004 0, &ofmt);
1005 }
1006
1007 flowstat_ofmt_check(oferr, state.us_parsable, ofmt);
1008 state.us_ofmt = ofmt;
1009
1010 if (F_arg && d_arg)
1011 die("incompatible -d and -F options");
1012
1013 if (F_arg && !valid_formatspec(formatspec_str))
1014 die("Format specifier %s not supported", formatspec_str);
1015
1016 if (d_arg) {
1017 /* Print log dates */
1018 status = dladm_usage_dates(show_history_date,
1019 DLADM_LOGTYPE_FLOW, file, resource, &state);
1020 } else if (resource == NULL && stime == NULL && etime == NULL &&
1021 !F_arg) {
1022 /* Print summary */
1023 status = dladm_usage_summary(show_history_res,
1024 DLADM_LOGTYPE_FLOW, file, &state);
1025 } else if (resource != NULL) {
1026 /* Print log entries for named resource */
1027 status = dladm_walk_usage_res(show_history_time,
1028 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
1029 } else {
1030 /* Print time and information for each flow */
1031 status = dladm_walk_usage_time(show_history_time,
1032 DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
1033 }
1034
1035 ofmt_close(ofmt);
1036 if (status != DLADM_STATUS_OK)
1037 die_dlerr(status, "-h");
1038 dladm_close(handle);
1039 }
1040
1041 static void
1042 warn(const char *format, ...)
1043 {
1044 va_list alist;
1045
1046 format = gettext(format);
1047 (void) fprintf(stderr, "%s: warning: ", progname);
1048
1049 va_start(alist, format);
1050 (void) vfprintf(stderr, format, alist);
1051 va_end(alist);
1052
1053 (void) putc('\n', stderr);
1054 }
1055
1056 /* PRINTFLIKE1 */
1057 static void
1058 die(const char *format, ...)
1059 {
1060 va_list alist;
1061
1062 format = gettext(format);
1063 (void) fprintf(stderr, "%s: ", progname);
1064
1065 va_start(alist, format);
1066 (void) vfprintf(stderr, format, alist);
1067 va_end(alist);
1068
1069 (void) putc('\n', stderr);
1070
1071 /* close dladm handle if it was opened */
1072 if (handle != NULL)
1073 dladm_close(handle);
1074
1075 exit(EXIT_FAILURE);
1076 }
1077
1078 static void
1079 die_optdup(int opt)
1080 {
1081 die("the option -%c cannot be specified more than once", opt);
1082 }
1083
1084 static void
1085 die_opterr(int opt, int opterr, const char *usage)
1086 {
1087 switch (opterr) {
1088 case ':':
1089 die("option '-%c' requires a value\nusage: %s", opt,
1090 gettext(usage));
1091 break;
1092 case '?':
1093 default:
1094 die("unrecognized option '-%c'\nusage: %s", opt,
1095 gettext(usage));
1096 break;
1097 }
1098 }
1099
1100 /* PRINTFLIKE2 */
1101 static void
1102 die_dlerr(dladm_status_t err, const char *format, ...)
1103 {
1104 va_list alist;
1105 char errmsg[DLADM_STRSIZE];
1106
1107 format = gettext(format);
1108 (void) fprintf(stderr, "%s: ", progname);
1109
1110 va_start(alist, format);
1111 (void) vfprintf(stderr, format, alist);
1112 va_end(alist);
1113 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1114
1115 /* close dladm handle if it was opened */
1116 if (handle != NULL)
1117 dladm_close(handle);
1118
1119 exit(EXIT_FAILURE);
1120 }
1121
1122
1123 /*
1124 * default output callback function that, when invoked from dladm_print_output,
1125 * prints string which is offset by of_arg->ofmt_id within buf.
1126 */
1127 static boolean_t
1128 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1129 {
1130 char *value;
1131
1132 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1133 (void) strlcpy(buf, value, bufsize);
1134 return (B_TRUE);
1135 }
1136
1137 static void
1138 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1139 ofmt_handle_t ofmt)
1140 {
1141 char buf[OFMT_BUFSIZE];
1142
1143 if (oferr == OFMT_SUCCESS)
1144 return;
1145 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1146 /*
1147 * All errors are considered fatal in parsable mode.
1148 * NOMEM errors are always fatal, regardless of mode.
1149 * For other errors, we print diagnostics in human-readable
1150 * mode and processs what we can.
1151 */
1152 if (parsable || oferr == OFMT_ENOFIELDS) {
1153 ofmt_close(ofmt);
1154 die(buf);
1155 } else {
1156 warn(buf);
1157 }
1158 }