/*
* 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
*/
/*
*/
#include <sys/sysevent.h>
#include <sys/sysevent_impl.h>
#include <sys/sysmacros.h>
#include <libsysevent.h>
#include <libnvpair.h>
#include <alloca.h>
#include <limits.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <zone.h>
#include <fmd_api.h>
#include <fmd_log.h>
#include <fmd_subr.h>
#include <fmd_dispq.h>
#include <fmd_dr.h>
#include <fmd_module.h>
#include <fmd_protocol.h>
#include <fmd_scheme.h>
#include <fmd_error.h>
#include <fmd.h>
static int sysev_xprt_refcnt;
static struct sysev_stats {
} sysev_stats = {
};
static int sysev_exiting;
/*
* Entry point for legacy sysevents. This function is responsible for two
* things: passing off interesting events to the DR handler, and converting
* sysevents into resource events that modules can then subscribe to.
*/
static void
{
char *fullclass;
/* notify the DR subsystem of the event */
/* get the matching sysevent name */
/* construct the event payload */
}
/*
* Add class and version after the nvlist_merge() just in case
* the sysevent has an attribute called class or version.
*/
/*
* Dispatch the event. Because we have used sysevent_bind_xhandle
* the delivery thread is blessed as a proper fmd thread so
* we may use regular fmd api calls.
*/
}
/*
* Receive an event from the SysEvent channel and post it to our transport.
* Under extreme low-memory situations where we cannot event unpack the event,
* we can request that SysEvent redeliver the event later by returning EAGAIN.
* If we do this too many times, the kernel will drop the event. Rather than
* keeping state per-event, we simply attempt a garbage-collect, hoping that
* enough free memory will be available by the time the event is redelivered.
*/
static int
{
int rc = 0;
(void) pthread_mutex_lock(&sysev_mutex);
if (sysev_exiting == 1) {
while (sysev_xprt_refcnt > 0)
(void) pthread_mutex_unlock(&sysev_mutex);
return (EAGAIN);
}
while (sysev_replay_wait)
(void) pthread_mutex_unlock(&sysev_mutex);
} else {
"missing or invalid payload", seq);
}
} else {
}
(void) pthread_mutex_lock(&sysev_mutex);
(void) pthread_cond_broadcast(&sysev_cv);
(void) pthread_mutex_unlock(&sysev_mutex);
return (rc);
}
/*
* Checksum algorithm used by the dump transport for verifying the content of
* error reports saved on the dump device (copy of the kernel's checksum32()).
*/
static uint32_t
{
return (sum);
}
/*
* Replay saved events from the dump transport. This function is installed as
* the timer callback and is called only once during the module's lifetime.
*/
/*ARGSUSED*/
static void
{
char *dumpdev;
/*
* Determine the appropriate dump device to use for replaying pending
* error reports. If the device property is NULL (default), we
*/
"to locate dump device for event replay");
goto done;
}
if (err == -1) {
"path to dump device for event replay");
}
goto done;
}
}
/*
* Open the appropriate device and then determine the offset of the
* start of the ereport dump region located at the end of the device.
*/
"(pending events will not be replayed)", dumpdev);
goto done;
}
"(pending events will not be replayed)", dumpdev);
goto done;
}
/*
* The ereport dump region is a sequence of erpt_dump_t headers each of
* which is followed by packed nvlist data. We iterate over them in
* order, unpacking and dispatching each one to our dispatch queue.
*/
for (;;) {
"transport %s (pending events lost)", dumpdev);
break;
}
break; /* end of list: all zero */
continue; /* continue searching */
}
/*
* Stop reading silently if the first record has the
* wrong magic number; this likely indicates that we
* rebooted from non-FMA bits or paged over the dump.
*/
break;
"record at %llx (magic number %x, expected %x)\n",
break;
}
"record at %llx size (%u exceeds limit)\n",
break;
}
goto next;
}
"offset %llx is corrupt (checksum %x != %x)\n",
goto next;
}
"transport event at offset %llx: %s\n",
goto next;
}
/*
* If ed_hrt_nsec is set it contains the gethrtime() value from
* when the event was originally enqueued for the transport.
* If it is zero, we use the weaker bound ed_hrt_base instead.
*/
if (ed.ed_hrt_nsec != 0)
else
/*
* If this is an FMA protocol event of class "ereport.*" that
* contains valid ENA, we can improve the precision of 'hrt'.
*/
/*
* Now convert 'hrt' to an adjustable TOD based on the values
* in ed_tod_base which correspond to one another and are
* sampled before reboot using the old gethrtime() clock.
* fmd_event_recreate() will use this TOD value to re-assign
* the event an updated gethrtime() value based on the current
* value of the non-adjustable gethrtime() clock. Phew.
*/
(void) nvlist_add_uint64_array(nvl,
next:
/*
* Reset the magic number for the event record to zero so that
* we do not replay the same event multiple times.
*/
}
}
done:
(void) pthread_mutex_lock(&sysev_mutex);
sysev_replay_wait = 0;
(void) pthread_cond_broadcast(&sysev_cv);
(void) pthread_mutex_unlock(&sysev_mutex);
}
};
NULL, /* fmdo_recv */
sysev_replay, /* fmdo_timeout */
NULL, /* fmdo_close */
NULL, /* fmdo_stats */
NULL, /* fmdo_gc */
NULL, /* fmdo_send */
};
};
/*
* Bind to the sysevent channel we use for listening for error events and then
* subscribe to appropriate events received over this channel. Setup the
* legacy sysevent handler for creating sysevent resources and forwarding DR
* events.
*/
void
{
/* This builtin is for the global zone only */
if (getzoneid() != GLOBAL_ZONEID)
return;
return; /* invalid property settings */
if (sysev_channel == NULL)
EVCH_CREAT | EVCH_HOLD_PEND)) != 0) {
"channel %s", sysev_channel);
}
/*
* If we're subscribing to the default channel, keep our subscription
* active even if we die unexpectedly so we continue queuing events.
* If we're not (e.g. running under fmsim), do not specify SUB_KEEP so
* that our event channel will be destroyed if we die unpleasantly.
*/
else
"attributes");
if (errno != 0) {
"active on transport channel %s\n", sysev_channel);
} else {
}
}
/*
* Once the transport is open, install a single timer to fire at once
* in the context of the module's thread to run sysev_replay(). This
* thread will block in its first fmd_xprt_post() until fmd is ready.
*/
/*
* Open the legacy sysevent handle and subscribe to all events. These
* are automatically converted to "resource.sysevent.*" events so that
* modules can manage these events without additional infrastructure.
*/
if (geteuid() != 0)
return;
if ((fmd.d_sysev_hdl =
subclasses, 1) != 0)
}
/*
* Close the channel by unsubscribing and unbinding. We only do this when a
* a non-default channel has been selected. If we're using FM_ERROR_CHAN,
* the system default, we do *not* want to unsubscribe because the kernel will
* remove the subscriber queue and any events published in our absence will
* therefore be lost. This scenario may occur when, for example, fmd is sent
* a SIGTERM by init(1M) during reboot but an error is detected and makes it
* into the sysevent channel queue before init(1M) manages to call uadmin(2).
*/
void
{
(void) sysevent_evc_unbind(sysev_evc);
}
}
if (sysev_xprt != NULL) {
/*
* Wait callback returns before destroy the transport.
*/
(void) pthread_mutex_lock(&sysev_mutex);
sysev_exiting = 1;
while (sysev_xprt_refcnt > 0)
(void) pthread_mutex_unlock(&sysev_mutex);
}
}