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