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.
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.
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]
21 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
22 * Use is subject to license terms.
26 #include <arpa/inet.h>
29 #include <ipmp_admin.h>
30 #include <ipmp_query.h>
32 #include <libnvpair.h>
33 #include <libsysevent.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>
50 * ipmpstat -- display IPMP subsystem status.
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:
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()).
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
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
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.
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.
100 * Data type used by the sfunc callbacks to obtain the requested information
101 * from the agreed-upon object.
103 typedef struct ipmpstat_sfunc_arg
{
106 } ipmpstat_sfunc_arg_t
;
109 * Function pointers used to iterate through IPMP objects.
111 typedef void ipmpstat_cbfunc_t(ipmp_handle_t
, void *, void *);
112 typedef void ipmpstat_walker_t(ipmp_handle_t
, ipmpstat_cbfunc_t
*, void *);
115 * Data type used to implement nested walks.
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
;
123 * Data type used by enum2str() to map an enumerated value to a string.
125 typedef struct ipmpstat_enum
{
126 const char *e_name
; /* string */
127 int e_val
; /* value */
131 * Data type used to pass state between probe_output() and probe_event().
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
;
139 * Options that modify the output mode; more than one may be lit.
142 IPMPSTAT_OPT_NUMERIC
= 0x1,
143 IPMPSTAT_OPT_PARSABLE
= 0x2
147 * Indices for the FLAGS field of the `-i' output format.
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
155 #define IPMPSTAT_NCOL 80
156 #define NS2FLOATMS(ns) (NSEC2MSEC((float)(ns)))
157 #define MS2FLOATSEC(ms) ((float)(ms) / 1000)
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
;
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 *, ...);
184 main(int argc
, char **argv
)
188 const char *ofields
= NULL
;
189 ofmt_status_t ofmterr
;
190 ofmt_field_t
*fields
= NULL
;
191 uint_t ofmtflags
= 0;
193 ipmp_qcontext_t qcontext
= IPMP_QCONTEXT_SNAP
;
194 ipmpstat_cbfunc_t
*cbfunc
;
195 ipmpstat_walker_t
*walker
;
196 char errbuf
[OFMT_BUFSIZE
];
198 if ((progname
= strrchr(argv
[0], '/')) == NULL
)
203 (void) setlocale(LC_ALL
, "");
204 (void) textdomain(TEXT_DOMAIN
);
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");
212 opt
|= IPMPSTAT_OPT_NUMERIC
;
215 /* Undocumented option: for testing use ONLY */
216 qcontext
= IPMP_QCONTEXT_LIVE
;
219 opt
|= IPMPSTAT_OPT_PARSABLE
;
220 ofmtflags
|= OFMT_PARSABLE
;
227 cbfunc
= info_output_cbfunc
;
228 fields
= addr_fields
;
232 cbfunc
= info_output_cbfunc
;
233 fields
= group_fields
;
237 cbfunc
= info_output_cbfunc
;
241 fields
= probe_fields
;
245 cbfunc
= targinfo_output_cbfunc
;
246 fields
= targ_fields
;
254 if (argc
> optind
|| fields
== NULL
)
258 * Open a handle to the formatted output engine.
260 ofmterr
= ofmt_open(ofields
, fields
, ofmtflags
, IPMPSTAT_NCOL
, &ofmt
);
261 if (ofmterr
!= OFMT_SUCCESS
) {
263 * If some fields were badly formed in human-friendly mode, we
264 * emit a warning and continue. Otherwise exit immediately.
266 (void) ofmt_strerror(ofmt
, ofmterr
, errbuf
, sizeof (errbuf
));
267 if (ofmterr
!= OFMT_EBADFIELDS
|| (opt
& IPMPSTAT_OPT_PARSABLE
))
270 warn("%s\n", errbuf
);
274 * Obtain the window size and monitor changes to the size. This data
275 * is used to redisplay the output headers when necessary.
277 (void) sigset(SIGWINCH
, sighandler
);
279 if ((err
= ipmp_open(&ih
)) != IPMP_SUCCESS
)
280 die_ipmperr(err
, "cannot create IPMP handle");
282 if (ipmp_ping_daemon(ih
) != IPMP_SUCCESS
)
283 die("cannot contact in.mpathd(8) -- is IPMP in use?\n");
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.
290 if (fields
== probe_fields
) {
291 probe_output(ih
, ofmt
);
293 if ((err
= ipmp_setqcontext(ih
, qcontext
)) != IPMP_SUCCESS
) {
294 if (qcontext
== IPMP_QCONTEXT_SNAP
)
295 die_ipmperr(err
, "cannot snapshot IPMP state");
297 die_ipmperr(err
, "cannot use live IPMP state");
299 (*walker
)(ih
, cbfunc
, ofmt
);
305 return (EXIT_SUCCESS
);
309 * Walks all IPMP groups on the system and invokes `cbfunc' on each, passing
310 * it `ih', the ipmp_groupinfo_t pointer, and `arg'.
313 walk_group(ipmp_handle_t ih
, ipmpstat_cbfunc_t
*cbfunc
, void *arg
)
317 ipmp_groupinfo_t
*grinfop
;
318 ipmp_grouplist_t
*grlistp
;
320 if ((err
= ipmp_getgrouplist(ih
, &grlistp
)) != IPMP_SUCCESS
)
321 die_ipmperr(err
, "cannot get IPMP group list");
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
]);
330 (*cbfunc
)(ih
, grinfop
, arg
);
331 ipmp_freegroupinfo(grinfop
);
334 ipmp_freegrouplist(grlistp
);
338 * Walks all IPMP interfaces on the system and invokes `cbfunc' on each,
339 * passing it `ih', the ipmp_ifinfo_t pointer, and `arg'.
342 walk_if(ipmp_handle_t ih
, ipmpstat_cbfunc_t
*cbfunc
, void *arg
)
344 ipmpstat_walkdata_t iw
= { cbfunc
, arg
};
346 walk_group(ih
, walk_if_cbfunc
, &iw
);
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'.
354 walk_addr(ipmp_handle_t ih
, ipmpstat_cbfunc_t
*cbfunc
, void *arg
)
356 ipmpstat_walkdata_t iw
= { cbfunc
, arg
};
358 walk_group(ih
, walk_addr_cbfunc
, &iw
);
362 * Nested walker callback function for walk_if().
365 walk_if_cbfunc(ipmp_handle_t ih
, void *infop
, void *arg
)
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
;
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'",
381 (*iwp
->iw_func
)(ih
, ifinfop
, iwp
->iw_funcarg
);
382 ipmp_freeifinfo(ifinfop
);
387 * Nested walker callback function for walk_addr().
390 walk_addr_cbfunc(ipmp_handle_t ih
, void *infop
, void *arg
)
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
;
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
);
409 (*iwp
->iw_func
)(ih
, adinfop
, iwp
->iw_funcarg
);
410 ipmp_freeaddrinfo(adinfop
);
415 sfunc_nvwarn(const char *nvname
)
417 warn("cannot retrieve %s\n", nvname
);
422 sfunc_addr_address(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
424 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
425 ipmp_addrinfo_t
*adinfop
= arg
->sa_data
;
427 sockaddr2str(&adinfop
->ad_addr
, buf
, bufsize
);
432 sfunc_addr_group(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
434 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
436 ipmp_addrinfo_t
*adinfop
= arg
->sa_data
;
437 ipmp_groupinfo_t
*grinfop
;
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'",
445 (void) strlcpy(buf
, grinfop
->gr_ifname
, bufsize
);
446 ipmp_freegroupinfo(grinfop
);
451 sfunc_addr_state(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
453 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
454 ipmp_addrinfo_t
*adinfop
= arg
->sa_data
;
456 enum2str(addr_state
, adinfop
->ad_state
, buf
, bufsize
);
461 sfunc_addr_inbound(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
463 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
464 ipmp_addrinfo_t
*adinfop
= arg
->sa_data
;
466 (void) strlcpy(buf
, adinfop
->ad_binding
, bufsize
);
471 sfunc_addr_outbound(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
473 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
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
;
481 if (adinfop
->ad_state
== IPMP_ADDR_DOWN
)
485 * If there's no inbound interface for this address, there can't
486 * be any outbound traffic.
488 if (adinfop
->ad_binding
[0] == '\0')
492 * The address can use any active interface in the group, so
493 * obtain all of those.
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'",
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'",
511 if (ifinfop
->if_flags
& IPMP_IFFLAG_ACTIVE
) {
513 (void) strlcat(buf
, " ", bufsize
);
514 (void) strlcat(buf
, ifinfop
->if_name
, bufsize
);
516 ipmp_freeifinfo(ifinfop
);
518 ipmp_freegroupinfo(grinfop
);
523 sfunc_group_name(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
525 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
526 ipmp_groupinfo_t
*grinfop
= arg
->sa_data
;
528 (void) strlcpy(buf
, grinfop
->gr_name
, bufsize
);
533 sfunc_group_ifname(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
535 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
536 ipmp_groupinfo_t
*grinfop
= arg
->sa_data
;
538 (void) strlcpy(buf
, grinfop
->gr_ifname
, bufsize
);
543 sfunc_group_state(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
545 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
546 ipmp_groupinfo_t
*grinfop
= arg
->sa_data
;
548 enum2str(group_state
, grinfop
->gr_state
, buf
, bufsize
);
553 sfunc_group_fdt(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
555 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
556 ipmp_groupinfo_t
*grinfop
= arg
->sa_data
;
558 if (grinfop
->gr_fdt
== 0)
561 (void) snprintf(buf
, bufsize
, "%.2fs", MS2FLOATSEC(grinfop
->gr_fdt
));
566 sfunc_group_interfaces(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
568 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
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
;
577 active
= alloca(bufsize
);
579 inactive
= alloca(bufsize
);
581 unusable
= alloca(bufsize
);
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'",
592 if (ifinfop
->if_flags
& IPMP_IFFLAG_ACTIVE
) {
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
);
601 if (nunusable
++ != 0)
602 (void) strlcat(unusable
, " ", bufsize
);
603 (void) strlcat(unusable
, ifinfop
->if_name
, bufsize
);
606 ipmp_freeifinfo(ifinfop
);
609 (void) strlcpy(buf
, active
, bufsize
);
613 (void) strlcat(buf
, " ", bufsize
);
615 (void) strlcat(buf
, "(", bufsize
);
616 (void) strlcat(buf
, inactive
, bufsize
);
617 (void) strlcat(buf
, ")", bufsize
);
621 if (nactive
+ ninactive
!= 0)
622 (void) strlcat(buf
, " ", bufsize
);
624 (void) strlcat(buf
, "[", bufsize
);
625 (void) strlcat(buf
, unusable
, bufsize
);
626 (void) strlcat(buf
, "]", bufsize
);
632 sfunc_if_name(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
634 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
635 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
637 (void) strlcpy(buf
, ifinfop
->if_name
, bufsize
);
642 sfunc_if_active(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
644 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
645 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
647 if (ifinfop
->if_flags
& IPMP_IFFLAG_ACTIVE
)
648 (void) strlcpy(buf
, "yes", bufsize
);
650 (void) strlcpy(buf
, "no", bufsize
);
655 sfunc_if_group(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
657 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
659 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
660 ipmp_groupinfo_t
*grinfop
;
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'",
669 (void) strlcpy(buf
, grinfop
->gr_ifname
, bufsize
);
670 ipmp_freegroupinfo(grinfop
);
675 sfunc_if_flags(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
677 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
679 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
680 ipmp_groupinfo_t
*grinfop
;
682 assert(bufsize
> IPMPSTAT_NUM_FLAGS
);
684 (void) memset(buf
, '-', IPMPSTAT_NUM_FLAGS
);
685 buf
[IPMPSTAT_NUM_FLAGS
] = '\0';
687 if (ifinfop
->if_type
== IPMP_IF_STANDBY
)
688 buf
[IPMPSTAT_SFLAG_INDEX
] = 's';
690 if (ifinfop
->if_flags
& IPMP_IFFLAG_INACTIVE
)
691 buf
[IPMPSTAT_IFLAG_INDEX
] = 'i';
693 if (ifinfop
->if_flags
& IPMP_IFFLAG_DOWN
)
694 buf
[IPMPSTAT_DFLAG_INDEX
] = 'd';
696 if (ifinfop
->if_flags
& IPMP_IFFLAG_HWADDRDUP
)
697 buf
[IPMPSTAT_HFLAG_INDEX
] = 'h';
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
);
706 if (strcmp(grinfop
->gr_m4ifname
, ifinfop
->if_name
) == 0)
707 buf
[IPMPSTAT_M4FLAG_INDEX
] = 'm';
709 if (strcmp(grinfop
->gr_m6ifname
, ifinfop
->if_name
) == 0)
710 buf
[IPMPSTAT_M6FLAG_INDEX
] = 'M';
712 if (strcmp(grinfop
->gr_bcifname
, ifinfop
->if_name
) == 0)
713 buf
[IPMPSTAT_BFLAG_INDEX
] = 'b';
715 ipmp_freegroupinfo(grinfop
);
720 sfunc_if_link(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
722 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
723 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
725 enum2str(if_link
, ifinfop
->if_linkstate
, buf
, bufsize
);
730 sfunc_if_probe(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
732 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
733 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
735 enum2str(if_probe
, ifinfop
->if_probestate
, buf
, bufsize
);
740 sfunc_if_state(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
742 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
743 ipmp_ifinfo_t
*ifinfop
= arg
->sa_data
;
745 enum2str(if_state
, ifinfop
->if_state
, buf
, bufsize
);
750 sfunc_probe_id(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
752 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
754 nvlist_t
*nvl
= arg
->sa_data
;
756 if (nvlist_lookup_uint32(nvl
, IPMP_PROBE_ID
, &probe_id
) != 0)
757 return (sfunc_nvwarn("IPMP_PROBE_ID"));
759 (void) snprintf(buf
, bufsize
, "%u", probe_id
);
764 sfunc_probe_ifname(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
766 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
768 nvlist_t
*nvl
= arg
->sa_data
;
770 if (nvlist_lookup_string(nvl
, IPMP_IF_NAME
, &ifname
) != 0)
771 return (sfunc_nvwarn("IPMP_IF_NAME"));
773 (void) strlcpy(buf
, ifname
, bufsize
);
778 sfunc_probe_time(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
780 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
782 nvlist_t
*nvl
= arg
->sa_data
;
784 if (nvlist_lookup_hrtime(nvl
, IPMP_PROBE_START_TIME
, &start
) != 0)
785 return (sfunc_nvwarn("IPMP_PROBE_START_TIME"));
787 (void) snprintf(buf
, bufsize
, "%.2fs",
788 (float)(start
- probe_output_start
) / NANOSEC
);
793 sfunc_probe_target(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
795 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
797 struct sockaddr_storage
*target
;
798 nvlist_t
*nvl
= arg
->sa_data
;
800 if (nvlist_lookup_byte_array(nvl
, IPMP_PROBE_TARGET
,
801 (uchar_t
**)&target
, &nelem
) != 0)
802 return (sfunc_nvwarn("IPMP_PROBE_TARGET"));
804 sockaddr2str(target
, buf
, bufsize
);
809 sfunc_probe_rtt(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
811 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
812 hrtime_t start
, ackproc
;
813 nvlist_t
*nvl
= arg
->sa_data
;
816 if (nvlist_lookup_uint32(nvl
, IPMP_PROBE_STATE
, &state
) != 0)
817 return (sfunc_nvwarn("IPMP_PROBE_STATE"));
819 if (state
!= IPMP_PROBE_ACKED
)
822 if (nvlist_lookup_hrtime(nvl
, IPMP_PROBE_START_TIME
, &start
) != 0)
823 return (sfunc_nvwarn("IPMP_PROBE_START_TIME"));
825 if (nvlist_lookup_hrtime(nvl
, IPMP_PROBE_ACKPROC_TIME
, &ackproc
) != 0)
826 return (sfunc_nvwarn("IPMP_PROBE_ACKPROC_TIME"));
828 (void) snprintf(buf
, bufsize
, "%.2fms", NS2FLOATMS(ackproc
- start
));
833 sfunc_probe_netrtt(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
835 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
836 hrtime_t sent
, ackrecv
;
837 nvlist_t
*nvl
= arg
->sa_data
;
840 if (nvlist_lookup_uint32(nvl
, IPMP_PROBE_STATE
, &state
) != 0)
841 return (sfunc_nvwarn("IPMP_PROBE_STATE"));
843 if (state
!= IPMP_PROBE_ACKED
)
846 if (nvlist_lookup_hrtime(nvl
, IPMP_PROBE_SENT_TIME
, &sent
) != 0)
847 return (sfunc_nvwarn("IPMP_PROBE_SENT_TIME"));
849 if (nvlist_lookup_hrtime(nvl
, IPMP_PROBE_ACKRECV_TIME
, &ackrecv
) != 0)
850 return (sfunc_nvwarn("IPMP_PROBE_ACKRECV_TIME"));
852 (void) snprintf(buf
, bufsize
, "%.2fms", NS2FLOATMS(ackrecv
- sent
));
857 sfunc_probe_rttavg(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
859 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
861 nvlist_t
*nvl
= arg
->sa_data
;
863 if (nvlist_lookup_int64(nvl
, IPMP_PROBE_TARGET_RTTAVG
, &rttavg
) != 0)
864 return (sfunc_nvwarn("IPMP_PROBE_TARGET_RTTAVG"));
867 (void) snprintf(buf
, bufsize
, "%.2fms", NS2FLOATMS(rttavg
));
872 sfunc_probe_rttdev(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
874 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
876 nvlist_t
*nvl
= arg
->sa_data
;
878 if (nvlist_lookup_int64(nvl
, IPMP_PROBE_TARGET_RTTDEV
, &rttdev
) != 0)
879 return (sfunc_nvwarn("IPMP_PROBE_TARGET_RTTDEV"));
882 (void) snprintf(buf
, bufsize
, "%.2fms", NS2FLOATMS(rttdev
));
888 probe_enabled_cbfunc(ipmp_handle_t ih
, void *infop
, void *arg
)
890 uint_t
*nenabledp
= arg
;
891 ipmp_ifinfo_t
*ifinfop
= infop
;
893 if (ifinfop
->if_probestate
!= IPMP_PROBE_DISABLED
)
898 probe_output(ipmp_handle_t ih
, ofmt_handle_t ofmt
)
900 char sub
[MAX_SUBID_LEN
];
902 ipmpstat_probe_state_t ps
= { ih
, ofmt
};
906 * Check if any interfaces are enabled for probe-based failure
907 * detection. If not, immediately fail.
909 walk_if(ih
, probe_enabled_cbfunc
, &nenabled
);
911 die("probe-based failure detection is disabled\n");
913 probe_output_start
= gethrtime();
916 * Unfortunately, until 4791900 is fixed, only privileged processes
917 * can bind and thus receive sysevents.
919 errno
= sysevent_evc_bind(IPMP_EVENT_CHAN
, &evch
, EVCH_CREAT
);
922 die("insufficient privileges for -p\n");
923 die("sysevent_evc_bind to channel %s failed", IPMP_EVENT_CHAN
);
927 * The subscriber must be unique in order for sysevent_evc_subscribe()
928 * to succeed, so combine our name and pid.
930 (void) snprintf(sub
, sizeof (sub
), "%d-%s", getpid(), progname
);
932 errno
= sysevent_evc_subscribe(evch
, sub
, EC_IPMP
, probe_event
, &ps
, 0);
934 die("sysevent_evc_subscribe for class %s failed", EC_IPMP
);
941 probe_event(sysevent_t
*ev
, void *arg
)
946 ipmpstat_probe_state_t
*psp
= arg
;
948 if (strcmp(sysevent_get_subclass_name(ev
), ESC_IPMP_PROBE_STATE
) != 0)
951 if (sysevent_get_attr_list(ev
, &nvl
) != 0) {
952 warn("sysevent_get_attr_list failed; dropping event");
956 if (nvlist_lookup_uint32(nvl
, IPMP_EVENT_VERSION
, &version
) != 0) {
957 warn("dropped event with no IPMP_EVENT_VERSION\n");
961 if (version
!= IPMP_EVENT_CUR_VERSION
) {
962 warn("dropped event with unsupported IPMP_EVENT_VERSION %d\n",
967 if (nvlist_lookup_uint32(nvl
, IPMP_PROBE_STATE
, &state
) != 0) {
968 warn("dropped event with no IPMP_PROBE_STATE\n");
972 if (state
== IPMP_PROBE_ACKED
|| state
== IPMP_PROBE_LOST
)
973 ofmt_output(psp
->ps_ofmt
, psp
->ps_ih
, nvl
);
980 sfunc_targ_ifname(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
982 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
983 ipmp_targinfo_t
*targinfop
= arg
->sa_data
;
985 (void) strlcpy(buf
, targinfop
->it_name
, bufsize
);
990 sfunc_targ_mode(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
992 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
993 ipmp_targinfo_t
*targinfop
= arg
->sa_data
;
995 enum2str(targ_mode
, targinfop
->it_targmode
, buf
, bufsize
);
1000 sfunc_targ_testaddr(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
1002 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
1003 ipmp_targinfo_t
*targinfop
= arg
->sa_data
;
1005 if (targinfop
->it_targmode
!= IPMP_TARG_DISABLED
)
1006 sockaddr2str(&targinfop
->it_testaddr
, buf
, bufsize
);
1011 sfunc_targ_targets(ofmt_arg_t
*ofmtarg
, char *buf
, uint_t bufsize
)
1013 ipmpstat_sfunc_arg_t
*arg
= ofmtarg
->ofmt_cbarg
;
1015 char *targname
= alloca(bufsize
);
1016 ipmp_targinfo_t
*targinfop
= arg
->sa_data
;
1017 ipmp_addrlist_t
*targlistp
= targinfop
->it_targlistp
;
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
);
1029 info_output_cbfunc(ipmp_handle_t ih
, void *infop
, void *arg
)
1031 ofmt_output(arg
, ih
, infop
);
1035 targinfo_output_cbfunc(ipmp_handle_t ih
, void *infop
, void *arg
)
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
;
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".
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
);
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.
1059 ofmt_output(const ofmt_handle_t ofmt
, ipmp_handle_t ih
, void *arg
)
1061 ipmpstat_sfunc_arg_t sfunc_arg
;
1063 sfunc_arg
.sa_ih
= ih
;
1064 sfunc_arg
.sa_data
= arg
;
1065 ofmt_print(ofmt
, &sfunc_arg
);
1069 * Uses `enums' to map `enumval' to a string, and stores at most `bufsize'
1070 * bytes of that string into `buf'.
1073 enum2str(const ipmpstat_enum_t
*enums
, int enumval
, char *buf
, uint_t bufsize
)
1075 const ipmpstat_enum_t
*enump
;
1077 for (enump
= enums
; enump
->e_name
!= NULL
; enump
++) {
1078 if (enump
->e_val
== enumval
) {
1079 (void) strlcpy(buf
, enump
->e_name
, bufsize
);
1083 (void) snprintf(buf
, bufsize
, "<%d>", enumval
);
1087 * Stores the stringified value of the sockaddr_storage pointed to by `ssp'
1088 * into at most `bufsize' bytes of `buf'.
1091 sockaddr2str(const struct sockaddr_storage
*ssp
, char *buf
, uint_t bufsize
)
1093 int flags
= NI_NOFQDN
;
1095 struct sockaddr
*sp
= (struct sockaddr
*)ssp
;
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.
1101 switch (ssp
->ss_family
) {
1103 socklen
= sizeof (struct sockaddr_in
);
1106 socklen
= sizeof (struct sockaddr_in6
);
1109 (void) strlcpy(buf
, "?", bufsize
);
1113 if (opt
& IPMPSTAT_OPT_NUMERIC
)
1114 flags
|= NI_NUMERICHOST
;
1116 (void) getnameinfo(sp
, socklen
, buf
, bufsize
, NULL
, 0, flags
);
1122 assert(sig
== SIGWINCH
);
1124 ofmt_update_winsize(ofmt
);
1130 const char *argstr
= gettext("[-n] [-o <field> [-P]] -a|-g|-i|-p|-t");
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"));
1148 warn(const char *format
, ...)
1153 format
= gettext(format
);
1154 (void) fprintf(stderr
, gettext("%s: warning: "), progname
);
1156 va_start(alist
, format
);
1157 (void) vfprintf(stderr
, format
, alist
);
1160 if (strchr(format
, '\n') == NULL
)
1161 (void) fprintf(stderr
, ": %s\n", strerror(error
));
1166 warn_ipmperr(int ipmperr
, const char *format
, ...)
1170 format
= gettext(format
);
1171 (void) fprintf(stderr
, gettext("%s: warning: "), progname
);
1173 va_start(alist
, format
);
1174 (void) vfprintf(stderr
, format
, alist
);
1177 (void) fprintf(stderr
, ": %s\n", ipmp_errmsg(ipmperr
));
1182 die(const char *format
, ...)
1187 format
= gettext(format
);
1188 (void) fprintf(stderr
, "%s: ", progname
);
1190 va_start(alist
, format
);
1191 (void) vfprintf(stderr
, format
, alist
);
1194 if (strchr(format
, '\n') == NULL
)
1195 (void) fprintf(stderr
, ": %s\n", strerror(error
));
1202 die_ipmperr(int ipmperr
, const char *format
, ...)
1206 format
= gettext(format
);
1207 (void) fprintf(stderr
, "%s: ", progname
);
1209 va_start(alist
, format
);
1210 (void) vfprintf(stderr
, format
, alist
);
1212 (void) fprintf(stderr
, ": %s\n", ipmp_errmsg(ipmperr
));
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
}
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
}
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
}
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
}
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
}
1266 static ipmpstat_enum_t addr_state
[] = {
1267 { "up", IPMP_ADDR_UP
},
1268 { "down", IPMP_ADDR_DOWN
},
1272 static ipmpstat_enum_t group_state
[] = {
1273 { "ok", IPMP_GROUP_OK
},
1274 { "failed", IPMP_GROUP_FAILED
},
1275 { "degraded", IPMP_GROUP_DEGRADED
},
1279 static ipmpstat_enum_t if_link
[] = {
1280 { "up", IPMP_LINK_UP
},
1281 { "down", IPMP_LINK_DOWN
},
1282 { "unknown", IPMP_LINK_UNKNOWN
},
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
},
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
},
1302 static ipmpstat_enum_t targ_mode
[] = {
1303 { "disabled", IPMP_TARG_DISABLED
},
1304 { "routes", IPMP_TARG_ROUTES
},
1305 { "multicast", IPMP_TARG_MULTICAST
},