etm.c revision 2ae66659c5a202af906daffb7064495db1792dd4
/*
* 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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* etm.c FMA Event Transport Module implementation, a plugin of FMD
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* --------------------------------- includes --------------------------------
*/
#include "etm_xport_api.h"
#include "etm_etm_proto.h"
#include "etm_impl.h"
#include <pthread.h>
#include <signal.h>
#include <stropts.h>
#include <locale.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <values.h>
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
/*
* ----------------------------- forward decls -------------------------------
*/
static void
/*
* ------------------------- data structs for FMD ----------------------------
*/
static const fmd_hdl_ops_t fmd_ops = {
etm_recv, /* fmdo_recv */
NULL, /* fmdo_timeout */
NULL, /* fmdo_close */
NULL, /* fmdo_stats */
NULL, /* fmdo_gc */
NULL, /* fmdo_send */
};
static const fmd_prop_t fmd_props[] = {
};
static const fmd_hdl_info_t fmd_info = {
};
/*
* ----------------------- private consts and defns --------------------------
*/
/* misc buffer for variable sized protocol header fields */
/* try limit for IO operations w/ capped exp backoff sleep on retry */
/*
* Design_Note: ETM will potentially retry forever IO operations that the
* transport fails with EAGAIN (aka EWOULDBLOCK) rather than
* giving up after some number of seconds. This avoids
* dropping FMA events while the service processor is down,
* but at the risk of pending fmdo_recv() forever and
* overflowing FMD's event queue for ETM.
* A future TBD enhancement would be to always recv
* the risk of failure between ETM msg hdr and body,
* assuming the MTU_SZ is large enough.
*/
#define ETM_TRY_BACKOFF_RATE (4)
#define ETM_TRY_BACKOFF_CAP (60)
/* amount to increment protocol transaction id on each new send */
#define ETM_XID_INC (2)
/*
* ---------------------------- global data ----------------------------------
*/
static fmd_hdl_t
static int
etm_debug_lvl = 0; /* debug level: 0 is off, 1 is on, 2 is more, etc */
static int
static fmd_xprt_t
static pthread_t
static volatile int
etm_is_dying = 0; /* bool for dying (killing self) */
static uint32_t
etm_xid_cur = 0; /* current transaction id for sends */
static uint32_t
etm_xid_ping = 0; /* xid of last CONTROL msg sent requesting ping */
static uint32_t
etm_xid_ver_negot = 0; /* xid of last CONTROL msg sent requesting ver negot */
static uint32_t
etm_xid_posted_ev = 0; /* xid of last FMA_EVENT msg/event posted OK to FMD */
static uint8_t
static pthread_mutex_t
static int syslog_facility; /* log(7D) facility (part of priority) */
static int syslog_file = 0; /* log to syslog_logfd */
static int syslog_cons = 0; /* log to syslog_msgfd */
static const struct facility {
const char *fac_name;
int fac_value;
} syslog_facs[] = {
{ "LOG_DAEMON", LOG_DAEMON },
{ "LOG_LOCAL0", LOG_LOCAL0 },
{ "LOG_LOCAL1", LOG_LOCAL1 },
{ "LOG_LOCAL2", LOG_LOCAL2 },
{ "LOG_LOCAL3", LOG_LOCAL3 },
{ "LOG_LOCAL4", LOG_LOCAL4 },
{ "LOG_LOCAL5", LOG_LOCAL5 },
{ "LOG_LOCAL6", LOG_LOCAL6 },
{ "LOG_LOCAL7", LOG_LOCAL7 },
{ NULL, 0 }
};
static struct stats {
/* ETM msg counters */
/* ETM byte counters */
/* ETM [dropped] FMA event counters */
/* ETM protocol failures */
/* IO operation failures */
/* IO operation retries */
/* system and library failures */
/* xport API failures */
/* FMD entry point bad arguments */
/* Alert logging errors */
} etm_stats = {
/* ETM msg counters */
{ "etm_rd_hdr_fmaevent", FMD_TYPE_UINT64,
"ETM fmaevent msg headers rcvd from xport" },
{ "etm_rd_hdr_control", FMD_TYPE_UINT64,
"ETM control msg headers rcvd from xport" },
{ "etm_rd_hdr_alert", FMD_TYPE_UINT64,
"ETM alert msg headers rcvd from xport" },
{ "etm_rd_hdr_response", FMD_TYPE_UINT64,
"ETM response msg headers rcvd from xport" },
{ "etm_rd_body_fmaevent", FMD_TYPE_UINT64,
"ETM fmaevent msg bodies rcvd from xport" },
{ "etm_rd_body_control", FMD_TYPE_UINT64,
"ETM control msg bodies rcvd from xport" },
{ "etm_rd_body_alert", FMD_TYPE_UINT64,
"ETM alert msg bodies rcvd from xport" },
{ "etm_rd_body_response", FMD_TYPE_UINT64,
"ETM response msg bodies rcvd from xport" },
{ "etm_wr_hdr_fmaevent", FMD_TYPE_UINT64,
"ETM fmaevent msg headers sent to xport" },
{ "etm_wr_hdr_control", FMD_TYPE_UINT64,
"ETM control msg headers sent to xport" },
{ "etm_wr_hdr_response", FMD_TYPE_UINT64,
"ETM response msg headers sent to xport" },
{ "etm_wr_body_fmaevent", FMD_TYPE_UINT64,
"ETM fmaevent msg bodies sent to xport" },
{ "etm_wr_body_control", FMD_TYPE_UINT64,
"ETM control msg bodies sent to xport" },
{ "etm_wr_body_response", FMD_TYPE_UINT64,
"ETM response msg bodies sent to xport" },
/* ETM byte counters */
{ "etm_wr_fmd_bytes", FMD_TYPE_UINT64,
"bytes of FMA events sent to FMD" },
{ "etm_rd_fmd_bytes", FMD_TYPE_UINT64,
"bytes of FMA events rcvd from FMD" },
{ "etm_wr_xport_bytes", FMD_TYPE_UINT64,
"bytes of FMA events sent to xport" },
{ "etm_rd_xport_bytes", FMD_TYPE_UINT64,
"bytes of FMA events rcvd from xport" },
{ "etm_magic_drop_bytes", FMD_TYPE_UINT64,
"bytes dropped from xport pre magic num" },
/* ETM [dropped] FMA event counters */
{ "etm_rd_fmd_fmaevent", FMD_TYPE_UINT64,
"FMA events rcvd from FMD" },
{ "etm_wr_fmd_fmaevent", FMD_TYPE_UINT64,
"FMA events sent to FMD" },
{ "etm_rd_drop_fmaevent", FMD_TYPE_UINT64,
"dropped FMA events from xport" },
{ "etm_wr_drop_fmaevent", FMD_TYPE_UINT64,
"dropped FMA events to xport" },
{ "etm_rd_dup_fmaevent", FMD_TYPE_UINT64,
"duplicate FMA events from xport" },
{ "etm_wr_dup_fmaevent", FMD_TYPE_UINT64,
"duplicate FMA events to xport" },
/* ETM protocol failures */
{ "etm_magic_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid magic num" },
{ "etm_ver_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid protocol version" },
{ "etm_msgtype_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid message type" },
{ "etm_subtype_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid sub type" },
{ "etm_xid_bad", FMD_TYPE_UINT64,
"ETM msgs w/ unmatched xid" },
{ "etm_fmaeventlen_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid FMA event length" },
{ "etm_respcode_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid response code" },
{ "etm_timeout_bad", FMD_TYPE_UINT64,
"ETM msgs w/ invalid timeout value" },
{ "etm_evlens_bad", FMD_TYPE_UINT64,
"ETM msgs w/ too many event lengths" },
/* IO operation failures */
{ "etm_xport_wr_fail", FMD_TYPE_UINT64,
"xport write failures" },
{ "etm_xport_rd_fail", FMD_TYPE_UINT64,
"xport read failures" },
{ "etm_xport_pk_fail", FMD_TYPE_UINT64,
"xport peek failures" },
/* IO operation retries */
{ "etm_xport_wr_retry", FMD_TYPE_UINT64,
"xport write retries" },
{ "etm_xport_rd_retry", FMD_TYPE_UINT64,
"xport read retries" },
{ "etm_xport_pk_retry", FMD_TYPE_UINT64,
"xport peek retries" },
/* system and library failures */
{ "etm_os_nvlist_pack_fail", FMD_TYPE_UINT64,
"nvlist_pack failures" },
{ "etm_os_nvlist_unpack_fail", FMD_TYPE_UINT64,
"nvlist_unpack failures" },
{ "etm_os_nvlist_size_fail", FMD_TYPE_UINT64,
"nvlist_size failures" },
{ "etm_os_pthread_create_fail", FMD_TYPE_UINT64,
"pthread_create failures" },
/* transport API failures */
{ "etm_xport_get_ev_addrv_fail", FMD_TYPE_UINT64,
"xport get event addrv API failures" },
{ "etm_xport_open_fail", FMD_TYPE_UINT64,
"xport open API failures" },
{ "etm_xport_close_fail", FMD_TYPE_UINT64,
"xport close API failures" },
{ "etm_xport_accept_fail", FMD_TYPE_UINT64,
"xport accept API failures" },
{ "etm_xport_open_retry", FMD_TYPE_UINT64,
"xport open API retries" },
/* FMD entry point bad arguments */
{ "etm_fmd_recv_badargs", FMD_TYPE_UINT64,
"bad arguments from fmd_recv entry point" },
{ "etm_fmd_init_badargs", FMD_TYPE_UINT64,
"bad arguments from fmd_init entry point" },
{ "etm_fmd_fini_badargs", FMD_TYPE_UINT64,
"bad arguments from fmd_fini entry point" },
/* Alert logging errors */
{ "etm_log_err", FMD_TYPE_UINT64,
"failed to log message to log(7D)" },
{ "etm_msg_err", FMD_TYPE_UINT64,
"failed to log message to sysmsg(7D)" }
};
/*
* -------------------------- support functions ------------------------------
*/
/*
* Design_Note: Each failure worth reporting to FMD should be done using
* a single call to fmd_hdl_error() as it logs an FMA event
* for each call. Also be aware that all the fmd_hdl_*()
* format strings currently use platform specific *printf()
* routines; so "%p" under Solaris does not prepend "0x" to
* the outputted hex digits, while Linux and VxWorks do.
*/
/*
* etm_show_time - display the current time of day (for debugging) using
* the given FMD module handle and annotation string
*/
static void
{
} /* etm_show_time() */
/*
* etm_hexdump - hexdump the given buffer (for debugging) using
* the given FMD module handle
*/
static void
{
int i, j; /* index */
unsigned int n; /* a byte of data for sprintf() */
j = 0;
/*
* Design_Note: fmd_hdl_debug() auto adds a newline if missing;
* hence cb exists to accumulate a longer string.
*/
for (i = 1; i <= byte_cnt; i++) {
n = *bp++;
j += 3;
/* add a newline every 16 bytes or at the buffer's end */
if (((i % 16) == 0) || (i >= byte_cnt)) {
j = 0;
}
} /* for each byte in the buffer */
} /* etm_hexdump() */
/*
* etm_sleep - sleep the caller for the given number of seconds,
* return 0 or -errno value
*
* Design_Note: To avoid interfering with FMD's signal mask (SIGALRM)
* do not use [Solaris] sleep(3C) and instead use
* pthread_cond_wait() or nanosleep(), both of which
* are POSIX spec-ed to leave signal masks alone.
* This is needed for Solaris and Linux (domain and SP).
*/
static int
{
/* errno assumed set by above call */
return (-errno);
}
return (0);
} /* etm_sleep() */
/*
* etm_conn_open - open a connection to the given transport address,
* return 0 and the opened connection handle
* or -errno value
*
* caveats: the err_substr is used in failure cases for calling
* fmd_hdl_error()
*/
static int
{
int nev; /* -errno value */
err_substr, errno);
return (nev);
} else {
return (0);
}
} /* etm_conn_open() */
/*
* etm_conn_close - close the given connection,
* return 0 or -errno value
*
* caveats: the err_substr is used in failure cases for calling
* fmd_hdl_error()
*/
static int
{
int nev; /* -errno value */
err_substr, errno);
return (nev);
} else {
return (0);
}
} /* etm_conn_close() */
/*
* etm_io_op - perform an IO operation on the given connection
* with the given buffer,
* accommodating MTU size and retrying op if needed,
* return how many bytes actually done by the op
* or -errno value
*
* caveats: the err_substr is used in failure cases for calling
* fmd_hdl_error()
*/
static ssize_t
{
ssize_t n; /* gen use */
void *, size_t);
int try_cnt; /* number of tries done */
int sleep_sec; /* exp backoff sleep period in sec */
int sleep_rv; /* ret val from sleeping */
return (-EINVAL);
}
switch (io_op) {
case ETM_IO_OP_RD:
break;
case ETM_IO_OP_WR:
break;
default:
return (-EINVAL);
}
if (byte_cnt == 0) {
return (byte_cnt); /* nop */
}
/* obtain [current] MTU size */
} else {
mtu_sz = n;
}
/* loop until all IO done, try limit exceeded, or real failure */
rv = 0;
try_cnt = 0;
sleep_sec = 0;
/* when give up, return -errno value even if partly done */
(-EAGAIN)) {
try_cnt++;
if (try_cnt > ETM_TRY_MAX_CNT) {
rv = n;
goto func_ret;
}
if (etm_is_dying) {
goto func_ret;
}
goto func_ret;
}
if (etm_debug_lvl >= 1) {
"due to EAGAIN\n", io_op);
}
} /* while trying the io operation */
if (etm_is_dying) {
goto func_ret;
}
if (n < 0) {
rv = n;
goto func_ret;
}
/* avoid spinning CPU when given 0 bytes but no error */
if (n == 0) {
goto func_ret;
}
}
rv += n;
datap += n;
} /* while still have more data */
if (rv < 0) {
err_substr, (int)(-rv));
}
if (etm_debug_lvl >= 3) {
}
return (rv);
} /* etm_io_op() */
/*
* etm_magic_read - read the magic number of an ETM message header
* from the given connection into the given buffer,
* return 0 or -errno value
*
* Design_Note: This routine is intended to help protect ETM from protocol
* framing errors as might be caused by an SP reset / crash in
* the middle of an ETM message send; the connection will be
* read from for as many bytes as needed until the magic number
* is found using a sliding buffer for comparisons.
*/
static int
{
int rv; /* ret val */
int byte_cnt; /* count of bytes read */
int i, j; /* indices into buf5 */
ssize_t n; /* gen use */
rv = 0; /* assume success */
magic_num = 0;
byte_cnt = 0;
j = 0;
/* magic number bytes are sent in network (big endian) order */
while (magic_num != ETM_PROTO_MAGIC_NUM) {
rv = n;
goto func_ret;
}
byte_cnt++;
continue;
}
for (i = 0; i < j; i++) {
} /* for sliding the buffer contents */
}
} /* for reading bytes until find magic number */
}
"first %d of %d bytes:\n",
}
if (rv == 0) {
}
return (rv);
} /* etm_magic_read() */
/*
* etm_hdr_read - allocate, read, and validate a [variable sized]
* ETM message header from the given connection,
* return the allocated ETM message header
* (which is guaranteed to be large enough to reuse as a
* RESPONSE msg hdr) and its size
* or NULL and set errno on failure
*/
static void *
{
ssize_t i, n; /* gen use */
int dummy_int; /* dummy var to appease lint */
/* read the magic number which starts the protocol preamble */
errno = (-n);
return (NULL);
}
/* read the rest of the protocol preamble all at once */
ETM_IO_OP_RD)) < 0) {
errno = (-n);
return (NULL);
}
/*
* Design_Note: The magic number was already network decoded; but
* some other preamble fields also need to be decoded,
* specifically pp_xid and pp_timeout. The rest of the
* preamble fields are byte sized and hence need no
* decoding.
*/
/* sanity check the header as best we can */
(int)pp.pp_proto_ver);
return (NULL);
}
if ((dummy_int <= ETM_MSG_TYPE_TOO_LOW) ||
(dummy_int >= ETM_MSG_TYPE_TOO_BIG)) {
return (NULL);
}
/* handle [var sized] hdrs for FMA_EVENT, CONTROL, RESPONSE msgs */
/* sanity check the header's timeout */
return (NULL);
}
/* get all FMA event lengths from the header */
i = -1; /* cnt of length entries preceding 0 */
do {
i++; lenp++;
return (NULL);
}
ETM_IO_OP_RD)) < 0) {
errno = (-n);
return (NULL);
}
} while (*lenp != 0);
i += 0; /* first len already counted by sizeof(ev_hdr) */
/* sanity check the header's sub type (control selector) */
return (NULL);
}
/* get the control length */
ETM_IO_OP_RD)) < 0) {
errno = (-n);
return (NULL);
}
/* sanity check the header's timeout */
return (NULL);
}
/* get the response code and length */
ETM_IO_OP_RD)) < 0) {
errno = (-n);
return (NULL);
}
/* sanity check the header's protocol version */
return (NULL);
}
/* get the priority and length */
sizeof (sa_hdrp->sa_priority) +
ETM_IO_OP_RD)) < 0) {
errno = (-n);
return (NULL);
}
} /* whether we have FMA_EVENT, ALERT, CONTROL, or RESPONSE msg */
/*
* choose a header size that allows hdr reuse for RESPONSE msgs,
* allocate and populate the message header, and
* return alloc size to caller for later free of hdrp
*/
if (etm_debug_lvl >= 3) {
hdr_sz);
}
return (hdrp);
} /* etm_hdr_read() */
/*
* etm_hdr_write - create and write a [variable sized] ETM message header
* to the given connection appropriate for the given FMA event
* and type of nvlist encoding,
* return the allocated ETM message header and its size
* or NULL and set errno on failure
*/
static void*
{
ssize_t n; /* gen use */
/* allocate and populate the message header for 1 FMA event */
/*
* Design_Note: Although the ETM protocol supports it, we do not (yet)
* such messages are sent with ETM_PROTO_V1_TIMEOUT_NONE.
*/
errno = n;
return (NULL);
}
/* indicate 1 FMA event, network encode its length, and 0-terminate */
/*
* write the network encoded header to the transport, and
* return alloc size to caller for later free
*/
errno = (-n);
return (NULL);
}
return (hdrp);
} /* etm_hdr_write() */
/*
* etm_post_to_fmd - post the given FMA event to FMD
* via a FMD transport API call,
* return 0 or -errno value
*
* caveats: the FMA event (evp) is freed by FMD,
* thus callers of this function should
* immediately discard any ptr they have to the
* nvlist without freeing or dereferencing it
*/
static int
{
if (etm_debug_lvl >= 2) {
}
if (etm_debug_lvl >= 1) {
}
if (etm_debug_lvl >= 2) {
}
return (0);
} /* etm_post_to_fmd() */
/*
* Ideally we would just use syslog(3C) for outputting our messages.
* Unfortunately, as this module is running within the FMA daemon context,
* that would create the situation where this module's openlog() would
* have the monopoly on syslog(3C) for the daemon and all its modules.
* To avoid that situation, this module uses the same logic as the
* syslog-msgs FM module to directly call into the log(7D) and sysmsg(7D)
* devices for syslog and console.
*/
static int
{
char *sysmessage; /* Formatted message */
if ((syslog_file == 0) && (syslog_cons == 0)) {
return (0);
}
if (etm_debug_lvl >= 2) {
}
if (syslog_file) {
"SC Alert: [ID %u FACILITY_AND_PRIORITY] %s", msgid,
body_buf);
}
}
if (syslog_cons) {
"SC Alert: %s\r\n", body_buf);
}
}
if (etm_debug_lvl >= 2) {
}
return (0);
}
/*
* etm_req_ver_negot - send an ETM control message to the other end requesting
* that the ETM protocol version be negotiated/set
*/
static void
{
ssize_t i; /* gen use */
/* populate an ETM control msg to send */
*body_buf++ = ETM_PROTO_V3;
*body_buf++ = ETM_PROTO_V2;
*body_buf++ = ETM_PROTO_V1;
*body_buf++ = '\0';
/*
* open and close a connection to send the ETM control msg
*/
"error: bad ctl dst addrs errno %d\n", errno);
goto func_ret;
}
continue;
}
ETM_IO_OP_WR) >= 0) {
}
conn);
} /* foreach dst addr */
}
} /* etm_req_ver_negot() */
/*
* Design_Note: We rely on the fact that all message types have
* a common protocol preamble; if this fact should
* ever change it may break the code below. We also
* rely on the fact that FMA_EVENT and CONTROL headers
* returned will be sized large enough to reuse them
* as RESPONSE headers if the remote endpt asked
* for a response via the pp_timeout field.
*/
/*
* etm_maybe_send_response - check the given message header to see
* whether a response has been requested,
* if so then send an appropriate response
* back on the given connection using the
* given response code,
* return 0 or -errno value
*/
static ssize_t
{
ssize_t n; /* gen use */
rv = 0; /* default is success */
/* bail out now if no response is to be sent */
if (orig_timeout == ETM_PROTO_V1_TIMEOUT_NONE) {
return (0);
} /* if a nop */
if ((orig_msg_type != ETM_MSG_TYPE_FMA_EVENT) &&
(orig_msg_type != ETM_MSG_TYPE_ALERT) &&
(orig_msg_type != ETM_MSG_TYPE_CONTROL)) {
return (-EINVAL);
} /* if inappropriate hdr for a response msg */
/* reuse the given header as a response header */
if (etm_debug_lvl >= 2) {
}
if ((orig_msg_type == ETM_MSG_TYPE_CONTROL) &&
resp_body[0] = ETM_PROTO_V2;
resp_body[2] = 0;
} /* if should send our/negotiated proto ver in resp body */
/* respond with the proto ver that was negotiated */
/*
* send the whole response msg in one write, header and body;
* avoid the alloc-and-copy if we can reuse the hdr as the msg,
* ie, if the body is empty
*
* update stats and note the xid associated with last ACKed FMA_EVENT
* known to be successfully posted to FMD to aid duplicate filtering
*/
hdr_sz = sizeof (etm_proto_v1_resp_hdr_t);
}
(void) pthread_mutex_lock(&etm_write_lock);
(void) pthread_mutex_unlock(&etm_write_lock);
rv = n;
goto func_ret;
}
(void) pthread_mutex_unlock(&etm_write_lock);
if ((orig_msg_type == ETM_MSG_TYPE_FMA_EVENT) &&
(resp_code >= 0)) {
}
"xid 0x%x code %d len %u\n",
}
if (etm_debug_lvl >= 2) {
}
return (rv);
} /* etm_maybe_send_response() */
/*
* etm_handle_new_conn - receive an ETM message sent from the other end via
* the given open connection, pull out any FMA events
* and post them to the local FMD (or handle any ETM
* control or response msg); when done, close the
* connection
*/
static void
{
char *class; /* FMA event class */
ssize_t i, n; /* gen use */
if (etm_debug_lvl >= 2) {
}
resp_code = 0; /* default is success */
/* read a network decoded message header from the connection */
/* errno assumed set by above call */
"bad hdr read errno %d\n", errno);
goto func_ret;
}
/*
* handle the message based on its preamble pp_msg_type
* which is known to be valid from etm_hdr_read() checks
*/
/*
* if a dup then resend response but skip repost to FMD
*/
"xid 0x%x\n", etm_xid_posted_ev);
goto func_ret;
}
/* allocate buf large enough for whole body / all FMA events */
body_sz = 0;
} /* for summing sizes of all FMA events */
ev_cnt = i;
if (etm_debug_lvl >= 1) {
}
/* read all the FMA events at once */
"bad io read on event bodies",
ETM_IO_OP_RD)) < 0) {
goto func_ret;
}
/* unpack each FMA event and post it to FMD */
for (i = 0; i < ev_cnt; i++) {
if ((n = nvlist_unpack((char *)bp,
resp_code = (-n);
"bad event body unpack "
"errno %d\n", n);
if (etm_debug_lvl >= 2) {
"hexdump %d bytes:\n",
}
ui64++;
ui64++;
continue;
}
if (etm_debug_lvl >= 1) {
&class);
class = "NULL";
}
}
} /* foreach FMA event in the body buffer */
if (etm_debug_lvl >= 1) {
}
/*
* if we have a VER_NEGOT_REQ read the body and validate
* the protocol version set contained therein,
* otherwise we have a PING_REQ (which has no body)
* and we [also] fall thru to the code which sends a
* response msg if the pp_timeout field requested one
*/
ETM_IO_OP_RD)) < 0) {
goto func_ret;
}
/* complain if version set completely incompatible */
for (i = 0; i < body_sz; i++) {
if ((body_buf[i] == ETM_PROTO_V1) ||
(body_buf[i] == ETM_PROTO_V2) ||
(body_buf[i] == ETM_PROTO_V3)) {
break;
}
}
if (i >= body_sz) {
}
} /* if got version set request */
if (etm_debug_lvl >= 1) {
}
goto func_ret;
}
/*
* look up the xid to interpret the response body
*
* ping is a nop; for ver negot confirm that a supported
* protocol version was negotiated and remember which one
*/
goto func_ret;
}
if ((body_buf[0] < ETM_PROTO_V1) ||
(body_buf[0] > ETM_PROTO_V3)) {
goto func_ret;
}
etm_resp_ver = body_buf[0];
} /* if have resp to last req to negotiate proto ver */
if (etm_debug_lvl >= 1) {
}
ETM_IO_OP_RD)) < 0) {
goto func_ret;
}
} /* whether we have a FMA_EVENT, CONTROL, RESPONSE or ALERT msg */
if (etm_debug_lvl >= 2) {
}
}
}
} /* etm_handle_new_conn() */
/*
* etm_server - loop forever accepting new connections
* using the given FMD handle,
* handling any ETM msgs sent from the other side
* via each such connection
*/
static void
etm_server(void *arg)
{
ssize_t n; /* gen use */
while (!etm_is_dying) {
/* errno assumed set by above call */
n = errno;
if (etm_is_dying) {
break;
}
"error: bad conn accept errno %d\n", n);
/* avoid spinning CPU */
(void) etm_sleep(ETM_SLEEP_SLOW);
continue;
}
/*
* Design_Note: etm_handle_new_conn() will close the
* accepted connection when done. In early designs
* etm_handle_new_conn() was spawned as a
* separate thread via pthread_create();
* however fmd_thr_create() constrains thread
* creation to prevent spawned threads from
* spawning others (ie, no grandchildren).
* Hence etm_handle_new_conn() is now called
* as a simple function [w/ multiple args].
*/
} /* while accepting new connections until ETM dies */
/* ETM is dying (probably due to "fmadm unload etm") */
if (etm_debug_lvl >= 1) {
}
} /* etm_server() */
static void *
{
}
static void
{
}
/*
* -------------------------- FMD entry points -------------------------------
*/
/*
* _fmd_init - initialize the transport for use by ETM and start the
* server daemon to accept new connections to us
*
* FMD will read our *.conf and subscribe us to FMA events
*/
void
{
ssize_t n; /* gen use */
char *facname; /* syslog facility property */
return; /* invalid data in configuration file */
}
/*
* Do not load this module if it is runing on a guest ldom.
*/
return;
} else {
}
/* setup statistics and properties from FMD */
sizeof (etm_stats) / sizeof (fmd_stat_t),
(fmd_stat_t *)&etm_stats);
"etm_debug_max_ev_cnt %d\n",
/* obtain an FMD transport handle so we can post FMA events later */
/* encourage protocol transaction id to be unique per module load */
/*
* init the transport,
* start the connection acceptance server, and
* request protocol version be negotiated
*/
if ((n = etm_xport_init(hdl)) != 0) {
return;
}
/*
* Cache any properties we use every time we receive an alert.
*/
syslog_file = 0;
}
syslog_cons = 0;
}
if (syslog_file) {
/*
* Look up the value of the "facility" property and use it to
* determine * what syslog LOG_* facility value we use to
* fill in our log_ctl_t.
*/
break;
}
" setting: %s\n", facname);
syslog_file = 0;
} else {
}
}
/*
* Wait a second for the receiver to be ready before start handshaking
* with the SP.
*/
(void) etm_sleep(ETM_SLEEP_QUIK);
} /* _fmd_init() */
/*
* etm_recv - receive an FMA event from FMD and transport it
* to the remote endpoint
*/
/*ARGSUSED*/
void
{
ssize_t i, n; /* gen use */
buflen = 0;
/*
* if the debug limit has been set, avoid excessive traffic,
* for example, an infinite cycle using loopback nodes
*/
if ((etm_debug_max_ev_cnt >= 0) &&
"event %p cnt %llu > debug max %d\n", evp,
return;
}
/* allocate a buffer for the FMA event and nvlist pack it */
NV_ENCODE_XDR, 0)) != 0) {
"event pack errno %d\n", n);
return;
}
/* get vector of dst addrs and send the FMA event to each one */
"bad event dst addrs errno %d\n", errno);
return;
}
/* open a new connection to this dst addr */
"bad conn open on new ev",
continue;
}
(void) pthread_mutex_lock(&etm_write_lock);
/* write the ETM message header */
(void) pthread_mutex_unlock(&etm_write_lock);
"bad hdr write errno %d\n", errno);
(void) etm_conn_close(hdl,
"bad conn close per bad hdr wr", conn);
continue;
}
evp);
/* write the ETM message body, ie, the packed nvlist */
"bad io write on event", conn,
(void) pthread_mutex_unlock(&etm_write_lock);
(void) etm_conn_close(hdl,
"bad conn close per bad body wr", conn);
continue;
}
(void) pthread_mutex_unlock(&etm_write_lock);
evp);
/* close the connection */
conn);
} /* foreach dst addr in the vector */
} /* etm_recv() */
/*
* _fmd_fini - stop the server daemon and teardown the transport
*/
void
{
ssize_t n; /* gen use */
/* kill the connection server ; wait for it to die */
etm_is_dying = 1;
if (etm_svr_tid != NULL) {
etm_svr_tid = NULL;
} /* if server thread was successfully created */
/* teardown the transport */
if ((n = etm_xport_fini(hdl)) != 0) {
}
if (etm_fmd_xprt != NULL) {
}
if (syslog_logfd != -1) {
(void) close(syslog_logfd);
}
if (syslog_msgfd != -1) {
(void) close(syslog_msgfd);
}
} /* _fmd_fini() */