ipmpstat.c revision ec382c61f99e6dbbdf119dcf5016ce0481d12e04
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <alloca.h>
#include <assert.h>
#include <errno.h>
#include <ipmp_admin.h>
#include <ipmp_query.h>
#include <libintl.h>
#include <libnvpair.h>
#include <libsysevent.h>
#include <locale.h>
#include <netdb.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/sysmacros.h>
/*
* ipmpstat -- display IPMP subsystem status.
*
* This utility makes extensive use of libipmp and IPMP sysevents to gather
* and pretty-print the status of the IPMP subsystem. All output formats
* except for -p (probe) use libipmp to create a point-in-time snapshot of the
* IPMP subsystem (unless the test-special -L flag is used), and then output
* the contents of that snapshot in a user-specified manner. Because the
* output format and requested fields aren't known until run-time, three sets
* of function pointers and two core data structures are used. Specifically:
*
* * The ipmpstat_walker_t function pointers (walk_*) iterate through
* all instances of a given IPMP object (group, interface, or address).
* At most one ipmpstat_walker_t is used per ipmpstat invocation.
* Since target information is included with the interface information,
* both -i and -t use the interface walker (walk_if()).
*
* * The ipmpstat_sfunc_t function pointers (sfunc_*) obtain a given
* value for a given IPMP object. Each ipmpstat_sunc_t is passed a
* buffer to write its result into, the buffer's size, and an
* ipmpstat_sfunc_arg_t state structure. The state structure consists
* of a pointer to the IPMP object to obtain information from
* (sa_data), and an open libipmp handle (sa_ih) which can be used to
* do additional libipmp queries, if necessary (e.g., because the
* object does not have all of the needed information).
*
* * The ipmpstat_field_t structure provides the list of supported fields
* for a given output format, along with output formatting information
* (e.g., field width), and a pointer to an ipmpstat_sfunc_t function
* that can obtain the value for a IPMP given object. For a given
* ipmpstat output format, there's a corresponding array of
* ipmpstat_field_t structures. Thus, one ipmpstat_field_t array is
* used per ipmpstat invocation.
*
* * The ipmpstat_ofmt_t provides an ordered list of the requested
* ipmpstat_field_t's (e.g., via -o) for a given ipmpstat invocation.
* It is built at runtime from the command-line arguments. This
* structure (and a given IPMP object) is used by ofmt_output() to
* output a single line of information about that IPMP object.
*
* * The ipmpstat_cbfunc_t function pointers (*_cbfunc) are called back
* by the walkers. They are used both internally to implement nested
* walks, and by the ipmpstat output logic to provide the glue between
* the IPMP object walkers and the ofmt_output() logic. Usually, a
* single line is output for each IPMP object, and thus ofmt_output()
* can be directly invoked (see info_output_cbfunc()). However, if
* multiple lines need to be output, then a more complex cbfunc is
* needed (see targinfo_output_cbfunc()). At most one cbfunc is used
* per ipmpstat invocation.
*/
/*
* Data type used by the sfunc callbacks to obtain the requested information
* from the agreed-upon object.
*/
typedef struct ipmpstat_sfunc_arg {
void *sa_data;
/*
* Data type that describes how to output a field; used by ofmt_output*().
*/
typedef struct ipmpstat_field {
const char *f_name; /* field name */
/*
* Data type that specifies the output field order; used by ofmt_output*()
*/
typedef struct ipmpstat_ofmt {
/*
* Function pointers used to iterate through IPMP objects.
*/
typedef void ipmpstat_cbfunc_t(ipmp_handle_t, void *, void *);
/*
* Data type used to implement nested walks.
*/
typedef struct ipmpstat_walkdata {
void *iw_funcarg; /* caller-specified arg */
/*
* Data type used by enum2str() to map an enumerated value to a string.
*/
typedef struct ipmpstat_enum {
const char *e_name; /* string */
int e_val; /* value */
/*
* Data type used to pass state between probe_output() and probe_event().
*/
typedef struct ipmpstat_probe_state {
/*
* Options that modify the output mode; more than one may be lit.
*/
typedef enum {
IPMPSTAT_OPT_NUMERIC = 0x1,
IPMPSTAT_OPT_PARSABLE = 0x2
/*
* Indices for the FLAGS field of the `-i' output format.
*/
enum {
};
#define IPMPSTAT_NCOL 80
static const char *progname;
static hrtime_t probe_output_start;
static ipmpstat_opt_t opt;
static int probe_event(sysevent_t *, void *);
static void ofmt_destroy(ipmpstat_ofmt_t *);
static void sighandler(int);
static void usage(void);
static void die(const char *, ...);
static void die_ipmperr(int, const char *, ...);
static void warn(const char *, ...);
static void warn_ipmperr(int, const char *, ...);
int
{
int c;
int err;
else
progname++;
(void) textdomain(TEXT_DOMAIN);
die("only one output format may be specified\n");
switch (c) {
case 'n':
break;
case 'L':
/* Undocumented option: for testing use ONLY */
break;
case 'P':
break;
case 'o':
break;
case 'a':
break;
case 'g':
walker = walk_group;
break;
case 'i':
break;
case 'p':
break;
case 't':
break;
default:
usage();
break;
}
}
usage();
if (opt & IPMPSTAT_OPT_PARSABLE) {
die("output field list (-o) required in parsable "
"output mode\n");
die("\"all\" not allowed in parsable output mode\n");
}
}
/*
* Obtain the window size and monitor changes to the size. This data
* is used to redisplay the output headers when necessary.
*/
die("cannot contact in.mpathd(1M) -- is IPMP in use?\n");
/*
* Create the ofmt linked list that will eventually be passed to
* to ofmt_output() to output the fields.
*/
/*
* If we've been asked to display probes, then call the probe output
* function. Otherwise, snapshot IPMP state (or use live state) and
* invoke the specified walker with the specified callback function.
*/
if (fields == probe_fields) {
} else {
if (qcontext == IPMP_QCONTEXT_SNAP)
else
}
}
ipmp_close(ih);
return (EXIT_SUCCESS);
}
/*
* Walks all IPMP groups on the system and invokes `cbfunc' on each, passing
* it `ih', the ipmp_groupinfo_t pointer, and `arg'.
*/
static void
{
int err;
uint_t i;
if (err != IPMP_SUCCESS) {
continue;
}
}
}
/*
* Walks all IPMP interfaces on the system and invokes `cbfunc' on each,
* passing it `ih', the ipmp_ifinfo_t pointer, and `arg'.
*/
static void
{
}
/*
* Walks all IPMP data addresses on the system and invokes `cbfunc' on each.
* passing it `ih', the ipmp_addrinfo_t pointer, and `arg'.
*/
static void
{
}
/*
* Nested walker callback function for walk_if().
*/
static void
{
int err;
uint_t i;
if (err != IPMP_SUCCESS) {
continue;
}
}
}
/*
* Nested walker callback function for walk_addr().
*/
static void
{
int err;
uint_t i;
char addr[INET6_ADDRSTRLEN];
struct sockaddr_storage *addrp;
if (err != IPMP_SUCCESS) {
continue;
}
}
}
static void
{
}
static void
{
}
static void
{
int err;
if (err != IPMP_SUCCESS) {
return;
}
}
static void
{
}
static void
{
}
static void
{
int err;
return;
/*
* If there's no inbound interface for this address, there can't
* be any outbound traffic.
*/
return;
/*
* The address can use any active interface in the group, so
* obtain all of those.
*/
if (err != IPMP_SUCCESS) {
return;
}
if (err != IPMP_SUCCESS) {
continue;
}
if (nactive++ != 0)
}
}
}
static void
{
}
static void
{
}
static void
{
}
static void
{
return;
}
static void
{
int err;
uint_t i;
active[0] = '\0';
inactive[0] = '\0';
unusable[0] = '\0';
if (err != IPMP_SUCCESS) {
continue;
}
if (nactive++ != 0)
if (ninactive++ != 0)
} else {
if (nunusable++ != 0)
}
}
if (ninactive > 0) {
if (nactive != 0)
}
if (nunusable > 0) {
}
}
static void
{
}
static void
{
else
}
static void
{
int err;
if (err != IPMP_SUCCESS) {
return;
}
}
static void
{
int err;
if (err != IPMP_SUCCESS) {
return;
}
}
static void
{
}
static void
{
}
static void
{
}
static void
{
return;
}
}
static void
{
char *ifname;
return;
}
}
static void
{
return;
}
}
static void
{
struct sockaddr_storage *target;
return;
}
}
static void
{
return;
}
if (state != IPMP_PROBE_ACKED)
return;
return;
}
return;
}
}
static void
{
return;
}
if (state != IPMP_PROBE_ACKED)
return;
return;
}
return;
}
}
static void
{
return;
}
if (rttavg != 0)
}
static void
{
return;
}
if (rttdev != 0)
}
/* ARGSUSED */
static void
{
(*nenabledp)++;
}
static void
{
char sub[MAX_SUBID_LEN];
/*
* Check if any interfaces are enabled for probe-based failure
* detection. If not, immediately fail.
*/
if (nenabled == 0)
die("probe-based failure detection is disabled\n");
/*
* Unfortunately, until 4791900 is fixed, only privileged processes
* can bind and thus receive sysevents.
*/
if (errno != 0) {
die("insufficient privileges for -p\n");
}
/*
* The subscriber must be unique in order for sysevent_evc_subscribe()
* to succeed, so combine our name and pid.
*/
if (errno != 0)
for (;;)
(void) pause();
}
static int
{
return (0);
warn("sysevent_get_attr_list failed; dropping event");
return (0);
}
warn("dropped event with no IPMP_EVENT_VERSION\n");
goto out;
}
if (version != IPMP_EVENT_CUR_VERSION) {
warn("dropped event with unsupported IPMP_EVENT_VERSION %d\n",
version);
goto out;
}
warn("dropped event with no IPMP_PROBE_STATE\n");
goto out;
}
out:
return (0);
}
static void
{
}
static void
{
}
static void
{
}
static void
{
uint_t i;
}
}
static void
{
}
static void
{
/*
* Usually, either IPv4 or IPv6 probing will be enabled, but the admin
* may enable both. If only one is enabled, omit the other one so as
* to not encourage the admin to enable both. If neither is enabled,
* we still print one just so the admin can see a MODE of "disabled".
*/
if (targmode6 != IPMP_TARG_DISABLED)
}
/*
* Creates an ipmpstat_ofmt_t field list from the comma-separated list of
* user-specified fields passed via `ofields'. The table of known fields
* (and their attributes) is passed via `fields'.
*/
static ipmpstat_ofmt_t *
{
const char *fieldname;
/*
* If "-o" was omitted or "-o all" was specified, build a list of
* field names. If "-o" was omitted, stop building the list when
* we run out of columns.
*/
break;
die("cannot allocate output format list");
} else {
}
}
return (ofmt_head);
}
die("cannot allocate output format list");
token = ofields_dup;
/*
* Since machine parsers are unlikely to be able to
* gracefully handle missing fields, die if we're in
* parsable mode. Otherwise, just print a warning.
*/
if (opt & IPMPSTAT_OPT_PARSABLE)
continue;
}
die("cannot allocate output format list");
} else {
}
}
die("no valid output fields specified\n");
return (ofmt_head);
}
/*
* Destroys the provided `ofmt' field list.
*/
static void
{
}
}
/*
* Outputs a header for the fields named by `ofmt'.
*/
static void
{
const ipmpstat_field_t *fieldp;
else
}
(void) printf("\n");
}
/*
* Outputs one row of values for the fields named by `ofmt'. The values to
* output are obtained through the `ofmt' function pointers, which are
* indirectly passed the `ih' and `arg' structures for state; see the block
* comment at the start of this file for details.
*/
static void
{
int i;
char buf[1024];
static int nrow;
const char *value;
const ipmpstat_field_t *fieldp;
/*
* For each screenful of data, display the header.
*/
nrow++;
}
/*
* Check if we'll be displaying multiple fields per line, and thus
* need to escape the field separator.
*/
buf[0] = '\0';
if (opt & IPMPSTAT_OPT_PARSABLE) {
for (i = 0; buf[i] != '\0'; i++) {
(void) putchar('\\');
}
(void) putchar(':');
} else {
/*
* To avoid needless line-wraps, for the last field,
* don't include any trailing whitespace.
*/
continue;
}
/*
* For other fields, grow the width as necessary to
* ensure the value completely fits. However, if
* there's unused whitespace in subsequent fields,
* then "compress" that whitespace to attempt to get
* the columns to line up again.
*/
continue;
}
if (overflow > 0) {
}
}
}
(void) printf("\n");
/*
* In case stdout has been redirected to e.g. a pipe, flush stdout so
* that commands can act on our output immediately.
*/
}
/*
* Searches the `fields' array for a field matching `fieldname'. Returns
* a pointer to that field on success, or NULL on failure.
*/
static ipmpstat_field_t *
{
return (fieldp);
}
return (NULL);
}
/*
* Uses `enums' to map `enumval' to a string, and stores at most `bufsize'
* bytes of that string into `buf'.
*/
static void
{
const ipmpstat_enum_t *enump;
return;
}
}
}
/*
* Stores the stringified value of the sockaddr_storage pointed to by `ssp'
* into at most `bufsize' bytes of `buf'.
*/
static void
{
/*
* Sadly, getnameinfo() does not allow the socklen to be oversized for
* a given family -- so we must determine the exact size to pass to it.
*/
case AF_INET:
socklen = sizeof (struct sockaddr_in);
break;
case AF_INET6:
socklen = sizeof (struct sockaddr_in6);
break;
default:
return;
}
if (opt & IPMPSTAT_OPT_NUMERIC)
flags |= NI_NUMERICHOST;
}
static void
sighandler(int sig)
{
}
}
static void
usage(void)
{
" output modes:\t -a display IPMP data address information\n"
"\t\t -g display IPMP group information\n"
"\t\t -i display IPMP-related IP interface information\n"
"\t\t -p display IPMP probe information\n"
"\t\t -t display IPMP target information\n\n"
" options:\t -n display IP addresses numerically\n"
"\t\t -o display only the specified fields, in order\n"
"\t\t -P display using parsable output mode\n"));
}
/* PRINTFLIKE1 */
static void
{
}
/* PRINTFLIKE2 */
static void
{
}
/* PRINTFLIKE1 */
static void
{
}
/* PRINTFLIKE2 */
static void
{
}
static ipmpstat_field_t addr_fields[] = {
};
static ipmpstat_field_t group_fields[] = {
};
static ipmpstat_field_t if_fields[] = {
};
static ipmpstat_field_t probe_fields[] = {
};
static ipmpstat_field_t targ_fields[] = {
};
static ipmpstat_enum_t addr_state[] = {
{ "up", IPMP_ADDR_UP },
{ "down", IPMP_ADDR_DOWN },
{ NULL, 0 }
};
static ipmpstat_enum_t group_state[] = {
{ "ok", IPMP_GROUP_OK },
{ "failed", IPMP_GROUP_FAILED },
{ "degraded", IPMP_GROUP_DEGRADED },
{ NULL, 0 }
};
static ipmpstat_enum_t if_link[] = {
{ "up", IPMP_LINK_UP },
{ "down", IPMP_LINK_DOWN },
{ "unknown", IPMP_LINK_UNKNOWN },
{ NULL, 0 }
};
static ipmpstat_enum_t if_probe[] = {
{ "ok", IPMP_PROBE_OK },
{ "failed", IPMP_PROBE_FAILED },
{ "unknown", IPMP_PROBE_UNKNOWN },
{ "disabled", IPMP_PROBE_DISABLED },
{ NULL, 0 }
};
static ipmpstat_enum_t if_state[] = {
{ "ok", IPMP_IF_OK },
{ "failed", IPMP_IF_FAILED },
{ "unknown", IPMP_IF_UNKNOWN },
{ "offline", IPMP_IF_OFFLINE },
{ NULL, 0 }
};
static ipmpstat_enum_t targ_mode[] = {
{ "disabled", IPMP_TARG_DISABLED },
{ "routes", IPMP_TARG_ROUTES },
{ "multicast", IPMP_TARG_MULTICAST },
{ NULL, 0 }
};