Home | History | Annotate | Download | only in ipmpstat
      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)	((float)(ns) / (NANOSEC / MILLISEC))
    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(1M) -- 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