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 <locale.h>
28 #include <stdarg.h>
29 #include <stdlib.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <stropts.h>
33 #include <errno.h>
34 #include <strings.h>
35 #include <getopt.h>
36 #include <unistd.h>
37 #include <priv.h>
38 #include <netdb.h>
39 #include <libintl.h>
40 #include <libdlflow.h>
41 #include <libdllink.h>
42 #include <libdlstat.h>
43 #include <sys/types.h>
44 #include <sys/socket.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <sys/ethernet.h>
48 #include <inet/ip.h>
49 #include <inet/ip6.h>
50 #include <stddef.h>
51 #include <ofmt.h>
52
53 typedef struct flow_chain_s {
54 char fc_flowname[MAXFLOWNAMELEN];
55 boolean_t fc_visited;
56 flow_stat_t *fc_stat;
57 struct flow_chain_s *fc_next;
58 } flow_chain_t;
59
60 typedef struct show_flow_state {
61 flow_chain_t *fs_flowchain;
62 ofmt_handle_t fs_ofmt;
63 char fs_unit;
64 boolean_t fs_parsable;
65 } show_flow_state_t;
66
67 typedef struct show_history_state_s {
68 boolean_t us_plot;
69 boolean_t us_parsable;
70 boolean_t us_printheader;
71 boolean_t us_first;
72 boolean_t us_showall;
73 ofmt_handle_t us_ofmt;
74 } show_history_state_t;
75
76 static void do_show_history(int, char **);
77
78 static int query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
79 static int query_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
80
81 static void die(const char *, ...);
82 static void die_optdup(int);
83 static void die_opterr(int, int, const char *);
84 static void die_dlerr(dladm_status_t, const char *, ...);
85 static void warn(const char *, ...);
86
87 /* callback functions for printing output */
88 static ofmt_cb_t print_default_cb, print_flow_stats_cb;
89 static void flowstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
90
91 #define NULL_OFMT {NULL, 0, 0, NULL}
92
93 /*
94 * structures for flowstat (printing live statistics)
95 */
96 typedef enum {
97 FLOW_S_FLOW,
98 FLOW_S_IPKTS,
99 FLOW_S_RBYTES,
100 FLOW_S_IERRORS,
101 FLOW_S_OPKTS,
102 FLOW_S_OBYTES,
103 FLOW_S_OERRORS
104 } flow_s_field_index_t;
105
106 static ofmt_field_t flow_s_fields[] = {
107 /* name, field width, index, callback */
108 { "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb},
109 { "IPKTS", 8, FLOW_S_IPKTS, print_flow_stats_cb},
110 { "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb},
111 { "IERRS", 8, FLOW_S_IERRORS, print_flow_stats_cb},
112 { "OPKTS", 8, FLOW_S_OPKTS, print_flow_stats_cb},
113 { "OBYTES", 8, FLOW_S_OBYTES, print_flow_stats_cb},
114 { "OERRS", 8, FLOW_S_OERRORS, print_flow_stats_cb},
115 NULL_OFMT}
116 ;
117
118 typedef struct flow_args_s {
119 char *flow_s_flow;
120 flow_stat_t *flow_s_stat;
121 char flow_s_unit;
122 boolean_t flow_s_parsable;
123 } flow_args_t;
124
125 /*
126 * structures for 'flowstat -h'
127 */
128 typedef struct history_fields_buf_s {
129 char history_flow[12];
130 char history_duration[10];
131 char history_ipackets[9];
132 char history_rbytes[10];
133 char history_opackets[9];
134 char history_obytes[10];
135 char history_bandwidth[14];
136 } history_fields_buf_t;
137
138 static ofmt_field_t history_fields[] = {
139 /* name, field width, offset */
140 { "FLOW", 13,
141 offsetof(history_fields_buf_t, history_flow), print_default_cb},
142 { "DURATION", 11,
143 offsetof(history_fields_buf_t, history_duration), print_default_cb},
144 { "IPACKETS", 10,
145 offsetof(history_fields_buf_t, history_ipackets), print_default_cb},
146 { "RBYTES", 11,
147 offsetof(history_fields_buf_t, history_rbytes), print_default_cb},
148 { "OPACKETS", 10,
149 offsetof(history_fields_buf_t, history_opackets), print_default_cb},
150 { "OBYTES", 11,
151 offsetof(history_fields_buf_t, history_obytes), print_default_cb},
152 { "BANDWIDTH", 15,
153 offsetof(history_fields_buf_t, history_bandwidth), print_default_cb},
154 NULL_OFMT}
155 ;
156
157 typedef struct history_l_fields_buf_s {
158 char history_l_flow[12];
159 char history_l_stime[13];
160 char history_l_etime[13];
161 char history_l_rbytes[8];
162 char history_l_obytes[8];
163 char history_l_bandwidth[14];
164 } history_l_fields_buf_t;
165
166 static ofmt_field_t history_l_fields[] = {
167 /* name, field width, offset */
168 { "FLOW", 13,
169 offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb},
170 { "START", 14,
171 offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb},
172 { "END", 14,
173 offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb},
174 { "RBYTES", 9,
175 offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb},
176 { "OBYTES", 9,
177 offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb},
178 { "BANDWIDTH", 15,
179 offsetof(history_l_fields_buf_t, history_l_bandwidth),
180 print_default_cb},
181 NULL_OFMT}
182 ;
183
184 static char *progname;
185
186 /*
187 * Handle to libdladm. Opened in main() before the sub-command
188 * specific function is called.
189 */
190 static dladm_handle_t handle = NULL;
191
192 const char *usage_ermsg = "usage: flowstat [-r | -t] [-i interval] "
193 "[-l link] [flow]\n"
194 " flowstat [-p | -u R|K|M|G|T|P] [ -o field[,...]] "
195 "[-l link] [flow]\n"
196 " flowstat [-A] [-l link] [flow]\n"
197 " flowstat [-S] [-i interval] [-l link] [flow]\n"
198 " flowstat -h [-a] [-d] [-F format] "
199 "[-s <DD/MM/YYYY,HH:MM:SS>]\n"
200 " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<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
561 char *total_stat_fields =
562 "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs";
563 char *rx_stat_fields =
564 "flow,ipkts,rbytes,ierrs";
565 char *tx_stat_fields =
566 "flow,opkts,obytes,oerrs";
567
568 ofmt_handle_t ofmt;
569 ofmt_status_t oferr;
570 uint_t ofmtflags = OFMT_RIGHTJUST;
571
572 dladm_flow_attr_t attr;
573
574 (void) setlocale(LC_ALL, "");
575 #if !defined(TEXT_DOMAIN)
576 #define TEXT_DOMAIN "SYS_TEST"
577 #endif
578 (void) textdomain(TEXT_DOMAIN);
579
580 progname = argv[0];
581
582 /* Open the libdladm handle */
583 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
584 die_dlerr(status, "could not open /dev/dld");
585
586 bzero(&state, sizeof (state));
587
588 opterr = 0;
589 while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:h",
590 NULL, NULL)) != -1) {
591 switch (option) {
592 case 'r':
593 if (r_arg)
594 die_optdup(option);
595
596 r_arg = B_TRUE;
597 break;
598 case 't':
599 if (t_arg)
600 die_optdup(option);
601
602 t_arg = B_TRUE;
603 break;
604 case 'A':
605 if (A_arg)
606 die_optdup(option);
607
608 A_arg = B_TRUE;
609 break;
610 case 'p':
611 if (p_arg)
612 die_optdup(option);
613
614 p_arg = B_TRUE;
615 break;
616 case 'S':
617 if (S_arg)
618 die_optdup(option);
619 S_arg = B_TRUE;
620 break;
621 case 'i':
622 if (i_arg)
623 die_optdup(option);
624
625 i_arg = B_TRUE;
626 if (!dladm_str2interval(optarg, &interval))
627 die("invalid interval value '%s'", optarg);
628 break;
629 case 'o':
630 o_arg = B_TRUE;
631 o_fields_str = optarg;
632 break;
633 case 'u':
634 if (u_arg)
635 die_optdup(option);
636
637 u_arg = B_TRUE;
638 if (!flowstat_unit(optarg, &unit))
639 die("invalid unit value '%s',"
640 "unit must be R|K|M|G|T|P", optarg);
641 break;
642 case 'l':
643 if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
644 >= MAXLINKNAMELEN)
645 die("link name too long\n");
646 if (dladm_name2info(handle, linkname, &linkid, NULL,
647 NULL, NULL) != DLADM_STATUS_OK)
648 die("invalid link '%s'", linkname);
649 break;
650 case 'h':
651 if (r_arg || t_arg || p_arg || o_arg || u_arg ||
652 i_arg || S_arg || A_arg) {
653 die("the option -h is not compatible with "
654 "-r, -t, -p, -o, -u, -i, -S, -A");
655 }
656 do_show_history(argc, argv);
657 return (0);
658 break;
659 default:
660 die_opterr(optopt, option, usage_ermsg);
661 break;
662 }
663 }
664
665 if (r_arg && t_arg)
666 die("the option -t and -r are not compatible");
667
668 if (u_arg && p_arg)
669 die("the option -u and -p are not compatible");
670
671 if (p_arg && !o_arg)
672 die("-p requires -o");
673
674 if (p_arg && strcasecmp(o_fields_str, "all") == 0)
675 die("\"-o all\" is invalid with -p");
676
677 if (S_arg &&
678 (r_arg || t_arg || p_arg || o_arg || u_arg))
679 die("the option -S is not compatible with "
680 "-r, -t, -p, -o, -u");
681
682 if (A_arg &&
683 (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg))
684 die("the option -A is not compatible with "
685 "-r, -t, -p, -o, -u, -i");
686
687 /* get flow name (optional last argument) */
688 if (optind == (argc-1)) {
689 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
690 >= MAXFLOWNAMELEN)
691 die("flow name too long");
692 flow_arg = B_TRUE;
693 } else if (optind != argc) {
694 usage();
695 }
696
697 if (S_arg) {
698 dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL),
699 interval, FLOW_REPORT);
700 return (0);
701 }
702
703 if (flow_arg &&
704 dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK)
705 die("invalid flow %s", flowname);
706
707 if (A_arg) {
708 dump_all_flow_stats(&attr, &state, linkid, flow_arg);
709 return (0);
710 }
711
712 state.fs_unit = unit;
713 state.fs_parsable = p_arg;
714
715 if (state.fs_parsable)
716 ofmtflags |= OFMT_PARSABLE;
717
718 if (r_arg)
719 fields_str = rx_stat_fields;
720 else if (t_arg)
721 fields_str = tx_stat_fields;
722 else
723 fields_str = total_stat_fields;
724
725 if (o_arg) {
726 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
727 fields_str : o_fields_str;
728 }
729
730 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
731 flowstat_ofmt_check(oferr, state.fs_parsable, ofmt);
732 state.fs_ofmt = ofmt;
733
734 for (;;) {
735 /* Show stats for named flow */
736 if (flow_arg) {
737 (void) query_flow_stats(handle, &attr, &state);
738
739 /* Show stats for flows on one link */
740 } else if (linkid != DATALINK_INVALID_LINKID) {
741 (void) dladm_walk_flow(query_flow_stats, handle, linkid,
742 &state, B_FALSE);
743
744 /* Show stats for all flows on all links */
745 } else {
746 (void) dladm_walk_datalink_id(query_link_flow_stats,
747 handle, &state, DATALINK_CLASS_ALL,
748 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
749 }
750
751 if (interval == 0)
752 break;
753
754 (void) fflush(stdout);
755 cleanup_removed_flows(&state);
756 (void) sleep(interval);
757 }
758 ofmt_close(ofmt);
759
760 dladm_close(handle);
761 return (0);
762 }
763
764 /* ARGSUSED */
765 static int
766 show_history_date(dladm_usage_t *history, void *arg)
767 {
768 show_history_state_t *state = (show_history_state_t *)arg;
769 time_t stime;
770 char timebuf[20];
771 dladm_flow_attr_t attr;
772 dladm_status_t status;
773
774 /*
775 * Only show historical information for existing flows unless '-a'
776 * is specified.
777 */
778 if (!state->us_showall && ((status = dladm_flow_info(handle,
779 history->du_name, &attr)) != DLADM_STATUS_OK)) {
780 return (status);
781 }
782
783 stime = history->du_stime;
784 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
785 localtime(&stime));
786 (void) printf("%s\n", timebuf);
787
788 return (DLADM_STATUS_OK);
789 }
790
791 static int
792 show_history_time(dladm_usage_t *history, void *arg)
793 {
794 show_history_state_t *state = (show_history_state_t *)arg;
795 char buf[DLADM_STRSIZE];
796 history_l_fields_buf_t ubuf;
797 time_t time;
798 double bw;
799 dladm_flow_attr_t attr;
800 dladm_status_t status;
801
802 /*
803 * Only show historical information for existing flows unless '-a'
804 * is specified.
805 */
806 if (!state->us_showall && ((status = dladm_flow_info(handle,
807 history->du_name, &attr)) != DLADM_STATUS_OK)) {
808 return (status);
809 }
810
811 if (state->us_plot) {
812 if (!state->us_printheader) {
813 if (state->us_first) {
814 (void) printf("# Time");
815 state->us_first = B_FALSE;
816 }
817 (void) printf(" %s", history->du_name);
818 if (history->du_last) {
819 (void) printf("\n");
820 state->us_first = B_TRUE;
821 state->us_printheader = B_TRUE;
822 }
823 } else {
824 if (state->us_first) {
825 time = history->du_etime;
826 (void) strftime(buf, sizeof (buf), "%T",
827 localtime(&time));
828 state->us_first = B_FALSE;
829 (void) printf("%s", buf);
830 }
831 bw = (double)history->du_bandwidth/1000;
832 (void) printf(" %.2f", bw);
833 if (history->du_last) {
834 (void) printf("\n");
835 state->us_first = B_TRUE;
836 }
837 }
838 return (DLADM_STATUS_OK);
839 }
840
841 bzero(&ubuf, sizeof (ubuf));
842
843 (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s",
844 history->du_name);
845 time = history->du_stime;
846 (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
847 (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime),
848 "%s", buf);
849 time = history->du_etime;
850 (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
851 (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime),
852 "%s", buf);
853 (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes),
854 "%llu", history->du_rbytes);
855 (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes),
856 "%llu", history->du_obytes);
857 (void) snprintf(ubuf.history_l_bandwidth,
858 sizeof (ubuf.history_l_bandwidth), "%s Mbps",
859 dladm_bw2str(history->du_bandwidth, buf));
860
861 ofmt_print(state->us_ofmt, (void *)&ubuf);
862 return (DLADM_STATUS_OK);
863 }
864
865 static int
866 show_history_res(dladm_usage_t *history, void *arg)
867 {
868 show_history_state_t *state = (show_history_state_t *)arg;
869 char buf[DLADM_STRSIZE];
870 history_fields_buf_t ubuf;
871 dladm_flow_attr_t attr;
872 dladm_status_t status;
873
874 /*
875 * Only show historical information for existing flows unless '-a'
876 * is specified.
877 */
878 if (!state->us_showall && ((status = dladm_flow_info(handle,
879 history->du_name, &attr)) != DLADM_STATUS_OK)) {
880 return (status);
881 }
882
883 bzero(&ubuf, sizeof (ubuf));
884
885 (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s",
886 history->du_name);
887 (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration),
888 "%llu", history->du_duration);
889 (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets),
890 "%llu", history->du_ipackets);
891 (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes),
892 "%llu", history->du_rbytes);
893 (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets),
894 "%llu", history->du_opackets);
895 (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes),
896 "%llu", history->du_obytes);
897 (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth),
898 "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
899
900 ofmt_print(state->us_ofmt, (void *)&ubuf);
901
902 return (DLADM_STATUS_OK);
903 }
904
905 static boolean_t
906 valid_formatspec(char *formatspec_str)
907 {
908 return (strcmp(formatspec_str, "gnuplot") == 0);
909 }
910
911 /* ARGSUSED */
912 static void
913 do_show_history(int argc, char *argv[])
914 {
915 char *file = NULL;
916 int opt;
917 dladm_status_t status;
918 boolean_t d_arg = B_FALSE;
919 char *stime = NULL;
920 char *etime = NULL;
921 char *resource = NULL;
922 show_history_state_t state;
923 boolean_t o_arg = B_FALSE;
924 boolean_t F_arg = B_FALSE;
925 char *fields_str = NULL;
926 char *formatspec_str = NULL;
927 char *all_fields =
928 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
929 char *all_l_fields =
930 "flow,start,end,rbytes,obytes,bandwidth";
931 ofmt_handle_t ofmt;
932 ofmt_status_t oferr;
933 uint_t ofmtflags = 0;
934
935 bzero(&state, sizeof (show_history_state_t));
936 state.us_parsable = B_FALSE;
937 state.us_printheader = B_FALSE;
938 state.us_plot = B_FALSE;
939 state.us_first = B_TRUE;
940
941 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
942 switch (opt) {
943 case 'd':
944 d_arg = B_TRUE;
945 break;
946 case 'a':
947 state.us_showall = B_TRUE;
948 break;
949 case 'f':
950 file = optarg;
951 break;
952 case 's':
953 stime = optarg;
954 break;
955 case 'e':
956 etime = optarg;
957 break;
958 case 'o':
959 o_arg = B_TRUE;
960 fields_str = optarg;
961 break;
962 case 'F':
963 state.us_plot = F_arg = B_TRUE;
964 formatspec_str = optarg;
965 break;
966 default:
967 die_opterr(optopt, opt, usage_ermsg);
968 }
969 }
970
971 if (file == NULL)
972 die("-h requires a file");
973
974 if (optind == (argc-1)) {
975 dladm_flow_attr_t attr;
976
977 resource = argv[optind];
978 if (!state.us_showall &&
979 dladm_flow_info(handle, resource, &attr) !=
980 DLADM_STATUS_OK) {
981 die("invalid flow: '%s'", resource);
982 }
983 }
984
985 if (state.us_parsable)
986 ofmtflags |= OFMT_PARSABLE;
987 if (resource == NULL && stime == NULL && etime == NULL) {
988 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
989 fields_str = all_fields;
990 oferr = ofmt_open(fields_str, history_fields, ofmtflags,
991 0, &ofmt);
992 } else {
993 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
994 fields_str = all_l_fields;
995 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags,
996 0, &ofmt);
997 }
998
999 flowstat_ofmt_check(oferr, state.us_parsable, ofmt);
1000 state.us_ofmt = ofmt;
1001
1002 if (F_arg && d_arg)
1003 die("incompatible -d and -F options");
1004
1005 if (F_arg && !valid_formatspec(formatspec_str))
1006 die("Format specifier %s not supported", formatspec_str);
1007
1008 if (d_arg) {
1009 /* Print log dates */
1010 status = dladm_usage_dates(show_history_date,
1011 DLADM_LOGTYPE_FLOW, file, resource, &state);
1012 } else if (resource == NULL && stime == NULL && etime == NULL &&
1013 !F_arg) {
1014 /* Print summary */
1015 status = dladm_usage_summary(show_history_res,
1016 DLADM_LOGTYPE_FLOW, file, &state);
1017 } else if (resource != NULL) {
1018 /* Print log entries for named resource */
1019 status = dladm_walk_usage_res(show_history_time,
1020 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
1021 } else {
1022 /* Print time and information for each flow */
1023 status = dladm_walk_usage_time(show_history_time,
1024 DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
1025 }
1026
1027 ofmt_close(ofmt);
1028 if (status != DLADM_STATUS_OK)
1029 die_dlerr(status, "-h");
1030 dladm_close(handle);
1031 }
1032
1033 static void
1034 warn(const char *format, ...)
1035 {
1036 va_list alist;
1037
1038 format = gettext(format);
1039 (void) fprintf(stderr, "%s: warning: ", progname);
1040
1041 va_start(alist, format);
1042 (void) vfprintf(stderr, format, alist);
1043 va_end(alist);
1044
1045 (void) putc('\n', stderr);
1046 }
1047
1048 /* PRINTFLIKE1 */
1049 static void
1050 die(const char *format, ...)
1051 {
1052 va_list alist;
1053
1054 format = gettext(format);
1055 (void) fprintf(stderr, "%s: ", progname);
1056
1057 va_start(alist, format);
1058 (void) vfprintf(stderr, format, alist);
1059 va_end(alist);
1060
1061 (void) putc('\n', stderr);
1062
1063 /* close dladm handle if it was opened */
1064 if (handle != NULL)
1065 dladm_close(handle);
1066
1067 exit(EXIT_FAILURE);
1068 }
1069
1070 static void
1071 die_optdup(int opt)
1072 {
1073 die("the option -%c cannot be specified more than once", opt);
1074 }
1075
1076 static void
1077 die_opterr(int opt, int opterr, const char *usage)
1078 {
1079 switch (opterr) {
1080 case ':':
1081 die("option '-%c' requires a value\n%s", opt,
1082 gettext(usage));
1083 break;
1084 case '?':
1085 default:
1086 die("unrecognized option '-%c'\n%s", opt,
1087 gettext(usage));
1088 break;
1089 }
1090 }
1091
1092 /* PRINTFLIKE2 */
1093 static void
1094 die_dlerr(dladm_status_t err, const char *format, ...)
1095 {
1096 va_list alist;
1097 char errmsg[DLADM_STRSIZE];
1098
1099 format = gettext(format);
1100 (void) fprintf(stderr, "%s: ", progname);
1101
1102 va_start(alist, format);
1103 (void) vfprintf(stderr, format, alist);
1104 va_end(alist);
1105 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1106
1107 /* close dladm handle if it was opened */
1108 if (handle != NULL)
1109 dladm_close(handle);
1110
1111 exit(EXIT_FAILURE);
1112 }
1113
1114
1115 /*
1116 * default output callback function that, when invoked from dladm_print_output,
1117 * prints string which is offset by of_arg->ofmt_id within buf.
1118 */
1119 static boolean_t
1120 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1121 {
1122 char *value;
1123
1124 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1125 (void) strlcpy(buf, value, bufsize);
1126 return (B_TRUE);
1127 }
1128
1129 static void
1130 flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
1131 ofmt_handle_t ofmt)
1132 {
1133 char buf[OFMT_BUFSIZE];
1134
1135 if (oferr == OFMT_SUCCESS)
1136 return;
1137 (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
1138 /*
1139 * All errors are considered fatal in parsable mode.
1140 * NOMEM errors are always fatal, regardless of mode.
1141 * For other errors, we print diagnostics in human-readable
1142 * mode and processs what we can.
1143 */
1144 if (parsable || oferr == OFMT_ENOFIELDS) {
1145 ofmt_close(ofmt);
1146 die(buf);
1147 } else {
1148 warn(buf);
1149 }
1150 }