17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
580ab886wesolows * Common Development and Distribution License (the "License").
680ab886wesolows * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
21d9638e5mws
227c478bdstevel@tonic-gate/*
23f6e214cGavin Maltby * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bdstevel@tonic-gate */
257c478bdstevel@tonic-gate
267c478bdstevel@tonic-gate#include <sys/types.h>
277c478bdstevel@tonic-gate#include <sys/fm/protocol.h>
28b7d3956stephh#include <fm/topo_hc.h>
29f6e214cGavin Maltby#include <uuid/uuid.h>
307c478bdstevel@tonic-gate
317c478bdstevel@tonic-gate#include <unistd.h>
327c478bdstevel@tonic-gate#include <signal.h>
337c478bdstevel@tonic-gate#include <limits.h>
347c478bdstevel@tonic-gate#include <syslog.h>
357c478bdstevel@tonic-gate#include <alloca.h>
3624db464eschrock#include <stddef.h>
37f6e214cGavin Maltby#include <door.h>
387c478bdstevel@tonic-gate
397c478bdstevel@tonic-gate#include <fmd_module.h>
407c478bdstevel@tonic-gate#include <fmd_api.h>
417c478bdstevel@tonic-gate#include <fmd_string.h>
427c478bdstevel@tonic-gate#include <fmd_subr.h>
437c478bdstevel@tonic-gate#include <fmd_error.h>
447c478bdstevel@tonic-gate#include <fmd_event.h>
457c478bdstevel@tonic-gate#include <fmd_eventq.h>
467c478bdstevel@tonic-gate#include <fmd_dispq.h>
477c478bdstevel@tonic-gate#include <fmd_timerq.h>
487c478bdstevel@tonic-gate#include <fmd_thread.h>
497c478bdstevel@tonic-gate#include <fmd_ustat.h>
507c478bdstevel@tonic-gate#include <fmd_case.h>
517c478bdstevel@tonic-gate#include <fmd_protocol.h>
527c478bdstevel@tonic-gate#include <fmd_buf.h>
537c478bdstevel@tonic-gate#include <fmd_asru.h>
547c478bdstevel@tonic-gate#include <fmd_fmri.h>
550eb822acindi#include <fmd_topo.h>
567c478bdstevel@tonic-gate#include <fmd_ckpt.h>
57d9638e5mws#include <fmd_xprt.h>
587c478bdstevel@tonic-gate
597c478bdstevel@tonic-gate#include <fmd.h>
607c478bdstevel@tonic-gate
617c478bdstevel@tonic-gate/*
627c478bdstevel@tonic-gate * Table of configuration file variable types ops-vector pointers.  We use this
637c478bdstevel@tonic-gate * to convert from the property description array specified by the module to an
647c478bdstevel@tonic-gate * array of fmd_conf_formal_t's.  The order of this array must match the order
657c478bdstevel@tonic-gate * of #define values specified in <fmd_api.h> (i.e. FMD_TYPE_BOOL must be 0).
667c478bdstevel@tonic-gate * For now, the fmd_conf_list and fmd_conf_path types are not supported as we
677c478bdstevel@tonic-gate * do not believe modules need them and they would require more complexity.
687c478bdstevel@tonic-gate */
697c478bdstevel@tonic-gatestatic const fmd_conf_ops_t *const _fmd_prop_ops[] = {
707c478bdstevel@tonic-gate	&fmd_conf_bool,		/* FMD_TYPE_BOOL */
717c478bdstevel@tonic-gate	&fmd_conf_int32,	/* FMD_TYPE_INT32 */
727c478bdstevel@tonic-gate	&fmd_conf_uint32,	/* FMD_TYPE_UINT32 */
737c478bdstevel@tonic-gate	&fmd_conf_int64,	/* FMD_TYPE_INT64 */
747c478bdstevel@tonic-gate	&fmd_conf_uint64,	/* FMD_TYPE_UINT64 */
757c478bdstevel@tonic-gate	&fmd_conf_string,	/* FMD_TYPE_STRING */
767c478bdstevel@tonic-gate	&fmd_conf_time,		/* FMD_TYPE_TIME */
777c478bdstevel@tonic-gate	&fmd_conf_size,		/* FMD_TYPE_SIZE */
787c478bdstevel@tonic-gate};
797c478bdstevel@tonic-gate
8080ab886wesolowsstatic void fmd_api_verror(fmd_module_t *, int, const char *, va_list)
8180ab886wesolows    __NORETURN;
8280ab886wesolowsstatic void fmd_api_error(fmd_module_t *, int, const char *, ...) __NORETURN;
8380ab886wesolows
847c478bdstevel@tonic-gate/*
857c478bdstevel@tonic-gate * fmd_api_vxerror() provides the engine underlying the fmd_hdl_[v]error() API
867c478bdstevel@tonic-gate * calls and the fmd_api_[v]error() utility routine defined below.  The routine
877c478bdstevel@tonic-gate * formats the error, optionally associated with a particular errno code 'err',
887c478bdstevel@tonic-gate * and logs it as an ereport associated with the calling module.  Depending on
897c478bdstevel@tonic-gate * other optional properties, we also emit a message to stderr and to syslog.
907c478bdstevel@tonic-gate */
917c478bdstevel@tonic-gatestatic void
927c478bdstevel@tonic-gatefmd_api_vxerror(fmd_module_t *mp, int err, const char *format, va_list ap)
937c478bdstevel@tonic-gate{
94d9638e5mws	int raw_err = err;
957c478bdstevel@tonic-gate	nvlist_t *nvl;
967c478bdstevel@tonic-gate	fmd_event_t *e;
977c478bdstevel@tonic-gate	char *class, *msg;
987c478bdstevel@tonic-gate	size_t len1, len2;
997c478bdstevel@tonic-gate	char c;
1007c478bdstevel@tonic-gate
1017c478bdstevel@tonic-gate	/*
1027c478bdstevel@tonic-gate	 * fmd_api_vxerror() counts as both an error of class EFMD_MODULE
1037c478bdstevel@tonic-gate	 * as well as an instance of 'err' w.r.t. our internal bean counters.
1047c478bdstevel@tonic-gate	 */
1057c478bdstevel@tonic-gate	(void) pthread_mutex_lock(&fmd.d_err_lock);
1067c478bdstevel@tonic-gate	fmd.d_errstats[EFMD_MODULE - EFMD_UNKNOWN].fmds_value.ui64++;
1077c478bdstevel@tonic-gate
1087c478bdstevel@tonic-gate	if (err > EFMD_UNKNOWN && err < EFMD_END)
1097c478bdstevel@tonic-gate		fmd.d_errstats[err - EFMD_UNKNOWN].fmds_value.ui64++;
1107c478bdstevel@tonic-gate
1117c478bdstevel@tonic-gate	(void) pthread_mutex_unlock(&fmd.d_err_lock);
1127c478bdstevel@tonic-gate
1137c478bdstevel@tonic-gate	/*
1147c478bdstevel@tonic-gate	 * Format the message using vsnprintf().  As usual, if the format has a
1157c478bdstevel@tonic-gate	 * newline in it, it is printed alone; otherwise strerror() is added.
1167c478bdstevel@tonic-gate	 */
1177c478bdstevel@tonic-gate	if (strchr(format, '\n') != NULL)
1187c478bdstevel@tonic-gate		err = 0; /* err is not relevant in the message */
1197c478bdstevel@tonic-gate
1207c478bdstevel@tonic-gate	len1 = vsnprintf(&c, 1, format, ap);
1217c478bdstevel@tonic-gate	len2 = err != 0 ? snprintf(&c, 1, ": %s\n", fmd_strerror(err)) : 0;
1227c478bdstevel@tonic-gate
1237c478bdstevel@tonic-gate	msg = fmd_alloc(len1 + len2 + 1, FMD_SLEEP);
1247c478bdstevel@tonic-gate	(void) vsnprintf(msg, len1 + 1, format, ap);
1257c478bdstevel@tonic-gate
1267c478bdstevel@tonic-gate	if (err != 0) {
1277c478bdstevel@tonic-gate		(void) snprintf(&msg[len1], len2 + 1,
1287c478bdstevel@tonic-gate		    ": %s\n", fmd_strerror(err));
1297c478bdstevel@tonic-gate	}
1307c478bdstevel@tonic-gate
1317c478bdstevel@tonic-gate	/*
1327c478bdstevel@tonic-gate	 * Create an error event corresponding to the error, insert it into the
1337c478bdstevel@tonic-gate	 * error log, and dispatch it to the fmd-self-diagnosis engine.
1347c478bdstevel@tonic-gate	 */
135d9638e5mws	if (mp != fmd.d_self && (raw_err != EFMD_HDL_ABORT || fmd.d_running)) {
1367c478bdstevel@tonic-gate		if ((c = msg[len1 + len2 - 1]) == '\n')
1377c478bdstevel@tonic-gate			msg[len1 + len2 - 1] = '\0'; /* strip \n for event */
1387c478bdstevel@tonic-gate
1397c478bdstevel@tonic-gate		nvl = fmd_protocol_moderror(mp, err, msg);
1407c478bdstevel@tonic-gate
1417c478bdstevel@tonic-gate		if (c == '\n')
1427c478bdstevel@tonic-gate			msg[len1 + len2 - 1] = c;
1437c478bdstevel@tonic-gate
1447c478bdstevel@tonic-gate		(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
1457c478bdstevel@tonic-gate		e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
1467c478bdstevel@tonic-gate
1477c478bdstevel@tonic-gate		(void) pthread_rwlock_rdlock(&fmd.d_log_lock);
1487c478bdstevel@tonic-gate		fmd_log_append(fmd.d_errlog, e, NULL);
1497c478bdstevel@tonic-gate		(void) pthread_rwlock_unlock(&fmd.d_log_lock);
1507c478bdstevel@tonic-gate
1517c478bdstevel@tonic-gate		fmd_event_transition(e, FMD_EVS_ACCEPTED);
1527c478bdstevel@tonic-gate		fmd_event_commit(e);
1537c478bdstevel@tonic-gate
1547c478bdstevel@tonic-gate		fmd_dispq_dispatch(fmd.d_disp, e, class);
1557c478bdstevel@tonic-gate	}
1567c478bdstevel@tonic-gate
1577c478bdstevel@tonic-gate	/*
1587c478bdstevel@tonic-gate	 * Similar to fmd_vdebug(), if the debugging switches are enabled we
1597c478bdstevel@tonic-gate	 * echo the module name and message to stderr and/or syslog.  Unlike
1607c478bdstevel@tonic-gate	 * fmd_vdebug(), we also print to stderr if foreground mode is enabled.
161d9638e5mws	 * We also print the message if a built-in module is aborting before
162d9638e5mws	 * fmd has detached from its parent (e.g. default transport failure).
1637c478bdstevel@tonic-gate	 */
164d9638e5mws	if (fmd.d_fg || (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) || (
165d9638e5mws	    raw_err == EFMD_HDL_ABORT && !fmd.d_running)) {
1667c478bdstevel@tonic-gate		(void) pthread_mutex_lock(&fmd.d_err_lock);
1677c478bdstevel@tonic-gate		(void) fprintf(stderr, "%s: %s: %s",
1687c478bdstevel@tonic-gate		    fmd.d_pname, mp->mod_name, msg);
1697c478bdstevel@tonic-gate		(void) pthread_mutex_unlock(&fmd.d_err_lock);
1707c478bdstevel@tonic-gate	}
1717c478bdstevel@tonic-gate
1727c478bdstevel@tonic-gate	if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) {
1737c478bdstevel@tonic-gate		syslog(LOG_ERR | LOG_DAEMON, "%s ERROR: %s: %s",
1747c478bdstevel@tonic-gate		    fmd.d_pname, mp->mod_name, msg);
1757c478bdstevel@tonic-gate	}
1767c478bdstevel@tonic-gate
1777c478bdstevel@tonic-gate	fmd_free(msg, len1 + len2 + 1);
1787c478bdstevel@tonic-gate}
1797c478bdstevel@tonic-gate
1807c478bdstevel@tonic-gate/*PRINTFLIKE3*/
1817c478bdstevel@tonic-gatestatic void
1827c478bdstevel@tonic-gatefmd_api_xerror(fmd_module_t *mp, int err, const char *format, ...)
1837c478bdstevel@tonic-gate{
1847c478bdstevel@tonic-gate	va_list ap;
1857c478bdstevel@tonic-gate
1867c478bdstevel@tonic-gate	va_start(ap, format);
1877c478bdstevel@tonic-gate	fmd_api_vxerror(mp, err, format, ap);
1887c478bdstevel@tonic-gate	va_end(ap);
1897c478bdstevel@tonic-gate}
1907c478bdstevel@tonic-gate
1917c478bdstevel@tonic-gate/*
1927c478bdstevel@tonic-gate * fmd_api_verror() is a wrapper around fmd_api_vxerror() for API subroutines.
1937c478bdstevel@tonic-gate * It calls fmd_module_unlock() on behalf of its caller, logs the error, and
1947c478bdstevel@tonic-gate * then aborts the API call and the surrounding module entry point by doing an
1957c478bdstevel@tonic-gate * fmd_module_abort(), which longjmps to the place where we entered the module.
1967c478bdstevel@tonic-gate */
1977c478bdstevel@tonic-gatestatic void
1987c478bdstevel@tonic-gatefmd_api_verror(fmd_module_t *mp, int err, const char *format, va_list ap)
1997c478bdstevel@tonic-gate{
2007c478bdstevel@tonic-gate	if (fmd_module_locked(mp))
2017c478bdstevel@tonic-gate		fmd_module_unlock(mp);
2027c478bdstevel@tonic-gate
2037c478bdstevel@tonic-gate	fmd_api_vxerror(mp, err, format, ap);
2047c478bdstevel@tonic-gate	fmd_module_abort(mp, err);
2057c478bdstevel@tonic-gate}
2067c478bdstevel@tonic-gate
2077c478bdstevel@tonic-gate/*PRINTFLIKE3*/
2087c478bdstevel@tonic-gatestatic void
2097c478bdstevel@tonic-gatefmd_api_error(fmd_module_t *mp, int err, const char *format, ...)
2107c478bdstevel@tonic-gate{
2117c478bdstevel@tonic-gate	va_list ap;
2127c478bdstevel@tonic-gate
2137c478bdstevel@tonic-gate	va_start(ap, format);
2147c478bdstevel@tonic-gate	fmd_api_verror(mp, err, format, ap);
2157c478bdstevel@tonic-gate	va_end(ap);
2167c478bdstevel@tonic-gate}
2177c478bdstevel@tonic-gate
2187c478bdstevel@tonic-gate/*
219d9638e5mws * Common code for fmd_api_module_lock() and fmd_api_transport_impl().  This
220d9638e5mws * code verifies that the handle is valid and associated with a proper thread.
2217c478bdstevel@tonic-gate */
2227c478bdstevel@tonic-gatestatic fmd_module_t *
223d9638e5mwsfmd_api_module(fmd_hdl_t *hdl)
2247c478bdstevel@tonic-gate{
2257c478bdstevel@tonic-gate	fmd_thread_t *tp;
2267c478bdstevel@tonic-gate	fmd_module_t *mp;
2277c478bdstevel@tonic-gate
2287c478bdstevel@tonic-gate	/*
2297c478bdstevel@tonic-gate	 * If our TSD is not present at all, this is either a serious bug or
2307c478bdstevel@tonic-gate	 * someone has created a thread behind our back and is using fmd's API.
2317c478bdstevel@tonic-gate	 * We can't call fmd_api_error() because we can't be sure that we can
2327c478bdstevel@tonic-gate	 * unwind our state back to an enclosing fmd_module_dispatch(), so we
2337c478bdstevel@tonic-gate	 * must panic instead.  This is likely a module design or coding error.
2347c478bdstevel@tonic-gate	 */
2357c478bdstevel@tonic-gate	if ((tp = pthread_getspecific(fmd.d_key)) == NULL) {
2367c478bdstevel@tonic-gate		fmd_panic("fmd module api call made using "
2377c478bdstevel@tonic-gate		    "client handle %p from unknown thread\n", (void *)hdl);
2387c478bdstevel@tonic-gate	}
2397c478bdstevel@tonic-gate
240d9638e5mws	/*
241f6e214cGavin Maltby	 * If our TSD refers to the root module and is a non-private
242f6e214cGavin Maltby	 * door server thread,  then it was created asynchronously at the
243f6e214cGavin Maltby	 * request of a module but is using now the module API as an
244f6e214cGavin Maltby	 * auxiliary module thread.  We reset tp->thr_mod to the module
245f6e214cGavin Maltby	 * handle so it can act as a module thread.
246f6e214cGavin Maltby	 *
247f6e214cGavin Maltby	 * If more than one module uses non-private doors then the
248f6e214cGavin Maltby	 * "client handle is not valid" check below can fail since
249f6e214cGavin Maltby	 * door server threads for such doors can service *any*
250f6e214cGavin Maltby	 * non-private door.  We use non-private door for legacy sysevent
251f6e214cGavin Maltby	 * alone.
252d9638e5mws	 */
253d9638e5mws	if (tp->thr_mod == fmd.d_rmod && tp->thr_func == &fmd_door_server)
254d9638e5mws		tp->thr_mod = (fmd_module_t *)hdl;
255d9638e5mws
2567c478bdstevel@tonic-gate	if ((mp = tp->thr_mod) != (fmd_module_t *)hdl) {
2577c478bdstevel@tonic-gate		fmd_api_error(mp, EFMD_HDL_INVAL,
2587c478bdstevel@tonic-gate		    "client handle %p is not valid\n", (void *)hdl);
2597c478bdstevel@tonic-gate	}
2607c478bdstevel@tonic-gate
2617c478bdstevel@tonic-gate	if (mp->mod_flags & FMD_MOD_FAIL) {
2627c478bdstevel@tonic-gate		fmd_api_error(mp, EFMD_MOD_FAIL,
2637c478bdstevel@tonic-gate		    "module has experienced an unrecoverable error\n");
2647c478bdstevel@tonic-gate	}
2657c478bdstevel@tonic-gate
266d9638e5mws	return (mp);
267d9638e5mws}
268d9638e5mws
269d9638e5mws/*
270d9638e5mws * fmd_api_module_lock() is used as a wrapper around fmd_module_lock() and a
271d9638e5mws * common prologue to each fmd_api.c routine.  It verifies that the handle is
272d9638e5mws * valid and owned by the current server thread, locks the handle, and then
273d9638e5mws * verifies that the caller is performing an operation on a registered handle.
274d9638e5mws * If any tests fail, the entire API call is aborted by fmd_api_error().
275d9638e5mws */
276d9638e5mwsstatic fmd_module_t *
277d9638e5mwsfmd_api_module_lock(fmd_hdl_t *hdl)
278d9638e5mws{
279d9638e5mws	fmd_module_t *mp = fmd_api_module(hdl);
280d9638e5mws
2817c478bdstevel@tonic-gate	fmd_module_lock(mp);
2827c478bdstevel@tonic-gate
2837c478bdstevel@tonic-gate	if (mp->mod_info == NULL) {
2847c478bdstevel@tonic-gate		fmd_api_error(mp, EFMD_HDL_NOTREG,
2857c478bdstevel@tonic-gate		    "client handle %p has not been registered\n", (void *)hdl);
2867c478bdstevel@tonic-gate	}
2877c478bdstevel@tonic-gate
2887c478bdstevel@tonic-gate	return (mp);
2897c478bdstevel@tonic-gate}
2907c478bdstevel@tonic-gate
2917c478bdstevel@tonic-gate/*
2927c478bdstevel@tonic-gate * Utility function for API entry points that accept fmd_case_t's.  We cast cp
2937c478bdstevel@tonic-gate * to fmd_case_impl_t and check to make sure the case is owned by the caller.
2947c478bdstevel@tonic-gate */
2957c478bdstevel@tonic-gatestatic fmd_case_impl_t *
2967c478bdstevel@tonic-gatefmd_api_case_impl(fmd_module_t *mp, fmd_case_t *cp)
2977c478bdstevel@tonic-gate{
2987c478bdstevel@tonic-gate	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
2997c478bdstevel@tonic-gate
3007c478bdstevel@tonic-gate	if (cip == NULL || cip->ci_mod != mp) {
3017c478bdstevel@tonic-gate		fmd_api_error(mp, EFMD_CASE_OWNER,
302d9638e5mws		    "case %p is invalid or not owned by caller\n", (void *)cip);
3037c478bdstevel@tonic-gate	}
3047c478bdstevel@tonic-gate
3057c478bdstevel@tonic-gate	return (cip);
3067c478bdstevel@tonic-gate}
3077c478bdstevel@tonic-gate
3087c478bdstevel@tonic-gate/*
309d9638e5mws * Utility function for API entry points that accept fmd_xprt_t's.  We cast xp
310d9638e5mws * to fmd_transport_t and check to make sure the case is owned by the caller.
311d9638e5mws * Note that we could make this check safer by actually walking mp's transport
312d9638e5mws * list, but that requires holding the module lock and this routine needs to be
313d9638e5mws * MT-hot w.r.t. auxiliary module threads.  Ultimately any loadable module can
314d9638e5mws * cause us to crash anyway, so we optimize for scalability over safety here.
315d9638e5mws */
316d9638e5mwsstatic fmd_xprt_impl_t *
317d9638e5mwsfmd_api_transport_impl(fmd_hdl_t *hdl, fmd_xprt_t *xp)
318d9638e5mws{
319d9638e5mws	fmd_module_t *mp = fmd_api_module(hdl);
320d9638e5mws	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
321d9638e5mws
322d9638e5mws	if (xip == NULL || xip->xi_queue->eq_mod != mp) {
323d9638e5mws		fmd_api_error(mp, EFMD_XPRT_OWNER,
324d9638e5mws		    "xprt %p is invalid or not owned by caller\n", (void *)xp);
325d9638e5mws	}
326d9638e5mws
327d9638e5mws	return (xip);
328d9638e5mws}
329d9638e5mws
330d9638e5mws/*
3317c478bdstevel@tonic-gate * fmd_hdl_register() is the one function which cannot use fmd_api_error() to
3327c478bdstevel@tonic-gate * report errors, because that routine causes the module to abort.  Failure to
3337c478bdstevel@tonic-gate * register is instead handled by having fmd_hdl_register() return an error to
3347c478bdstevel@tonic-gate * the _fmd_init() function and then detecting no registration when it returns.
3357c478bdstevel@tonic-gate * So we use this routine for fmd_hdl_register() error paths instead.
3367c478bdstevel@tonic-gate */
3377c478bdstevel@tonic-gatestatic int
3387c478bdstevel@tonic-gatefmd_hdl_register_error(fmd_module_t *mp, int err)
3397c478bdstevel@tonic-gate{
3407c478bdstevel@tonic-gate	if (fmd_module_locked(mp))
3417c478bdstevel@tonic-gate		fmd_module_unlock(mp);
3427c478bdstevel@tonic-gate
3437c478bdstevel@tonic-gate	fmd_api_xerror(mp, err, "failed to register");
3447c478bdstevel@tonic-gate	return (fmd_set_errno(err));
3457c478bdstevel@tonic-gate}
3467c478bdstevel@tonic-gate
3477c478bdstevel@tonic-gatestatic void
3487c478bdstevel@tonic-gatefmd_hdl_nop(void)
3497c478bdstevel@tonic-gate{
3507c478bdstevel@tonic-gate	/* empty function for use with unspecified module entry points */
3517c478bdstevel@tonic-gate}
3527c478bdstevel@tonic-gate
3537c478bdstevel@tonic-gateint
3547c478bdstevel@tonic-gatefmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
3557c478bdstevel@tonic-gate{
3567c478bdstevel@tonic-gate	fmd_thread_t *tp = pthread_getspecific(fmd.d_key);
3577c478bdstevel@tonic-gate	fmd_module_t *mp = tp->thr_mod;
3587c478bdstevel@tonic-gate
3597c478bdstevel@tonic-gate	const fmd_prop_t *prop;
3607c478bdstevel@tonic-gate	const fmd_conf_path_t *pap;
3617c478bdstevel@tonic-gate	fmd_conf_formal_t *cfp;
362d9638e5mws	fmd_hdl_ops_t ops;
3637c478bdstevel@tonic-gate
3647c478bdstevel@tonic-gate	const char *conf = NULL;
3657c478bdstevel@tonic-gate	char buf[PATH_MAX];
3667c478bdstevel@tonic-gate	int i;
3677c478bdstevel@tonic-gate
3687c478bdstevel@tonic-gate	if (mp != (fmd_module_t *)hdl)
3697c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_HDL_INVAL));
3707c478bdstevel@tonic-gate
3717c478bdstevel@tonic-gate	fmd_module_lock(mp);
3727c478bdstevel@tonic-gate
3737c478bdstevel@tonic-gate	/*
3747c478bdstevel@tonic-gate	 * First perform some sanity checks on our input.  The API version must
3757c478bdstevel@tonic-gate	 * be supported by FMD and the handle can only be registered once by
3767c478bdstevel@tonic-gate	 * the module thread to which we assigned this client handle.  The info
3777c478bdstevel@tonic-gate	 * provided for the handle must be valid and have the minimal settings.
3787c478bdstevel@tonic-gate	 */
379f6e214cGavin Maltby	if (version > FMD_API_VERSION_5)
3807c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_VER_NEW));
3817c478bdstevel@tonic-gate
3827c478bdstevel@tonic-gate	if (version < FMD_API_VERSION_1)
3837c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_VER_OLD));
3847c478bdstevel@tonic-gate
3857c478bdstevel@tonic-gate	if (mp->mod_conf != NULL)
3867c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_HDL_REG));
3877c478bdstevel@tonic-gate
3887c478bdstevel@tonic-gate	if (pthread_self() != mp->mod_thread->thr_tid)
3897c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_HDL_TID));
3907c478bdstevel@tonic-gate
391d9638e5mws	if (mip == NULL || mip->fmdi_desc == NULL ||
392d9638e5mws	    mip->fmdi_vers == NULL || mip->fmdi_ops == NULL)
3937c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_HDL_INFO));
3947c478bdstevel@tonic-gate
3957c478bdstevel@tonic-gate	/*
396d9638e5mws	 * Copy the module's ops vector into a local variable to account for
397d9638e5mws	 * changes in the module ABI.  Then if any of the optional entry points
398d9638e5mws	 * are NULL, set them to nop so we don't have to check before calling.
399d9638e5mws	 */
400d9638e5mws	bzero(&ops, sizeof (ops));
401d9638e5mws
402d9638e5mws	if (version < FMD_API_VERSION_3)
40324db464eschrock		bcopy(mip->fmdi_ops, &ops, offsetof(fmd_hdl_ops_t, fmdo_send));
40424db464eschrock	else if (version < FMD_API_VERSION_4)
40524db464eschrock		bcopy(mip->fmdi_ops, &ops,
40624db464eschrock		    offsetof(fmd_hdl_ops_t, fmdo_topo));
407d9638e5mws	else
408d9638e5mws		bcopy(mip->fmdi_ops, &ops, sizeof (ops));
409d9638e5mws
410d9638e5mws	if (ops.fmdo_recv == NULL)
411d9638e5mws		ops.fmdo_recv = (void (*)())fmd_hdl_nop;
412d9638e5mws	if (ops.fmdo_timeout == NULL)
413d9638e5mws		ops.fmdo_timeout = (void (*)())fmd_hdl_nop;
414d9638e5mws	if (ops.fmdo_close == NULL)
415d9638e5mws		ops.fmdo_close = (void (*)())fmd_hdl_nop;
416d9638e5mws	if (ops.fmdo_stats == NULL)
417d9638e5mws		ops.fmdo_stats = (void (*)())fmd_hdl_nop;
418d9638e5mws	if (ops.fmdo_gc == NULL)
419d9638e5mws		ops.fmdo_gc = (void (*)())fmd_hdl_nop;
420d9638e5mws	if (ops.fmdo_send == NULL)
421d9638e5mws		ops.fmdo_send = (int (*)())fmd_hdl_nop;
42224db464eschrock	if (ops.fmdo_topo == NULL)
42324db464eschrock		ops.fmdo_topo = (void (*)())fmd_hdl_nop;
424d9638e5mws
425d9638e5mws	/*
4267c478bdstevel@tonic-gate	 * Make two passes through the property array to initialize the formals
4277c478bdstevel@tonic-gate	 * to use for processing the module's .conf file.  In the first pass,
4287c478bdstevel@tonic-gate	 * we validate the types and count the number of properties.  In the
4297c478bdstevel@tonic-gate	 * second pass we copy the strings and fill in the appropriate ops.
4307c478bdstevel@tonic-gate	 */
4317c478bdstevel@tonic-gate	for (prop = mip->fmdi_props, i = 0; prop != NULL &&
4327c478bdstevel@tonic-gate	    prop->fmdp_name != NULL; prop++, i++) {
4337c478bdstevel@tonic-gate		if (prop->fmdp_type >=
4347c478bdstevel@tonic-gate		    sizeof (_fmd_prop_ops) / sizeof (_fmd_prop_ops[0])) {
4357c478bdstevel@tonic-gate			fmd_api_xerror(mp, EFMD_HDL_PROP,
4367c478bdstevel@tonic-gate			    "property %s uses invalid type %u\n",
4377c478bdstevel@tonic-gate			    prop->fmdp_name, prop->fmdp_type);
4387c478bdstevel@tonic-gate			return (fmd_hdl_register_error(mp, EFMD_HDL_PROP));
4397c478bdstevel@tonic-gate		}
4407c478bdstevel@tonic-gate	}
4417c478bdstevel@tonic-gate
4427c478bdstevel@tonic-gate	mp->mod_argc = i;
4437c478bdstevel@tonic-gate	mp->mod_argv = fmd_zalloc(sizeof (fmd_conf_formal_t) * i, FMD_SLEEP);
4447c478bdstevel@tonic-gate
4457c478bdstevel@tonic-gate	prop = mip->fmdi_props;
4467c478bdstevel@tonic-gate	cfp = mp->mod_argv;
4477c478bdstevel@tonic-gate
4487c478bdstevel@tonic-gate	for (i = 0; i < mp->mod_argc; i++, prop++, cfp++) {
4497c478bdstevel@tonic-gate		cfp->cf_name = fmd_strdup(prop->fmdp_name, FMD_SLEEP);
4507c478bdstevel@tonic-gate		cfp->cf_ops = _fmd_prop_ops[prop->fmdp_type];
4517c478bdstevel@tonic-gate		cfp->cf_default = fmd_strdup(prop->fmdp_defv, FMD_SLEEP);
4527c478bdstevel@tonic-gate	}
4537c478bdstevel@tonic-gate
4547c478bdstevel@tonic-gate	/*
4557c478bdstevel@tonic-gate	 * If this module came from an on-disk file, compute the name of the
4567c478bdstevel@tonic-gate	 * corresponding .conf file and parse properties from it if it exists.
4577c478bdstevel@tonic-gate	 */
4587c478bdstevel@tonic-gate	if (mp->mod_path != NULL) {
4597c478bdstevel@tonic-gate		(void) strlcpy(buf, mp->mod_path, sizeof (buf));
4607c478bdstevel@tonic-gate		(void) fmd_strdirname(buf);
4617c478bdstevel@tonic-gate
4627c478bdstevel@tonic-gate		(void) strlcat(buf, "/", sizeof (buf));
4637c478bdstevel@tonic-gate		(void) strlcat(buf, mp->mod_name, sizeof (buf));
4647c478bdstevel@tonic-gate		(void) strlcat(buf, ".conf", sizeof (buf));
4657c478bdstevel@tonic-gate
4667c478bdstevel@tonic-gate		if (access(buf, F_OK) == 0)
4677c478bdstevel@tonic-gate			conf = buf;
4687c478bdstevel@tonic-gate	}
4697c478bdstevel@tonic-gate
4707c478bdstevel@tonic-gate	if ((mp->mod_conf = fmd_conf_open(conf,
471d9638e5mws	    mp->mod_argc, mp->mod_argv, 0)) == NULL)
4727c478bdstevel@tonic-gate		return (fmd_hdl_register_error(mp, EFMD_MOD_CONF));
4737c478bdstevel@tonic-gate
474d9638e5mws	fmd_conf_propagate(fmd.d_conf, mp->mod_conf, mp->mod_name);
475d9638e5mws
4767c478bdstevel@tonic-gate	/*
4777c478bdstevel@tonic-gate	 * Look up the list of the libdiagcode dictionaries associated with the
4787c478bdstevel@tonic-gate	 * module.  If none were specified, use the value from daemon's config.
4797c478bdstevel@tonic-gate	 * We only fail if the module specified an explicit dictionary.
4807c478bdstevel@tonic-gate	 */
4817c478bdstevel@tonic-gate	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap);
4827c478bdstevel@tonic-gate	if (pap->cpa_argc == 0 && mp->mod_ops == &fmd_bltin_ops)
4837c478bdstevel@tonic-gate		(void) fmd_conf_getprop(fmd.d_conf, "self.dict", &pap);
4847c478bdstevel@tonic-gate
4857c478bdstevel@tonic-gate	for (i = 0; i < pap->cpa_argc; i++) {
4867c478bdstevel@tonic-gate		if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) {
4877c478bdstevel@tonic-gate			fmd_api_xerror(mp, errno,
4887c478bdstevel@tonic-gate			    "failed to open dictionary %s", pap->cpa_argv[i]);
4897c478bdstevel@tonic-gate			return (fmd_hdl_register_error(mp, EFMD_MOD_CONF));
4907c478bdstevel@tonic-gate		}
4917c478bdstevel@tonic-gate	}
4927c478bdstevel@tonic-gate
4937c478bdstevel@tonic-gate	/*
4947c478bdstevel@tonic-gate	 * Make a copy of the handle information and store it in mod_info.  We
4957c478bdstevel@tonic-gate	 * do not need to bother copying fmdi_props since they're already read.
4967c478bdstevel@tonic-gate	 */
4977c478bdstevel@tonic-gate	mp->mod_info = fmd_alloc(sizeof (fmd_hdl_info_t), FMD_SLEEP);
4987c478bdstevel@tonic-gate	mp->mod_info->fmdi_desc = fmd_strdup(mip->fmdi_desc, FMD_SLEEP);
4997c478bdstevel@tonic-gate	mp->mod_info->fmdi_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP);
5007c478bdstevel@tonic-gate	mp->mod_info->fmdi_ops = fmd_alloc(sizeof (fmd_hdl_ops_t), FMD_SLEEP);
501d9638e5mws	bcopy(&ops, (void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t));
5027c478bdstevel@tonic-gate	mp->mod_info->fmdi_props = NULL;
5037c478bdstevel@tonic-gate
5047c478bdstevel@tonic-gate	/*
50519e1255cy	 * Store a copy of module version in mp for fmd_scheme_fmd_present()
50619e1255cy	 */
50719e1255cy	if (mp->mod_vers == NULL)
50819e1255cy		mp->mod_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP);
50919e1255cy
51019e1255cy	/*
5117c478bdstevel@tonic-gate	 * Allocate an FMRI representing this module.  We'll use this later
5127c478bdstevel@tonic-gate	 * if the module decides to publish any events (e.g. list.suspects).
5137c478bdstevel@tonic-gate	 */
5147c478bdstevel@tonic-gate	mp->mod_fmri = fmd_protocol_fmri_module(mp);
5157c478bdstevel@tonic-gate
5167c478bdstevel@tonic-gate	/*
5177c478bdstevel@tonic-gate	 * Any subscriptions specified in the conf file are now stored in the
5187c478bdstevel@tonic-gate	 * corresponding property.  Add all of these to the dispatch queue.
5197c478bdstevel@tonic-gate	 */
5207c478bdstevel@tonic-gate	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap);
5217c478bdstevel@tonic-gate
522d9638e5mws	for (i = 0; i < pap->cpa_argc; i++) {
523d9638e5mws		fmd_dispq_insert(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]);
524d9638e5mws		fmd_xprt_subscribe_all(pap->cpa_argv[i]);
525d9638e5mws	}
5267c478bdstevel@tonic-gate
5277c478bdstevel@tonic-gate	/*
5287c478bdstevel@tonic-gate	 * Unlock the module and restore any pre-existing module checkpoint.
5297c478bdstevel@tonic-gate	 * If the checkpoint is missing or corrupt, we just keep going.
5307c478bdstevel@tonic-gate	 */
5317c478bdstevel@tonic-gate	fmd_module_unlock(mp);
5327c478bdstevel@tonic-gate	fmd_ckpt_restore(mp);
5337c478bdstevel@tonic-gate	return (0);
5347c478bdstevel@tonic-gate}
5357c478bdstevel@tonic-gate
536d9638e5mws/*
537d9638e5mws * If an auxiliary thread exists for the specified module at unregistration
538d9638e5mws * time, send it an asynchronous cancellation to force it to exit and then
539d9638e5mws * join with it (we expect this to either succeed quickly or return ESRCH).
540d9638e5mws * Once this is complete we can destroy the associated fmd_thread_t data.
541d9638e5mws */
542d9638e5mwsstatic void
543d9638e5mwsfmd_module_thrcancel(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
544d9638e5mws{
545d9638e5mws	fmd_thread_t *tp = fmd_idspace_getspecific(ids, id);
546d9638e5mws
547f6e214cGavin Maltby	/*
548f6e214cGavin Maltby	 * Door service threads are not cancellable (worse - if they're
549f6e214cGavin Maltby	 * waiting in door_return then that is interrupted, but they then spin
550f6e214cGavin Maltby	 * endlessly!).  Non-private door service threads are not tracked
551f6e214cGavin Maltby	 * in the module thread idspace so it's only private server threads
552f6e214cGavin Maltby	 * created via fmd_doorthr_create that we'll encounter.  In most
553f6e214cGavin Maltby	 * cases the module _fini should have tidied up (e.g., calling
554f6e214cGavin Maltby	 * sysevent_evc_unbind which will cleanup door threads if
555f6e214cGavin Maltby	 * sysevent_evc_xsubscribe was used).  One case that does not
556f6e214cGavin Maltby	 * clean up is sysev_fini which explicitly does not unbind the
557f6e214cGavin Maltby	 * channel, so we must skip any remaining door threads here.
558f6e214cGavin Maltby	 */
559f6e214cGavin Maltby	if (tp->thr_isdoor) {
560f6e214cGavin Maltby		fmd_dprintf(FMD_DBG_MOD, "not cancelling %s private door "
561f6e214cGavin Maltby		    "thread %u\n", mp->mod_name, tp->thr_tid);
562f6e214cGavin Maltby		fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
563f6e214cGavin Maltby		return;
564f6e214cGavin Maltby	}
565f6e214cGavin Maltby
566d9638e5mws	fmd_dprintf(FMD_DBG_MOD, "cancelling %s auxiliary thread %u\n",
567d9638e5mws	    mp->mod_name, tp->thr_tid);
568d9638e5mws
569d9638e5mws	ASSERT(tp->thr_tid == id);
570d9638e5mws	(void) pthread_cancel(tp->thr_tid);
571d9638e5mws	(void) pthread_join(tp->thr_tid, NULL);
572d9638e5mws
573d9638e5mws	fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
574d9638e5mws}
575d9638e5mws
5767c478bdstevel@tonic-gatevoid
5777c478bdstevel@tonic-gatefmd_module_unregister(fmd_module_t *mp)
5787c478bdstevel@tonic-gate{
5797c478bdstevel@tonic-gate	fmd_conf_formal_t *cfp = mp->mod_argv;
5807c478bdstevel@tonic-gate	const fmd_conf_path_t *pap;
581d9638e5mws	fmd_case_t *cp;
582d9638e5mws	fmd_xprt_t *xp;
5837c478bdstevel@tonic-gate	int i;
5847c478bdstevel@tonic-gate
5857c478bdstevel@tonic-gate	TRACE((FMD_DBG_MOD, "unregister %p (%s)", (void *)mp, mp->mod_name));
5867c478bdstevel@tonic-gate	ASSERT(fmd_module_locked(mp));
5877c478bdstevel@tonic-gate
588d9638e5mws	/*
589d9638e5mws	 * If any transports are still open, they have send threads that are
590d9638e5mws	 * using the module handle: shut them down and join with these threads.
591d9638e5mws	 */
592d9638e5mws	while ((xp = fmd_list_next(&mp->mod_transports)) != NULL)
593d9638e5mws		fmd_xprt_destroy(xp);
594d9638e5mws
595d9638e5mws	/*
596d9638e5mws	 * If any auxiliary threads exist, they may be using our module handle,
597d9638e5mws	 * and therefore could cause a fault as soon as we start destroying it.
598d9638e5mws	 * Module writers should clean up any threads before unregistering: we
599d9638e5mws	 * forcibly cancel any remaining auxiliary threads before proceeding.
600d9638e5mws	 */
601d9638e5mws	fmd_idspace_apply(mp->mod_threads,
602d9638e5mws	    (void (*)())fmd_module_thrcancel, mp);
603d9638e5mws
60433129b3ayznaga	if (mp->mod_error == 0)
60533129b3ayznaga		fmd_ckpt_save(mp); /* take one more checkpoint if needed */
60633129b3ayznaga
607d9638e5mws	/*
608d9638e5mws	 * Delete any cases associated with the module (UNSOLVED, SOLVED, or
609d9638e5mws	 * CLOSE_WAIT) as if fmdo_close() has finished processing them.
610d9638e5mws	 */
611d9638e5mws	while ((cp = fmd_list_next(&mp->mod_cases)) != NULL)
612d9638e5mws		fmd_case_delete(cp);
613d9638e5mws
614d9638e5mws	fmd_ustat_delete_references(mp->mod_ustat);
6157c478bdstevel@tonic-gate	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap);
6167c478bdstevel@tonic-gate
617d9638e5mws	for (i = 0; i < pap->cpa_argc; i++) {
618d9638e5mws		fmd_xprt_unsubscribe_all(pap->cpa_argv[i]);
619d9638e5mws		fmd_dispq_delete(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]);
6207c478bdstevel@tonic-gate	}
6217c478bdstevel@tonic-gate
6227c478bdstevel@tonic-gate	fmd_conf_close(mp->mod_conf);
6237c478bdstevel@tonic-gate	mp->mod_conf = NULL;
6247c478bdstevel@tonic-gate
6257c478bdstevel@tonic-gate	for (i = 0; i < mp->mod_argc; i++, cfp++) {
6267c478bdstevel@tonic-gate		fmd_strfree((char *)cfp->cf_name);
6277c478bdstevel@tonic-gate		fmd_strfree((char *)cfp->cf_default);
6287c478bdstevel@tonic-gate	}
6297c478bdstevel@tonic-gate
6307c478bdstevel@tonic-gate	fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc);
6317c478bdstevel@tonic-gate	mp->mod_argv = NULL;
6327c478bdstevel@tonic-gate	mp->mod_argc = 0;
6337c478bdstevel@tonic-gate
6347c478bdstevel@tonic-gate	nvlist_free(mp->mod_fmri);
6357c478bdstevel@tonic-gate	mp->mod_fmri = NULL;
6367c478bdstevel@tonic-gate
6377c478bdstevel@tonic-gate	fmd_strfree((char *)mp->mod_info->fmdi_desc);
6387c478bdstevel@tonic-gate	fmd_strfree((char *)mp->mod_info->fmdi_vers);
6397c478bdstevel@tonic-gate	fmd_free((void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t));
6407c478bdstevel@tonic-gate	fmd_free(mp->mod_info, sizeof (fmd_hdl_info_t));
6417c478bdstevel@tonic-gate	mp->mod_info = NULL;
6427c478bdstevel@tonic-gate
6437c478bdstevel@tonic-gate	fmd_eventq_abort(mp->mod_queue);
6447c478bdstevel@tonic-gate}
6457c478bdstevel@tonic-gate
6467c478bdstevel@tonic-gatevoid
6477c478bdstevel@tonic-gatefmd_hdl_unregister(fmd_hdl_t *hdl)
6487c478bdstevel@tonic-gate{
6497c478bdstevel@tonic-gate	fmd_module_t *mp = fmd_api_module_lock(hdl);
6507c478bdstevel@tonic-gate	fmd_module_unregister(mp);
6517c478bdstevel@tonic-gate	fmd_module_unlock(mp);
6527c478bdstevel@tonic-gate}
6537c478bdstevel@tonic-gate
6547c478bdstevel@tonic-gatevoid
6557c478bdstevel@tonic-gatefmd_hdl_subscribe(fmd_hdl_t *hdl, const char *class)
6567c478bdstevel@tonic-gate{
6577c478bdstevel@tonic-gate	fmd_module_t *mp = fmd_api_module_lock(hdl);
6587c478bdstevel@tonic-gate
659d9638e5mws	if (fmd_conf_setprop(mp->mod_conf,
660d9638e5mws	    FMD_PROP_SUBSCRIPTIONS, class) == 0) {
661d9638e5mws		fmd_dispq_insert(fmd.d_disp, mp->mod_queue, class);
662d9638e5mws		fmd_xprt_subscribe_all(class);
663d9638e5mws	}
6647c478bdstevel@tonic-gate
6657c478bdstevel@tonic-gate	fmd_module_unlock(mp);
6667c478bdstevel@tonic-gate}
6677c478bdstevel@tonic-gate
668d9638e5mws
6697c478bdstevel@tonic-gatevoid
6707c478bdstevel@tonic-gatefmd_hdl_unsubscribe(fmd_hdl_t *hdl, const char *class)
6717c478bdstevel@tonic-gate{
6727c478bdstevel@tonic-gate	fmd_module_t *mp = fmd_api_module_lock(hdl);
6737c478bdstevel@tonic-gate
674d9638e5mws	if (fmd_conf_delprop(mp->mod_conf,
675d9638e5mws	    FMD_PROP_SUBSCRIPTIONS, class) == 0) {
676d9638e5mws		fmd_xprt_unsubscribe_all(class);
677d9638e5mws		fmd_dispq_delete(fmd.d_disp, mp->mod_queue, class);
678d9638e5mws	}
6797c478bdstevel@tonic-gate
6807c478bdstevel@tonic-gate	fmd_module_unlock(mp);
6817c478bdstevel@tonic-gate	fmd_eventq_cancel(mp->mod_queue, FMD_EVT_PROTOCOL, (void *)class);
6827c478bdstevel@tonic-gate}
6837c478bdstevel@tonic-gate
6847c478bdstevel@tonic-gatevoid
6857c478bdstevel@tonic-gatefmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec)
6867c478bdstevel@tonic-gate{
6877c478bdstevel@tonic-gate	fmd_module_t *mp = fmd_api_module_lock(hdl);
6887c478bdstevel@tonic-gate
6897c478bdstevel@tonic-gate	mp->mod_spec = spec;
6907c478bdstevel@tonic-gate	fmd_module_unlock(mp);
6917c478bdstevel@tonic-gate}
6927c478bdstevel@tonic-gate
6937c478bdstevel@tonic-gatevoid *
6947c478bdstevel@tonic-gatefmd_hdl_getspecific(fmd_hdl_t *hdl)
6957c478bdstevel@tonic-gate{
6967c478bdstevel@tonic-gate	fmd_module_t *mp = fmd_api_module_lock(hdl);
6977c478bdstevel@tonic-gate	void *spec = mp->mod_spec;
6987c478bdstevel@tonic-gate
6997c478bdstevel@tonic-gate	fmd_module_unlock(mp);
7007c478bdstevel@tonic-gate	return (spec);
7017c478bdstevel@tonic-gate}
7027c478bdstevel@tonic-gate
7037c478bdstevel@tonic-gatevoid
7047c478bdstevel@tonic-gatefmd_hdl_opendict(fmd_hdl_t *hdl, const char *dict)
7057c478bdstevel@tonic-gate{
7067c478bdstevel@tonic-gate	fmd_module_t *mp = fmd_api_module_lock(hdl);
7077c478bdstevel@tonic-gate	const fmd_conf_path_t *pap;
7087c478bdstevel@tonic-gate	int i;
7097c478bdstevel@tonic-gate
7107c478bdstevel@tonic-gate	/*
7117c478bdstevel@tonic-gate	 * Update the dictionary property in order to preserve the list of
7127c478bdstevel@tonic-gate	 * pathnames and expand any % tokens in the path.  Then retrieve the
7137c478bdstevel@tonic-gate	 * new dictionary names from cpa_argv[] and open them one at a time.
7147c478bdstevel@tonic-gate	 */
7157c478bdstevel@tonic-gate	(void) fmd_conf_setprop(mp->mod_conf, FMD_PROP_DICTIONARIES, dict);
7167c478bdstevel@tonic-gate	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap);
7177c478bdstevel@tonic-gate
7187c478bdstevel@tonic-gate	ASSERT(pap->cpa_argc > mp->mod_dictc);
7197c478bdstevel@tonic-gate
7207c478bdstevel@tonic-gate	for (i = mp->mod_dictc; i < pap->cpa_argc; i++) {
7217c478bdstevel@tonic-gate		if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) {
7227c478bdstevel@tonic-gate			fmd_api_error(mp, EFMD_MOD_DICT,
7237c478bdstevel@tonic-gate			    "failed to open dictionary %s for module %s",
7247c478bdstevel@tonic-gate			    pap->cpa_argv[i], mp->mod_name);
7257c478bdstevel@tonic-gate		}
7267c478bdstevel@tonic-gate	}
7277c478bdstevel@tonic-gate
7287c478bdstevel@tonic-gate	fmd_module_unlock(mp);
7297c478bdstevel@tonic-gate}
7307c478bdstevel@tonic-gate
731