8507 dladm show-link segfaults if you specify too many fields
[unleashed.git] / usr / src / cmd / flowstat / flowstat.c
blob757fcb5149ea472170ea6d19fe8f1e4ba5836a6a
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * Copyright 2017 Joyent, Inc.
30 #include <stdio.h>
31 #include <locale.h>
32 #include <stdarg.h>
33 #include <stdlib.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <stropts.h>
37 #include <errno.h>
38 #include <strings.h>
39 #include <getopt.h>
40 #include <unistd.h>
41 #include <priv.h>
42 #include <netdb.h>
43 #include <libintl.h>
44 #include <libdlflow.h>
45 #include <libdllink.h>
46 #include <libdlstat.h>
47 #include <sys/types.h>
48 #include <sys/socket.h>
49 #include <netinet/in.h>
50 #include <arpa/inet.h>
51 #include <sys/ethernet.h>
52 #include <inet/ip.h>
53 #include <inet/ip6.h>
54 #include <stddef.h>
55 #include <ofmt.h>
57 typedef struct flow_chain_s {
58 char fc_flowname[MAXFLOWNAMELEN];
59 boolean_t fc_visited;
60 flow_stat_t *fc_stat;
61 struct flow_chain_s *fc_next;
62 } flow_chain_t;
64 typedef struct show_flow_state {
65 flow_chain_t *fs_flowchain;
66 ofmt_handle_t fs_ofmt;
67 char fs_unit;
68 boolean_t fs_parsable;
69 } show_flow_state_t;
71 typedef struct show_history_state_s {
72 boolean_t us_plot;
73 boolean_t us_parsable;
74 boolean_t us_printheader;
75 boolean_t us_first;
76 boolean_t us_showall;
77 ofmt_handle_t us_ofmt;
78 } show_history_state_t;
80 static void do_show_history(int, char **);
82 static int query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
83 static int query_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
85 static void die(const char *, ...);
86 static void die_optdup(int);
87 static void die_opterr(int, int, const char *);
88 static void die_dlerr(dladm_status_t, const char *, ...);
89 static void warn(const char *, ...);
91 /* callback functions for printing output */
92 static ofmt_cb_t print_default_cb, print_flow_stats_cb;
94 #define NULL_OFMT {NULL, 0, 0, NULL}
97 * structures for flowstat (printing live statistics)
99 typedef enum {
100 FLOW_S_FLOW,
101 FLOW_S_IPKTS,
102 FLOW_S_RBYTES,
103 FLOW_S_IERRORS,
104 FLOW_S_OPKTS,
105 FLOW_S_OBYTES,
106 FLOW_S_OERRORS
107 } flow_s_field_index_t;
109 static ofmt_field_t flow_s_fields[] = {
110 /* name, field width, index, callback */
111 { "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb},
112 { "IPKTS", 8, FLOW_S_IPKTS, print_flow_stats_cb},
113 { "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb},
114 { "IERRS", 8, FLOW_S_IERRORS, print_flow_stats_cb},
115 { "OPKTS", 8, FLOW_S_OPKTS, print_flow_stats_cb},
116 { "OBYTES", 8, FLOW_S_OBYTES, print_flow_stats_cb},
117 { "OERRS", 8, FLOW_S_OERRORS, print_flow_stats_cb},
118 NULL_OFMT}
121 typedef struct flow_args_s {
122 char *flow_s_flow;
123 flow_stat_t *flow_s_stat;
124 char flow_s_unit;
125 boolean_t flow_s_parsable;
126 } flow_args_t;
129 * structures for 'flowstat -h'
131 typedef struct history_fields_buf_s {
132 char history_flow[12];
133 char history_duration[10];
134 char history_ipackets[9];
135 char history_rbytes[10];
136 char history_opackets[9];
137 char history_obytes[10];
138 char history_bandwidth[14];
139 } history_fields_buf_t;
141 static ofmt_field_t history_fields[] = {
142 /* name, field width, offset */
143 { "FLOW", 13,
144 offsetof(history_fields_buf_t, history_flow), print_default_cb},
145 { "DURATION", 11,
146 offsetof(history_fields_buf_t, history_duration), print_default_cb},
147 { "IPACKETS", 10,
148 offsetof(history_fields_buf_t, history_ipackets), print_default_cb},
149 { "RBYTES", 11,
150 offsetof(history_fields_buf_t, history_rbytes), print_default_cb},
151 { "OPACKETS", 10,
152 offsetof(history_fields_buf_t, history_opackets), print_default_cb},
153 { "OBYTES", 11,
154 offsetof(history_fields_buf_t, history_obytes), print_default_cb},
155 { "BANDWIDTH", 15,
156 offsetof(history_fields_buf_t, history_bandwidth), print_default_cb},
157 NULL_OFMT}
160 typedef struct history_l_fields_buf_s {
161 char history_l_flow[12];
162 char history_l_stime[13];
163 char history_l_etime[13];
164 char history_l_rbytes[8];
165 char history_l_obytes[8];
166 char history_l_bandwidth[14];
167 } history_l_fields_buf_t;
169 static ofmt_field_t history_l_fields[] = {
170 /* name, field width, offset */
171 { "FLOW", 13,
172 offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb},
173 { "START", 14,
174 offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb},
175 { "END", 14,
176 offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb},
177 { "RBYTES", 9,
178 offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb},
179 { "OBYTES", 9,
180 offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb},
181 { "BANDWIDTH", 15,
182 offsetof(history_l_fields_buf_t, history_l_bandwidth),
183 print_default_cb},
184 NULL_OFMT}
187 static char *progname;
190 * Handle to libdladm. Opened in main() before the sub-command
191 * specific function is called.
193 static dladm_handle_t handle = NULL;
195 const char *usage_ermsg = "flowstat [-r | -t] [-i interval] "
196 "[-l link] [flow]\n"
197 " flowstat [-A] [-i interval] [-p] [ -o field[,...]]\n"
198 " [-u R|K|M|G|T|P] [-l link] [flow]\n"
199 " flowstat -h [-a] [-d] [-F format]"
200 " [-s <DD/MM/YYYY,HH:MM:SS>]\n"
201 " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> "
202 "[<flow>]";
204 static void
205 usage(void)
207 (void) fprintf(stderr, "%s\n", gettext(usage_ermsg));
209 /* close dladm handle if it was opened */
210 if (handle != NULL)
211 dladm_close(handle);
213 exit(1);
216 boolean_t
217 flowstat_unit(char *oarg, char *unit)
219 if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
220 (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
221 (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
222 *unit = oarg[0];
223 return (B_TRUE);
226 return (B_FALSE);
229 void
230 map_to_units(char *buf, uint_t bufsize, double num, char unit,
231 boolean_t parsable)
233 if (parsable) {
234 (void) snprintf(buf, bufsize, "%.0lf", num);
235 return;
238 if (unit == '\0') {
239 int index;
241 for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
244 switch (index) {
245 case 0:
246 unit = '\0';
247 break;
248 case 1:
249 unit = 'K';
250 break;
251 case 2:
252 unit = 'M';
253 break;
254 case 3:
255 unit = 'G';
256 break;
257 case 4:
258 unit = 'T';
259 break;
260 case 5:
261 /* Largest unit supported */
262 default:
263 unit = 'P';
264 break;
266 } else {
267 switch (unit) {
268 case 'R':
269 /* Already raw numbers */
270 unit = '\0';
271 break;
272 case 'K':
273 num /= 1000;
274 break;
275 case 'M':
276 num /= (1000*1000);
277 break;
278 case 'G':
279 num /= (1000*1000*1000);
280 break;
281 case 'T':
282 num /= (1000.0*1000.0*1000.0*1000.0);
283 break;
284 case 'P':
285 /* Largest unit supported */
286 default:
287 num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
288 break;
292 if (unit == '\0')
293 (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
294 else
295 (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
298 flow_chain_t *
299 get_flow_prev_stat(const char *flowname, void *arg)
301 show_flow_state_t *state = arg;
302 flow_chain_t *flow_curr = NULL;
304 /* Scan prev flowname list and look for entry matching this entry */
305 for (flow_curr = state->fs_flowchain; flow_curr;
306 flow_curr = flow_curr->fc_next) {
307 if (strcmp(flow_curr->fc_flowname, flowname) == 0)
308 break;
311 /* New flow, add it */
312 if (flow_curr == NULL) {
313 flow_curr = (flow_chain_t *)malloc(sizeof (flow_chain_t));
314 if (flow_curr == NULL)
315 goto done;
316 (void) strncpy(flow_curr->fc_flowname, flowname,
317 MAXFLOWNAMELEN);
318 flow_curr->fc_stat = NULL;
319 flow_curr->fc_next = state->fs_flowchain;
320 state->fs_flowchain = flow_curr;
322 done:
323 return (flow_curr);
327 * Number of flows may change while flowstat -i is executing.
328 * Free memory allocated for flows that are no longer there.
329 * Prepare for next iteration by marking visited = false for
330 * existing stat entries.
332 static void
333 cleanup_removed_flows(show_flow_state_t *state)
335 flow_chain_t *fcurr;
336 flow_chain_t *fprev;
337 flow_chain_t *tofree;
339 /* Delete all nodes from the list that have fc_visited marked false */
340 fcurr = state->fs_flowchain;
341 while (fcurr != NULL) {
342 if (fcurr->fc_visited) {
343 fcurr->fc_visited = B_FALSE;
344 fprev = fcurr;
345 fcurr = fcurr->fc_next;
346 continue;
349 /* Is it head of the list? */
350 if (fcurr == state->fs_flowchain)
351 state->fs_flowchain = fcurr->fc_next;
352 else
353 fprev->fc_next = fcurr->fc_next;
355 /* fprev remains the same */
356 tofree = fcurr;
357 fcurr = fcurr->fc_next;
359 /* Free stats memory for the removed flow */
360 dladm_flow_stat_free(tofree->fc_stat);
361 free(tofree);
365 static boolean_t
366 print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
368 flow_args_t *fargs = of_arg->ofmt_cbarg;
369 flow_stat_t *diff_stats = fargs->flow_s_stat;
370 char unit = fargs->flow_s_unit;
371 boolean_t parsable = fargs->flow_s_parsable;
373 switch (of_arg->ofmt_id) {
374 case FLOW_S_FLOW:
375 (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow);
376 break;
377 case FLOW_S_IPKTS:
378 map_to_units(buf, bufsize, diff_stats->fl_ipackets, unit,
379 parsable);
380 break;
381 case FLOW_S_RBYTES:
382 map_to_units(buf, bufsize, diff_stats->fl_rbytes, unit,
383 parsable);
384 break;
385 case FLOW_S_IERRORS:
386 map_to_units(buf, bufsize, diff_stats->fl_ierrors, unit,
387 parsable);
388 break;
389 case FLOW_S_OPKTS:
390 map_to_units(buf, bufsize, diff_stats->fl_opackets, unit,
391 parsable);
392 break;
393 case FLOW_S_OBYTES:
394 map_to_units(buf, bufsize, diff_stats->fl_obytes, unit,
395 parsable);
396 break;
397 case FLOW_S_OERRORS:
398 map_to_units(buf, bufsize, diff_stats->fl_oerrors, unit,
399 parsable);
400 break;
401 default:
402 die("invalid input");
403 break;
405 return (B_TRUE);
408 /* ARGSUSED */
409 static int
410 query_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
412 show_flow_state_t *state = arg;
413 flow_chain_t *flow_node;
414 flow_stat_t *curr_stat;
415 flow_stat_t *prev_stat;
416 flow_stat_t *diff_stat;
417 char *flowname = attr->fa_flowname;
418 flow_args_t fargs;
420 /* Get previous stats for the flow */
421 flow_node = get_flow_prev_stat(flowname, arg);
422 if (flow_node == NULL)
423 goto done;
425 flow_node->fc_visited = B_TRUE;
426 prev_stat = flow_node->fc_stat;
428 /* Query library for current stats */
429 curr_stat = dladm_flow_stat_query(flowname);
430 if (curr_stat == NULL)
431 goto done;
433 /* current stats - prev iteration stats */
434 diff_stat = dladm_flow_stat_diff(curr_stat, prev_stat);
436 /* Free prev stats */
437 dladm_flow_stat_free(prev_stat);
439 /* Prev <- curr stats */
440 flow_node->fc_stat = curr_stat;
442 if (diff_stat == NULL)
443 goto done;
445 /* Print stats */
446 fargs.flow_s_flow = flowname;
447 fargs.flow_s_stat = diff_stat;
448 fargs.flow_s_unit = state->fs_unit;
449 fargs.flow_s_parsable = state->fs_parsable;
450 ofmt_print(state->fs_ofmt, &fargs);
452 /* Free diff stats */
453 dladm_flow_stat_free(diff_stat);
454 done:
455 return (DLADM_WALK_CONTINUE);
459 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
460 * dladm_walk_datalink_id(). Used for showing flow stats for
461 * all flows on all links.
463 static int
464 query_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
466 if (dladm_walk_flow(query_flow_stats, dh, linkid, arg, B_FALSE)
467 == DLADM_STATUS_OK)
468 return (DLADM_WALK_CONTINUE);
469 else
470 return (DLADM_WALK_TERMINATE);
473 void
474 print_all_stats(name_value_stat_entry_t *stat_entry)
476 name_value_stat_t *curr_stat;
478 printf("%s\n", stat_entry->nve_header);
480 for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
481 curr_stat = curr_stat->nv_nextstat) {
482 printf("\t%15s", curr_stat->nv_statname);
483 printf("\t%15llu\n", curr_stat->nv_statval);
487 /* ARGSUSED */
488 static int
489 dump_one_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
491 char *flowname = attr->fa_flowname;
492 void *stat;
494 stat = dladm_flow_stat_query_all(flowname);
495 if (stat == NULL)
496 goto done;
497 print_all_stats(stat);
498 dladm_flow_stat_query_all_free(stat);
500 done:
501 return (DLADM_WALK_CONTINUE);
505 * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
506 * dladm_walk_datalink_id(). Used for showing flow stats for
507 * all flows on all links.
509 static int
510 dump_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
512 if (dladm_walk_flow(dump_one_flow_stats, dh, linkid, arg, B_FALSE)
513 == DLADM_STATUS_OK)
514 return (DLADM_WALK_CONTINUE);
515 else
516 return (DLADM_WALK_TERMINATE);
519 static void
520 dump_all_flow_stats(dladm_flow_attr_t *attrp, void *arg, datalink_id_t linkid,
521 boolean_t flow_arg)
523 /* Show stats for named flow */
524 if (flow_arg) {
525 (void) dump_one_flow_stats(handle, attrp, arg);
527 /* Show stats for flows on one link */
528 } else if (linkid != DATALINK_INVALID_LINKID) {
529 (void) dladm_walk_flow(dump_one_flow_stats, handle, linkid,
530 arg, B_FALSE);
532 /* Show stats for all flows on all links */
533 } else {
534 (void) dladm_walk_datalink_id(dump_link_flow_stats,
535 handle, arg, DATALINK_CLASS_ALL,
536 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
541 main(int argc, char *argv[])
543 dladm_status_t status;
544 int option;
545 boolean_t r_arg = B_FALSE;
546 boolean_t t_arg = B_FALSE;
547 boolean_t p_arg = B_FALSE;
548 boolean_t i_arg = B_FALSE;
549 boolean_t o_arg = B_FALSE;
550 boolean_t u_arg = B_FALSE;
551 boolean_t A_arg = B_FALSE;
552 boolean_t flow_arg = B_FALSE;
553 datalink_id_t linkid = DATALINK_ALL_LINKID;
554 char linkname[MAXLINKNAMELEN];
555 char flowname[MAXFLOWNAMELEN];
556 uint32_t interval = 0;
557 char unit = '\0';
558 show_flow_state_t state;
559 char *fields_str = NULL;
560 char *o_fields_str = NULL;
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";
569 ofmt_handle_t ofmt;
570 ofmt_status_t oferr;
571 uint_t ofmtflags = OFMT_RIGHTJUST;
573 dladm_flow_attr_t attr;
575 (void) setlocale(LC_ALL, "");
576 #if !defined(TEXT_DOMAIN)
577 #define TEXT_DOMAIN "SYS_TEST"
578 #endif
579 (void) textdomain(TEXT_DOMAIN);
581 progname = argv[0];
583 /* Open the libdladm handle */
584 if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
585 die_dlerr(status, "could not open /dev/dld");
587 bzero(&state, sizeof (state));
589 opterr = 0;
590 while ((option = getopt_long(argc, argv, ":rtApi:o:u:l:h",
591 NULL, NULL)) != -1) {
592 switch (option) {
593 case 'r':
594 if (r_arg)
595 die_optdup(option);
597 r_arg = B_TRUE;
598 break;
599 case 't':
600 if (t_arg)
601 die_optdup(option);
603 t_arg = B_TRUE;
604 break;
605 case 'A':
606 if (A_arg)
607 die_optdup(option);
609 A_arg = B_TRUE;
610 break;
611 case 'p':
612 if (p_arg)
613 die_optdup(option);
615 p_arg = B_TRUE;
616 break;
617 case 'i':
618 if (i_arg)
619 die_optdup(option);
621 i_arg = B_TRUE;
622 if (!dladm_str2interval(optarg, &interval))
623 die("invalid interval value '%s'", optarg);
624 break;
625 case 'o':
626 o_arg = B_TRUE;
627 o_fields_str = optarg;
628 break;
629 case 'u':
630 if (u_arg)
631 die_optdup(option);
633 u_arg = B_TRUE;
634 if (!flowstat_unit(optarg, &unit))
635 die("invalid unit value '%s',"
636 "unit must be R|K|M|G|T|P", optarg);
637 break;
638 case 'l':
639 if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
640 >= MAXLINKNAMELEN)
641 die("link name too long\n");
642 if (dladm_name2info(handle, linkname, &linkid, NULL,
643 NULL, NULL) != DLADM_STATUS_OK)
644 die("invalid link '%s'", linkname);
645 break;
646 case 'h':
647 if (r_arg || t_arg || p_arg || o_arg || u_arg ||
648 i_arg || A_arg) {
649 die("the option -h is not compatible with "
650 "-r, -t, -p, -o, -u, -i, -A");
652 do_show_history(argc, argv);
653 return (0);
654 break;
655 default:
656 die_opterr(optopt, option, usage_ermsg);
657 break;
661 if (r_arg && t_arg)
662 die("the option -t and -r are not compatible");
664 if (u_arg && p_arg)
665 die("the option -u and -p are not compatible");
667 if (p_arg && !o_arg)
668 die("-p requires -o");
670 if (p_arg && strcasecmp(o_fields_str, "all") == 0)
671 die("\"-o all\" is invalid with -p");
673 if (A_arg &&
674 (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg))
675 die("the option -A is not compatible with "
676 "-r, -t, -p, -o, -u, -i");
678 /* get flow name (optional last argument) */
679 if (optind == (argc-1)) {
680 if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
681 >= MAXFLOWNAMELEN)
682 die("flow name too long");
683 flow_arg = B_TRUE;
684 } else if (optind != argc) {
685 usage();
688 if (flow_arg &&
689 dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK)
690 die("invalid flow %s", flowname);
692 if (A_arg) {
693 dump_all_flow_stats(&attr, &state, linkid, flow_arg);
694 return (0);
697 state.fs_unit = unit;
698 state.fs_parsable = p_arg;
700 if (state.fs_parsable)
701 ofmtflags |= OFMT_PARSABLE;
703 if (r_arg)
704 fields_str = rx_stat_fields;
705 else if (t_arg)
706 fields_str = tx_stat_fields;
707 else
708 fields_str = total_stat_fields;
710 if (o_arg) {
711 fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
712 fields_str : o_fields_str;
715 oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
716 ofmt_check(oferr, state.fs_parsable, ofmt, die, warn);
717 state.fs_ofmt = ofmt;
719 for (;;) {
720 /* Show stats for named flow */
721 if (flow_arg) {
722 (void) query_flow_stats(handle, &attr, &state);
724 /* Show stats for flows on one link */
725 } else if (linkid != DATALINK_INVALID_LINKID) {
726 (void) dladm_walk_flow(query_flow_stats, handle, linkid,
727 &state, B_FALSE);
729 /* Show stats for all flows on all links */
730 } else {
731 (void) dladm_walk_datalink_id(query_link_flow_stats,
732 handle, &state, DATALINK_CLASS_ALL,
733 DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
736 if (interval == 0)
737 break;
739 (void) fflush(stdout);
740 cleanup_removed_flows(&state);
741 (void) sleep(interval);
743 ofmt_close(ofmt);
745 dladm_close(handle);
746 return (0);
749 /* ARGSUSED */
750 static int
751 show_history_date(dladm_usage_t *history, void *arg)
753 show_history_state_t *state = (show_history_state_t *)arg;
754 time_t stime;
755 char timebuf[20];
756 dladm_flow_attr_t attr;
757 dladm_status_t status;
760 * Only show historical information for existing flows unless '-a'
761 * is specified.
763 if (!state->us_showall && ((status = dladm_flow_info(handle,
764 history->du_name, &attr)) != DLADM_STATUS_OK)) {
765 return (status);
768 stime = history->du_stime;
769 (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
770 localtime(&stime));
771 (void) printf("%s\n", timebuf);
773 return (DLADM_STATUS_OK);
776 static int
777 show_history_time(dladm_usage_t *history, void *arg)
779 show_history_state_t *state = (show_history_state_t *)arg;
780 char buf[DLADM_STRSIZE];
781 history_l_fields_buf_t ubuf;
782 time_t time;
783 double bw;
784 dladm_flow_attr_t attr;
785 dladm_status_t status;
788 * Only show historical information for existing flows unless '-a'
789 * is specified.
791 if (!state->us_showall && ((status = dladm_flow_info(handle,
792 history->du_name, &attr)) != DLADM_STATUS_OK)) {
793 return (status);
796 if (state->us_plot) {
797 if (!state->us_printheader) {
798 if (state->us_first) {
799 (void) printf("# Time");
800 state->us_first = B_FALSE;
802 (void) printf(" %s", history->du_name);
803 if (history->du_last) {
804 (void) printf("\n");
805 state->us_first = B_TRUE;
806 state->us_printheader = B_TRUE;
808 } else {
809 if (state->us_first) {
810 time = history->du_etime;
811 (void) strftime(buf, sizeof (buf), "%T",
812 localtime(&time));
813 state->us_first = B_FALSE;
814 (void) printf("%s", buf);
816 bw = (double)history->du_bandwidth/1000;
817 (void) printf(" %.2f", bw);
818 if (history->du_last) {
819 (void) printf("\n");
820 state->us_first = B_TRUE;
823 return (DLADM_STATUS_OK);
826 bzero(&ubuf, sizeof (ubuf));
828 (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s",
829 history->du_name);
830 time = history->du_stime;
831 (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
832 (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime),
833 "%s", buf);
834 time = history->du_etime;
835 (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
836 (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime),
837 "%s", buf);
838 (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes),
839 "%llu", history->du_rbytes);
840 (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes),
841 "%llu", history->du_obytes);
842 (void) snprintf(ubuf.history_l_bandwidth,
843 sizeof (ubuf.history_l_bandwidth), "%s Mbps",
844 dladm_bw2str(history->du_bandwidth, buf));
846 ofmt_print(state->us_ofmt, (void *)&ubuf);
847 return (DLADM_STATUS_OK);
850 static int
851 show_history_res(dladm_usage_t *history, void *arg)
853 show_history_state_t *state = (show_history_state_t *)arg;
854 char buf[DLADM_STRSIZE];
855 history_fields_buf_t ubuf;
856 dladm_flow_attr_t attr;
857 dladm_status_t status;
860 * Only show historical information for existing flows unless '-a'
861 * is specified.
863 if (!state->us_showall && ((status = dladm_flow_info(handle,
864 history->du_name, &attr)) != DLADM_STATUS_OK)) {
865 return (status);
868 bzero(&ubuf, sizeof (ubuf));
870 (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s",
871 history->du_name);
872 (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration),
873 "%llu", history->du_duration);
874 (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets),
875 "%llu", history->du_ipackets);
876 (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes),
877 "%llu", history->du_rbytes);
878 (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets),
879 "%llu", history->du_opackets);
880 (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes),
881 "%llu", history->du_obytes);
882 (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth),
883 "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
885 ofmt_print(state->us_ofmt, (void *)&ubuf);
887 return (DLADM_STATUS_OK);
890 static boolean_t
891 valid_formatspec(char *formatspec_str)
893 return (strcmp(formatspec_str, "gnuplot") == 0);
896 /* ARGSUSED */
897 static void
898 do_show_history(int argc, char *argv[])
900 char *file = NULL;
901 int opt;
902 dladm_status_t status;
903 boolean_t d_arg = B_FALSE;
904 char *stime = NULL;
905 char *etime = NULL;
906 char *resource = NULL;
907 show_history_state_t state;
908 boolean_t o_arg = B_FALSE;
909 boolean_t F_arg = B_FALSE;
910 char *fields_str = NULL;
911 char *formatspec_str = NULL;
912 char *all_fields =
913 "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
914 char *all_l_fields =
915 "flow,start,end,rbytes,obytes,bandwidth";
916 ofmt_handle_t ofmt;
917 ofmt_status_t oferr;
918 uint_t ofmtflags = 0;
920 bzero(&state, sizeof (show_history_state_t));
921 state.us_parsable = B_FALSE;
922 state.us_printheader = B_FALSE;
923 state.us_plot = B_FALSE;
924 state.us_first = B_TRUE;
926 while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
927 switch (opt) {
928 case 'd':
929 d_arg = B_TRUE;
930 break;
931 case 'a':
932 state.us_showall = B_TRUE;
933 break;
934 case 'f':
935 file = optarg;
936 break;
937 case 's':
938 stime = optarg;
939 break;
940 case 'e':
941 etime = optarg;
942 break;
943 case 'o':
944 o_arg = B_TRUE;
945 fields_str = optarg;
946 break;
947 case 'F':
948 state.us_plot = F_arg = B_TRUE;
949 formatspec_str = optarg;
950 break;
951 default:
952 die_opterr(optopt, opt, usage_ermsg);
956 if (file == NULL)
957 die("-h requires a file");
959 if (optind == (argc-1)) {
960 dladm_flow_attr_t attr;
962 resource = argv[optind];
963 if (!state.us_showall &&
964 dladm_flow_info(handle, resource, &attr) !=
965 DLADM_STATUS_OK) {
966 die("invalid flow: '%s'", resource);
970 if (state.us_parsable)
971 ofmtflags |= OFMT_PARSABLE;
972 if (resource == NULL && stime == NULL && etime == NULL) {
973 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
974 fields_str = all_fields;
975 oferr = ofmt_open(fields_str, history_fields, ofmtflags,
976 0, &ofmt);
977 } else {
978 if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
979 fields_str = all_l_fields;
980 oferr = ofmt_open(fields_str, history_l_fields, ofmtflags,
981 0, &ofmt);
984 ofmt_check(oferr, state.us_parsable, ofmt, die, warn);
985 state.us_ofmt = ofmt;
987 if (F_arg && d_arg)
988 die("incompatible -d and -F options");
990 if (F_arg && !valid_formatspec(formatspec_str))
991 die("Format specifier %s not supported", formatspec_str);
993 if (d_arg) {
994 /* Print log dates */
995 status = dladm_usage_dates(show_history_date,
996 DLADM_LOGTYPE_FLOW, file, resource, &state);
997 } else if (resource == NULL && stime == NULL && etime == NULL &&
998 !F_arg) {
999 /* Print summary */
1000 status = dladm_usage_summary(show_history_res,
1001 DLADM_LOGTYPE_FLOW, file, &state);
1002 } else if (resource != NULL) {
1003 /* Print log entries for named resource */
1004 status = dladm_walk_usage_res(show_history_time,
1005 DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
1006 } else {
1007 /* Print time and information for each flow */
1008 status = dladm_walk_usage_time(show_history_time,
1009 DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
1012 ofmt_close(ofmt);
1013 if (status != DLADM_STATUS_OK)
1014 die_dlerr(status, "-h");
1015 dladm_close(handle);
1018 static void
1019 warn(const char *format, ...)
1021 va_list alist;
1023 format = gettext(format);
1024 (void) fprintf(stderr, "%s: warning: ", progname);
1026 va_start(alist, format);
1027 (void) vfprintf(stderr, format, alist);
1028 va_end(alist);
1030 (void) putc('\n', stderr);
1033 /* PRINTFLIKE1 */
1034 static void
1035 die(const char *format, ...)
1037 va_list alist;
1039 format = gettext(format);
1040 (void) fprintf(stderr, "%s: ", progname);
1042 va_start(alist, format);
1043 (void) vfprintf(stderr, format, alist);
1044 va_end(alist);
1046 (void) putc('\n', stderr);
1048 /* close dladm handle if it was opened */
1049 if (handle != NULL)
1050 dladm_close(handle);
1052 exit(EXIT_FAILURE);
1055 static void
1056 die_optdup(int opt)
1058 die("the option -%c cannot be specified more than once", opt);
1061 static void
1062 die_opterr(int opt, int opterr, const char *usage)
1064 switch (opterr) {
1065 case ':':
1066 die("option '-%c' requires a value\nusage: %s", opt,
1067 gettext(usage));
1068 break;
1069 case '?':
1070 default:
1071 die("unrecognized option '-%c'\nusage: %s", opt,
1072 gettext(usage));
1073 break;
1077 /* PRINTFLIKE2 */
1078 static void
1079 die_dlerr(dladm_status_t err, const char *format, ...)
1081 va_list alist;
1082 char errmsg[DLADM_STRSIZE];
1084 format = gettext(format);
1085 (void) fprintf(stderr, "%s: ", progname);
1087 va_start(alist, format);
1088 (void) vfprintf(stderr, format, alist);
1089 va_end(alist);
1090 (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1092 /* close dladm handle if it was opened */
1093 if (handle != NULL)
1094 dladm_close(handle);
1096 exit(EXIT_FAILURE);
1101 * default output callback function that, when invoked from dladm_print_output,
1102 * prints string which is offset by of_arg->ofmt_id within buf.
1104 static boolean_t
1105 print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
1107 char *value;
1109 value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
1110 (void) strlcpy(buf, value, bufsize);
1111 return (B_TRUE);