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