xref: /titanic_51/usr/src/cmd/flowadm/flowadm.c (revision da14cebe459d3275048785f25bd869cb09b5307f)
1*da14cebeSEric Cheng /*
2*da14cebeSEric Cheng  * CDDL HEADER START
3*da14cebeSEric Cheng  *
4*da14cebeSEric Cheng  * The contents of this file are subject to the terms of the
5*da14cebeSEric Cheng  * Common Development and Distribution License (the "License").
6*da14cebeSEric Cheng  * You may not use this file except in compliance with the License.
7*da14cebeSEric Cheng  *
8*da14cebeSEric Cheng  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*da14cebeSEric Cheng  * or http://www.opensolaris.org/os/licensing.
10*da14cebeSEric Cheng  * See the License for the specific language governing permissions
11*da14cebeSEric Cheng  * and limitations under the License.
12*da14cebeSEric Cheng  *
13*da14cebeSEric Cheng  * When distributing Covered Code, include this CDDL HEADER in each
14*da14cebeSEric Cheng  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*da14cebeSEric Cheng  * If applicable, add the following below this CDDL HEADER, with the
16*da14cebeSEric Cheng  * fields enclosed by brackets "[]" replaced with your own identifying
17*da14cebeSEric Cheng  * information: Portions Copyright [yyyy] [name of copyright owner]
18*da14cebeSEric Cheng  *
19*da14cebeSEric Cheng  * CDDL HEADER END
20*da14cebeSEric Cheng  */
21*da14cebeSEric Cheng /*
22*da14cebeSEric Cheng  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*da14cebeSEric Cheng  * Use is subject to license terms.
24*da14cebeSEric Cheng  */
25*da14cebeSEric Cheng 
26*da14cebeSEric Cheng #include <stdio.h>
27*da14cebeSEric Cheng #include <locale.h>
28*da14cebeSEric Cheng #include <stdarg.h>
29*da14cebeSEric Cheng #include <stdlib.h>
30*da14cebeSEric Cheng #include <fcntl.h>
31*da14cebeSEric Cheng #include <string.h>
32*da14cebeSEric Cheng #include <stropts.h>
33*da14cebeSEric Cheng #include <errno.h>
34*da14cebeSEric Cheng #include <kstat.h>
35*da14cebeSEric Cheng #include <strings.h>
36*da14cebeSEric Cheng #include <getopt.h>
37*da14cebeSEric Cheng #include <unistd.h>
38*da14cebeSEric Cheng #include <priv.h>
39*da14cebeSEric Cheng #include <netdb.h>
40*da14cebeSEric Cheng #include <libintl.h>
41*da14cebeSEric Cheng #include <libdlflow.h>
42*da14cebeSEric Cheng #include <libdllink.h>
43*da14cebeSEric Cheng #include <libdlstat.h>
44*da14cebeSEric Cheng #include <sys/types.h>
45*da14cebeSEric Cheng #include <sys/socket.h>
46*da14cebeSEric Cheng #include <netinet/in.h>
47*da14cebeSEric Cheng #include <arpa/inet.h>
48*da14cebeSEric Cheng #include <sys/ethernet.h>
49*da14cebeSEric Cheng #include <inet/ip.h>
50*da14cebeSEric Cheng #include <inet/ip6.h>
51*da14cebeSEric Cheng #include <stddef.h>
52*da14cebeSEric Cheng 
53*da14cebeSEric Cheng #define	CMD_TYPE_ANY	0xffffffff
54*da14cebeSEric Cheng #define	STR_UNDEF_VAL	"--"
55*da14cebeSEric Cheng 
56*da14cebeSEric Cheng 
57*da14cebeSEric Cheng /*
58*da14cebeSEric Cheng  * data structures and routines for printing output.
59*da14cebeSEric Cheng  */
60*da14cebeSEric Cheng 
61*da14cebeSEric Cheng typedef struct print_field_s {
62*da14cebeSEric Cheng 	const char	*pf_name;
63*da14cebeSEric Cheng 	const char	*pf_header;
64*da14cebeSEric Cheng 	uint_t		pf_width;
65*da14cebeSEric Cheng 	union {
66*da14cebeSEric Cheng 		uint_t	_pf_index;
67*da14cebeSEric Cheng 		size_t	_pf_offset;
68*da14cebeSEric Cheng 	}_pf_un;
69*da14cebeSEric Cheng #define	pf_index	_pf_un._pf_index
70*da14cebeSEric Cheng #define	pf_offset	_pf_un._pf_offset;
71*da14cebeSEric Cheng 	uint_t	pf_cmdtype;
72*da14cebeSEric Cheng } print_field_t;
73*da14cebeSEric Cheng 
74*da14cebeSEric Cheng typedef struct print_state_s {
75*da14cebeSEric Cheng 	print_field_t	**ps_fields;
76*da14cebeSEric Cheng 	uint_t		ps_nfields;
77*da14cebeSEric Cheng 	boolean_t	ps_lastfield;
78*da14cebeSEric Cheng 	uint_t		ps_overflow;
79*da14cebeSEric Cheng } print_state_t;
80*da14cebeSEric Cheng 
81*da14cebeSEric Cheng typedef struct show_usage_state_s {
82*da14cebeSEric Cheng 	boolean_t	us_plot;
83*da14cebeSEric Cheng 	boolean_t	us_parseable;
84*da14cebeSEric Cheng 	boolean_t	us_printheader;
85*da14cebeSEric Cheng 	boolean_t	us_first;
86*da14cebeSEric Cheng 	print_state_t	us_print;
87*da14cebeSEric Cheng } show_usage_state_t;
88*da14cebeSEric Cheng 
89*da14cebeSEric Cheng typedef char *(*print_callback_t)(print_field_t *, void *);
90*da14cebeSEric Cheng static print_field_t **parse_output_fields(char *, print_field_t *, int,
91*da14cebeSEric Cheng     uint_t, uint_t *);
92*da14cebeSEric Cheng 
93*da14cebeSEric Cheng static void print_header(print_state_t *);
94*da14cebeSEric Cheng static void print_field(print_state_t *, print_field_t *, const char *,
95*da14cebeSEric Cheng     boolean_t);
96*da14cebeSEric Cheng 
97*da14cebeSEric Cheng static void flowadm_print_output(print_state_t *, boolean_t,
98*da14cebeSEric Cheng     print_callback_t, void *);
99*da14cebeSEric Cheng 
100*da14cebeSEric Cheng /*
101*da14cebeSEric Cheng  * helper function that, when invoked as flowadm(print_field(pf, buf)
102*da14cebeSEric Cheng  * prints string which is offset by pf->pf_offset within buf.
103*da14cebeSEric Cheng  */
104*da14cebeSEric Cheng static char *flowadm_print_field(print_field_t *, void *);
105*da14cebeSEric Cheng 
106*da14cebeSEric Cheng #define	MAX_FIELD_LEN	32
107*da14cebeSEric Cheng 
108*da14cebeSEric Cheng typedef void cmdfunc_t(int, char **);
109*da14cebeSEric Cheng 
110*da14cebeSEric Cheng static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow;
111*da14cebeSEric Cheng static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop;
112*da14cebeSEric Cheng static cmdfunc_t do_show_usage;
113*da14cebeSEric Cheng 
114*da14cebeSEric Cheng static int	show_flow(dladm_flow_attr_t *, void *);
115*da14cebeSEric Cheng static int	show_flows_onelink(datalink_id_t, void *);
116*da14cebeSEric Cheng 
117*da14cebeSEric Cheng static void	flow_stats(const char *, datalink_id_t,  uint_t);
118*da14cebeSEric Cheng static void	get_flow_stats(const char *, pktsum_t *);
119*da14cebeSEric Cheng static int	show_flow_stats(dladm_flow_attr_t *, void *);
120*da14cebeSEric Cheng static int	show_link_flow_stats(datalink_id_t, void *);
121*da14cebeSEric Cheng 
122*da14cebeSEric Cheng static int	remove_flow(dladm_flow_attr_t *, void *);
123*da14cebeSEric Cheng 
124*da14cebeSEric Cheng static int	show_flowprop(dladm_flow_attr_t *, void *);
125*da14cebeSEric Cheng static void	show_flowprop_one_flow(void *, const char *);
126*da14cebeSEric Cheng static int	show_flowprop_onelink(datalink_id_t, void *);
127*da14cebeSEric Cheng 
128*da14cebeSEric Cheng static void	die(const char *, ...);
129*da14cebeSEric Cheng static void	die_optdup(int);
130*da14cebeSEric Cheng static void	die_opterr(int, int);
131*da14cebeSEric Cheng static void	die_dlerr(dladm_status_t, const char *, ...);
132*da14cebeSEric Cheng static void	warn(const char *, ...);
133*da14cebeSEric Cheng static void	warn_dlerr(dladm_status_t, const char *, ...);
134*da14cebeSEric Cheng 
135*da14cebeSEric Cheng typedef struct	cmd {
136*da14cebeSEric Cheng 	char	*c_name;
137*da14cebeSEric Cheng 	void	(*c_fn)(int, char **);
138*da14cebeSEric Cheng } cmd_t;
139*da14cebeSEric Cheng 
140*da14cebeSEric Cheng static cmd_t	cmds[] = {
141*da14cebeSEric Cheng 	{ "add-flow", do_add_flow },
142*da14cebeSEric Cheng 	{ "remove-flow", do_remove_flow },
143*da14cebeSEric Cheng 	{ "show-flowprop", do_show_flowprop },
144*da14cebeSEric Cheng 	{ "set-flowprop", do_set_flowprop },
145*da14cebeSEric Cheng 	{ "reset-flowprop", do_reset_flowprop },
146*da14cebeSEric Cheng 	{ "show-flow", do_show_flow },
147*da14cebeSEric Cheng 	{ "init-flow", do_init_flow },
148*da14cebeSEric Cheng 	{ "show-usage", do_show_usage }
149*da14cebeSEric Cheng };
150*da14cebeSEric Cheng 
151*da14cebeSEric Cheng static const struct option longopts[] = {
152*da14cebeSEric Cheng 	{"link",		required_argument,	0, 'l'},
153*da14cebeSEric Cheng 	{"parseable",		no_argument,		0, 'p'},
154*da14cebeSEric Cheng 	{"statistics",		no_argument,		0, 's'},
155*da14cebeSEric Cheng 	{"interval",		required_argument,	0, 'i'},
156*da14cebeSEric Cheng 	{"temporary",		no_argument,		0, 't'},
157*da14cebeSEric Cheng 	{"root-dir",		required_argument,	0, 'R'},
158*da14cebeSEric Cheng 	{ 0, 0, 0, 0 }
159*da14cebeSEric Cheng };
160*da14cebeSEric Cheng 
161*da14cebeSEric Cheng static const struct option prop_longopts[] = {
162*da14cebeSEric Cheng 	{"link",		required_argument,	0, 'l'},
163*da14cebeSEric Cheng 	{"temporary",		no_argument,		0, 't'},
164*da14cebeSEric Cheng 	{"root-dir",		required_argument,	0, 'R'},
165*da14cebeSEric Cheng 	{"prop",		required_argument,	0, 'p'},
166*da14cebeSEric Cheng 	{"attr",		required_argument,	0, 'a'},
167*da14cebeSEric Cheng 	{ 0, 0, 0, 0 }
168*da14cebeSEric Cheng };
169*da14cebeSEric Cheng 
170*da14cebeSEric Cheng /*
171*da14cebeSEric Cheng  * structures for 'flowadm show-flow'
172*da14cebeSEric Cheng  */
173*da14cebeSEric Cheng 
174*da14cebeSEric Cheng typedef struct show_flow_state {
175*da14cebeSEric Cheng 	boolean_t		fs_firstonly;
176*da14cebeSEric Cheng 	boolean_t		fs_donefirst;
177*da14cebeSEric Cheng 	pktsum_t		fs_prevstats;
178*da14cebeSEric Cheng 	uint32_t		fs_flags;
179*da14cebeSEric Cheng 	dladm_status_t		fs_status;
180*da14cebeSEric Cheng 	print_state_t		fs_print;
181*da14cebeSEric Cheng 	const char		*fs_flow;
182*da14cebeSEric Cheng 	const char		*fs_link;
183*da14cebeSEric Cheng 	boolean_t		fs_parseable;
184*da14cebeSEric Cheng 	boolean_t		fs_printheader;
185*da14cebeSEric Cheng 	boolean_t		fs_persist;
186*da14cebeSEric Cheng 	boolean_t		fs_stats;
187*da14cebeSEric Cheng 	uint64_t		fs_mask;
188*da14cebeSEric Cheng } show_flow_state_t;
189*da14cebeSEric Cheng 
190*da14cebeSEric Cheng /*
191*da14cebeSEric Cheng  * structures for 'flowadm remove-flow'
192*da14cebeSEric Cheng  */
193*da14cebeSEric Cheng 
194*da14cebeSEric Cheng typedef struct remove_flow_state {
195*da14cebeSEric Cheng 	boolean_t	fs_tempop;
196*da14cebeSEric Cheng 	const char	*fs_altroot;
197*da14cebeSEric Cheng 	dladm_status_t	fs_status;
198*da14cebeSEric Cheng } remove_flow_state_t;
199*da14cebeSEric Cheng 
200*da14cebeSEric Cheng typedef struct flow_args_s {
201*da14cebeSEric Cheng 	const char		*fa_link;
202*da14cebeSEric Cheng 	int			fa_attrno;	/* -1 indicates flow itself */
203*da14cebeSEric Cheng 	uint64_t		fa_mask;
204*da14cebeSEric Cheng 	dladm_flow_attr_t	*fa_finfop;
205*da14cebeSEric Cheng 	dladm_status_t		*fa_status;
206*da14cebeSEric Cheng 	boolean_t		fa_parseable;
207*da14cebeSEric Cheng } flow_args_t;
208*da14cebeSEric Cheng 
209*da14cebeSEric Cheng #define	PROTO_MAXSTR_LEN	7
210*da14cebeSEric Cheng #define	PORT_MAXSTR_LEN		6
211*da14cebeSEric Cheng #define	DSFIELD_MAXSTR_LEN	10
212*da14cebeSEric Cheng 
213*da14cebeSEric Cheng typedef struct flow_fields_buf_s
214*da14cebeSEric Cheng {
215*da14cebeSEric Cheng 	char flow_name[MAXNAMELEN];
216*da14cebeSEric Cheng 	char flow_link[MAXLINKNAMELEN];
217*da14cebeSEric Cheng 	char flow_ipaddr[INET6_ADDRSTRLEN+4];
218*da14cebeSEric Cheng 	char flow_proto[PROTO_MAXSTR_LEN];
219*da14cebeSEric Cheng 	char flow_port[PORT_MAXSTR_LEN];
220*da14cebeSEric Cheng 	char flow_dsfield[DSFIELD_MAXSTR_LEN];
221*da14cebeSEric Cheng } flow_fields_buf_t;
222*da14cebeSEric Cheng 
223*da14cebeSEric Cheng static print_field_t flow_fields[] = {
224*da14cebeSEric Cheng /* name,	header,		field width,	index,		cmdtype	*/
225*da14cebeSEric Cheng {  "flow",	"FLOW",		11,
226*da14cebeSEric Cheng     offsetof(flow_fields_buf_t, flow_name),	CMD_TYPE_ANY},
227*da14cebeSEric Cheng {  "link",	"LINK",		11,
228*da14cebeSEric Cheng     offsetof(flow_fields_buf_t, flow_link),	CMD_TYPE_ANY},
229*da14cebeSEric Cheng {  "ipaddr",	"IP ADDR",	30,
230*da14cebeSEric Cheng     offsetof(flow_fields_buf_t, flow_ipaddr),	CMD_TYPE_ANY},
231*da14cebeSEric Cheng {  "transport",	"PROTO",	6,
232*da14cebeSEric Cheng     offsetof(flow_fields_buf_t, flow_proto),	CMD_TYPE_ANY},
233*da14cebeSEric Cheng {  "port",	 "PORT",	7,
234*da14cebeSEric Cheng     offsetof(flow_fields_buf_t, flow_port),	CMD_TYPE_ANY},
235*da14cebeSEric Cheng {  "dsfield",	"DSFLD",	9,
236*da14cebeSEric Cheng     offsetof(flow_fields_buf_t, flow_dsfield),	CMD_TYPE_ANY}}
237*da14cebeSEric Cheng ;
238*da14cebeSEric Cheng 
239*da14cebeSEric Cheng #define	FLOW_MAX_FIELDS		(sizeof (flow_fields) / sizeof (print_field_t))
240*da14cebeSEric Cheng 
241*da14cebeSEric Cheng /*
242*da14cebeSEric Cheng  * structures for 'flowadm show-flowprop'
243*da14cebeSEric Cheng  */
244*da14cebeSEric Cheng typedef enum {
245*da14cebeSEric Cheng 	FLOWPROP_FLOW,
246*da14cebeSEric Cheng 	FLOWPROP_PROPERTY,
247*da14cebeSEric Cheng 	FLOWPROP_VALUE,
248*da14cebeSEric Cheng 	FLOWPROP_DEFAULT,
249*da14cebeSEric Cheng 	FLOWPROP_POSSIBLE
250*da14cebeSEric Cheng } flowprop_field_index_t;
251*da14cebeSEric Cheng 
252*da14cebeSEric Cheng static print_field_t flowprop_fields[] = {
253*da14cebeSEric Cheng /* name,	header,		fieldwidth,	index,		cmdtype */
254*da14cebeSEric Cheng { "flow",	"FLOW",		12,	FLOWPROP_FLOW,		CMD_TYPE_ANY},
255*da14cebeSEric Cheng { "property",	"PROPERTY",	15,	FLOWPROP_PROPERTY,	CMD_TYPE_ANY},
256*da14cebeSEric Cheng { "value",	"VALUE",	14,	FLOWPROP_VALUE,		CMD_TYPE_ANY},
257*da14cebeSEric Cheng { "default",	"DEFAULT",	14,	FLOWPROP_DEFAULT,	CMD_TYPE_ANY},
258*da14cebeSEric Cheng { "possible",	"POSSIBLE",	20,	FLOWPROP_POSSIBLE,	CMD_TYPE_ANY}}
259*da14cebeSEric Cheng ;
260*da14cebeSEric Cheng #define	FLOWPROP_MAX_FIELDS					\
261*da14cebeSEric Cheng 	(sizeof (flowprop_fields) / sizeof (print_field_t))
262*da14cebeSEric Cheng 
263*da14cebeSEric Cheng #define	MAX_PROP_LINE		512
264*da14cebeSEric Cheng 
265*da14cebeSEric Cheng typedef struct show_flowprop_state {
266*da14cebeSEric Cheng 	const char		*fs_flow;
267*da14cebeSEric Cheng 	datalink_id_t		fs_linkid;
268*da14cebeSEric Cheng 	char			*fs_line;
269*da14cebeSEric Cheng 	char			**fs_propvals;
270*da14cebeSEric Cheng 	dladm_arg_list_t	*fs_proplist;
271*da14cebeSEric Cheng 	boolean_t		fs_parseable;
272*da14cebeSEric Cheng 	boolean_t		fs_persist;
273*da14cebeSEric Cheng 	boolean_t		fs_header;
274*da14cebeSEric Cheng 	dladm_status_t		fs_status;
275*da14cebeSEric Cheng 	dladm_status_t		fs_retstatus;
276*da14cebeSEric Cheng 	print_state_t		fs_print;
277*da14cebeSEric Cheng } show_flowprop_state_t;
278*da14cebeSEric Cheng 
279*da14cebeSEric Cheng typedef struct set_flowprop_state {
280*da14cebeSEric Cheng 	const char	*fs_name;
281*da14cebeSEric Cheng 	boolean_t	fs_reset;
282*da14cebeSEric Cheng 	boolean_t	fs_temp;
283*da14cebeSEric Cheng 	dladm_status_t	fs_status;
284*da14cebeSEric Cheng } set_flowprop_state_t;
285*da14cebeSEric Cheng 
286*da14cebeSEric Cheng typedef struct flowprop_args_s {
287*da14cebeSEric Cheng 	show_flowprop_state_t	*fs_state;
288*da14cebeSEric Cheng 	char			*fs_propname;
289*da14cebeSEric Cheng 	char			*fs_flowname;
290*da14cebeSEric Cheng } flowprop_args_t;
291*da14cebeSEric Cheng 
292*da14cebeSEric Cheng /*
293*da14cebeSEric Cheng  * structures for 'flow show-usage'
294*da14cebeSEric Cheng  */
295*da14cebeSEric Cheng 
296*da14cebeSEric Cheng typedef struct  usage_fields_buf_s {
297*da14cebeSEric Cheng 	char	usage_flow[12];
298*da14cebeSEric Cheng 	char	usage_duration[10];
299*da14cebeSEric Cheng 	char	usage_ipackets[9];
300*da14cebeSEric Cheng 	char	usage_rbytes[10];
301*da14cebeSEric Cheng 	char	usage_opackets[9];
302*da14cebeSEric Cheng 	char	usage_obytes[10];
303*da14cebeSEric Cheng 	char	usage_bandwidth[14];
304*da14cebeSEric Cheng } usage_fields_buf_t;
305*da14cebeSEric Cheng 
306*da14cebeSEric Cheng static print_field_t usage_fields[] = {
307*da14cebeSEric Cheng /* name,	header,		field width,	offset,	cmdtype		*/
308*da14cebeSEric Cheng { "flow",	"FLOW",			12,
309*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_flow),		CMD_TYPE_ANY},
310*da14cebeSEric Cheng { "duration",	"DURATION",		10,
311*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_duration),	CMD_TYPE_ANY},
312*da14cebeSEric Cheng { "ipackets",	"IPACKETS",		9,
313*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_ipackets),	CMD_TYPE_ANY},
314*da14cebeSEric Cheng { "rbytes",	"RBYTES",		10,
315*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_rbytes),		CMD_TYPE_ANY},
316*da14cebeSEric Cheng { "opackets",	"OPACKETS",		9,
317*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_opackets),	CMD_TYPE_ANY},
318*da14cebeSEric Cheng { "obytes",	"OBYTES",		10,
319*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_obytes),		CMD_TYPE_ANY},
320*da14cebeSEric Cheng { "bandwidth",	"BANDWIDTH",		14,
321*da14cebeSEric Cheng     offsetof(usage_fields_buf_t, usage_bandwidth),	CMD_TYPE_ANY}}
322*da14cebeSEric Cheng ;
323*da14cebeSEric Cheng 
324*da14cebeSEric Cheng #define	USAGE_MAX_FIELDS	(sizeof (usage_fields) / sizeof (print_field_t))
325*da14cebeSEric Cheng 
326*da14cebeSEric Cheng /*
327*da14cebeSEric Cheng  * structures for 'dladm show-usage link'
328*da14cebeSEric Cheng  */
329*da14cebeSEric Cheng 
330*da14cebeSEric Cheng typedef struct  usage_l_fields_buf_s {
331*da14cebeSEric Cheng 	char	usage_l_flow[12];
332*da14cebeSEric Cheng 	char	usage_l_stime[13];
333*da14cebeSEric Cheng 	char	usage_l_etime[13];
334*da14cebeSEric Cheng 	char	usage_l_rbytes[8];
335*da14cebeSEric Cheng 	char	usage_l_obytes[8];
336*da14cebeSEric Cheng 	char	usage_l_bandwidth[14];
337*da14cebeSEric Cheng } usage_l_fields_buf_t;
338*da14cebeSEric Cheng 
339*da14cebeSEric Cheng static print_field_t usage_l_fields[] = {
340*da14cebeSEric Cheng /* name,	header,		field width,	offset,	cmdtype		*/
341*da14cebeSEric Cheng { "flow",	"FLOW",		12,
342*da14cebeSEric Cheng     offsetof(usage_l_fields_buf_t, usage_l_flow),	CMD_TYPE_ANY},
343*da14cebeSEric Cheng { "start",	"START",	13,
344*da14cebeSEric Cheng     offsetof(usage_l_fields_buf_t, usage_l_stime),	CMD_TYPE_ANY},
345*da14cebeSEric Cheng { "end",	"END",		13,
346*da14cebeSEric Cheng     offsetof(usage_l_fields_buf_t, usage_l_etime),	CMD_TYPE_ANY},
347*da14cebeSEric Cheng { "rbytes",	"RBYTES",	8,
348*da14cebeSEric Cheng     offsetof(usage_l_fields_buf_t, usage_l_rbytes),	CMD_TYPE_ANY},
349*da14cebeSEric Cheng { "obytes",	"OBYTES",	8,
350*da14cebeSEric Cheng     offsetof(usage_l_fields_buf_t, usage_l_obytes),	CMD_TYPE_ANY},
351*da14cebeSEric Cheng { "bandwidth",	"BANDWIDTH",	14,
352*da14cebeSEric Cheng     offsetof(usage_l_fields_buf_t, usage_l_bandwidth),	CMD_TYPE_ANY}}
353*da14cebeSEric Cheng ;
354*da14cebeSEric Cheng 
355*da14cebeSEric Cheng #define	USAGE_L_MAX_FIELDS \
356*da14cebeSEric Cheng 	(sizeof (usage_l_fields) /sizeof (print_field_t))
357*da14cebeSEric Cheng 
358*da14cebeSEric Cheng #define	PRI_HI		100
359*da14cebeSEric Cheng #define	PRI_LO 		10
360*da14cebeSEric Cheng #define	PRI_NORM	50
361*da14cebeSEric Cheng 
362*da14cebeSEric Cheng #define	FLOWADM_CONF	"/etc/dladm/flowadm.conf"
363*da14cebeSEric Cheng #define	BLANK_LINE(s)	((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
364*da14cebeSEric Cheng 
365*da14cebeSEric Cheng static char *progname;
366*da14cebeSEric Cheng 
367*da14cebeSEric Cheng boolean_t		t_arg = B_FALSE; /* changes are persistent */
368*da14cebeSEric Cheng char			*altroot = NULL;
369*da14cebeSEric Cheng 
370*da14cebeSEric Cheng static const char *attr_table[] =
371*da14cebeSEric Cheng 	{"local_ip", "remote_ip", "transport", "local_port", "dsfield"};
372*da14cebeSEric Cheng 
373*da14cebeSEric Cheng #define	NATTR	(sizeof (attr_table)/sizeof (char *))
374*da14cebeSEric Cheng 
375*da14cebeSEric Cheng static void
376*da14cebeSEric Cheng usage(void)
377*da14cebeSEric Cheng {
378*da14cebeSEric Cheng 	(void) fprintf(stderr, gettext("usage: flowadm <subcommand>"
379*da14cebeSEric Cheng 	    " <args>...\n"
380*da14cebeSEric Cheng 	    "\tadd-flow [-t] [-R <root-dir>] -l <link>\n"
381*da14cebeSEric Cheng 	    "\t\t-a attr=value[,...] [-p prop=value,...]\n"
382*da14cebeSEric Cheng 	    "\t\tflow-name\n"
383*da14cebeSEric Cheng 	    "\tremove-flow [-t] [-R <root-dir>] {-l <link> | flow-name}\n"
384*da14cebeSEric Cheng 	    "\tset-flowprop [-t] [-R <root-dir>] \n"
385*da14cebeSEric Cheng 	    "\t\t-p prop=value[,...] flowname\n"
386*da14cebeSEric Cheng 	    "\treset-flowprop [-t] [-R <root-dir>] \n"
387*da14cebeSEric Cheng 	    "\t\t[-p prop,...] flowname\n"
388*da14cebeSEric Cheng 	    "\tshow-flowprop [-cP] [-l <link>] [-p prop,...] [flow-name]\n"
389*da14cebeSEric Cheng 	    "\tshow-flow [-p] [-s [-i <interval>]] [-l <link>] [flow-name]\n"
390*da14cebeSEric Cheng 	    "\tshow-usage [-d|-p -F <format>] [-s <DD/MM/YYYY,HH:MM:SS>]\n"
391*da14cebeSEric Cheng 	    "\t\t[-e <DD/MM/YYYY,HH:MM:SS>]] -f <logfile> [<name>]\n"));
392*da14cebeSEric Cheng 	exit(1);
393*da14cebeSEric Cheng }
394*da14cebeSEric Cheng 
395*da14cebeSEric Cheng int
396*da14cebeSEric Cheng main(int argc, char *argv[])
397*da14cebeSEric Cheng {
398*da14cebeSEric Cheng 	int	i, arglen, cmdlen;
399*da14cebeSEric Cheng 	cmd_t	*cmdp;
400*da14cebeSEric Cheng 
401*da14cebeSEric Cheng 	(void) setlocale(LC_ALL, "");
402*da14cebeSEric Cheng #if !defined(TEXT_DOMAIN)
403*da14cebeSEric Cheng #define	TEXT_DOMAIN "SYS_TEST"
404*da14cebeSEric Cheng #endif
405*da14cebeSEric Cheng 	(void) textdomain(TEXT_DOMAIN);
406*da14cebeSEric Cheng 
407*da14cebeSEric Cheng 	progname = argv[0];
408*da14cebeSEric Cheng 
409*da14cebeSEric Cheng 	if (argc < 2)
410*da14cebeSEric Cheng 		usage();
411*da14cebeSEric Cheng 
412*da14cebeSEric Cheng 	for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
413*da14cebeSEric Cheng 		cmdp = &cmds[i];
414*da14cebeSEric Cheng 		arglen = strlen(argv[1]);
415*da14cebeSEric Cheng 		cmdlen = strlen(cmdp->c_name);
416*da14cebeSEric Cheng 		if ((arglen == cmdlen) && (strncmp(argv[1], cmdp->c_name,
417*da14cebeSEric Cheng 		    cmdlen) == 0)) {
418*da14cebeSEric Cheng 			cmdp->c_fn(argc - 1, &argv[1]);
419*da14cebeSEric Cheng 			exit(0);
420*da14cebeSEric Cheng 		}
421*da14cebeSEric Cheng 	}
422*da14cebeSEric Cheng 
423*da14cebeSEric Cheng 	(void) fprintf(stderr, gettext("%s: unknown subcommand '%s'\n"),
424*da14cebeSEric Cheng 	    progname, argv[1]);
425*da14cebeSEric Cheng 	usage();
426*da14cebeSEric Cheng 
427*da14cebeSEric Cheng 	return (0);
428*da14cebeSEric Cheng }
429*da14cebeSEric Cheng 
430*da14cebeSEric Cheng static const char *
431*da14cebeSEric Cheng match_attr(char *attr)
432*da14cebeSEric Cheng {
433*da14cebeSEric Cheng 	int i;
434*da14cebeSEric Cheng 
435*da14cebeSEric Cheng 	for (i = 0; i < NATTR; i++) {
436*da14cebeSEric Cheng 		if (strlen(attr) == strlen(attr_table[i]) &&
437*da14cebeSEric Cheng 		    strncmp(attr, attr_table[i], strlen(attr_table[i])) == 0) {
438*da14cebeSEric Cheng 			return (attr);
439*da14cebeSEric Cheng 		}
440*da14cebeSEric Cheng 	}
441*da14cebeSEric Cheng 	return (NULL);
442*da14cebeSEric Cheng }
443*da14cebeSEric Cheng 
444*da14cebeSEric Cheng /* ARGSUSED */
445*da14cebeSEric Cheng static void
446*da14cebeSEric Cheng do_init_flow(int argc, char *argv[])
447*da14cebeSEric Cheng {
448*da14cebeSEric Cheng 	dladm_status_t status;
449*da14cebeSEric Cheng 
450*da14cebeSEric Cheng 	status = dladm_flow_init();
451*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
452*da14cebeSEric Cheng 		die_dlerr(status, "flows initialization failed");
453*da14cebeSEric Cheng }
454*da14cebeSEric Cheng 
455*da14cebeSEric Cheng /* ARGSUSED */
456*da14cebeSEric Cheng static int
457*da14cebeSEric Cheng show_usage_date(dladm_usage_t *usage, void *arg)
458*da14cebeSEric Cheng {
459*da14cebeSEric Cheng 
460*da14cebeSEric Cheng 	time_t	stime;
461*da14cebeSEric Cheng 	char	timebuf[20];
462*da14cebeSEric Cheng 
463*da14cebeSEric Cheng 	stime = usage->du_stime;
464*da14cebeSEric Cheng 	(void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
465*da14cebeSEric Cheng 	    localtime(&stime));
466*da14cebeSEric Cheng 	(void) printf("%s\n", timebuf);
467*da14cebeSEric Cheng 
468*da14cebeSEric Cheng 	return (DLADM_STATUS_OK);
469*da14cebeSEric Cheng }
470*da14cebeSEric Cheng 
471*da14cebeSEric Cheng static int
472*da14cebeSEric Cheng show_usage_time(dladm_usage_t *usage, void *arg)
473*da14cebeSEric Cheng {
474*da14cebeSEric Cheng 	show_usage_state_t	*state = (show_usage_state_t *)arg;
475*da14cebeSEric Cheng 	char			buf[DLADM_STRSIZE];
476*da14cebeSEric Cheng 	usage_l_fields_buf_t 	ubuf;
477*da14cebeSEric Cheng 	time_t			time;
478*da14cebeSEric Cheng 	double			bw;
479*da14cebeSEric Cheng 
480*da14cebeSEric Cheng 	if (state->us_plot) {
481*da14cebeSEric Cheng 		if (!state->us_printheader) {
482*da14cebeSEric Cheng 			if (state->us_first) {
483*da14cebeSEric Cheng 				(void) printf("# Time");
484*da14cebeSEric Cheng 				state->us_first = B_FALSE;
485*da14cebeSEric Cheng 			}
486*da14cebeSEric Cheng 			(void) printf(" %s", usage->du_name);
487*da14cebeSEric Cheng 			if (usage->du_last) {
488*da14cebeSEric Cheng 				(void) printf("\n");
489*da14cebeSEric Cheng 				state->us_first = B_TRUE;
490*da14cebeSEric Cheng 				state->us_printheader = B_TRUE;
491*da14cebeSEric Cheng 			}
492*da14cebeSEric Cheng 		} else {
493*da14cebeSEric Cheng 			if (state->us_first) {
494*da14cebeSEric Cheng 				time = usage->du_etime;
495*da14cebeSEric Cheng 				(void) strftime(buf, sizeof (buf), "%T",
496*da14cebeSEric Cheng 				    localtime(&time));
497*da14cebeSEric Cheng 				state->us_first = B_FALSE;
498*da14cebeSEric Cheng 				(void) printf("%s", buf);
499*da14cebeSEric Cheng 			}
500*da14cebeSEric Cheng 			bw = (double)usage->du_bandwidth/1000;
501*da14cebeSEric Cheng 			(void) printf(" %.2f", bw);
502*da14cebeSEric Cheng 			if (usage->du_last) {
503*da14cebeSEric Cheng 				(void) printf("\n");
504*da14cebeSEric Cheng 				state->us_first = B_TRUE;
505*da14cebeSEric Cheng 			}
506*da14cebeSEric Cheng 		}
507*da14cebeSEric Cheng 		return (DLADM_STATUS_OK);
508*da14cebeSEric Cheng 	}
509*da14cebeSEric Cheng 
510*da14cebeSEric Cheng 	bzero(&ubuf, sizeof (ubuf));
511*da14cebeSEric Cheng 
512*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s",
513*da14cebeSEric Cheng 	    usage->du_name);
514*da14cebeSEric Cheng 	time = usage->du_stime;
515*da14cebeSEric Cheng 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
516*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s",
517*da14cebeSEric Cheng 	    buf);
518*da14cebeSEric Cheng 	time = usage->du_etime;
519*da14cebeSEric Cheng 	(void) strftime(buf, sizeof (buf), "%T", localtime(&time));
520*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s",
521*da14cebeSEric Cheng 	    buf);
522*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes),
523*da14cebeSEric Cheng 	    "%llu", usage->du_rbytes);
524*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes),
525*da14cebeSEric Cheng 	    "%llu", usage->du_obytes);
526*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth),
527*da14cebeSEric Cheng 	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
528*da14cebeSEric Cheng 
529*da14cebeSEric Cheng 	if (!state->us_parseable && !state->us_printheader) {
530*da14cebeSEric Cheng 		print_header(&state->us_print);
531*da14cebeSEric Cheng 		state->us_printheader = B_TRUE;
532*da14cebeSEric Cheng 	}
533*da14cebeSEric Cheng 
534*da14cebeSEric Cheng 	flowadm_print_output(&state->us_print, state->us_parseable,
535*da14cebeSEric Cheng 	    flowadm_print_field, (void *)&ubuf);
536*da14cebeSEric Cheng 
537*da14cebeSEric Cheng 	return (DLADM_STATUS_OK);
538*da14cebeSEric Cheng }
539*da14cebeSEric Cheng 
540*da14cebeSEric Cheng static int
541*da14cebeSEric Cheng show_usage_res(dladm_usage_t *usage, void *arg)
542*da14cebeSEric Cheng {
543*da14cebeSEric Cheng 	show_usage_state_t	*state = (show_usage_state_t *)arg;
544*da14cebeSEric Cheng 	char			buf[DLADM_STRSIZE];
545*da14cebeSEric Cheng 	usage_fields_buf_t	ubuf;
546*da14cebeSEric Cheng 
547*da14cebeSEric Cheng 	bzero(&ubuf, sizeof (ubuf));
548*da14cebeSEric Cheng 
549*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s",
550*da14cebeSEric Cheng 	    usage->du_name);
551*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration),
552*da14cebeSEric Cheng 	    "%llu", usage->du_duration);
553*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets),
554*da14cebeSEric Cheng 	    "%llu", usage->du_ipackets);
555*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes),
556*da14cebeSEric Cheng 	    "%llu", usage->du_rbytes);
557*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets),
558*da14cebeSEric Cheng 	    "%llu", usage->du_opackets);
559*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes),
560*da14cebeSEric Cheng 	    "%llu", usage->du_obytes);
561*da14cebeSEric Cheng 	(void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth),
562*da14cebeSEric Cheng 	    "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
563*da14cebeSEric Cheng 
564*da14cebeSEric Cheng 	if (!state->us_parseable && !state->us_printheader) {
565*da14cebeSEric Cheng 		print_header(&state->us_print);
566*da14cebeSEric Cheng 		state->us_printheader = B_TRUE;
567*da14cebeSEric Cheng 	}
568*da14cebeSEric Cheng 
569*da14cebeSEric Cheng 	flowadm_print_output(&state->us_print, state->us_parseable,
570*da14cebeSEric Cheng 	    flowadm_print_field, (void *)&ubuf);
571*da14cebeSEric Cheng 
572*da14cebeSEric Cheng 	return (DLADM_STATUS_OK);
573*da14cebeSEric Cheng }
574*da14cebeSEric Cheng 
575*da14cebeSEric Cheng static boolean_t
576*da14cebeSEric Cheng valid_formatspec(char *formatspec_str)
577*da14cebeSEric Cheng {
578*da14cebeSEric Cheng 	if (strcmp(formatspec_str, "gnuplot") == 0)
579*da14cebeSEric Cheng 		return (B_TRUE);
580*da14cebeSEric Cheng 	return (B_FALSE);
581*da14cebeSEric Cheng }
582*da14cebeSEric Cheng 
583*da14cebeSEric Cheng /* ARGSUSED */
584*da14cebeSEric Cheng static void
585*da14cebeSEric Cheng do_show_usage(int argc, char *argv[])
586*da14cebeSEric Cheng {
587*da14cebeSEric Cheng 	char			*file = NULL;
588*da14cebeSEric Cheng 	int			opt;
589*da14cebeSEric Cheng 	dladm_status_t		status;
590*da14cebeSEric Cheng 	boolean_t		d_arg = B_FALSE;
591*da14cebeSEric Cheng 	boolean_t		p_arg = B_FALSE;
592*da14cebeSEric Cheng 	char			*stime = NULL;
593*da14cebeSEric Cheng 	char			*etime = NULL;
594*da14cebeSEric Cheng 	char			*resource = NULL;
595*da14cebeSEric Cheng 	show_usage_state_t	state;
596*da14cebeSEric Cheng 	boolean_t		o_arg = B_FALSE;
597*da14cebeSEric Cheng 	boolean_t		F_arg = B_FALSE;
598*da14cebeSEric Cheng 	char			*fields_str = NULL;
599*da14cebeSEric Cheng 	char			*formatspec_str = NULL;
600*da14cebeSEric Cheng 	print_field_t		**fields;
601*da14cebeSEric Cheng 	uint_t			nfields;
602*da14cebeSEric Cheng 	char			*all_fields =
603*da14cebeSEric Cheng 	    "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
604*da14cebeSEric Cheng 	char			*all_l_fields =
605*da14cebeSEric Cheng 	    "flow,start,end,rbytes,obytes,bandwidth";
606*da14cebeSEric Cheng 
607*da14cebeSEric Cheng 	bzero(&state, sizeof (show_usage_state_t));
608*da14cebeSEric Cheng 	state.us_parseable = B_FALSE;
609*da14cebeSEric Cheng 	state.us_printheader = B_FALSE;
610*da14cebeSEric Cheng 	state.us_plot = B_FALSE;
611*da14cebeSEric Cheng 	state.us_first = B_TRUE;
612*da14cebeSEric Cheng 
613*da14cebeSEric Cheng 	while ((opt = getopt(argc, argv, "dps:e:o:f:F:")) != -1) {
614*da14cebeSEric Cheng 		switch (opt) {
615*da14cebeSEric Cheng 		case 'd':
616*da14cebeSEric Cheng 			d_arg = B_TRUE;
617*da14cebeSEric Cheng 			break;
618*da14cebeSEric Cheng 		case 'p':
619*da14cebeSEric Cheng 			state.us_plot = p_arg = B_TRUE;
620*da14cebeSEric Cheng 			break;
621*da14cebeSEric Cheng 		case 'f':
622*da14cebeSEric Cheng 			file = optarg;
623*da14cebeSEric Cheng 			break;
624*da14cebeSEric Cheng 		case 's':
625*da14cebeSEric Cheng 			stime = optarg;
626*da14cebeSEric Cheng 			break;
627*da14cebeSEric Cheng 		case 'e':
628*da14cebeSEric Cheng 			etime = optarg;
629*da14cebeSEric Cheng 			break;
630*da14cebeSEric Cheng 		case 'o':
631*da14cebeSEric Cheng 			o_arg = B_TRUE;
632*da14cebeSEric Cheng 			fields_str = optarg;
633*da14cebeSEric Cheng 			break;
634*da14cebeSEric Cheng 		case 'F':
635*da14cebeSEric Cheng 			F_arg = B_TRUE;
636*da14cebeSEric Cheng 			formatspec_str = optarg;
637*da14cebeSEric Cheng 			break;
638*da14cebeSEric Cheng 		default:
639*da14cebeSEric Cheng 			die_opterr(optopt, opt);
640*da14cebeSEric Cheng 		}
641*da14cebeSEric Cheng 	}
642*da14cebeSEric Cheng 
643*da14cebeSEric Cheng 	if (file == NULL)
644*da14cebeSEric Cheng 		die("show-usage requires a file");
645*da14cebeSEric Cheng 
646*da14cebeSEric Cheng 	if (optind == (argc-1)) {
647*da14cebeSEric Cheng 		resource = argv[optind];
648*da14cebeSEric Cheng 	}
649*da14cebeSEric Cheng 
650*da14cebeSEric Cheng 	if (resource == NULL && stime == NULL && etime == NULL) {
651*da14cebeSEric Cheng 		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
652*da14cebeSEric Cheng 			fields_str = all_fields;
653*da14cebeSEric Cheng 		fields = parse_output_fields(fields_str, usage_fields,
654*da14cebeSEric Cheng 		    USAGE_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
655*da14cebeSEric Cheng 	} else {
656*da14cebeSEric Cheng 		if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
657*da14cebeSEric Cheng 			fields_str = all_l_fields;
658*da14cebeSEric Cheng 		fields = parse_output_fields(fields_str, usage_l_fields,
659*da14cebeSEric Cheng 		    USAGE_L_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
660*da14cebeSEric Cheng 	}
661*da14cebeSEric Cheng 
662*da14cebeSEric Cheng 	if (fields == NULL) {
663*da14cebeSEric Cheng 		die("invalid fields(s) specified");
664*da14cebeSEric Cheng 		return;
665*da14cebeSEric Cheng 	}
666*da14cebeSEric Cheng 	state.us_print.ps_fields = fields;
667*da14cebeSEric Cheng 	state.us_print.ps_nfields = nfields;
668*da14cebeSEric Cheng 
669*da14cebeSEric Cheng 	if (p_arg && d_arg)
670*da14cebeSEric Cheng 		die("plot and date options are incompatible");
671*da14cebeSEric Cheng 
672*da14cebeSEric Cheng 	if (p_arg && !F_arg)
673*da14cebeSEric Cheng 		die("specify format speicifier: -F <format>");
674*da14cebeSEric Cheng 
675*da14cebeSEric Cheng 	if (F_arg && valid_formatspec(formatspec_str) == B_FALSE)
676*da14cebeSEric Cheng 		die("Format specifier %s not supported", formatspec_str);
677*da14cebeSEric Cheng 
678*da14cebeSEric Cheng 	if (d_arg) {
679*da14cebeSEric Cheng 		/* Print log dates */
680*da14cebeSEric Cheng 		status = dladm_usage_dates(show_usage_date,
681*da14cebeSEric Cheng 		    DLADM_LOGTYPE_FLOW, file, resource, &state);
682*da14cebeSEric Cheng 	} else if (resource == NULL && stime == NULL && etime == NULL &&
683*da14cebeSEric Cheng 	    !p_arg) {
684*da14cebeSEric Cheng 		/* Print summary */
685*da14cebeSEric Cheng 		status = dladm_usage_summary(show_usage_res,
686*da14cebeSEric Cheng 		    DLADM_LOGTYPE_FLOW, file, &state);
687*da14cebeSEric Cheng 	} else if (resource != NULL) {
688*da14cebeSEric Cheng 		/* Print log entries for named resource */
689*da14cebeSEric Cheng 		status = dladm_walk_usage_res(show_usage_time,
690*da14cebeSEric Cheng 		    DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
691*da14cebeSEric Cheng 	} else {
692*da14cebeSEric Cheng 		/* Print time and information for each link */
693*da14cebeSEric Cheng 		status = dladm_walk_usage_time(show_usage_time,
694*da14cebeSEric Cheng 		    DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
695*da14cebeSEric Cheng 	}
696*da14cebeSEric Cheng 
697*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
698*da14cebeSEric Cheng 		die_dlerr(status, "show-usage");
699*da14cebeSEric Cheng }
700*da14cebeSEric Cheng 
701*da14cebeSEric Cheng static void
702*da14cebeSEric Cheng do_add_flow(int argc, char *argv[])
703*da14cebeSEric Cheng {
704*da14cebeSEric Cheng 	char			devname[MAXNAMELEN];
705*da14cebeSEric Cheng 	char			*name = NULL;
706*da14cebeSEric Cheng 	uint_t			index;
707*da14cebeSEric Cheng 	datalink_id_t		linkid;
708*da14cebeSEric Cheng 
709*da14cebeSEric Cheng 	char			option;
710*da14cebeSEric Cheng 	boolean_t		l_arg = B_FALSE;
711*da14cebeSEric Cheng 	dladm_arg_list_t	*proplist = NULL;
712*da14cebeSEric Cheng 	dladm_arg_list_t	*attrlist = NULL;
713*da14cebeSEric Cheng 	dladm_status_t		status;
714*da14cebeSEric Cheng 
715*da14cebeSEric Cheng 	while ((option = getopt_long(argc, argv, "tR:l:a:p:",
716*da14cebeSEric Cheng 	    prop_longopts, NULL)) != -1) {
717*da14cebeSEric Cheng 		switch (option) {
718*da14cebeSEric Cheng 		case 't':
719*da14cebeSEric Cheng 			t_arg = B_TRUE;
720*da14cebeSEric Cheng 			break;
721*da14cebeSEric Cheng 		case 'R':
722*da14cebeSEric Cheng 			altroot = optarg;
723*da14cebeSEric Cheng 			break;
724*da14cebeSEric Cheng 		case 'l':
725*da14cebeSEric Cheng 			if (strlcpy(devname, optarg,
726*da14cebeSEric Cheng 			    MAXNAMELEN) >= MAXNAMELEN) {
727*da14cebeSEric Cheng 				die("link name too long");
728*da14cebeSEric Cheng 			}
729*da14cebeSEric Cheng 			if (dladm_name2info(devname, &linkid, NULL,
730*da14cebeSEric Cheng 			    NULL, NULL) != DLADM_STATUS_OK)
731*da14cebeSEric Cheng 				die("invalid link '%s'", devname);
732*da14cebeSEric Cheng 			l_arg = B_TRUE;
733*da14cebeSEric Cheng 			break;
734*da14cebeSEric Cheng 		case 'a':
735*da14cebeSEric Cheng 			if (dladm_parse_flow_attrs(optarg, &attrlist, B_FALSE)
736*da14cebeSEric Cheng 			    != DLADM_STATUS_OK)
737*da14cebeSEric Cheng 				die("invalid flow attribute specified");
738*da14cebeSEric Cheng 			break;
739*da14cebeSEric Cheng 		case 'p':
740*da14cebeSEric Cheng 			if (dladm_parse_flow_props(optarg, &proplist, B_FALSE)
741*da14cebeSEric Cheng 			    != DLADM_STATUS_OK)
742*da14cebeSEric Cheng 				die("invalid flow property specified");
743*da14cebeSEric Cheng 			break;
744*da14cebeSEric Cheng 		default:
745*da14cebeSEric Cheng 			die_opterr(optopt, option);
746*da14cebeSEric Cheng 		}
747*da14cebeSEric Cheng 	}
748*da14cebeSEric Cheng 	if (!l_arg) {
749*da14cebeSEric Cheng 		die("link is required");
750*da14cebeSEric Cheng 	}
751*da14cebeSEric Cheng 
752*da14cebeSEric Cheng 	opterr = 0;
753*da14cebeSEric Cheng 	index = optind;
754*da14cebeSEric Cheng 
755*da14cebeSEric Cheng 	if ((index != (argc - 1)) || match_attr(argv[index]) != NULL) {
756*da14cebeSEric Cheng 		die("flow name is required");
757*da14cebeSEric Cheng 	} else {
758*da14cebeSEric Cheng 		/* get flow name; required last argument */
759*da14cebeSEric Cheng 		if (strlen(argv[index]) >= MAXFLOWNAME)
760*da14cebeSEric Cheng 			die("flow name too long");
761*da14cebeSEric Cheng 		name = argv[index];
762*da14cebeSEric Cheng 	}
763*da14cebeSEric Cheng 
764*da14cebeSEric Cheng 	status = dladm_flow_add(linkid, attrlist, proplist, name,
765*da14cebeSEric Cheng 	    t_arg, altroot);
766*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
767*da14cebeSEric Cheng 		die_dlerr(status, "add flow failed");
768*da14cebeSEric Cheng 
769*da14cebeSEric Cheng 	dladm_free_attrs(attrlist);
770*da14cebeSEric Cheng 	dladm_free_props(proplist);
771*da14cebeSEric Cheng }
772*da14cebeSEric Cheng 
773*da14cebeSEric Cheng static void
774*da14cebeSEric Cheng do_remove_flow(int argc, char *argv[])
775*da14cebeSEric Cheng {
776*da14cebeSEric Cheng 	char			option;
777*da14cebeSEric Cheng 	char			*flowname = NULL;
778*da14cebeSEric Cheng 	char			linkname[MAXNAMELEN];
779*da14cebeSEric Cheng 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
780*da14cebeSEric Cheng 	boolean_t		l_arg = B_FALSE;
781*da14cebeSEric Cheng 	remove_flow_state_t	state;
782*da14cebeSEric Cheng 	dladm_status_t		status;
783*da14cebeSEric Cheng 
784*da14cebeSEric Cheng 	bzero(&state, sizeof (state));
785*da14cebeSEric Cheng 
786*da14cebeSEric Cheng 	opterr = 0;
787*da14cebeSEric Cheng 	while ((option = getopt_long(argc, argv, ":tR:l:",
788*da14cebeSEric Cheng 	    longopts, NULL)) != -1) {
789*da14cebeSEric Cheng 		switch (option) {
790*da14cebeSEric Cheng 		case 't':
791*da14cebeSEric Cheng 			t_arg = B_TRUE;
792*da14cebeSEric Cheng 			break;
793*da14cebeSEric Cheng 		case 'R':
794*da14cebeSEric Cheng 			altroot = optarg;
795*da14cebeSEric Cheng 			break;
796*da14cebeSEric Cheng 		case 'l':
797*da14cebeSEric Cheng 			if (strlcpy(linkname, optarg,
798*da14cebeSEric Cheng 			    MAXLINKNAMELEN) >= MAXLINKNAMELEN) {
799*da14cebeSEric Cheng 				die("link name too long");
800*da14cebeSEric Cheng 			}
801*da14cebeSEric Cheng 			if (dladm_name2info(linkname, &linkid, NULL,
802*da14cebeSEric Cheng 			    NULL, NULL) != DLADM_STATUS_OK) {
803*da14cebeSEric Cheng 				die("invalid link '%s'", linkname);
804*da14cebeSEric Cheng 			}
805*da14cebeSEric Cheng 			l_arg = B_TRUE;
806*da14cebeSEric Cheng 			break;
807*da14cebeSEric Cheng 		default:
808*da14cebeSEric Cheng 			die_opterr(optopt, option);
809*da14cebeSEric Cheng 			break;
810*da14cebeSEric Cheng 		}
811*da14cebeSEric Cheng 	}
812*da14cebeSEric Cheng 
813*da14cebeSEric Cheng 	/* when link not specified get flow name */
814*da14cebeSEric Cheng 	if (!l_arg) {
815*da14cebeSEric Cheng 		if (optind != (argc-1)) {
816*da14cebeSEric Cheng 			usage();
817*da14cebeSEric Cheng 		} else {
818*da14cebeSEric Cheng 			if (strlen(argv[optind]) >= MAXFLOWNAME)
819*da14cebeSEric Cheng 				die("flow name too long");
820*da14cebeSEric Cheng 			flowname = argv[optind];
821*da14cebeSEric Cheng 		}
822*da14cebeSEric Cheng 		status = dladm_flow_remove(flowname, t_arg, altroot);
823*da14cebeSEric Cheng 	} else {
824*da14cebeSEric Cheng 		/* if link is specified then flow name should not be there */
825*da14cebeSEric Cheng 		if (optind == argc-1)
826*da14cebeSEric Cheng 			usage();
827*da14cebeSEric Cheng 		/* walk the link to find flows and remove them */
828*da14cebeSEric Cheng 		state.fs_tempop = t_arg;
829*da14cebeSEric Cheng 		state.fs_altroot = altroot;
830*da14cebeSEric Cheng 		state.fs_status = DLADM_STATUS_OK;
831*da14cebeSEric Cheng 		status = dladm_walk_flow(remove_flow, linkid, &state, B_FALSE);
832*da14cebeSEric Cheng 		/*
833*da14cebeSEric Cheng 		 * check if dladm_walk_flow terminated early and see if the
834*da14cebeSEric Cheng 		 * walker function as any status for us
835*da14cebeSEric Cheng 		 */
836*da14cebeSEric Cheng 		if (status == DLADM_STATUS_OK)
837*da14cebeSEric Cheng 			status = state.fs_status;
838*da14cebeSEric Cheng 	}
839*da14cebeSEric Cheng 
840*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
841*da14cebeSEric Cheng 		die_dlerr(status, "remove flow failed");
842*da14cebeSEric Cheng }
843*da14cebeSEric Cheng 
844*da14cebeSEric Cheng /*
845*da14cebeSEric Cheng  * Walker function for removing a flow through dladm_walk_flow();
846*da14cebeSEric Cheng  */
847*da14cebeSEric Cheng static int
848*da14cebeSEric Cheng remove_flow(dladm_flow_attr_t *attr, void *arg)
849*da14cebeSEric Cheng {
850*da14cebeSEric Cheng 	remove_flow_state_t	*state = (remove_flow_state_t *)arg;
851*da14cebeSEric Cheng 
852*da14cebeSEric Cheng 	state->fs_status = dladm_flow_remove(attr->fa_flowname,
853*da14cebeSEric Cheng 	    state->fs_tempop, state->fs_altroot);
854*da14cebeSEric Cheng 
855*da14cebeSEric Cheng 	if (state->fs_status == DLADM_STATUS_OK)
856*da14cebeSEric Cheng 		return (DLADM_WALK_CONTINUE);
857*da14cebeSEric Cheng 	else
858*da14cebeSEric Cheng 		return (DLADM_WALK_TERMINATE);
859*da14cebeSEric Cheng }
860*da14cebeSEric Cheng 
861*da14cebeSEric Cheng static char *
862*da14cebeSEric Cheng flowadm_print_field(print_field_t *pf, void *arg)
863*da14cebeSEric Cheng {
864*da14cebeSEric Cheng 	char *value;
865*da14cebeSEric Cheng 
866*da14cebeSEric Cheng 	value = (char *)arg + pf->pf_offset;
867*da14cebeSEric Cheng 	return (value);
868*da14cebeSEric Cheng }
869*da14cebeSEric Cheng 
870*da14cebeSEric Cheng /*ARGSUSED*/
871*da14cebeSEric Cheng static dladm_status_t
872*da14cebeSEric Cheng print_flow(show_flow_state_t *state, dladm_flow_attr_t *attr,
873*da14cebeSEric Cheng     flow_fields_buf_t *fbuf)
874*da14cebeSEric Cheng {
875*da14cebeSEric Cheng 	char		link[MAXLINKNAMELEN];
876*da14cebeSEric Cheng 	dladm_status_t	status;
877*da14cebeSEric Cheng 
878*da14cebeSEric Cheng 	if ((status = dladm_datalink_id2info(attr->fa_linkid, NULL, NULL,
879*da14cebeSEric Cheng 	    NULL, link, sizeof (link))) != DLADM_STATUS_OK) {
880*da14cebeSEric Cheng 		return (status);
881*da14cebeSEric Cheng 	}
882*da14cebeSEric Cheng 
883*da14cebeSEric Cheng 	(void) snprintf(fbuf->flow_name, sizeof (fbuf->flow_name),
884*da14cebeSEric Cheng 	    "%s", attr->fa_flowname);
885*da14cebeSEric Cheng 	(void) snprintf(fbuf->flow_link, sizeof (fbuf->flow_link),
886*da14cebeSEric Cheng 	    "%s", link);
887*da14cebeSEric Cheng 
888*da14cebeSEric Cheng 	(void) dladm_flow_attr_ip2str(attr, fbuf->flow_ipaddr,
889*da14cebeSEric Cheng 	    sizeof (fbuf->flow_ipaddr));
890*da14cebeSEric Cheng 	(void) dladm_flow_attr_proto2str(attr, fbuf->flow_proto,
891*da14cebeSEric Cheng 	    sizeof (fbuf->flow_proto));
892*da14cebeSEric Cheng 	(void) dladm_flow_attr_port2str(attr, fbuf->flow_port,
893*da14cebeSEric Cheng 	    sizeof (fbuf->flow_port));
894*da14cebeSEric Cheng 	(void) dladm_flow_attr_dsfield2str(attr, fbuf->flow_dsfield,
895*da14cebeSEric Cheng 	    sizeof (fbuf->flow_dsfield));
896*da14cebeSEric Cheng 
897*da14cebeSEric Cheng 	return (DLADM_STATUS_OK);
898*da14cebeSEric Cheng }
899*da14cebeSEric Cheng 
900*da14cebeSEric Cheng /*
901*da14cebeSEric Cheng  * Walker function for showing flow attributes through dladm_walk_flow().
902*da14cebeSEric Cheng  */
903*da14cebeSEric Cheng static int
904*da14cebeSEric Cheng show_flow(dladm_flow_attr_t *attr, void *arg)
905*da14cebeSEric Cheng {
906*da14cebeSEric Cheng 	show_flow_state_t	*statep = arg;
907*da14cebeSEric Cheng 	dladm_status_t		status;
908*da14cebeSEric Cheng 	flow_fields_buf_t	fbuf;
909*da14cebeSEric Cheng 
910*da14cebeSEric Cheng 	/*
911*da14cebeSEric Cheng 	 * first get all the flow attributes into fbuf;
912*da14cebeSEric Cheng 	 */
913*da14cebeSEric Cheng 	bzero(&fbuf, sizeof (fbuf));
914*da14cebeSEric Cheng 	status = print_flow(statep, attr, &fbuf);
915*da14cebeSEric Cheng 
916*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
917*da14cebeSEric Cheng 		goto done;
918*da14cebeSEric Cheng 
919*da14cebeSEric Cheng 	if (!statep->fs_parseable && !statep->fs_printheader) {
920*da14cebeSEric Cheng 		print_header(&statep->fs_print);
921*da14cebeSEric Cheng 		statep->fs_printheader = B_TRUE;
922*da14cebeSEric Cheng 	}
923*da14cebeSEric Cheng 
924*da14cebeSEric Cheng 	flowadm_print_output(&statep->fs_print, statep->fs_parseable,
925*da14cebeSEric Cheng 	    flowadm_print_field, (void *)&fbuf);
926*da14cebeSEric Cheng 
927*da14cebeSEric Cheng done:
928*da14cebeSEric Cheng 	statep->fs_status = status;
929*da14cebeSEric Cheng 	return (DLADM_WALK_CONTINUE);
930*da14cebeSEric Cheng }
931*da14cebeSEric Cheng 
932*da14cebeSEric Cheng static void
933*da14cebeSEric Cheng show_one_flow(void *arg, const char *name)
934*da14cebeSEric Cheng {
935*da14cebeSEric Cheng 	dladm_flow_attr_t	attr;
936*da14cebeSEric Cheng 	dladm_status_t		status;
937*da14cebeSEric Cheng 
938*da14cebeSEric Cheng 	if (dladm_flow_info(name, &attr) != DLADM_STATUS_OK)
939*da14cebeSEric Cheng 		die("invalid flow: '%s'", name);
940*da14cebeSEric Cheng 	else
941*da14cebeSEric Cheng 		show_flow(&attr, arg);
942*da14cebeSEric Cheng }
943*da14cebeSEric Cheng 
944*da14cebeSEric Cheng /*
945*da14cebeSEric Cheng  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable to
946*da14cebeSEric Cheng  * dladm_walk_datalink_id(). Used for showing flow attributes for
947*da14cebeSEric Cheng  * all flows on all links.
948*da14cebeSEric Cheng  */
949*da14cebeSEric Cheng static int
950*da14cebeSEric Cheng show_flows_onelink(datalink_id_t linkid, void *arg)
951*da14cebeSEric Cheng {
952*da14cebeSEric Cheng 	show_flow_state_t *state = arg;
953*da14cebeSEric Cheng 
954*da14cebeSEric Cheng 	(void) dladm_walk_flow(show_flow, linkid, arg, state->fs_persist);
955*da14cebeSEric Cheng 
956*da14cebeSEric Cheng 	return (DLADM_WALK_CONTINUE);
957*da14cebeSEric Cheng }
958*da14cebeSEric Cheng 
959*da14cebeSEric Cheng static void
960*da14cebeSEric Cheng get_flow_stats(const char *flowname, pktsum_t *stats)
961*da14cebeSEric Cheng {
962*da14cebeSEric Cheng 	kstat_ctl_t	*kcp;
963*da14cebeSEric Cheng 	kstat_t		*ksp;
964*da14cebeSEric Cheng 
965*da14cebeSEric Cheng 	bzero(stats, sizeof (*stats));
966*da14cebeSEric Cheng 
967*da14cebeSEric Cheng 	if ((kcp = kstat_open()) == NULL) {
968*da14cebeSEric Cheng 		warn("kstat open operation failed");
969*da14cebeSEric Cheng 		return;
970*da14cebeSEric Cheng 	}
971*da14cebeSEric Cheng 
972*da14cebeSEric Cheng 	ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
973*da14cebeSEric Cheng 
974*da14cebeSEric Cheng 	if (ksp != NULL)
975*da14cebeSEric Cheng 		dladm_get_stats(kcp, ksp, stats);
976*da14cebeSEric Cheng 
977*da14cebeSEric Cheng 	(void) kstat_close(kcp);
978*da14cebeSEric Cheng }
979*da14cebeSEric Cheng 
980*da14cebeSEric Cheng /* ARGSUSED */
981*da14cebeSEric Cheng static int
982*da14cebeSEric Cheng show_flow_stats(dladm_flow_attr_t *attr, void *arg)
983*da14cebeSEric Cheng {
984*da14cebeSEric Cheng 	show_flow_state_t *state = (show_flow_state_t *)arg;
985*da14cebeSEric Cheng 	const char *name = attr->fa_flowname;
986*da14cebeSEric Cheng 	pktsum_t stats, diff_stats;
987*da14cebeSEric Cheng 
988*da14cebeSEric Cheng 	if (state->fs_firstonly) {
989*da14cebeSEric Cheng 		if (state->fs_donefirst)
990*da14cebeSEric Cheng 			return (DLADM_WALK_TERMINATE);
991*da14cebeSEric Cheng 		state->fs_donefirst = B_TRUE;
992*da14cebeSEric Cheng 	} else {
993*da14cebeSEric Cheng 		bzero(&state->fs_prevstats, sizeof (state->fs_prevstats));
994*da14cebeSEric Cheng 	}
995*da14cebeSEric Cheng 
996*da14cebeSEric Cheng 	get_flow_stats(name, &stats);
997*da14cebeSEric Cheng 	dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats);
998*da14cebeSEric Cheng 
999*da14cebeSEric Cheng 	(void) printf("%-12s", name);
1000*da14cebeSEric Cheng 	(void) printf("%-10llu", diff_stats.ipackets);
1001*da14cebeSEric Cheng 	(void) printf("%-12llu", diff_stats.rbytes);
1002*da14cebeSEric Cheng 	(void) printf("%-8llu", diff_stats.ierrors);
1003*da14cebeSEric Cheng 	(void) printf("%-10llu", diff_stats.opackets);
1004*da14cebeSEric Cheng 	(void) printf("%-12llu", diff_stats.obytes);
1005*da14cebeSEric Cheng 	(void) printf("%-8llu\n", diff_stats.oerrors);
1006*da14cebeSEric Cheng 
1007*da14cebeSEric Cheng 	state->fs_prevstats = stats;
1008*da14cebeSEric Cheng 
1009*da14cebeSEric Cheng 	return (DLADM_WALK_CONTINUE);
1010*da14cebeSEric Cheng }
1011*da14cebeSEric Cheng 
1012*da14cebeSEric Cheng /*
1013*da14cebeSEric Cheng  * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for
1014*da14cebeSEric Cheng  * dladm_walk_datalink_id(). Used for showing flow stats for
1015*da14cebeSEric Cheng  * all flows on all links.
1016*da14cebeSEric Cheng  */
1017*da14cebeSEric Cheng static int
1018*da14cebeSEric Cheng show_link_flow_stats(datalink_id_t linkid, void * arg)
1019*da14cebeSEric Cheng {
1020*da14cebeSEric Cheng 	if (dladm_walk_flow(show_flow_stats, linkid, arg, B_FALSE)
1021*da14cebeSEric Cheng 	    == DLADM_STATUS_OK)
1022*da14cebeSEric Cheng 		return (DLADM_WALK_CONTINUE);
1023*da14cebeSEric Cheng 	else
1024*da14cebeSEric Cheng 		return (DLADM_WALK_TERMINATE);
1025*da14cebeSEric Cheng }
1026*da14cebeSEric Cheng 
1027*da14cebeSEric Cheng /* ARGSUSED */
1028*da14cebeSEric Cheng static void
1029*da14cebeSEric Cheng flow_stats(const char *flow, datalink_id_t linkid,  uint_t interval)
1030*da14cebeSEric Cheng {
1031*da14cebeSEric Cheng 	show_flow_state_t	state;
1032*da14cebeSEric Cheng 	dladm_flow_attr_t	attr;
1033*da14cebeSEric Cheng 
1034*da14cebeSEric Cheng 	if (flow != NULL && dladm_flow_info(flow, &attr) != DLADM_STATUS_OK)
1035*da14cebeSEric Cheng 		die("invalid flow %s", flow);
1036*da14cebeSEric Cheng 
1037*da14cebeSEric Cheng 	bzero(&state, sizeof (state));
1038*da14cebeSEric Cheng 
1039*da14cebeSEric Cheng 	/*
1040*da14cebeSEric Cheng 	 * If an interval is specified, continuously show the stats
1041*da14cebeSEric Cheng 	 * for only the first flow.
1042*da14cebeSEric Cheng 	 */
1043*da14cebeSEric Cheng 	state.fs_firstonly = (interval != 0);
1044*da14cebeSEric Cheng 
1045*da14cebeSEric Cheng 	for (;;) {
1046*da14cebeSEric Cheng 		if (!state.fs_donefirst)
1047*da14cebeSEric Cheng 			(void) printf("%-12s%-10s%-12s%-8s%-10s%-12s%-8s\n",
1048*da14cebeSEric Cheng 			    "FLOW", "IPACKETS", "RBYTES", "IERRORS",
1049*da14cebeSEric Cheng 			    "OPACKETS", "OBYTES", "OERRORS");
1050*da14cebeSEric Cheng 
1051*da14cebeSEric Cheng 		state.fs_donefirst = B_FALSE;
1052*da14cebeSEric Cheng 
1053*da14cebeSEric Cheng 		/* Show stats for named flow */
1054*da14cebeSEric Cheng 		if (flow != NULL)  {
1055*da14cebeSEric Cheng 			state.fs_flow = flow;
1056*da14cebeSEric Cheng 			(void) show_flow_stats(&attr, &state);
1057*da14cebeSEric Cheng 
1058*da14cebeSEric Cheng 		/* Show all stats on a link */
1059*da14cebeSEric Cheng 		} else if (linkid != DATALINK_INVALID_LINKID) {
1060*da14cebeSEric Cheng 			(void) dladm_walk_flow(show_flow_stats, linkid, &state,
1061*da14cebeSEric Cheng 			    B_FALSE);
1062*da14cebeSEric Cheng 
1063*da14cebeSEric Cheng 		/* Show all stats by datalink */
1064*da14cebeSEric Cheng 		} else {
1065*da14cebeSEric Cheng 			(void) dladm_walk_datalink_id(show_link_flow_stats,
1066*da14cebeSEric Cheng 			    &state, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1067*da14cebeSEric Cheng 			    DLADM_OPT_ACTIVE);
1068*da14cebeSEric Cheng 		}
1069*da14cebeSEric Cheng 
1070*da14cebeSEric Cheng 		if (interval == 0)
1071*da14cebeSEric Cheng 			break;
1072*da14cebeSEric Cheng 
1073*da14cebeSEric Cheng 		(void) sleep(interval);
1074*da14cebeSEric Cheng 	}
1075*da14cebeSEric Cheng }
1076*da14cebeSEric Cheng 
1077*da14cebeSEric Cheng static void
1078*da14cebeSEric Cheng do_show_flow(int argc, char *argv[])
1079*da14cebeSEric Cheng {
1080*da14cebeSEric Cheng 	char			flowname[MAXFLOWNAME];
1081*da14cebeSEric Cheng 	char			linkname[MAXNAMELEN];
1082*da14cebeSEric Cheng 	datalink_id_t		linkid = DATALINK_ALL_LINKID;
1083*da14cebeSEric Cheng 	int			option;
1084*da14cebeSEric Cheng 	boolean_t		s_arg = B_FALSE;
1085*da14cebeSEric Cheng 	boolean_t		S_arg = B_FALSE;
1086*da14cebeSEric Cheng 	boolean_t		i_arg = B_FALSE;
1087*da14cebeSEric Cheng 	boolean_t		l_arg = B_FALSE;
1088*da14cebeSEric Cheng 	boolean_t		o_arg = B_FALSE;
1089*da14cebeSEric Cheng 	uint32_t		interval = 0;
1090*da14cebeSEric Cheng 	char			*endp = NULL;
1091*da14cebeSEric Cheng 	show_flow_state_t	state;
1092*da14cebeSEric Cheng 	char			*fields_str = NULL;
1093*da14cebeSEric Cheng 	print_field_t		**fields;
1094*da14cebeSEric Cheng 	uint_t			nfields;
1095*da14cebeSEric Cheng 	char			*all_fields =
1096*da14cebeSEric Cheng 	    "flow,link,ipaddr,transport,port,dsfield";
1097*da14cebeSEric Cheng 	dladm_status_t		status;
1098*da14cebeSEric Cheng 
1099*da14cebeSEric Cheng 	bzero(&state, sizeof (state));
1100*da14cebeSEric Cheng 
1101*da14cebeSEric Cheng 	opterr = 0;
1102*da14cebeSEric Cheng 	while ((option = getopt_long(argc, argv, ":pPsSi:l:o:",
1103*da14cebeSEric Cheng 	    longopts, NULL)) != -1) {
1104*da14cebeSEric Cheng 		switch (option) {
1105*da14cebeSEric Cheng 		case 'p':
1106*da14cebeSEric Cheng 			state.fs_parseable = B_TRUE;
1107*da14cebeSEric Cheng 			break;
1108*da14cebeSEric Cheng 		case 'P':
1109*da14cebeSEric Cheng 			state.fs_persist = B_TRUE;
1110*da14cebeSEric Cheng 			break;
1111*da14cebeSEric Cheng 		case 's':
1112*da14cebeSEric Cheng 			if (s_arg)
1113*da14cebeSEric Cheng 				die_optdup(option);
1114*da14cebeSEric Cheng 
1115*da14cebeSEric Cheng 			s_arg = B_TRUE;
1116*da14cebeSEric Cheng 			break;
1117*da14cebeSEric Cheng 		case 'S':
1118*da14cebeSEric Cheng 			if (S_arg)
1119*da14cebeSEric Cheng 				die_optdup(option);
1120*da14cebeSEric Cheng 
1121*da14cebeSEric Cheng 			S_arg = B_TRUE;
1122*da14cebeSEric Cheng 			break;
1123*da14cebeSEric Cheng 		case 'o':
1124*da14cebeSEric Cheng 			if (o_arg)
1125*da14cebeSEric Cheng 				die_optdup(option);
1126*da14cebeSEric Cheng 
1127*da14cebeSEric Cheng 			o_arg = B_TRUE;
1128*da14cebeSEric Cheng 			fields_str = optarg;
1129*da14cebeSEric Cheng 			break;
1130*da14cebeSEric Cheng 		case 'i':
1131*da14cebeSEric Cheng 			if (i_arg)
1132*da14cebeSEric Cheng 				die_optdup(option);
1133*da14cebeSEric Cheng 
1134*da14cebeSEric Cheng 			i_arg = B_TRUE;
1135*da14cebeSEric Cheng 
1136*da14cebeSEric Cheng 			errno = 0;
1137*da14cebeSEric Cheng 			interval = (int)strtol(optarg, &endp, 10);
1138*da14cebeSEric Cheng 			if (errno != 0 || interval == 0 || *endp != '\0')
1139*da14cebeSEric Cheng 				die("invalid interval value" " '%d'\n",
1140*da14cebeSEric Cheng 				    interval);
1141*da14cebeSEric Cheng 			break;
1142*da14cebeSEric Cheng 		case 'l':
1143*da14cebeSEric Cheng 			if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
1144*da14cebeSEric Cheng 			    >= MAXLINKNAMELEN)
1145*da14cebeSEric Cheng 				die("link name too long\n");
1146*da14cebeSEric Cheng 			if (dladm_name2info(linkname, &linkid, NULL,
1147*da14cebeSEric Cheng 			    NULL, NULL) != DLADM_STATUS_OK)
1148*da14cebeSEric Cheng 				die("invalid link '%s'", linkname);
1149*da14cebeSEric Cheng 			l_arg = B_TRUE;
1150*da14cebeSEric Cheng 			break;
1151*da14cebeSEric Cheng 		default:
1152*da14cebeSEric Cheng 			die_opterr(optopt, option);
1153*da14cebeSEric Cheng 			break;
1154*da14cebeSEric Cheng 		}
1155*da14cebeSEric Cheng 	}
1156*da14cebeSEric Cheng 	if (i_arg && !(s_arg || S_arg))
1157*da14cebeSEric Cheng 		die("the -i option can be used only with -s or -S");
1158*da14cebeSEric Cheng 
1159*da14cebeSEric Cheng 	if (s_arg && S_arg)
1160*da14cebeSEric Cheng 		die("the -s option cannot be used with -S");
1161*da14cebeSEric Cheng 
1162*da14cebeSEric Cheng 	/* get flow name (optional last argument */
1163*da14cebeSEric Cheng 	if (optind == (argc-1)) {
1164*da14cebeSEric Cheng 		if (strlcpy(flowname, argv[optind], MAXFLOWNAME)
1165*da14cebeSEric Cheng 		    >= MAXFLOWNAME)
1166*da14cebeSEric Cheng 			die("flow name too long");
1167*da14cebeSEric Cheng 		state.fs_flow = flowname;
1168*da14cebeSEric Cheng 	}
1169*da14cebeSEric Cheng 
1170*da14cebeSEric Cheng 	if (s_arg) {
1171*da14cebeSEric Cheng 		flow_stats(state.fs_flow, linkid, interval);
1172*da14cebeSEric Cheng 		return;
1173*da14cebeSEric Cheng 	}
1174*da14cebeSEric Cheng 
1175*da14cebeSEric Cheng 	if (S_arg) {
1176*da14cebeSEric Cheng 		dladm_continuous(linkid, state.fs_flow, interval, FLOW_REPORT);
1177*da14cebeSEric Cheng 		return;
1178*da14cebeSEric Cheng 	}
1179*da14cebeSEric Cheng 
1180*da14cebeSEric Cheng 	if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
1181*da14cebeSEric Cheng 		fields_str = all_fields;
1182*da14cebeSEric Cheng 
1183*da14cebeSEric Cheng 	fields = parse_output_fields(fields_str, flow_fields, FLOW_MAX_FIELDS,
1184*da14cebeSEric Cheng 	    CMD_TYPE_ANY, &nfields);
1185*da14cebeSEric Cheng 
1186*da14cebeSEric Cheng 	if (fields == NULL) {
1187*da14cebeSEric Cheng 		die("invalid fields(s) specified");
1188*da14cebeSEric Cheng 		return;
1189*da14cebeSEric Cheng 	}
1190*da14cebeSEric Cheng 
1191*da14cebeSEric Cheng 	state.fs_print.ps_fields = fields;
1192*da14cebeSEric Cheng 	state.fs_print.ps_nfields = nfields;
1193*da14cebeSEric Cheng 
1194*da14cebeSEric Cheng 	/* Show attributes of one flow */
1195*da14cebeSEric Cheng 	if (state.fs_flow != NULL) {
1196*da14cebeSEric Cheng 		show_one_flow(&state, state.fs_flow);
1197*da14cebeSEric Cheng 
1198*da14cebeSEric Cheng 	/* Show attributes of flows on one link */
1199*da14cebeSEric Cheng 	} else if (l_arg) {
1200*da14cebeSEric Cheng 		(void) show_flows_onelink(linkid, &state);
1201*da14cebeSEric Cheng 
1202*da14cebeSEric Cheng 	/* Show attributes of all flows on all links */
1203*da14cebeSEric Cheng 	} else {
1204*da14cebeSEric Cheng 		(void) dladm_walk_datalink_id(show_flows_onelink, &state,
1205*da14cebeSEric Cheng 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1206*da14cebeSEric Cheng 		    DLADM_OPT_ACTIVE);
1207*da14cebeSEric Cheng 	}
1208*da14cebeSEric Cheng }
1209*da14cebeSEric Cheng 
1210*da14cebeSEric Cheng static dladm_status_t
1211*da14cebeSEric Cheng set_flowprop_persist(const char *flow, const char *prop_name, char **prop_val,
1212*da14cebeSEric Cheng     uint_t val_cnt, boolean_t reset)
1213*da14cebeSEric Cheng {
1214*da14cebeSEric Cheng 	dladm_status_t	status;
1215*da14cebeSEric Cheng 	char		*errprop;
1216*da14cebeSEric Cheng 
1217*da14cebeSEric Cheng 	status = dladm_set_flowprop(flow, prop_name, prop_val, val_cnt,
1218*da14cebeSEric Cheng 	    DLADM_OPT_PERSIST, &errprop);
1219*da14cebeSEric Cheng 
1220*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK) {
1221*da14cebeSEric Cheng 		warn_dlerr(status, "cannot persistently %s flow "
1222*da14cebeSEric Cheng 		    "property '%s' on '%s'", reset? "reset": "set",
1223*da14cebeSEric Cheng 		    errprop, flow);
1224*da14cebeSEric Cheng 	}
1225*da14cebeSEric Cheng 	return (status);
1226*da14cebeSEric Cheng }
1227*da14cebeSEric Cheng 
1228*da14cebeSEric Cheng static void
1229*da14cebeSEric Cheng set_flowprop(int argc, char **argv, boolean_t reset)
1230*da14cebeSEric Cheng {
1231*da14cebeSEric Cheng 	int		i, option;
1232*da14cebeSEric Cheng 	char		errmsg[DLADM_STRSIZE];
1233*da14cebeSEric Cheng 	const char	*flow = NULL;
1234*da14cebeSEric Cheng 	dladm_arg_list_t	*proplist = NULL;
1235*da14cebeSEric Cheng 	boolean_t	temp = B_FALSE;
1236*da14cebeSEric Cheng 	dladm_status_t	status = DLADM_STATUS_OK;
1237*da14cebeSEric Cheng 
1238*da14cebeSEric Cheng 	opterr = 0;
1239*da14cebeSEric Cheng 	while ((option = getopt_long(argc, argv, ":p:R:t",
1240*da14cebeSEric Cheng 	    prop_longopts, NULL)) != -1) {
1241*da14cebeSEric Cheng 		switch (option) {
1242*da14cebeSEric Cheng 		case 'p':
1243*da14cebeSEric Cheng 			if (dladm_parse_flow_props(optarg, &proplist, reset)
1244*da14cebeSEric Cheng 			    != DLADM_STATUS_OK)
1245*da14cebeSEric Cheng 				die("invalid flow property specified");
1246*da14cebeSEric Cheng 			break;
1247*da14cebeSEric Cheng 		case 't':
1248*da14cebeSEric Cheng 			temp = B_TRUE;
1249*da14cebeSEric Cheng 			break;
1250*da14cebeSEric Cheng 		case 'R':
1251*da14cebeSEric Cheng 			status = dladm_set_rootdir(optarg);
1252*da14cebeSEric Cheng 			if (status != DLADM_STATUS_OK) {
1253*da14cebeSEric Cheng 				die_dlerr(status, "invalid directory "
1254*da14cebeSEric Cheng 				    "specified");
1255*da14cebeSEric Cheng 			}
1256*da14cebeSEric Cheng 			break;
1257*da14cebeSEric Cheng 		default:
1258*da14cebeSEric Cheng 			die_opterr(optopt, option);
1259*da14cebeSEric Cheng 			break;
1260*da14cebeSEric Cheng 		}
1261*da14cebeSEric Cheng 	}
1262*da14cebeSEric Cheng 
1263*da14cebeSEric Cheng 	if (optind == (argc - 1)) {
1264*da14cebeSEric Cheng 		if (strlen(argv[optind]) >= MAXFLOWNAME)
1265*da14cebeSEric Cheng 			die("flow name too long");
1266*da14cebeSEric Cheng 		flow = argv[optind];
1267*da14cebeSEric Cheng 	} else if (optind != argc) {
1268*da14cebeSEric Cheng 		usage();
1269*da14cebeSEric Cheng 	}
1270*da14cebeSEric Cheng 	if (flow == NULL)
1271*da14cebeSEric Cheng 		die("flow name must be specified");
1272*da14cebeSEric Cheng 
1273*da14cebeSEric Cheng 	if (proplist == NULL) {
1274*da14cebeSEric Cheng 		char *errprop;
1275*da14cebeSEric Cheng 
1276*da14cebeSEric Cheng 		if (!reset)
1277*da14cebeSEric Cheng 			die("flow property must be specified");
1278*da14cebeSEric Cheng 
1279*da14cebeSEric Cheng 		status = dladm_set_flowprop(flow, NULL, NULL, 0,
1280*da14cebeSEric Cheng 		    DLADM_OPT_ACTIVE, &errprop);
1281*da14cebeSEric Cheng 		if (status != DLADM_STATUS_OK) {
1282*da14cebeSEric Cheng 			warn_dlerr(status, "cannot reset flow property '%s' "
1283*da14cebeSEric Cheng 			    "on '%s'", errprop, flow);
1284*da14cebeSEric Cheng 		}
1285*da14cebeSEric Cheng 		if (!temp) {
1286*da14cebeSEric Cheng 			dladm_status_t	s;
1287*da14cebeSEric Cheng 
1288*da14cebeSEric Cheng 			s = set_flowprop_persist(flow, NULL, NULL, 0, reset);
1289*da14cebeSEric Cheng 			if (s != DLADM_STATUS_OK)
1290*da14cebeSEric Cheng 				status = s;
1291*da14cebeSEric Cheng 		}
1292*da14cebeSEric Cheng 		goto done;
1293*da14cebeSEric Cheng 	}
1294*da14cebeSEric Cheng 
1295*da14cebeSEric Cheng 	for (i = 0; i < proplist->al_count; i++) {
1296*da14cebeSEric Cheng 		dladm_arg_info_t	*aip = &proplist->al_info[i];
1297*da14cebeSEric Cheng 		char		**val;
1298*da14cebeSEric Cheng 		uint_t		count;
1299*da14cebeSEric Cheng 		dladm_status_t	s;
1300*da14cebeSEric Cheng 
1301*da14cebeSEric Cheng 		if (reset) {
1302*da14cebeSEric Cheng 			val = NULL;
1303*da14cebeSEric Cheng 			count = 0;
1304*da14cebeSEric Cheng 		} else {
1305*da14cebeSEric Cheng 			val = aip->ai_val;
1306*da14cebeSEric Cheng 			count = aip->ai_count;
1307*da14cebeSEric Cheng 			if (count == 0) {
1308*da14cebeSEric Cheng 				warn("no value specified for '%s'",
1309*da14cebeSEric Cheng 				    aip->ai_name);
1310*da14cebeSEric Cheng 				status = DLADM_STATUS_BADARG;
1311*da14cebeSEric Cheng 				continue;
1312*da14cebeSEric Cheng 			}
1313*da14cebeSEric Cheng 		}
1314*da14cebeSEric Cheng 		s = dladm_set_flowprop(flow, aip->ai_name, val, count,
1315*da14cebeSEric Cheng 		    DLADM_OPT_ACTIVE, NULL);
1316*da14cebeSEric Cheng 		if (s == DLADM_STATUS_OK) {
1317*da14cebeSEric Cheng 			if (!temp) {
1318*da14cebeSEric Cheng 				s = set_flowprop_persist(flow,
1319*da14cebeSEric Cheng 				    aip->ai_name, val, count, reset);
1320*da14cebeSEric Cheng 				if (s != DLADM_STATUS_OK)
1321*da14cebeSEric Cheng 					status = s;
1322*da14cebeSEric Cheng 			}
1323*da14cebeSEric Cheng 			continue;
1324*da14cebeSEric Cheng 		}
1325*da14cebeSEric Cheng 		status = s;
1326*da14cebeSEric Cheng 		switch (s) {
1327*da14cebeSEric Cheng 		case DLADM_STATUS_NOTFOUND:
1328*da14cebeSEric Cheng 			warn("invalid flow property '%s'", aip->ai_name);
1329*da14cebeSEric Cheng 			break;
1330*da14cebeSEric Cheng 		case DLADM_STATUS_BADVAL: {
1331*da14cebeSEric Cheng 			int		j;
1332*da14cebeSEric Cheng 			char		*ptr, *lim;
1333*da14cebeSEric Cheng 			char		**propvals = NULL;
1334*da14cebeSEric Cheng 			uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1335*da14cebeSEric Cheng 
1336*da14cebeSEric Cheng 			ptr = malloc((sizeof (char *) +
1337*da14cebeSEric Cheng 			    DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT +
1338*da14cebeSEric Cheng 			    MAX_PROP_LINE);
1339*da14cebeSEric Cheng 
1340*da14cebeSEric Cheng 			if (ptr == NULL)
1341*da14cebeSEric Cheng 				die("insufficient memory");
1342*da14cebeSEric Cheng 			propvals = (char **)(void *)ptr;
1343*da14cebeSEric Cheng 
1344*da14cebeSEric Cheng 			for (j = 0; j < DLADM_MAX_PROP_VALCNT; j++) {
1345*da14cebeSEric Cheng 				propvals[j] = ptr + sizeof (char *) *
1346*da14cebeSEric Cheng 				    DLADM_MAX_PROP_VALCNT +
1347*da14cebeSEric Cheng 				    j * DLADM_PROP_VAL_MAX;
1348*da14cebeSEric Cheng 			}
1349*da14cebeSEric Cheng 			s = dladm_get_flowprop(flow, DLADM_PROP_VAL_MODIFIABLE,
1350*da14cebeSEric Cheng 			    aip->ai_name, propvals, &valcnt);
1351*da14cebeSEric Cheng 
1352*da14cebeSEric Cheng 			ptr = errmsg;
1353*da14cebeSEric Cheng 			lim = ptr + DLADM_STRSIZE;
1354*da14cebeSEric Cheng 			*ptr = '\0';
1355*da14cebeSEric Cheng 			for (j = 0; j < valcnt && s == DLADM_STATUS_OK; j++) {
1356*da14cebeSEric Cheng 				ptr += snprintf(ptr, lim - ptr, "%s,",
1357*da14cebeSEric Cheng 				    propvals[j]);
1358*da14cebeSEric Cheng 				if (ptr >= lim)
1359*da14cebeSEric Cheng 					break;
1360*da14cebeSEric Cheng 			}
1361*da14cebeSEric Cheng 			if (ptr > errmsg) {
1362*da14cebeSEric Cheng 				*(ptr - 1) = '\0';
1363*da14cebeSEric Cheng 				warn("flow property '%s' must be one of: %s",
1364*da14cebeSEric Cheng 				    aip->ai_name, errmsg);
1365*da14cebeSEric Cheng 			} else
1366*da14cebeSEric Cheng 				warn("%s is an invalid value for "
1367*da14cebeSEric Cheng 				    "flow property %s", *val, aip->ai_name);
1368*da14cebeSEric Cheng 			free(propvals);
1369*da14cebeSEric Cheng 			break;
1370*da14cebeSEric Cheng 		}
1371*da14cebeSEric Cheng 		default:
1372*da14cebeSEric Cheng 			if (reset) {
1373*da14cebeSEric Cheng 				warn_dlerr(status, "cannot reset flow property "
1374*da14cebeSEric Cheng 				    "'%s' on '%s'", aip->ai_name, flow);
1375*da14cebeSEric Cheng 			} else {
1376*da14cebeSEric Cheng 				warn_dlerr(status, "cannot set flow property "
1377*da14cebeSEric Cheng 				    "'%s' on '%s'", aip->ai_name, flow);
1378*da14cebeSEric Cheng 			}
1379*da14cebeSEric Cheng 			break;
1380*da14cebeSEric Cheng 		}
1381*da14cebeSEric Cheng 	}
1382*da14cebeSEric Cheng done:
1383*da14cebeSEric Cheng 	dladm_free_props(proplist);
1384*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK)
1385*da14cebeSEric Cheng 		exit(1);
1386*da14cebeSEric Cheng }
1387*da14cebeSEric Cheng 
1388*da14cebeSEric Cheng static void
1389*da14cebeSEric Cheng do_set_flowprop(int argc, char **argv)
1390*da14cebeSEric Cheng {
1391*da14cebeSEric Cheng 	set_flowprop(argc, argv, B_FALSE);
1392*da14cebeSEric Cheng }
1393*da14cebeSEric Cheng 
1394*da14cebeSEric Cheng static void
1395*da14cebeSEric Cheng do_reset_flowprop(int argc, char **argv)
1396*da14cebeSEric Cheng {
1397*da14cebeSEric Cheng 	set_flowprop(argc, argv, B_TRUE);
1398*da14cebeSEric Cheng }
1399*da14cebeSEric Cheng 
1400*da14cebeSEric Cheng static void
1401*da14cebeSEric Cheng warn(const char *format, ...)
1402*da14cebeSEric Cheng {
1403*da14cebeSEric Cheng 	va_list alist;
1404*da14cebeSEric Cheng 
1405*da14cebeSEric Cheng 	format = gettext(format);
1406*da14cebeSEric Cheng 	(void) fprintf(stderr, "%s: warning: ", progname);
1407*da14cebeSEric Cheng 
1408*da14cebeSEric Cheng 	va_start(alist, format);
1409*da14cebeSEric Cheng 	(void) vfprintf(stderr, format, alist);
1410*da14cebeSEric Cheng 	va_end(alist);
1411*da14cebeSEric Cheng 
1412*da14cebeSEric Cheng 	(void) putchar('\n');
1413*da14cebeSEric Cheng }
1414*da14cebeSEric Cheng 
1415*da14cebeSEric Cheng /* PRINTFLIKE2 */
1416*da14cebeSEric Cheng static void
1417*da14cebeSEric Cheng warn_dlerr(dladm_status_t err, const char *format, ...)
1418*da14cebeSEric Cheng {
1419*da14cebeSEric Cheng 	va_list alist;
1420*da14cebeSEric Cheng 	char    errmsg[DLADM_STRSIZE];
1421*da14cebeSEric Cheng 
1422*da14cebeSEric Cheng 	format = gettext(format);
1423*da14cebeSEric Cheng 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
1424*da14cebeSEric Cheng 
1425*da14cebeSEric Cheng 	va_start(alist, format);
1426*da14cebeSEric Cheng 	(void) vfprintf(stderr, format, alist);
1427*da14cebeSEric Cheng 	va_end(alist);
1428*da14cebeSEric Cheng 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1429*da14cebeSEric Cheng }
1430*da14cebeSEric Cheng 
1431*da14cebeSEric Cheng /* PRINTFLIKE1 */
1432*da14cebeSEric Cheng static void
1433*da14cebeSEric Cheng die(const char *format, ...)
1434*da14cebeSEric Cheng {
1435*da14cebeSEric Cheng 	va_list alist;
1436*da14cebeSEric Cheng 
1437*da14cebeSEric Cheng 	format = gettext(format);
1438*da14cebeSEric Cheng 	(void) fprintf(stderr, "%s: ", progname);
1439*da14cebeSEric Cheng 
1440*da14cebeSEric Cheng 	va_start(alist, format);
1441*da14cebeSEric Cheng 	(void) vfprintf(stderr, format, alist);
1442*da14cebeSEric Cheng 	va_end(alist);
1443*da14cebeSEric Cheng 
1444*da14cebeSEric Cheng 	(void) putchar('\n');
1445*da14cebeSEric Cheng 	exit(EXIT_FAILURE);
1446*da14cebeSEric Cheng }
1447*da14cebeSEric Cheng 
1448*da14cebeSEric Cheng static void
1449*da14cebeSEric Cheng die_optdup(int opt)
1450*da14cebeSEric Cheng {
1451*da14cebeSEric Cheng 	die("the option -%c cannot be specified more than once", opt);
1452*da14cebeSEric Cheng }
1453*da14cebeSEric Cheng 
1454*da14cebeSEric Cheng static void
1455*da14cebeSEric Cheng die_opterr(int opt, int opterr)
1456*da14cebeSEric Cheng {
1457*da14cebeSEric Cheng 	switch (opterr) {
1458*da14cebeSEric Cheng 	case ':':
1459*da14cebeSEric Cheng 		die("option '-%c' requires a value", opt);
1460*da14cebeSEric Cheng 		break;
1461*da14cebeSEric Cheng 	case '?':
1462*da14cebeSEric Cheng 	default:
1463*da14cebeSEric Cheng 		die("unrecognized option '-%c'", opt);
1464*da14cebeSEric Cheng 		break;
1465*da14cebeSEric Cheng 	}
1466*da14cebeSEric Cheng }
1467*da14cebeSEric Cheng 
1468*da14cebeSEric Cheng /* PRINTFLIKE2 */
1469*da14cebeSEric Cheng static void
1470*da14cebeSEric Cheng die_dlerr(dladm_status_t err, const char *format, ...)
1471*da14cebeSEric Cheng {
1472*da14cebeSEric Cheng 	va_list alist;
1473*da14cebeSEric Cheng 	char	errmsg[DLADM_STRSIZE];
1474*da14cebeSEric Cheng 
1475*da14cebeSEric Cheng 	format = gettext(format);
1476*da14cebeSEric Cheng 	(void) fprintf(stderr, "%s: ", progname);
1477*da14cebeSEric Cheng 
1478*da14cebeSEric Cheng 	va_start(alist, format);
1479*da14cebeSEric Cheng 	(void) vfprintf(stderr, format, alist);
1480*da14cebeSEric Cheng 	va_end(alist);
1481*da14cebeSEric Cheng 	(void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
1482*da14cebeSEric Cheng 
1483*da14cebeSEric Cheng 	exit(EXIT_FAILURE);
1484*da14cebeSEric Cheng }
1485*da14cebeSEric Cheng 
1486*da14cebeSEric Cheng static void
1487*da14cebeSEric Cheng print_flowprop(const char *flowname, show_flowprop_state_t *statep,
1488*da14cebeSEric Cheng     const char *propname, dladm_prop_type_t type,
1489*da14cebeSEric Cheng     const char *format, char **pptr)
1490*da14cebeSEric Cheng {
1491*da14cebeSEric Cheng 	int		i;
1492*da14cebeSEric Cheng 	char		*ptr, *lim;
1493*da14cebeSEric Cheng 	char		buf[DLADM_STRSIZE];
1494*da14cebeSEric Cheng 	char		*unknown = "--", *notsup = "";
1495*da14cebeSEric Cheng 	char		**propvals = statep->fs_propvals;
1496*da14cebeSEric Cheng 	uint_t		valcnt = DLADM_MAX_PROP_VALCNT;
1497*da14cebeSEric Cheng 	dladm_status_t	status;
1498*da14cebeSEric Cheng 
1499*da14cebeSEric Cheng 	status = dladm_get_flowprop(flowname, type, propname, propvals,
1500*da14cebeSEric Cheng 	    &valcnt);
1501*da14cebeSEric Cheng 	if (status != DLADM_STATUS_OK) {
1502*da14cebeSEric Cheng 		if (status == DLADM_STATUS_TEMPONLY) {
1503*da14cebeSEric Cheng 			if (type == DLADM_PROP_VAL_MODIFIABLE &&
1504*da14cebeSEric Cheng 			    statep->fs_persist) {
1505*da14cebeSEric Cheng 				valcnt = 1;
1506*da14cebeSEric Cheng 				propvals = &unknown;
1507*da14cebeSEric Cheng 			} else {
1508*da14cebeSEric Cheng 				statep->fs_status = status;
1509*da14cebeSEric Cheng 				statep->fs_retstatus = status;
1510*da14cebeSEric Cheng 				return;
1511*da14cebeSEric Cheng 			}
1512*da14cebeSEric Cheng 		} else if (status == DLADM_STATUS_NOTSUP ||
1513*da14cebeSEric Cheng 		    statep->fs_persist) {
1514*da14cebeSEric Cheng 			valcnt = 1;
1515*da14cebeSEric Cheng 			if (type == DLADM_PROP_VAL_CURRENT)
1516*da14cebeSEric Cheng 				propvals = &unknown;
1517*da14cebeSEric Cheng 			else
1518*da14cebeSEric Cheng 				propvals = &notsup;
1519*da14cebeSEric Cheng 		} else {
1520*da14cebeSEric Cheng 			if ((statep->fs_proplist != NULL) &&
1521*da14cebeSEric Cheng 			    statep->fs_status == DLADM_STATUS_OK) {
1522*da14cebeSEric Cheng 				warn("invalid flow property '%s'", propname);
1523*da14cebeSEric Cheng 			}
1524*da14cebeSEric Cheng 			statep->fs_status = status;
1525*da14cebeSEric Cheng 			statep->fs_retstatus = status;
1526*da14cebeSEric Cheng 			return;
1527*da14cebeSEric Cheng 		}
1528*da14cebeSEric Cheng 	}
1529*da14cebeSEric Cheng 
1530*da14cebeSEric Cheng 	statep->fs_status = DLADM_STATUS_OK;
1531*da14cebeSEric Cheng 
1532*da14cebeSEric Cheng 	ptr = buf;
1533*da14cebeSEric Cheng 	lim = buf + DLADM_STRSIZE;
1534*da14cebeSEric Cheng 	for (i = 0; i < valcnt; i++) {
1535*da14cebeSEric Cheng 		if (propvals[i][0] == '\0' && !statep->fs_parseable)
1536*da14cebeSEric Cheng 			ptr += snprintf(ptr, lim - ptr, STR_UNDEF_VAL",");
1537*da14cebeSEric Cheng 		else
1538*da14cebeSEric Cheng 			ptr += snprintf(ptr, lim - ptr, "%s,", propvals[i]);
1539*da14cebeSEric Cheng 		if (ptr >= lim)
1540*da14cebeSEric Cheng 			break;
1541*da14cebeSEric Cheng 	}
1542*da14cebeSEric Cheng 	if (valcnt > 0)
1543*da14cebeSEric Cheng 		buf[strlen(buf) - 1] = '\0';
1544*da14cebeSEric Cheng 
1545*da14cebeSEric Cheng 	lim = statep->fs_line + MAX_PROP_LINE;
1546*da14cebeSEric Cheng 	if (statep->fs_parseable) {
1547*da14cebeSEric Cheng 		*pptr += snprintf(*pptr, lim - *pptr,
1548*da14cebeSEric Cheng 		    "%s", buf);
1549*da14cebeSEric Cheng 	} else {
1550*da14cebeSEric Cheng 		*pptr += snprintf(*pptr, lim - *pptr, format, buf);
1551*da14cebeSEric Cheng 	}
1552*da14cebeSEric Cheng }
1553*da14cebeSEric Cheng 
1554*da14cebeSEric Cheng static char *
1555*da14cebeSEric Cheng flowprop_callback(print_field_t *pf, void *fs_arg)
1556*da14cebeSEric Cheng {
1557*da14cebeSEric Cheng 	flowprop_args_t		*arg = fs_arg;
1558*da14cebeSEric Cheng 	char 			*propname = arg->fs_propname;
1559*da14cebeSEric Cheng 	show_flowprop_state_t	*statep = arg->fs_state;
1560*da14cebeSEric Cheng 	char			*ptr = statep->fs_line;
1561*da14cebeSEric Cheng 	char			*lim = ptr + MAX_PROP_LINE;
1562*da14cebeSEric Cheng 	char			*flowname = arg->fs_flowname;
1563*da14cebeSEric Cheng 
1564*da14cebeSEric Cheng 	switch (pf->pf_index) {
1565*da14cebeSEric Cheng 	case FLOWPROP_FLOW:
1566*da14cebeSEric Cheng 		(void) snprintf(ptr, lim - ptr, "%s", statep->fs_flow);
1567*da14cebeSEric Cheng 		break;
1568*da14cebeSEric Cheng 	case FLOWPROP_PROPERTY:
1569*da14cebeSEric Cheng 		(void) snprintf(ptr, lim - ptr, "%s", propname);
1570*da14cebeSEric Cheng 		break;
1571*da14cebeSEric Cheng 	case FLOWPROP_VALUE:
1572*da14cebeSEric Cheng 		print_flowprop(flowname, statep, propname,
1573*da14cebeSEric Cheng 		    statep->fs_persist ? DLADM_PROP_VAL_PERSISTENT :
1574*da14cebeSEric Cheng 		    DLADM_PROP_VAL_CURRENT, "%s", &ptr);
1575*da14cebeSEric Cheng 		/*
1576*da14cebeSEric Cheng 		 * If we failed to query the flow property, for example, query
1577*da14cebeSEric Cheng 		 * the persistent value of a non-persistable flow property,
1578*da14cebeSEric Cheng 		 * simply skip the output.
1579*da14cebeSEric Cheng 		 */
1580*da14cebeSEric Cheng 		if (statep->fs_status != DLADM_STATUS_OK)
1581*da14cebeSEric Cheng 			goto skip;
1582*da14cebeSEric Cheng 		ptr = statep->fs_line;
1583*da14cebeSEric Cheng 		break;
1584*da14cebeSEric Cheng 	case FLOWPROP_DEFAULT:
1585*da14cebeSEric Cheng 		print_flowprop(flowname, statep, propname,
1586*da14cebeSEric Cheng 		    DLADM_PROP_VAL_DEFAULT, "%s", &ptr);
1587*da14cebeSEric Cheng 		if (statep->fs_status != DLADM_STATUS_OK)
1588*da14cebeSEric Cheng 			goto skip;
1589*da14cebeSEric Cheng 		ptr = statep->fs_line;
1590*da14cebeSEric Cheng 		break;
1591*da14cebeSEric Cheng 	case FLOWPROP_POSSIBLE:
1592*da14cebeSEric Cheng 		print_flowprop(flowname, statep, propname,
1593*da14cebeSEric Cheng 		    DLADM_PROP_VAL_MODIFIABLE, "%s ", &ptr);
1594*da14cebeSEric Cheng 		if (statep->fs_status != DLADM_STATUS_OK)
1595*da14cebeSEric Cheng 			goto skip;
1596*da14cebeSEric Cheng 		ptr = statep->fs_line;
1597*da14cebeSEric Cheng 		break;
1598*da14cebeSEric Cheng 	default:
1599*da14cebeSEric Cheng 		die("invalid input");
1600*da14cebeSEric Cheng 		break;
1601*da14cebeSEric Cheng 	}
1602*da14cebeSEric Cheng 	return (ptr);
1603*da14cebeSEric Cheng skip:
1604*da14cebeSEric Cheng 	if (statep->fs_status != DLADM_STATUS_OK)
1605*da14cebeSEric Cheng 		return (NULL);
1606*da14cebeSEric Cheng 	else
1607*da14cebeSEric Cheng 		return ("");
1608*da14cebeSEric Cheng }
1609*da14cebeSEric Cheng 
1610*da14cebeSEric Cheng static int
1611*da14cebeSEric Cheng show_one_flowprop(void *arg, const char *propname)
1612*da14cebeSEric Cheng {
1613*da14cebeSEric Cheng 	show_flowprop_state_t	*statep = arg;
1614*da14cebeSEric Cheng 	flowprop_args_t		fs_arg;
1615*da14cebeSEric Cheng 
1616*da14cebeSEric Cheng 	bzero(&fs_arg, sizeof (fs_arg));
1617*da14cebeSEric Cheng 	fs_arg.fs_state = statep;
1618*da14cebeSEric Cheng 	fs_arg.fs_propname = (char *)propname;
1619*da14cebeSEric Cheng 	fs_arg.fs_flowname = (char *)statep->fs_flow;
1620*da14cebeSEric Cheng 
1621*da14cebeSEric Cheng 	if (statep->fs_header) {
1622*da14cebeSEric Cheng 		statep->fs_header = B_FALSE;
1623*da14cebeSEric Cheng 		if (!statep ->fs_parseable)
1624*da14cebeSEric Cheng 			print_header(&statep->fs_print);
1625*da14cebeSEric Cheng 	}
1626*da14cebeSEric Cheng 	flowadm_print_output(&statep->fs_print, statep->fs_parseable,
1627*da14cebeSEric Cheng 	    flowprop_callback, (void *)&fs_arg);
1628*da14cebeSEric Cheng 
1629*da14cebeSEric Cheng 	return (DLADM_WALK_CONTINUE);
1630*da14cebeSEric Cheng }
1631*da14cebeSEric Cheng 
1632*da14cebeSEric Cheng /* Walker function called by dladm_walk_flow to display flow properties */
1633*da14cebeSEric Cheng static int
1634*da14cebeSEric Cheng show_flowprop(dladm_flow_attr_t *attr, void *arg)
1635*da14cebeSEric Cheng {
1636*da14cebeSEric Cheng 	show_flowprop_one_flow(arg, attr->fa_flowname);
1637*da14cebeSEric Cheng 	return (DLADM_WALK_CONTINUE);
1638*da14cebeSEric Cheng }
1639*da14cebeSEric Cheng 
1640*da14cebeSEric Cheng /*
1641*da14cebeSEric Cheng  * Wrapper of dladm_walk_flow(show_walk_fn,...) to make it
1642*da14cebeSEric Cheng  * usable to dladm_walk_datalink_id()
1643*da14cebeSEric Cheng  */
1644*da14cebeSEric Cheng static int
1645*da14cebeSEric Cheng show_flowprop_onelink(datalink_id_t linkid, void *arg)
1646*da14cebeSEric Cheng {
1647*da14cebeSEric Cheng 	char	name[MAXLINKNAMELEN];
1648*da14cebeSEric Cheng 
1649*da14cebeSEric Cheng 	if (dladm_datalink_id2info(linkid, NULL, NULL, NULL,
1650*da14cebeSEric Cheng 	    name, sizeof (name)) != DLADM_STATUS_OK)
1651*da14cebeSEric Cheng 		return (DLADM_WALK_TERMINATE);
1652*da14cebeSEric Cheng 
1653*da14cebeSEric Cheng 	(void) dladm_walk_flow(show_flowprop, linkid, arg, B_FALSE);
1654*da14cebeSEric Cheng 
1655*da14cebeSEric Cheng 	return (DLADM_WALK_CONTINUE);
1656*da14cebeSEric Cheng }
1657*da14cebeSEric Cheng 
1658*da14cebeSEric Cheng static void
1659*da14cebeSEric Cheng do_show_flowprop(int argc, char **argv)
1660*da14cebeSEric Cheng {
1661*da14cebeSEric Cheng 	int			option;
1662*da14cebeSEric Cheng 	dladm_arg_list_t	*proplist = NULL;
1663*da14cebeSEric Cheng 	show_flowprop_state_t	state;
1664*da14cebeSEric Cheng 	char			*fields_str = NULL;
1665*da14cebeSEric Cheng 	print_field_t		**fields;
1666*da14cebeSEric Cheng 	uint_t			nfields;
1667*da14cebeSEric Cheng 	char			*all_fields =
1668*da14cebeSEric Cheng 	    "flow,property,value,default,possible";
1669*da14cebeSEric Cheng 
1670*da14cebeSEric Cheng 	fields_str = all_fields;
1671*da14cebeSEric Cheng 	opterr = 0;
1672*da14cebeSEric Cheng 	state.fs_propvals = NULL;
1673*da14cebeSEric Cheng 	state.fs_line = NULL;
1674*da14cebeSEric Cheng 	state.fs_parseable = B_FALSE;
1675*da14cebeSEric Cheng 	state.fs_persist = B_FALSE;
1676*da14cebeSEric Cheng 	state.fs_header = B_TRUE;
1677*da14cebeSEric Cheng 	state.fs_retstatus = DLADM_STATUS_OK;
1678*da14cebeSEric Cheng 	state.fs_linkid = DATALINK_INVALID_LINKID;
1679*da14cebeSEric Cheng 	state.fs_flow = NULL;
1680*da14cebeSEric Cheng 
1681*da14cebeSEric Cheng 	while ((option = getopt_long(argc, argv, ":p:cPl:o:",
1682*da14cebeSEric Cheng 	    prop_longopts, NULL)) != -1) {
1683*da14cebeSEric Cheng 		switch (option) {
1684*da14cebeSEric Cheng 		case 'p':
1685*da14cebeSEric Cheng 			if (dladm_parse_flow_props(optarg, &proplist, B_TRUE)
1686*da14cebeSEric Cheng 			    != DLADM_STATUS_OK)
1687*da14cebeSEric Cheng 				die("invalid flow properties specified");
1688*da14cebeSEric Cheng 			break;
1689*da14cebeSEric Cheng 		case 'c':
1690*da14cebeSEric Cheng 			state.fs_parseable = B_TRUE;
1691*da14cebeSEric Cheng 			break;
1692*da14cebeSEric Cheng 		case 'P':
1693*da14cebeSEric Cheng 			state.fs_persist = B_TRUE;
1694*da14cebeSEric Cheng 			break;
1695*da14cebeSEric Cheng 		case 'l':
1696*da14cebeSEric Cheng 			if (dladm_name2info(optarg, &state.fs_linkid,
1697*da14cebeSEric Cheng 			    NULL, NULL, NULL) != DLADM_STATUS_OK)
1698*da14cebeSEric Cheng 				die("invalid link '%s'", optarg);
1699*da14cebeSEric Cheng 			break;
1700*da14cebeSEric Cheng 		case 'o':
1701*da14cebeSEric Cheng 			if (strcasecmp(optarg, "all") == 0)
1702*da14cebeSEric Cheng 				fields_str = all_fields;
1703*da14cebeSEric Cheng 			else
1704*da14cebeSEric Cheng 				fields_str = optarg;
1705*da14cebeSEric Cheng 			break;
1706*da14cebeSEric Cheng 		default:
1707*da14cebeSEric Cheng 			die_opterr(optopt, option);
1708*da14cebeSEric Cheng 			break;
1709*da14cebeSEric Cheng 		}
1710*da14cebeSEric Cheng 	}
1711*da14cebeSEric Cheng 
1712*da14cebeSEric Cheng 	if (optind == (argc - 1)) {
1713*da14cebeSEric Cheng 		if (strlen(argv[optind]) >= MAXFLOWNAME)
1714*da14cebeSEric Cheng 			die("flow name too long");
1715*da14cebeSEric Cheng 		state.fs_flow = argv[optind];
1716*da14cebeSEric Cheng 	} else if (optind != argc) {
1717*da14cebeSEric Cheng 		usage();
1718*da14cebeSEric Cheng 	}
1719*da14cebeSEric Cheng 	bzero(&state.fs_print, sizeof (print_state_t));
1720*da14cebeSEric Cheng 	state.fs_proplist = proplist;
1721*da14cebeSEric Cheng 	state.fs_status = DLADM_STATUS_OK;
1722*da14cebeSEric Cheng 
1723*da14cebeSEric Cheng 	fields = parse_output_fields(fields_str, flowprop_fields,
1724*da14cebeSEric Cheng 	    FLOWPROP_MAX_FIELDS, CMD_TYPE_ANY, &nfields);
1725*da14cebeSEric Cheng 
1726*da14cebeSEric Cheng 	if (fields == NULL) {
1727*da14cebeSEric Cheng 		die("invalid field(s) specified");
1728*da14cebeSEric Cheng 		return;
1729*da14cebeSEric Cheng 	}
1730*da14cebeSEric Cheng 
1731*da14cebeSEric Cheng 	state.fs_print.ps_fields = fields;
1732*da14cebeSEric Cheng 	state.fs_print.ps_nfields = nfields;
1733*da14cebeSEric Cheng 
1734*da14cebeSEric Cheng 	/* Show properties for one flow */
1735*da14cebeSEric Cheng 	if (state.fs_flow != NULL) {
1736*da14cebeSEric Cheng 		show_flowprop_one_flow(&state, state.fs_flow);
1737*da14cebeSEric Cheng 
1738*da14cebeSEric Cheng 	/* Show properties for all flows on one link */
1739*da14cebeSEric Cheng 	} else if (state.fs_linkid != DATALINK_INVALID_LINKID) {
1740*da14cebeSEric Cheng 		(void) show_flowprop_onelink(state.fs_linkid, &state);
1741*da14cebeSEric Cheng 
1742*da14cebeSEric Cheng 	/* Show properties for all flows on all links */
1743*da14cebeSEric Cheng 	} else {
1744*da14cebeSEric Cheng 		(void) dladm_walk_datalink_id(show_flowprop_onelink, &state,
1745*da14cebeSEric Cheng 		    DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE,
1746*da14cebeSEric Cheng 		    DLADM_OPT_ACTIVE);
1747*da14cebeSEric Cheng 	}
1748*da14cebeSEric Cheng 
1749*da14cebeSEric Cheng 	dladm_free_props(proplist);
1750*da14cebeSEric Cheng }
1751*da14cebeSEric Cheng 
1752*da14cebeSEric Cheng static void
1753*da14cebeSEric Cheng show_flowprop_one_flow(void *arg, const char *flow)
1754*da14cebeSEric Cheng {
1755*da14cebeSEric Cheng 	int			i;
1756*da14cebeSEric Cheng 	char			*buf;
1757*da14cebeSEric Cheng 	dladm_status_t		status;
1758*da14cebeSEric Cheng 	dladm_arg_list_t	*proplist = NULL;
1759*da14cebeSEric Cheng 	show_flowprop_state_t	*statep = arg;
1760*da14cebeSEric Cheng 	dladm_flow_attr_t	attr;
1761*da14cebeSEric Cheng 	const char		*savep;
1762*da14cebeSEric Cheng 
1763*da14cebeSEric Cheng 	/*
1764*da14cebeSEric Cheng 	 * Do not print flow props for invalid flows.
1765*da14cebeSEric Cheng 	 */
1766*da14cebeSEric Cheng 	if ((status = dladm_flow_info(flow, &attr)) != DLADM_STATUS_OK) {
1767*da14cebeSEric Cheng 		die("invalid flow: '%s'", flow);
1768*da14cebeSEric Cheng 	}
1769*da14cebeSEric Cheng 
1770*da14cebeSEric Cheng 	savep = statep->fs_flow;
1771*da14cebeSEric Cheng 	statep->fs_flow = flow;
1772*da14cebeSEric Cheng 
1773*da14cebeSEric Cheng 	proplist = statep->fs_proplist;
1774*da14cebeSEric Cheng 
1775*da14cebeSEric Cheng 	buf = malloc((sizeof (char *) + DLADM_PROP_VAL_MAX)
1776*da14cebeSEric Cheng 	    * DLADM_MAX_PROP_VALCNT + MAX_PROP_LINE);
1777*da14cebeSEric Cheng 	if (buf == NULL)
1778*da14cebeSEric Cheng 		die("insufficient memory");
1779*da14cebeSEric Cheng 
1780*da14cebeSEric Cheng 	statep->fs_propvals = (char **)(void *)buf;
1781*da14cebeSEric Cheng 	for (i = 0; i < DLADM_MAX_PROP_VALCNT; i++) {
1782*da14cebeSEric Cheng 		statep->fs_propvals[i] = buf +
1783*da14cebeSEric Cheng 		    sizeof (char *) * DLADM_MAX_PROP_VALCNT +
1784*da14cebeSEric Cheng 		    i * DLADM_PROP_VAL_MAX;
1785*da14cebeSEric Cheng 	}
1786*da14cebeSEric Cheng 	statep->fs_line = buf +
1787*da14cebeSEric Cheng 	    (sizeof (char *) + DLADM_PROP_VAL_MAX) * DLADM_MAX_PROP_VALCNT;
1788*da14cebeSEric Cheng 
1789*da14cebeSEric Cheng 	/* show only specified flow properties */
1790*da14cebeSEric Cheng 	if (proplist != NULL) {
1791*da14cebeSEric Cheng 		for (i = 0; i < proplist->al_count; i++) {
1792*da14cebeSEric Cheng 			if (show_one_flowprop(statep,
1793*da14cebeSEric Cheng 			    proplist->al_info[i].ai_name) != DLADM_STATUS_OK)
1794*da14cebeSEric Cheng 				break;
1795*da14cebeSEric Cheng 		}
1796*da14cebeSEric Cheng 
1797*da14cebeSEric Cheng 	/* show all flow properties */
1798*da14cebeSEric Cheng 	} else {
1799*da14cebeSEric Cheng 		status = dladm_walk_flowprop(show_one_flowprop, flow, statep);
1800*da14cebeSEric Cheng 		if (status != DLADM_STATUS_OK)
1801*da14cebeSEric Cheng 			die_dlerr(status, "show-flowprop");
1802*da14cebeSEric Cheng 	}
1803*da14cebeSEric Cheng 	free(buf);
1804*da14cebeSEric Cheng 	statep->fs_flow = savep;
1805*da14cebeSEric Cheng }
1806*da14cebeSEric Cheng 
1807*da14cebeSEric Cheng typedef struct {
1808*da14cebeSEric Cheng 	char	*s_buf;
1809*da14cebeSEric Cheng 	char	**s_fields;	/* array of pointer to the fields in s_buf */
1810*da14cebeSEric Cheng 	uint_t	s_nfields;	/* the number of fields in s_buf */
1811*da14cebeSEric Cheng } split_t;
1812*da14cebeSEric Cheng 
1813*da14cebeSEric Cheng /*
1814*da14cebeSEric Cheng  * Free the split_t structure pointed to by `sp'.
1815*da14cebeSEric Cheng  */
1816*da14cebeSEric Cheng static void
1817*da14cebeSEric Cheng splitfree(split_t *sp)
1818*da14cebeSEric Cheng {
1819*da14cebeSEric Cheng 	free(sp->s_buf);
1820*da14cebeSEric Cheng 	free(sp->s_fields);
1821*da14cebeSEric Cheng 	free(sp);
1822*da14cebeSEric Cheng }
1823*da14cebeSEric Cheng 
1824*da14cebeSEric Cheng /*
1825*da14cebeSEric Cheng  * Split `str' into at most `maxfields' fields, each field at most `maxlen' in
1826*da14cebeSEric Cheng  * length.  Return a pointer to a split_t containing the split fields, or NULL
1827*da14cebeSEric Cheng  * on failure.
1828*da14cebeSEric Cheng  */
1829*da14cebeSEric Cheng static split_t *
1830*da14cebeSEric Cheng split(const char *str, uint_t maxfields, uint_t maxlen)
1831*da14cebeSEric Cheng {
1832*da14cebeSEric Cheng 	char	*field, *token, *lasts = NULL;
1833*da14cebeSEric Cheng 	split_t	*sp;
1834*da14cebeSEric Cheng 
1835*da14cebeSEric Cheng 	if (*str == '\0' || maxfields == 0 || maxlen == 0)
1836*da14cebeSEric Cheng 		return (NULL);
1837*da14cebeSEric Cheng 
1838*da14cebeSEric Cheng 	sp = calloc(sizeof (split_t), 1);
1839*da14cebeSEric Cheng 	if (sp == NULL)
1840*da14cebeSEric Cheng 		return (NULL);
1841*da14cebeSEric Cheng 
1842*da14cebeSEric Cheng 	sp->s_buf = strdup(str);
1843*da14cebeSEric Cheng 	sp->s_fields = malloc(sizeof (char *) * maxfields);
1844*da14cebeSEric Cheng 	if (sp->s_buf == NULL || sp->s_fields == NULL)
1845*da14cebeSEric Cheng 		goto fail;
1846*da14cebeSEric Cheng 
1847*da14cebeSEric Cheng 	token = sp->s_buf;
1848*da14cebeSEric Cheng 	while ((field = strtok_r(token, ",", &lasts)) != NULL) {
1849*da14cebeSEric Cheng 		if (sp->s_nfields == maxfields || strlen(field) > maxlen)
1850*da14cebeSEric Cheng 			goto fail;
1851*da14cebeSEric Cheng 		token = NULL;
1852*da14cebeSEric Cheng 		sp->s_fields[sp->s_nfields++] = field;
1853*da14cebeSEric Cheng 	}
1854*da14cebeSEric Cheng 	return (sp);
1855*da14cebeSEric Cheng fail:
1856*da14cebeSEric Cheng 	splitfree(sp);
1857*da14cebeSEric Cheng 	return (NULL);
1858*da14cebeSEric Cheng }
1859*da14cebeSEric Cheng 
1860*da14cebeSEric Cheng static print_field_t **
1861*da14cebeSEric Cheng parse_output_fields(char *str, print_field_t *template, int max_fields,
1862*da14cebeSEric Cheng     uint_t cmdtype, uint_t *countp)
1863*da14cebeSEric Cheng {
1864*da14cebeSEric Cheng 	split_t		*sp;
1865*da14cebeSEric Cheng 	boolean_t	good_match = B_FALSE;
1866*da14cebeSEric Cheng 	uint_t		i, j;
1867*da14cebeSEric Cheng 	print_field_t	**pf = NULL;
1868*da14cebeSEric Cheng 
1869*da14cebeSEric Cheng 	sp = split(str, max_fields, MAX_FIELD_LEN);
1870*da14cebeSEric Cheng 
1871*da14cebeSEric Cheng 	if (sp == NULL)
1872*da14cebeSEric Cheng 		return (NULL);
1873*da14cebeSEric Cheng 
1874*da14cebeSEric Cheng 	pf = malloc(sp->s_nfields * sizeof (print_field_t *));
1875*da14cebeSEric Cheng 	if (pf == NULL)
1876*da14cebeSEric Cheng 		goto fail;
1877*da14cebeSEric Cheng 
1878*da14cebeSEric Cheng 	for (i = 0; i < sp->s_nfields; i++) {
1879*da14cebeSEric Cheng 		for (j = 0; j < max_fields; j++) {
1880*da14cebeSEric Cheng 			if (strcasecmp(sp->s_fields[i],
1881*da14cebeSEric Cheng 			    template[j].pf_name) == 0) {
1882*da14cebeSEric Cheng 				good_match = template[j]. pf_cmdtype & cmdtype;
1883*da14cebeSEric Cheng 				break;
1884*da14cebeSEric Cheng 			}
1885*da14cebeSEric Cheng 		}
1886*da14cebeSEric Cheng 		if (!good_match)
1887*da14cebeSEric Cheng 			goto fail;
1888*da14cebeSEric Cheng 
1889*da14cebeSEric Cheng 		good_match = B_FALSE;
1890*da14cebeSEric Cheng 		pf[i] = &template[j];
1891*da14cebeSEric Cheng 	}
1892*da14cebeSEric Cheng 	*countp = i;
1893*da14cebeSEric Cheng 	splitfree(sp);
1894*da14cebeSEric Cheng 	return (pf);
1895*da14cebeSEric Cheng fail:
1896*da14cebeSEric Cheng 	free(pf);
1897*da14cebeSEric Cheng 	splitfree(sp);
1898*da14cebeSEric Cheng 	return (NULL);
1899*da14cebeSEric Cheng }
1900*da14cebeSEric Cheng 
1901*da14cebeSEric Cheng static void
1902*da14cebeSEric Cheng flowadm_print_output(print_state_t *statep, boolean_t parseable,
1903*da14cebeSEric Cheng     print_callback_t fn, void *arg)
1904*da14cebeSEric Cheng {
1905*da14cebeSEric Cheng 	int i;
1906*da14cebeSEric Cheng 	char *value;
1907*da14cebeSEric Cheng 	print_field_t **pf;
1908*da14cebeSEric Cheng 
1909*da14cebeSEric Cheng 	pf = statep->ps_fields;
1910*da14cebeSEric Cheng 	for (i = 0; i < statep->ps_nfields; i++) {
1911*da14cebeSEric Cheng 		statep->ps_lastfield = (i + 1 == statep->ps_nfields);
1912*da14cebeSEric Cheng 		value = (*fn)(pf[i], arg);
1913*da14cebeSEric Cheng 		if (value != NULL)
1914*da14cebeSEric Cheng 			print_field(statep, pf[i], value, parseable);
1915*da14cebeSEric Cheng 	}
1916*da14cebeSEric Cheng 	(void) putchar('\n');
1917*da14cebeSEric Cheng }
1918*da14cebeSEric Cheng 
1919*da14cebeSEric Cheng static void
1920*da14cebeSEric Cheng print_header(print_state_t *ps)
1921*da14cebeSEric Cheng {
1922*da14cebeSEric Cheng 	int i;
1923*da14cebeSEric Cheng 	print_field_t **pf;
1924*da14cebeSEric Cheng 
1925*da14cebeSEric Cheng 	pf = ps->ps_fields;
1926*da14cebeSEric Cheng 	for (i = 0; i < ps->ps_nfields; i++) {
1927*da14cebeSEric Cheng 		ps->ps_lastfield = (i + 1 == ps->ps_nfields);
1928*da14cebeSEric Cheng 		print_field(ps, pf[i], pf[i]->pf_header, B_FALSE);
1929*da14cebeSEric Cheng 	}
1930*da14cebeSEric Cheng 	(void) putchar('\n');
1931*da14cebeSEric Cheng }
1932*da14cebeSEric Cheng 
1933*da14cebeSEric Cheng static void
1934*da14cebeSEric Cheng print_field(print_state_t *statep, print_field_t *pfp, const char *value,
1935*da14cebeSEric Cheng     boolean_t parseable)
1936*da14cebeSEric Cheng {
1937*da14cebeSEric Cheng 	uint_t	width = pfp->pf_width;
1938*da14cebeSEric Cheng 	uint_t	valwidth = strlen(value);
1939*da14cebeSEric Cheng 	uint_t	compress;
1940*da14cebeSEric Cheng 
1941*da14cebeSEric Cheng 	if (parseable) {
1942*da14cebeSEric Cheng 		(void) printf("%s=\"%s\"", pfp->pf_header, value);
1943*da14cebeSEric Cheng 	} else {
1944*da14cebeSEric Cheng 		if (value[0] == '\0')
1945*da14cebeSEric Cheng 			value = STR_UNDEF_VAL;
1946*da14cebeSEric Cheng 		if (statep->ps_lastfield) {
1947*da14cebeSEric Cheng 			(void) printf("%s", value);
1948*da14cebeSEric Cheng 			return;
1949*da14cebeSEric Cheng 		}
1950*da14cebeSEric Cheng 
1951*da14cebeSEric Cheng 		if (valwidth > width) {
1952*da14cebeSEric Cheng 			statep->ps_overflow += valwidth - width;
1953*da14cebeSEric Cheng 		} else if (valwidth < width && statep->ps_overflow > 0) {
1954*da14cebeSEric Cheng 			compress = min(statep->ps_overflow, width - valwidth);
1955*da14cebeSEric Cheng 			statep->ps_overflow -= compress;
1956*da14cebeSEric Cheng 			width -= compress;
1957*da14cebeSEric Cheng 		}
1958*da14cebeSEric Cheng 		(void) printf("%-*s", width, value);
1959*da14cebeSEric Cheng 	}
1960*da14cebeSEric Cheng 
1961*da14cebeSEric Cheng 	if (!statep->ps_lastfield)
1962*da14cebeSEric Cheng 		(void) putchar(' ');
1963*da14cebeSEric Cheng }
1964