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