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