xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ipmpstat/ipmpstat.c (revision 7bb0eb348e1119aed76a61d633a9106b6b9912f1)
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  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
22  * Use is subject to license terms.
23  */
24 
25 #include <alloca.h>
26 #include <arpa/inet.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <ipmp_admin.h>
30 #include <ipmp_query.h>
31 #include <libintl.h>
32 #include <libnvpair.h>
33 #include <libsysevent.h>
34 #include <locale.h>
35 #include <netdb.h>
36 #include <ofmt.h>
37 #include <signal.h>
38 #include <stdarg.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <sys/sysevent/eventdefs.h>
44 #include <sys/sysevent/ipmp.h>
45 #include <sys/sysmacros.h>
46 #include <sys/termios.h>
47 #include <sys/types.h>
48 
49 /*
50  * ipmpstat -- display IPMP subsystem status.
51  *
52  * This utility makes extensive use of libipmp and IPMP sysevents to gather
53  * and pretty-print the status of the IPMP subsystem.  All output formats
54  * except for -p (probe) use libipmp to create a point-in-time snapshot of the
55  * IPMP subsystem (unless the test-special -L flag is used), and then output
56  * the contents of that snapshot in a user-specified manner.  Because the
57  * output format and requested fields aren't known until run-time, three sets
58  * of function pointers and two core data structures are used. Specifically:
59  *
60  *      * The ipmpstat_walker_t function pointers (walk_*) iterate through
61  *	  all instances of a given IPMP object (group, interface, or address).
62  *	  At most one ipmpstat_walker_t is used per ipmpstat invocation.
63  *	  Since target information is included with the interface information,
64  *	  both -i and -t use the interface walker (walk_if()).
65  *
66  *      * The ofmt_sfunc_t function pointers (sfunc_*) obtain a given value
67  *	  for a given IPMP object.  Each ofmt_sfunc_t is passed a buffer to
68  *	  write its result into, the buffer's size, and an ipmpstat_sfunc_arg_t
69  *	  state structure.  The state structure consists of a pointer to the
70  *	  IPMP object to obtain information from (sa_data), and an open libipmp
71  *	  handle (sa_ih) which can be used to do additional libipmp queries, if
72  *	  necessary (e.g., because the object does not have all of the needed
73  *	  information).
74  *
75  *	* The ofmt_field_t arrays (*_fields[]) provide the supported fields for
76  *	  a given output format, along with output formatting information
77  *	  (e.g., field width) and a pointer to an ofmt_sfunc_t function that
78  *	  can obtain the value for a given IPMP object.  One ofmt_field_t array
79  *	  is used per ipmpstat invocation, and is passed to ofmt_open() (along
80  *	  with the output fields and modes requested by the user) to create an
81  *	  ofmt_t.
82  *
83  *      * The ofmt_t structure is a handle that tracks all information
84  *        related to output formatting and is used by libinetutil`ofmt_print()
85  *	  (indirectly through our local ofmt_output() utility routine) to
86  *	  output a single line of information about the provided IPMP object.
87  *
88  *	* The ipmpstat_cbfunc_t function pointers (*_cbfunc) are called back
89  *	  by the walkers.  They are used both internally to implement nested
90  *	  walks, and by the ipmpstat output logic to provide the glue between
91  *	  the IPMP object walkers and the ofmt_output() logic.  Usually, a
92  *	  single line is output for each IPMP object, and thus ofmt_output()
93  *	  can be directly invoked (see info_output_cbfunc()).  However, if
94  *	  multiple lines need to be output, then a more complex cbfunc is
95  *	  needed (see targinfo_output_cbfunc()).  At most one cbfunc is used
96  *	  per ipmpstat invocation.
97  */
98 
99 /*
100  * Data type used by the sfunc callbacks to obtain the requested information
101  * from the agreed-upon object.
102  */
103 typedef struct ipmpstat_sfunc_arg {
104 	ipmp_handle_t		sa_ih;
105 	void			*sa_data;
106 } ipmpstat_sfunc_arg_t;
107 
108 /*
109  * Function pointers used to iterate through IPMP objects.
110  */
111 typedef void ipmpstat_cbfunc_t(ipmp_handle_t, void *, void *);
112 typedef void ipmpstat_walker_t(ipmp_handle_t, ipmpstat_cbfunc_t *, void *);
113 
114 /*
115  * Data type used to implement nested walks.
116  */
117 typedef struct ipmpstat_walkdata {
118 	ipmpstat_cbfunc_t	*iw_func; 	/* caller-specified callback */
119 	void			*iw_funcarg; 	/* caller-specified arg */
120 } ipmpstat_walkdata_t;
121 
122 /*
123  * Data type used by enum2str() to map an enumerated value to a string.
124  */
125 typedef struct ipmpstat_enum {
126 	const char		*e_name;	/* string */
127 	int			e_val;		/* value */
128 } ipmpstat_enum_t;
129 
130 /*
131  * Data type used to pass state between probe_output() and probe_event().
132  */
133 typedef struct ipmpstat_probe_state {
134 	ipmp_handle_t	ps_ih;		/* open IPMP handle */
135 	ofmt_handle_t	ps_ofmt;	/* open formatted-output handle */
136 } ipmpstat_probe_state_t;
137 
138 /*
139  * Options that modify the output mode; more than one may be lit.
140  */
141 typedef enum {
142 	IPMPSTAT_OPT_NUMERIC	= 0x1,
143 	IPMPSTAT_OPT_PARSABLE 	= 0x2
144 } ipmpstat_opt_t;
145 
146 /*
147  * Indices for the FLAGS field of the `-i' output format.
148  */
149 enum {
150 	IPMPSTAT_IFLAG_INDEX,	IPMPSTAT_SFLAG_INDEX,	IPMPSTAT_M4FLAG_INDEX,
151 	IPMPSTAT_BFLAG_INDEX,	IPMPSTAT_M6FLAG_INDEX,	IPMPSTAT_DFLAG_INDEX,
152 	IPMPSTAT_HFLAG_INDEX,	IPMPSTAT_NUM_FLAGS
153 };
154 
155 #define	IPMPSTAT_NCOL	80
156 #define	NS2FLOATMS(ns)	(NSEC2MSEC((float)(ns)))
157 #define	MS2FLOATSEC(ms)	((float)(ms) / 1000)
158 
159 static const char	*progname;
160 static hrtime_t		probe_output_start;
161 static ipmpstat_opt_t	opt;
162 static ofmt_handle_t	ofmt;
163 static ipmpstat_enum_t	addr_state[], group_state[], if_state[], if_link[];
164 static ipmpstat_enum_t	if_probe[], targ_mode[];
165 static ofmt_field_t	addr_fields[], group_fields[], if_fields[];
166 static ofmt_field_t	probe_fields[], targ_fields[];
167 static ipmpstat_cbfunc_t walk_addr_cbfunc, walk_if_cbfunc;
168 static ipmpstat_cbfunc_t info_output_cbfunc, targinfo_output_cbfunc;
169 static ipmpstat_walker_t walk_addr, walk_if, walk_group;
170 
171 static int probe_event(sysevent_t *, void *);
172 static void probe_output(ipmp_handle_t, ofmt_handle_t);
173 static void ofmt_output(ofmt_handle_t, ipmp_handle_t, void *);
174 static void enum2str(const ipmpstat_enum_t *, int, char *, uint_t);
175 static void sockaddr2str(const struct sockaddr_storage *, char *, uint_t);
176 static void sighandler(int);
177 static void usage(void);
178 static void die(const char *, ...);
179 static void die_ipmperr(int, const char *, ...);
180 static void warn(const char *, ...);
181 static void warn_ipmperr(int, const char *, ...);
182 
183 int
184 main(int argc, char **argv)
185 {
186 	int c;
187 	int err;
188 	const char *ofields = NULL;
189 	ofmt_status_t ofmterr;
190 	ofmt_field_t *fields = NULL;
191 	uint_t ofmtflags = 0;
192 	ipmp_handle_t ih;
193 	ipmp_qcontext_t qcontext = IPMP_QCONTEXT_SNAP;
194 	ipmpstat_cbfunc_t *cbfunc;
195 	ipmpstat_walker_t *walker;
196 	char errbuf[OFMT_BUFSIZE];
197 
198 	if ((progname = strrchr(argv[0], '/')) == NULL)
199 		progname = argv[0];
200 	else
201 		progname++;
202 
203 	(void) setlocale(LC_ALL, "");
204 	(void) textdomain(TEXT_DOMAIN);
205 
206 	while ((c = getopt(argc, argv, "nLPo:agipt")) != EOF) {
207 		if (fields != NULL && strchr("agipt", c) != NULL)
208 			die("only one output format may be specified\n");
209 
210 		switch (c) {
211 		case 'n':
212 			opt |= IPMPSTAT_OPT_NUMERIC;
213 			break;
214 		case 'L':
215 			/* Undocumented option: for testing use ONLY */
216 			qcontext = IPMP_QCONTEXT_LIVE;
217 			break;
218 		case 'P':
219 			opt |= IPMPSTAT_OPT_PARSABLE;
220 			ofmtflags |= OFMT_PARSABLE;
221 			break;
222 		case 'o':
223 			ofields = optarg;
224 			break;
225 		case 'a':
226 			walker = walk_addr;
227 			cbfunc = info_output_cbfunc;
228 			fields = addr_fields;
229 			break;
230 		case 'g':
231 			walker = walk_group;
232 			cbfunc = info_output_cbfunc;
233 			fields = group_fields;
234 			break;
235 		case 'i':
236 			walker = walk_if;
237 			cbfunc = info_output_cbfunc;
238 			fields = if_fields;
239 			break;
240 		case 'p':
241 			fields = probe_fields;
242 			break;
243 		case 't':
244 			walker = walk_if;
245 			cbfunc = targinfo_output_cbfunc;
246 			fields = targ_fields;
247 			break;
248 		default:
249 			usage();
250 			break;
251 		}
252 	}
253 
254 	if (argc > optind || fields == NULL)
255 		usage();
256 
257 	/*
258 	 * Open a handle to the formatted output engine.
259 	 */
260 	ofmterr = ofmt_open(ofields, fields, ofmtflags, IPMPSTAT_NCOL, &ofmt);
261 	if (ofmterr != OFMT_SUCCESS) {
262 		/*
263 		 * If some fields were badly formed in human-friendly mode, we
264 		 * emit a warning and continue.  Otherwise exit immediately.
265 		 */
266 		(void) ofmt_strerror(ofmt, ofmterr, errbuf, sizeof (errbuf));
267 		if (ofmterr != OFMT_EBADFIELDS || (opt & IPMPSTAT_OPT_PARSABLE))
268 			die("%s\n", errbuf);
269 		else
270 			warn("%s\n", errbuf);
271 	}
272 
273 	/*
274 	 * Obtain the window size and monitor changes to the size.  This data
275 	 * is used to redisplay the output headers when necessary.
276 	 */
277 	(void) sigset(SIGWINCH, sighandler);
278 
279 	if ((err = ipmp_open(&ih)) != IPMP_SUCCESS)
280 		die_ipmperr(err, "cannot create IPMP handle");
281 
282 	if (ipmp_ping_daemon(ih) != IPMP_SUCCESS)
283 		die("cannot contact in.mpathd(8) -- is IPMP in use?\n");
284 
285 	/*
286 	 * If we've been asked to display probes, then call the probe output
287 	 * function.  Otherwise, snapshot IPMP state (or use live state) and
288 	 * invoke the specified walker with the specified callback function.
289 	 */
290 	if (fields == probe_fields) {
291 		probe_output(ih, ofmt);
292 	} else {
293 		if ((err = ipmp_setqcontext(ih, qcontext)) != IPMP_SUCCESS) {
294 			if (qcontext == IPMP_QCONTEXT_SNAP)
295 				die_ipmperr(err, "cannot snapshot IPMP state");
296 			else
297 				die_ipmperr(err, "cannot use live IPMP state");
298 		}
299 		(*walker)(ih, cbfunc, ofmt);
300 	}
301 
302 	ofmt_close(ofmt);
303 	ipmp_close(ih);
304 
305 	return (EXIT_SUCCESS);
306 }
307 
308 /*
309  * Walks all IPMP groups on the system and invokes `cbfunc' on each, passing
310  * it `ih', the ipmp_groupinfo_t pointer, and `arg'.
311  */
312 static void
313 walk_group(ipmp_handle_t ih, ipmpstat_cbfunc_t *cbfunc, void *arg)
314 {
315 	int err;
316 	uint_t i;
317 	ipmp_groupinfo_t *grinfop;
318 	ipmp_grouplist_t *grlistp;
319 
320 	if ((err = ipmp_getgrouplist(ih, &grlistp)) != IPMP_SUCCESS)
321 		die_ipmperr(err, "cannot get IPMP group list");
322 
323 	for (i = 0; i < grlistp->gl_ngroup; i++) {
324 		err = ipmp_getgroupinfo(ih, grlistp->gl_groups[i], &grinfop);
325 		if (err != IPMP_SUCCESS) {
326 			warn_ipmperr(err, "cannot get info for group `%s'",
327 			    grlistp->gl_groups[i]);
328 			continue;
329 		}
330 		(*cbfunc)(ih, grinfop, arg);
331 		ipmp_freegroupinfo(grinfop);
332 	}
333 
334 	ipmp_freegrouplist(grlistp);
335 }
336 
337 /*
338  * Walks all IPMP interfaces on the system and invokes `cbfunc' on each,
339  * passing it `ih', the ipmp_ifinfo_t pointer, and `arg'.
340  */
341 static void
342 walk_if(ipmp_handle_t ih, ipmpstat_cbfunc_t *cbfunc, void *arg)
343 {
344 	ipmpstat_walkdata_t iw = { cbfunc, arg };
345 
346 	walk_group(ih, walk_if_cbfunc, &iw);
347 }
348 
349 /*
350  * Walks all IPMP data addresses on the system and invokes `cbfunc' on each.
351  * passing it `ih', the ipmp_addrinfo_t pointer, and `arg'.
352  */
353 static void
354 walk_addr(ipmp_handle_t ih, ipmpstat_cbfunc_t *cbfunc, void *arg)
355 {
356 	ipmpstat_walkdata_t iw = { cbfunc, arg };
357 
358 	walk_group(ih, walk_addr_cbfunc, &iw);
359 }
360 
361 /*
362  * Nested walker callback function for walk_if().
363  */
364 static void
365 walk_if_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
366 {
367 	int err;
368 	uint_t i;
369 	ipmp_groupinfo_t *grinfop = infop;
370 	ipmp_ifinfo_t *ifinfop;
371 	ipmp_iflist_t *iflistp = grinfop->gr_iflistp;
372 	ipmpstat_walkdata_t *iwp = arg;
373 
374 	for (i = 0; i < iflistp->il_nif; i++) {
375 		err = ipmp_getifinfo(ih, iflistp->il_ifs[i], &ifinfop);
376 		if (err != IPMP_SUCCESS) {
377 			warn_ipmperr(err, "cannot get info for interface `%s'",
378 			    iflistp->il_ifs[i]);
379 			continue;
380 		}
381 		(*iwp->iw_func)(ih, ifinfop, iwp->iw_funcarg);
382 		ipmp_freeifinfo(ifinfop);
383 	}
384 }
385 
386 /*
387  * Nested walker callback function for walk_addr().
388  */
389 static void
390 walk_addr_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
391 {
392 	int err;
393 	uint_t i;
394 	ipmp_groupinfo_t *grinfop = infop;
395 	ipmp_addrinfo_t *adinfop;
396 	ipmp_addrlist_t *adlistp = grinfop->gr_adlistp;
397 	ipmpstat_walkdata_t *iwp = arg;
398 	char addr[INET6_ADDRSTRLEN];
399 	struct sockaddr_storage *addrp;
400 
401 	for (i = 0; i < adlistp->al_naddr; i++) {
402 		addrp = &adlistp->al_addrs[i];
403 		err = ipmp_getaddrinfo(ih, grinfop->gr_name, addrp, &adinfop);
404 		if (err != IPMP_SUCCESS) {
405 			sockaddr2str(addrp, addr, sizeof (addr));
406 			warn_ipmperr(err, "cannot get info for `%s'", addr);
407 			continue;
408 		}
409 		(*iwp->iw_func)(ih, adinfop, iwp->iw_funcarg);
410 		ipmp_freeaddrinfo(adinfop);
411 	}
412 }
413 
414 static boolean_t
415 sfunc_nvwarn(const char *nvname)
416 {
417 	warn("cannot retrieve %s\n", nvname);
418 	return (B_FALSE);
419 }
420 
421 static boolean_t
422 sfunc_addr_address(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
423 {
424 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
425 	ipmp_addrinfo_t *adinfop = arg->sa_data;
426 
427 	sockaddr2str(&adinfop->ad_addr, buf, bufsize);
428 	return (B_TRUE);
429 }
430 
431 static boolean_t
432 sfunc_addr_group(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
433 {
434 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
435 	int err;
436 	ipmp_addrinfo_t *adinfop = arg->sa_data;
437 	ipmp_groupinfo_t *grinfop;
438 
439 	err = ipmp_getgroupinfo(arg->sa_ih, adinfop->ad_group, &grinfop);
440 	if (err != IPMP_SUCCESS) {
441 		warn_ipmperr(err, "cannot get info for group `%s'",
442 		    adinfop->ad_group);
443 		return (B_FALSE);
444 	}
445 	(void) strlcpy(buf, grinfop->gr_ifname, bufsize);
446 	ipmp_freegroupinfo(grinfop);
447 	return (B_TRUE);
448 }
449 
450 static boolean_t
451 sfunc_addr_state(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
452 {
453 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
454 	ipmp_addrinfo_t *adinfop = arg->sa_data;
455 
456 	enum2str(addr_state, adinfop->ad_state, buf, bufsize);
457 	return (B_TRUE);
458 }
459 
460 static boolean_t
461 sfunc_addr_inbound(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
462 {
463 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
464 	ipmp_addrinfo_t *adinfop = arg->sa_data;
465 
466 	(void) strlcpy(buf, adinfop->ad_binding, bufsize);
467 	return (B_TRUE);
468 }
469 
470 static boolean_t
471 sfunc_addr_outbound(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
472 {
473 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
474 	int err;
475 	uint_t i, nactive = 0;
476 	ipmp_ifinfo_t *ifinfop;
477 	ipmp_iflist_t *iflistp;
478 	ipmp_addrinfo_t *adinfop = arg->sa_data;
479 	ipmp_groupinfo_t *grinfop;
480 
481 	if (adinfop->ad_state == IPMP_ADDR_DOWN)
482 		return (B_TRUE);
483 
484 	/*
485 	 * If there's no inbound interface for this address, there can't
486 	 * be any outbound traffic.
487 	 */
488 	if (adinfop->ad_binding[0] == '\0')
489 		return (B_TRUE);
490 
491 	/*
492 	 * The address can use any active interface in the group, so
493 	 * obtain all of those.
494 	 */
495 	err = ipmp_getgroupinfo(arg->sa_ih, adinfop->ad_group, &grinfop);
496 	if (err != IPMP_SUCCESS) {
497 		warn_ipmperr(err, "cannot get info for group `%s'",
498 		    adinfop->ad_group);
499 		return (B_FALSE);
500 	}
501 
502 	iflistp = grinfop->gr_iflistp;
503 	for (i = 0; i < iflistp->il_nif; i++) {
504 		err = ipmp_getifinfo(arg->sa_ih, iflistp->il_ifs[i], &ifinfop);
505 		if (err != IPMP_SUCCESS) {
506 			warn_ipmperr(err, "cannot get info for interface `%s'",
507 			    iflistp->il_ifs[i]);
508 			continue;
509 		}
510 
511 		if (ifinfop->if_flags & IPMP_IFFLAG_ACTIVE) {
512 			if (nactive++ != 0)
513 				(void) strlcat(buf, " ", bufsize);
514 			(void) strlcat(buf, ifinfop->if_name, bufsize);
515 		}
516 		ipmp_freeifinfo(ifinfop);
517 	}
518 	ipmp_freegroupinfo(grinfop);
519 	return (B_TRUE);
520 }
521 
522 static boolean_t
523 sfunc_group_name(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
524 {
525 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
526 	ipmp_groupinfo_t *grinfop = arg->sa_data;
527 
528 	(void) strlcpy(buf, grinfop->gr_name, bufsize);
529 	return (B_TRUE);
530 }
531 
532 static boolean_t
533 sfunc_group_ifname(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
534 {
535 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
536 	ipmp_groupinfo_t *grinfop = arg->sa_data;
537 
538 	(void) strlcpy(buf, grinfop->gr_ifname, bufsize);
539 	return (B_TRUE);
540 }
541 
542 static boolean_t
543 sfunc_group_state(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
544 {
545 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
546 	ipmp_groupinfo_t *grinfop = arg->sa_data;
547 
548 	enum2str(group_state, grinfop->gr_state, buf, bufsize);
549 	return (B_TRUE);
550 }
551 
552 static boolean_t
553 sfunc_group_fdt(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
554 {
555 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
556 	ipmp_groupinfo_t *grinfop = arg->sa_data;
557 
558 	if (grinfop->gr_fdt == 0)
559 		return (B_TRUE);
560 
561 	(void) snprintf(buf, bufsize, "%.2fs", MS2FLOATSEC(grinfop->gr_fdt));
562 	return (B_TRUE);
563 }
564 
565 static boolean_t
566 sfunc_group_interfaces(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
567 {
568 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
569 	int err;
570 	uint_t i;
571 	char *active, *inactive, *unusable;
572 	uint_t nactive = 0, ninactive = 0, nunusable = 0;
573 	ipmp_groupinfo_t *grinfop = arg->sa_data;
574 	ipmp_iflist_t *iflistp = grinfop->gr_iflistp;
575 	ipmp_ifinfo_t *ifinfop;
576 
577 	active = alloca(bufsize);
578 	active[0] = '\0';
579 	inactive = alloca(bufsize);
580 	inactive[0] = '\0';
581 	unusable = alloca(bufsize);
582 	unusable[0] = '\0';
583 
584 	for (i = 0; i < iflistp->il_nif; i++) {
585 		err = ipmp_getifinfo(arg->sa_ih, iflistp->il_ifs[i], &ifinfop);
586 		if (err != IPMP_SUCCESS) {
587 			warn_ipmperr(err, "cannot get info for interface `%s'",
588 			    iflistp->il_ifs[i]);
589 			continue;
590 		}
591 
592 		if (ifinfop->if_flags & IPMP_IFFLAG_ACTIVE) {
593 			if (nactive++ != 0)
594 				(void) strlcat(active, " ", bufsize);
595 			(void) strlcat(active, ifinfop->if_name, bufsize);
596 		} else if (ifinfop->if_flags & IPMP_IFFLAG_INACTIVE) {
597 			if (ninactive++ != 0)
598 				(void) strlcat(inactive, " ", bufsize);
599 			(void) strlcat(inactive, ifinfop->if_name, bufsize);
600 		} else {
601 			if (nunusable++ != 0)
602 				(void) strlcat(unusable, " ", bufsize);
603 			(void) strlcat(unusable, ifinfop->if_name, bufsize);
604 		}
605 
606 		ipmp_freeifinfo(ifinfop);
607 	}
608 
609 	(void) strlcpy(buf, active, bufsize);
610 
611 	if (ninactive > 0) {
612 		if (nactive != 0)
613 			(void) strlcat(buf, " ", bufsize);
614 
615 		(void) strlcat(buf, "(", bufsize);
616 		(void) strlcat(buf, inactive, bufsize);
617 		(void) strlcat(buf, ")", bufsize);
618 	}
619 
620 	if (nunusable > 0) {
621 		if (nactive + ninactive != 0)
622 			(void) strlcat(buf, " ", bufsize);
623 
624 		(void) strlcat(buf, "[", bufsize);
625 		(void) strlcat(buf, unusable, bufsize);
626 		(void) strlcat(buf, "]", bufsize);
627 	}
628 	return (B_TRUE);
629 }
630 
631 static boolean_t
632 sfunc_if_name(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
633 {
634 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
635 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
636 
637 	(void) strlcpy(buf, ifinfop->if_name, bufsize);
638 	return (B_TRUE);
639 }
640 
641 static boolean_t
642 sfunc_if_active(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
643 {
644 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
645 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
646 
647 	if (ifinfop->if_flags & IPMP_IFFLAG_ACTIVE)
648 		(void) strlcpy(buf, "yes", bufsize);
649 	else
650 		(void) strlcpy(buf, "no", bufsize);
651 	return (B_TRUE);
652 }
653 
654 static boolean_t
655 sfunc_if_group(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
656 {
657 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
658 	int err;
659 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
660 	ipmp_groupinfo_t *grinfop;
661 
662 	err = ipmp_getgroupinfo(arg->sa_ih, ifinfop->if_group, &grinfop);
663 	if (err != IPMP_SUCCESS) {
664 		warn_ipmperr(err, "cannot get info for group `%s'",
665 		    ifinfop->if_group);
666 		return (B_TRUE);
667 	}
668 
669 	(void) strlcpy(buf, grinfop->gr_ifname, bufsize);
670 	ipmp_freegroupinfo(grinfop);
671 	return (B_TRUE);
672 }
673 
674 static boolean_t
675 sfunc_if_flags(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
676 {
677 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
678 	int err;
679 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
680 	ipmp_groupinfo_t *grinfop;
681 
682 	assert(bufsize > IPMPSTAT_NUM_FLAGS);
683 
684 	(void) memset(buf, '-', IPMPSTAT_NUM_FLAGS);
685 	buf[IPMPSTAT_NUM_FLAGS] = '\0';
686 
687 	if (ifinfop->if_type == IPMP_IF_STANDBY)
688 		buf[IPMPSTAT_SFLAG_INDEX] = 's';
689 
690 	if (ifinfop->if_flags & IPMP_IFFLAG_INACTIVE)
691 		buf[IPMPSTAT_IFLAG_INDEX] = 'i';
692 
693 	if (ifinfop->if_flags & IPMP_IFFLAG_DOWN)
694 		buf[IPMPSTAT_DFLAG_INDEX] = 'd';
695 
696 	if (ifinfop->if_flags & IPMP_IFFLAG_HWADDRDUP)
697 		buf[IPMPSTAT_HFLAG_INDEX] = 'h';
698 
699 	err = ipmp_getgroupinfo(arg->sa_ih, ifinfop->if_group, &grinfop);
700 	if (err != IPMP_SUCCESS) {
701 		warn_ipmperr(err, "cannot get broadcast/multicast info for "
702 		    "group `%s'", ifinfop->if_group);
703 		return (B_TRUE);
704 	}
705 
706 	if (strcmp(grinfop->gr_m4ifname, ifinfop->if_name) == 0)
707 		buf[IPMPSTAT_M4FLAG_INDEX] = 'm';
708 
709 	if (strcmp(grinfop->gr_m6ifname, ifinfop->if_name) == 0)
710 		buf[IPMPSTAT_M6FLAG_INDEX] = 'M';
711 
712 	if (strcmp(grinfop->gr_bcifname, ifinfop->if_name) == 0)
713 		buf[IPMPSTAT_BFLAG_INDEX] = 'b';
714 
715 	ipmp_freegroupinfo(grinfop);
716 	return (B_TRUE);
717 }
718 
719 static boolean_t
720 sfunc_if_link(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
721 {
722 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
723 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
724 
725 	enum2str(if_link, ifinfop->if_linkstate, buf, bufsize);
726 	return (B_TRUE);
727 }
728 
729 static boolean_t
730 sfunc_if_probe(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
731 {
732 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
733 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
734 
735 	enum2str(if_probe, ifinfop->if_probestate, buf, bufsize);
736 	return (B_TRUE);
737 }
738 
739 static boolean_t
740 sfunc_if_state(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
741 {
742 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
743 	ipmp_ifinfo_t *ifinfop = arg->sa_data;
744 
745 	enum2str(if_state, ifinfop->if_state, buf, bufsize);
746 	return (B_TRUE);
747 }
748 
749 static boolean_t
750 sfunc_probe_id(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
751 {
752 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
753 	uint32_t probe_id;
754 	nvlist_t *nvl = arg->sa_data;
755 
756 	if (nvlist_lookup_uint32(nvl, IPMP_PROBE_ID, &probe_id) != 0)
757 		return (sfunc_nvwarn("IPMP_PROBE_ID"));
758 
759 	(void) snprintf(buf, bufsize, "%u", probe_id);
760 	return (B_TRUE);
761 }
762 
763 static boolean_t
764 sfunc_probe_ifname(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
765 {
766 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
767 	char *ifname;
768 	nvlist_t *nvl = arg->sa_data;
769 
770 	if (nvlist_lookup_string(nvl, IPMP_IF_NAME, &ifname) != 0)
771 		return (sfunc_nvwarn("IPMP_IF_NAME"));
772 
773 	(void) strlcpy(buf, ifname, bufsize);
774 	return (B_TRUE);
775 }
776 
777 static boolean_t
778 sfunc_probe_time(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
779 {
780 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
781 	hrtime_t start;
782 	nvlist_t *nvl = arg->sa_data;
783 
784 	if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_START_TIME, &start) != 0)
785 		return (sfunc_nvwarn("IPMP_PROBE_START_TIME"));
786 
787 	(void) snprintf(buf, bufsize, "%.2fs",
788 	    (float)(start - probe_output_start) / NANOSEC);
789 	return (B_TRUE);
790 }
791 
792 static boolean_t
793 sfunc_probe_target(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
794 {
795 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
796 	uint_t nelem;
797 	struct sockaddr_storage *target;
798 	nvlist_t *nvl = arg->sa_data;
799 
800 	if (nvlist_lookup_byte_array(nvl, IPMP_PROBE_TARGET,
801 	    (uchar_t **)&target, &nelem) != 0)
802 		return (sfunc_nvwarn("IPMP_PROBE_TARGET"));
803 
804 	sockaddr2str(target, buf, bufsize);
805 	return (B_TRUE);
806 }
807 
808 static boolean_t
809 sfunc_probe_rtt(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
810 {
811 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
812 	hrtime_t start, ackproc;
813 	nvlist_t *nvl = arg->sa_data;
814 	uint32_t state;
815 
816 	if (nvlist_lookup_uint32(nvl, IPMP_PROBE_STATE, &state) != 0)
817 		return (sfunc_nvwarn("IPMP_PROBE_STATE"));
818 
819 	if (state != IPMP_PROBE_ACKED)
820 		return (B_TRUE);
821 
822 	if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_START_TIME, &start) != 0)
823 		return (sfunc_nvwarn("IPMP_PROBE_START_TIME"));
824 
825 	if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_ACKPROC_TIME, &ackproc) != 0)
826 		return (sfunc_nvwarn("IPMP_PROBE_ACKPROC_TIME"));
827 
828 	(void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(ackproc - start));
829 	return (B_TRUE);
830 }
831 
832 static boolean_t
833 sfunc_probe_netrtt(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
834 {
835 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
836 	hrtime_t sent, ackrecv;
837 	nvlist_t *nvl = arg->sa_data;
838 	uint32_t state;
839 
840 	if (nvlist_lookup_uint32(nvl, IPMP_PROBE_STATE, &state) != 0)
841 		return (sfunc_nvwarn("IPMP_PROBE_STATE"));
842 
843 	if (state != IPMP_PROBE_ACKED)
844 		return (B_TRUE);
845 
846 	if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_SENT_TIME, &sent) != 0)
847 		return (sfunc_nvwarn("IPMP_PROBE_SENT_TIME"));
848 
849 	if (nvlist_lookup_hrtime(nvl, IPMP_PROBE_ACKRECV_TIME, &ackrecv) != 0)
850 		return (sfunc_nvwarn("IPMP_PROBE_ACKRECV_TIME"));
851 
852 	(void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(ackrecv - sent));
853 	return (B_TRUE);
854 }
855 
856 static boolean_t
857 sfunc_probe_rttavg(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
858 {
859 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
860 	int64_t rttavg;
861 	nvlist_t *nvl = arg->sa_data;
862 
863 	if (nvlist_lookup_int64(nvl, IPMP_PROBE_TARGET_RTTAVG, &rttavg) != 0)
864 		return (sfunc_nvwarn("IPMP_PROBE_TARGET_RTTAVG"));
865 
866 	if (rttavg != 0)
867 		(void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(rttavg));
868 	return (B_TRUE);
869 }
870 
871 static boolean_t
872 sfunc_probe_rttdev(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
873 {
874 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
875 	int64_t rttdev;
876 	nvlist_t *nvl = arg->sa_data;
877 
878 	if (nvlist_lookup_int64(nvl, IPMP_PROBE_TARGET_RTTDEV, &rttdev) != 0)
879 		return (sfunc_nvwarn("IPMP_PROBE_TARGET_RTTDEV"));
880 
881 	if (rttdev != 0)
882 		(void) snprintf(buf, bufsize, "%.2fms", NS2FLOATMS(rttdev));
883 	return (B_TRUE);
884 }
885 
886 /* ARGSUSED */
887 static void
888 probe_enabled_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
889 {
890 	uint_t *nenabledp = arg;
891 	ipmp_ifinfo_t *ifinfop = infop;
892 
893 	if (ifinfop->if_probestate != IPMP_PROBE_DISABLED)
894 		(*nenabledp)++;
895 }
896 
897 static void
898 probe_output(ipmp_handle_t ih, ofmt_handle_t ofmt)
899 {
900 	char sub[MAX_SUBID_LEN];
901 	evchan_t *evch;
902 	ipmpstat_probe_state_t ps = { ih, ofmt };
903 	uint_t nenabled = 0;
904 
905 	/*
906 	 * Check if any interfaces are enabled for probe-based failure
907 	 * detection.  If not, immediately fail.
908 	 */
909 	walk_if(ih, probe_enabled_cbfunc, &nenabled);
910 	if (nenabled == 0)
911 		die("probe-based failure detection is disabled\n");
912 
913 	probe_output_start = gethrtime();
914 
915 	/*
916 	 * Unfortunately, until 4791900 is fixed, only privileged processes
917 	 * can bind and thus receive sysevents.
918 	 */
919 	errno = sysevent_evc_bind(IPMP_EVENT_CHAN, &evch, EVCH_CREAT);
920 	if (errno != 0) {
921 		if (errno == EPERM)
922 			die("insufficient privileges for -p\n");
923 		die("sysevent_evc_bind to channel %s failed", IPMP_EVENT_CHAN);
924 	}
925 
926 	/*
927 	 * The subscriber must be unique in order for sysevent_evc_subscribe()
928 	 * to succeed, so combine our name and pid.
929 	 */
930 	(void) snprintf(sub, sizeof (sub), "%d-%s", getpid(), progname);
931 
932 	errno = sysevent_evc_subscribe(evch, sub, EC_IPMP, probe_event, &ps, 0);
933 	if (errno != 0)
934 		die("sysevent_evc_subscribe for class %s failed", EC_IPMP);
935 
936 	for (;;)
937 		(void) pause();
938 }
939 
940 static int
941 probe_event(sysevent_t *ev, void *arg)
942 {
943 	nvlist_t *nvl;
944 	uint32_t state;
945 	uint32_t version;
946 	ipmpstat_probe_state_t *psp = arg;
947 
948 	if (strcmp(sysevent_get_subclass_name(ev), ESC_IPMP_PROBE_STATE) != 0)
949 		return (0);
950 
951 	if (sysevent_get_attr_list(ev, &nvl) != 0) {
952 		warn("sysevent_get_attr_list failed; dropping event");
953 		return (0);
954 	}
955 
956 	if (nvlist_lookup_uint32(nvl, IPMP_EVENT_VERSION, &version) != 0) {
957 		warn("dropped event with no IPMP_EVENT_VERSION\n");
958 		goto out;
959 	}
960 
961 	if (version != IPMP_EVENT_CUR_VERSION) {
962 		warn("dropped event with unsupported IPMP_EVENT_VERSION %d\n",
963 		    version);
964 		goto out;
965 	}
966 
967 	if (nvlist_lookup_uint32(nvl, IPMP_PROBE_STATE, &state) != 0) {
968 		warn("dropped event with no IPMP_PROBE_STATE\n");
969 		goto out;
970 	}
971 
972 	if (state == IPMP_PROBE_ACKED || state == IPMP_PROBE_LOST)
973 		ofmt_output(psp->ps_ofmt, psp->ps_ih, nvl);
974 out:
975 	nvlist_free(nvl);
976 	return (0);
977 }
978 
979 static boolean_t
980 sfunc_targ_ifname(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
981 {
982 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
983 	ipmp_targinfo_t *targinfop = arg->sa_data;
984 
985 	(void) strlcpy(buf, targinfop->it_name, bufsize);
986 	return (B_TRUE);
987 }
988 
989 static boolean_t
990 sfunc_targ_mode(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
991 {
992 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
993 	ipmp_targinfo_t *targinfop = arg->sa_data;
994 
995 	enum2str(targ_mode, targinfop->it_targmode, buf, bufsize);
996 	return (B_TRUE);
997 }
998 
999 static boolean_t
1000 sfunc_targ_testaddr(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
1001 {
1002 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
1003 	ipmp_targinfo_t *targinfop = arg->sa_data;
1004 
1005 	if (targinfop->it_targmode != IPMP_TARG_DISABLED)
1006 		sockaddr2str(&targinfop->it_testaddr, buf, bufsize);
1007 	return (B_TRUE);
1008 }
1009 
1010 static boolean_t
1011 sfunc_targ_targets(ofmt_arg_t *ofmtarg, char *buf, uint_t bufsize)
1012 {
1013 	ipmpstat_sfunc_arg_t *arg = ofmtarg->ofmt_cbarg;
1014 	uint_t i;
1015 	char *targname = alloca(bufsize);
1016 	ipmp_targinfo_t *targinfop = arg->sa_data;
1017 	ipmp_addrlist_t *targlistp = targinfop->it_targlistp;
1018 
1019 	for (i = 0; i < targlistp->al_naddr; i++) {
1020 		sockaddr2str(&targlistp->al_addrs[i], targname, bufsize);
1021 		(void) strlcat(buf, targname, bufsize);
1022 		if ((i + 1) < targlistp->al_naddr)
1023 			(void) strlcat(buf, " ", bufsize);
1024 	}
1025 	return (B_TRUE);
1026 }
1027 
1028 static void
1029 info_output_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
1030 {
1031 	ofmt_output(arg, ih, infop);
1032 }
1033 
1034 static void
1035 targinfo_output_cbfunc(ipmp_handle_t ih, void *infop, void *arg)
1036 {
1037 	ipmp_ifinfo_t *ifinfop = infop;
1038 	ipmp_if_targmode_t targmode4 = ifinfop->if_targinfo4.it_targmode;
1039 	ipmp_if_targmode_t targmode6 = ifinfop->if_targinfo6.it_targmode;
1040 
1041 	/*
1042 	 * Usually, either IPv4 or IPv6 probing will be enabled, but the admin
1043 	 * may enable both.  If only one is enabled, omit the other one so as
1044 	 * to not encourage the admin to enable both.  If neither is enabled,
1045 	 * we still print one just so the admin can see a MODE of "disabled".
1046 	 */
1047 	if (targmode4 != IPMP_TARG_DISABLED || targmode6 == IPMP_TARG_DISABLED)
1048 		ofmt_output(arg, ih, &ifinfop->if_targinfo4);
1049 	if (targmode6 != IPMP_TARG_DISABLED)
1050 		ofmt_output(arg, ih, &ifinfop->if_targinfo6);
1051 }
1052 
1053 /*
1054  * Outputs one row of values.  The values to output are obtained through the
1055  * callback function pointers.  The actual values are computed from the `ih'
1056  * and `arg' structures passed to the callback function.
1057  */
1058 static void
1059 ofmt_output(const ofmt_handle_t ofmt, ipmp_handle_t ih, void *arg)
1060 {
1061 	ipmpstat_sfunc_arg_t	sfunc_arg;
1062 
1063 	sfunc_arg.sa_ih = ih;
1064 	sfunc_arg.sa_data = arg;
1065 	ofmt_print(ofmt, &sfunc_arg);
1066 }
1067 
1068 /*
1069  * Uses `enums' to map `enumval' to a string, and stores at most `bufsize'
1070  * bytes of that string into `buf'.
1071  */
1072 static void
1073 enum2str(const ipmpstat_enum_t *enums, int enumval, char *buf, uint_t bufsize)
1074 {
1075 	const ipmpstat_enum_t *enump;
1076 
1077 	for (enump = enums; enump->e_name != NULL; enump++) {
1078 		if (enump->e_val == enumval) {
1079 			(void) strlcpy(buf, enump->e_name, bufsize);
1080 			return;
1081 		}
1082 	}
1083 	(void) snprintf(buf, bufsize, "<%d>", enumval);
1084 }
1085 
1086 /*
1087  * Stores the stringified value of the sockaddr_storage pointed to by `ssp'
1088  * into at most `bufsize' bytes of `buf'.
1089  */
1090 static void
1091 sockaddr2str(const struct sockaddr_storage *ssp, char *buf, uint_t bufsize)
1092 {
1093 	int flags = NI_NOFQDN;
1094 	socklen_t socklen;
1095 	struct sockaddr *sp = (struct sockaddr *)ssp;
1096 
1097 	/*
1098 	 * Sadly, getnameinfo() does not allow the socklen to be oversized for
1099 	 * a given family -- so we must determine the exact size to pass to it.
1100 	 */
1101 	switch (ssp->ss_family) {
1102 	case AF_INET:
1103 		socklen = sizeof (struct sockaddr_in);
1104 		break;
1105 	case AF_INET6:
1106 		socklen = sizeof (struct sockaddr_in6);
1107 		break;
1108 	default:
1109 		(void) strlcpy(buf, "?", bufsize);
1110 		return;
1111 	}
1112 
1113 	if (opt & IPMPSTAT_OPT_NUMERIC)
1114 		flags |= NI_NUMERICHOST;
1115 
1116 	(void) getnameinfo(sp, socklen, buf, bufsize, NULL, 0, flags);
1117 }
1118 
1119 static void
1120 sighandler(int sig)
1121 {
1122 	assert(sig == SIGWINCH);
1123 
1124 	ofmt_update_winsize(ofmt);
1125 }
1126 
1127 static void
1128 usage(void)
1129 {
1130 	const char *argstr = gettext("[-n] [-o <field> [-P]] -a|-g|-i|-p|-t");
1131 
1132 	(void) fprintf(stderr, gettext("usage: %s %s\n"), progname, argstr);
1133 	(void) fprintf(stderr, gettext("\n"
1134 	    "  output modes:\t -a  display IPMP data address information\n"
1135 	    "\t\t -g  display IPMP group information\n"
1136 	    "\t\t -i  display IPMP-related IP interface information\n"
1137 	    "\t\t -p  display IPMP probe information\n"
1138 	    "\t\t -t  display IPMP target information\n\n"
1139 	    "       options:\t -n  display IP addresses numerically\n"
1140 	    "\t\t -o  display only the specified fields, in order\n"
1141 	    "\t\t -P  display using parsable output mode\n"));
1142 
1143 	exit(EXIT_FAILURE);
1144 }
1145 
1146 /* PRINTFLIKE1 */
1147 static void
1148 warn(const char *format, ...)
1149 {
1150 	va_list alist;
1151 	int error = errno;
1152 
1153 	format = gettext(format);
1154 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
1155 
1156 	va_start(alist, format);
1157 	(void) vfprintf(stderr, format, alist);
1158 	va_end(alist);
1159 
1160 	if (strchr(format, '\n') == NULL)
1161 		(void) fprintf(stderr, ": %s\n", strerror(error));
1162 }
1163 
1164 /* PRINTFLIKE2 */
1165 static void
1166 warn_ipmperr(int ipmperr, const char *format, ...)
1167 {
1168 	va_list alist;
1169 
1170 	format = gettext(format);
1171 	(void) fprintf(stderr, gettext("%s: warning: "), progname);
1172 
1173 	va_start(alist, format);
1174 	(void) vfprintf(stderr, format, alist);
1175 	va_end(alist);
1176 
1177 	(void) fprintf(stderr, ": %s\n", ipmp_errmsg(ipmperr));
1178 }
1179 
1180 /* PRINTFLIKE1 */
1181 static void
1182 die(const char *format, ...)
1183 {
1184 	va_list alist;
1185 	int error = errno;
1186 
1187 	format = gettext(format);
1188 	(void) fprintf(stderr, "%s: ", progname);
1189 
1190 	va_start(alist, format);
1191 	(void) vfprintf(stderr, format, alist);
1192 	va_end(alist);
1193 
1194 	if (strchr(format, '\n') == NULL)
1195 		(void) fprintf(stderr, ": %s\n", strerror(error));
1196 
1197 	exit(EXIT_FAILURE);
1198 }
1199 
1200 /* PRINTFLIKE2 */
1201 static void
1202 die_ipmperr(int ipmperr, const char *format, ...)
1203 {
1204 	va_list alist;
1205 
1206 	format = gettext(format);
1207 	(void) fprintf(stderr, "%s: ", progname);
1208 
1209 	va_start(alist, format);
1210 	(void) vfprintf(stderr, format, alist);
1211 	va_end(alist);
1212 	(void) fprintf(stderr, ": %s\n", ipmp_errmsg(ipmperr));
1213 
1214 	exit(EXIT_FAILURE);
1215 }
1216 
1217 static ofmt_field_t addr_fields[] = {
1218 	{ "ADDRESS",    26,	0, sfunc_addr_address		},
1219 	{ "STATE",	7,	0, sfunc_addr_state		},
1220 	{ "GROUP",	12,	0, sfunc_addr_group		},
1221 	{ "INBOUND",	12,	0, sfunc_addr_inbound		},
1222 	{ "OUTBOUND",	23,	0, sfunc_addr_outbound		},
1223 	{ NULL,		0, 	0, NULL				}
1224 };
1225 
1226 static ofmt_field_t group_fields[] = {
1227 	{ "GROUP",	12, 	0, sfunc_group_ifname		},
1228 	{ "GROUPNAME",	12,	0, sfunc_group_name 		},
1229 	{ "STATE",	10,	0, sfunc_group_state		},
1230 	{ "FDT",	10,	0, sfunc_group_fdt		},
1231 	{ "INTERFACES",	30,	0, sfunc_group_interfaces	},
1232 	{ NULL,		0, 	0, NULL				}
1233 };
1234 
1235 static ofmt_field_t if_fields[] = {
1236 	{ "INTERFACE",	12,	0, sfunc_if_name		},
1237 	{ "ACTIVE",	8, 	0, sfunc_if_active		},
1238 	{ "GROUP",	12,	0, sfunc_if_group		},
1239 	{ "FLAGS",	10,	0, sfunc_if_flags		},
1240 	{ "LINK",	10,	0, sfunc_if_link		},
1241 	{ "PROBE",	10,	0, sfunc_if_probe		},
1242 	{ "STATE",	10, 	0, sfunc_if_state		},
1243 	{ NULL,		0, 	0, NULL				}
1244 };
1245 
1246 static ofmt_field_t probe_fields[] = {
1247 	{ "TIME",	10,	0, sfunc_probe_time		},
1248 	{ "INTERFACE",	12,	0, sfunc_probe_ifname		},
1249 	{ "PROBE",	7,	0, sfunc_probe_id		},
1250 	{ "NETRTT",	10,	0, sfunc_probe_netrtt		},
1251 	{ "RTT",	10,	0, sfunc_probe_rtt		},
1252 	{ "RTTAVG",	10,	0, sfunc_probe_rttavg		},
1253 	{ "TARGET",	20,	0, sfunc_probe_target		},
1254 	{ "RTTDEV",	10,	0, sfunc_probe_rttdev		},
1255 	{ NULL,		0, 	0, NULL				}
1256 };
1257 
1258 static ofmt_field_t targ_fields[] = {
1259 	{ "INTERFACE",	12,	0, sfunc_targ_ifname		},
1260 	{ "MODE",	10,	0, sfunc_targ_mode		},
1261 	{ "TESTADDR",	20,	0, sfunc_targ_testaddr		},
1262 	{ "TARGETS",	38,	0, sfunc_targ_targets		},
1263 	{ NULL,		0, 	0, NULL				}
1264 };
1265 
1266 static ipmpstat_enum_t	addr_state[] = {
1267 	{ "up",		IPMP_ADDR_UP				},
1268 	{ "down",	IPMP_ADDR_DOWN				},
1269 	{ NULL,		0 					}
1270 };
1271 
1272 static ipmpstat_enum_t	group_state[] = {
1273 	{ "ok",		IPMP_GROUP_OK 				},
1274 	{ "failed",	IPMP_GROUP_FAILED			},
1275 	{ "degraded",	IPMP_GROUP_DEGRADED			},
1276 	{ NULL,		0 					}
1277 };
1278 
1279 static ipmpstat_enum_t	if_link[] = {
1280 	{ "up",		IPMP_LINK_UP 				},
1281 	{ "down",	IPMP_LINK_DOWN				},
1282 	{ "unknown",	IPMP_LINK_UNKNOWN			},
1283 	{ NULL,		0 					}
1284 };
1285 
1286 static ipmpstat_enum_t	if_probe[] = {
1287 	{ "ok",		IPMP_PROBE_OK 				},
1288 	{ "failed",	IPMP_PROBE_FAILED			},
1289 	{ "unknown",	IPMP_PROBE_UNKNOWN			},
1290 	{ "disabled",	IPMP_PROBE_DISABLED			},
1291 	{ NULL,		0 					}
1292 };
1293 
1294 static ipmpstat_enum_t	if_state[] = {
1295 	{ "ok",		IPMP_IF_OK 				},
1296 	{ "failed",	IPMP_IF_FAILED				},
1297 	{ "unknown",	IPMP_IF_UNKNOWN				},
1298 	{ "offline",	IPMP_IF_OFFLINE				},
1299 	{ NULL,		0 					}
1300 };
1301 
1302 static ipmpstat_enum_t	targ_mode[] = {
1303 	{ "disabled",	IPMP_TARG_DISABLED			},
1304 	{ "routes",	IPMP_TARG_ROUTES			},
1305 	{ "multicast",	IPMP_TARG_MULTICAST			},
1306 	{ NULL,		0 					}
1307 };
1308