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(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 };