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