fmd_module.c revision d10eddf70e75478ac778e9b04d3d711fe8c3273b
/*
* 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 <signal.h>
#include <dirent.h>
#include <limits.h>
#include <alloca.h>
#include <unistd.h>
#include <stdio.h>
#include <fmd_string.h>
#include <fmd_alloc.h>
#include <fmd_module.h>
#include <fmd_error.h>
#include <fmd_conf.h>
#include <fmd_dispq.h>
#include <fmd_eventq.h>
#include <fmd_timerq.h>
#include <fmd_subr.h>
#include <fmd_thread.h>
#include <fmd_ustat.h>
#include <fmd_case.h>
#include <fmd_protocol.h>
#include <fmd_buf.h>
#include <fmd_ckpt.h>
#include <fmd_xprt.h>
#include <fmd_topo.h>
#include <fmd.h>
/*
* Template for per-module statistics installed by fmd on behalf of each active
* module. These are used to initialize the per-module mp->mod_stats below.
* NOTE: FMD_TYPE_STRING statistics should not be used here. If they are
* required in the future, the FMD_ADM_MODDSTAT service routine must change.
*/
static const fmd_modstat_t _fmd_modstat_tmpl = {
{
},
};
static void
fmd_module_start(void *arg)
{
fmd_xprt_t *xp;
goto out;
}
/*
* If the module opened any transports while executing _fmd_init(),
* they are suspended. Now that _fmd_init() is done, wake them up.
*/
/*
* Wait for events to arrive by checking mod_error and then sleeping in
* fmd_eventq_delete(). If a NULL event is returned, the eventq has
* been aborted and we continue on to call fini and exit the thread.
*/
/*
* If the module has failed, discard the event without ever
* passing it to the module and go back to sleep.
*/
continue;
}
/*
* Once mop_dispatch() is complete, grab the lock and perform
* any event-specific post-processing. Finally, if necessary,
* checkpoint the state of the module after this event.
*/
}
out:
}
{
const char *dir;
int err;
*p = '\0'; /* strip trailing .so from any module name */
/*
* Initialize the module statistics that are kept on its behalf by fmd.
* These are set up using a template defined at the top of this file.
*/
return (NULL);
}
&fmd_module_nva_ops_sleep, mp) != 0 ||
&fmd_module_nva_ops_nosleep, mp) != 0) {
"allocation routines");
return (NULL);
}
/*
* Place a hold on the module and grab the module lock before creating
* the module's thread to ensure that it cannot destroy the module and
* that it cannot call ops->mop_init() before we're done setting up.
* NOTE: from now on, we must use fmd_module_rele() for error paths.
*/
return (NULL);
}
/*
* At this point our module structure is nearly finished and its thread
* is starting execution in fmd_module_start() above, which will begin
* by blocking for mod_lock. We now drop mod_lock and wait for either
* FMD_MOD_INIT or mod_error to be set before proceeding.
*/
/*
* If the module has failed to initialize, copy its errno to the errno
* of the caller, wait for it to unload, and then destroy it.
*/
if (err == EFMD_CKPT_INVAL)
/*
* If we're in the background, keep quiet about failure to
* load because a handle wasn't registered: this is a module's
* way of telling us it didn't want to be loaded for some
* reason related to system configuration. If we're in the
* foreground we log this too in order to inform developers.
*/
}
(void) fmd_set_errno(err);
return (NULL);
}
return (mp);
}
static void
{
/*
* The root module calls fmd_timerq_install() directly and must take
* responsibility for any cleanup of timer arguments that is required.
* All other modules use fmd_modtimer_t's as the arg data; free them.
*/
}
void
{
return; /* module is already unloading */
}
/*
* Wait for the module's thread to stop processing events and call
* _fmd_fini() and exit. We do this by waiting for FMD_MOD_FINI to be
* set if INIT was set, and then attempting to join with the thread.
*/
/*
* Once the module is no longer active, clean up any data structures
* that are only required when the module is loaded.
*/
(void (*)())fmd_module_untimeout, mp);
}
}
}
}
void
{
int i;
}
/*
* Once the module's thread is dead, we can safely remove the module
* from global visibility and by removing it from d_mod_list. Any
* modhash pointers are already gone by virtue of mod_refs being zero.
*/
/*
* Once the module is no longer processing events and no longer visible
* through any program data structures, we can free all of its content.
*/
}
}
}
}
/*
* fmd_module_error() is called after the stack is unwound from a call to
* fmd_module_abort() to indicate that the module has failed. The mod_error
* field is used to hold the error code of the first fatal error to the module.
* An EFMD_MOD_FAIL event is then created and sent to fmd-self-diagnosis.
*/
static void
{
fmd_event_t *e;
char *class;
return; /* do not post event if fmd.d_self itself fails */
/*
* Send an error indicating the module has now failed to fmd.d_self.
* Since the error causing the failure has already been logged by
* fmd_api_xerror(), we do not need to bother logging this event.
* It only exists for the purpose of notifying fmd.d_self that it can
* close the case associated with this module because mod_error is set.
*/
}
void
{
fmd_modtimer_t *t;
volatile int err;
/*
* Before calling the appropriate module callback, enter the module as
* if by fmd_module_enter() and establish mod_jmpbuf for any aborts.
*/
}
/*
* If it's the first time through fmd_module_dispatch(), call the
* appropriate module callback based on the event type. If the call
* triggers an fmd_module_abort(), we'll return to setjmp() above with
* err set to a non-zero value and then bypass this before exiting.
*/
if (err == 0) {
case FMD_EVT_PROTOCOL:
break;
case FMD_EVT_TIMEOUT:
break;
case FMD_EVT_CLOSE:
break;
case FMD_EVT_STATS:
break;
case FMD_EVT_GC:
break;
case FMD_EVT_PUBLISH:
break;
case FMD_EVT_TOPO:
/*
* Save the pointer to the old topology and update
* the pointer with the updated topology.
* With this approach, other threads that reference the
* topology either
* - finishes with old topology since
* it is released after updating
* mod_topo_current.
* - or is blocked while mod_topo_current is updated.
*/
break;
}
}
}
int
{
}
void
{
fmd_event_t *e;
}
/*
* Garbage collection is initiated by a timer callback once per day or at the
* request of fmadm. Purge old SERD entries and send the module a GC event.
*/
void
{
fmd_event_t *e;
return; /* do not do anything if the module has failed */
}
}
}
void
{
if (fmd_module_trylock(mp)) {
}
}
int
{
fmd_case_t *cp;
int rv = 0;
break;
}
if (rv == 0)
return (rv);
}
void
{
}
void
{
}
void
{
fmd_case_t *cp;
}
}
}
void
{
fmd_case_t *cp;
}
}
}
void
{
else
}
}
void
{
}
int
{
return (0);
}
return (1);
}
int
{
}
int
{
volatile int err;
}
/*
* If it's the first time through fmd_module_enter(), call the provided
* function on the module. If no fmd_module_abort() results, we will
* fall through and return zero. Otherwise we'll longjmp with an err,
* return to the setjmp() above, and return the error to our caller.
*/
return (err);
}
void
{
}
/*
* If the client.error policy has been set by a developer, stop or dump core
* based on the policy; if we stop and are resumed we'll continue and execute
* the default behavior to discard events in fmd_module_start(). If the caller
* is the primary module thread, we reach this state by longjmp'ing back to
* fmd_module_enter(), above. If the caller is an auxiliary thread, we cancel
* ourself and arrange for the primary thread to call fmd_module_abort().
*/
void
{
if (policy == FMD_CERROR_STOP) {
} else if (policy == FMD_CERROR_ABORT) {
fmd_panic("aborting due to %s in client %s (%p)\n",
}
/*
* If the caller is an auxiliary thread, cancel the current thread. We
* prefer to cancel because it affords developers the option of using
* the pthread_cleanup* APIs. If cancellations have been disabled,
* fall through to forcing the current thread to exit. In either case
* we update mod_error (if zero) to enter the failed state. Once that
* is set, further events received by the module will be discarded.
*
* We also set the FMD_MOD_FAIL bit, indicating an unrecoverable error.
* When an auxiliary thread fails, the module is left in a delicate
* state where it is likely not able to continue execution (even to
* execute its _fmd_fini() routine) because our caller may hold locks
* that are private to the module and can no longer be released. The
* FMD_MOD_FAIL bit forces fmd_api_module_lock() to abort if any other
* module threads reach an API call, in an attempt to get them to exit.
*/
(void) pthread_cancel(tid);
}
}
void
{
}
void
{
else
}
/*
* Wrapper around libdiagcode's fm_dc_opendict() to load module dictionaries.
* If the dictionary open is successful, the new dictionary is added to the
* mod_dictv[] array and mod_codelen is updated with the new maximum length.
*/
int
{
strcmp(p, ".dict") == 0)
*p = '\0'; /* eliminate any trailing .dict suffix */
/*
* If 'dict' is an absolute path, dictdir = $rootdir/`dirname dict`
* If 'dict' is not an absolute path, dictdir = $dictdir/`dirname dict`
*/
if (dict[0] == '/') {
(void) fmd_strdirname(dictdir);
} else {
(void) fmd_strdirname(dictdir);
}
return (-1); /* errno is set for us */
return (0);
}
/*
* Wrapper around libdiagcode's fm_dc_key2code() that examines all the module's
* dictionaries. We adhere to the libdiagcode return values and semantics.
*/
int
{
int i, err;
return (err);
}
return (fmd_set_errno(ENOMSG));
}
fmd_modhash_create(void)
{
return (mhp);
}
void
{
uint_t i;
for (i = 0; i < mhp->mh_hashlen; i++) {
}
}
}
static void
{
const char *p;
return; /* failed to open directory; just skip it */
continue; /* skip "." and ".." */
continue; /* skip .conf files */
continue; /* skip files with the wrong suffix */
}
}
void
{
int i;
}
void
{
uint_t i;
for (i = 0; i < mhp->mh_hashlen; i++) {
}
}
}
void
{
uint_t i;
return; /* not initialized or couldn't grab lock */
for (i = 0; i < mhp->mh_hashlen; i++) {
}
}
}
void
{
uint_t i;
for (i = 0; i < mhp->mh_hashlen; i++) {
/*
* If FMD_MOD_INIT is set but MOD_FINI, MOD_QUIT, and
* mod_error are all zero, then the module is active:
* enqueue the event in the corresponding event queue.
*/
/*
* If the event we're dispatching is of type
* FMD_EVT_TOPO and there are already redundant
* FMD_EVT_TOPO events in this module's queue,
* then drop those before adding the new one.
*/
}
}
}
}
{
uint_t h;
break;
}
else
(void) fmd_set_errno(EFMD_MOD_NOMOD);
return (mp);
}
{
int tries = 0;
uint_t h;
*p = '\0'; /* strip trailing .so from any module name */
/*
* First check to see if a module is already present in the hash table
* for this name. If so, the module is already loaded: skip it.
*/
break;
}
(void) fmd_set_errno(EFMD_MOD_LOADED);
return (NULL);
}
/*
* fmd_module_create() will return a held (as if by fmd_module_hold())
* module. We leave this hold in place to correspond to the hash-in.
*/
return (NULL); /* errno is set for us */
}
}
return (mp);
}
int
{
uint_t h;
break;
else
}
return (fmd_set_errno(EFMD_MOD_NOMOD));
}
return (0);
}
void
{
}
int
{
fmd_event_t *e;
int err;
/*
* Grab the module lock and wait for the STSUB bit to be clear. Then
* set it to indicate we are a subscriber and everyone else must wait.
*/
return (fmd_set_errno(EFMD_HDL_ABORT));
}
/*
* Create a stats pseudo-event and dispatch it to the module, forcing
* it to next execute its custom snapshot routine (or the empty one).
*/
/*
* Grab the module lock and then wait on mod_cv for STPUB to be set,
* indicating the snapshot routine is completed and the module is idle.
*/
}
return (fmd_set_errno(EFMD_HDL_ABORT));
}
/*
* Update ms_snaptime and take the actual snapshot of the various
* statistics while the module is quiescent and waiting for us.
*/
} else
/*
* With the snapshot complete, grab the module lock and clear both
* STSUB and STPUB, permitting everyone to wake up and continue.
*/
return (err);
}
struct topo_hdl *
{
}
int
{
break;
}
return (-1);
return (0);
}