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