xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_api.c (revision 7f7322febbcfe774b7270abc3b191c094bfcc517)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 
23 /*
24  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25  * Use is subject to license terms.
26  */
27 
28 #pragma ident	"%Z%%M%	%I%	%E% SMI"
29 
30 #include <sys/types.h>
31 #include <sys/fm/protocol.h>
32 
33 #include <unistd.h>
34 #include <signal.h>
35 #include <limits.h>
36 #include <syslog.h>
37 #include <alloca.h>
38 
39 #include <fmd_module.h>
40 #include <fmd_api.h>
41 #include <fmd_string.h>
42 #include <fmd_subr.h>
43 #include <fmd_error.h>
44 #include <fmd_event.h>
45 #include <fmd_eventq.h>
46 #include <fmd_dispq.h>
47 #include <fmd_timerq.h>
48 #include <fmd_thread.h>
49 #include <fmd_ustat.h>
50 #include <fmd_case.h>
51 #include <fmd_protocol.h>
52 #include <fmd_buf.h>
53 #include <fmd_asru.h>
54 #include <fmd_fmri.h>
55 #include <fmd_ckpt.h>
56 #include <fmd_xprt.h>
57 
58 #include <fmd.h>
59 
60 /*
61  * Table of configuration file variable types ops-vector pointers.  We use this
62  * to convert from the property description array specified by the module to an
63  * array of fmd_conf_formal_t's.  The order of this array must match the order
64  * of #define values specified in <fmd_api.h> (i.e. FMD_TYPE_BOOL must be 0).
65  * For now, the fmd_conf_list and fmd_conf_path types are not supported as we
66  * do not believe modules need them and they would require more complexity.
67  */
68 static const fmd_conf_ops_t *const _fmd_prop_ops[] = {
69 	&fmd_conf_bool,		/* FMD_TYPE_BOOL */
70 	&fmd_conf_int32,	/* FMD_TYPE_INT32 */
71 	&fmd_conf_uint32,	/* FMD_TYPE_UINT32 */
72 	&fmd_conf_int64,	/* FMD_TYPE_INT64 */
73 	&fmd_conf_uint64,	/* FMD_TYPE_UINT64 */
74 	&fmd_conf_string,	/* FMD_TYPE_STRING */
75 	&fmd_conf_time,		/* FMD_TYPE_TIME */
76 	&fmd_conf_size,		/* FMD_TYPE_SIZE */
77 };
78 
79 /*
80  * fmd_api_vxerror() provides the engine underlying the fmd_hdl_[v]error() API
81  * calls and the fmd_api_[v]error() utility routine defined below.  The routine
82  * formats the error, optionally associated with a particular errno code 'err',
83  * and logs it as an ereport associated with the calling module.  Depending on
84  * other optional properties, we also emit a message to stderr and to syslog.
85  */
86 static void
87 fmd_api_vxerror(fmd_module_t *mp, int err, const char *format, va_list ap)
88 {
89 	int raw_err = err;
90 	nvlist_t *nvl;
91 	fmd_event_t *e;
92 	char *class, *msg;
93 	size_t len1, len2;
94 	char c;
95 
96 	/*
97 	 * fmd_api_vxerror() counts as both an error of class EFMD_MODULE
98 	 * as well as an instance of 'err' w.r.t. our internal bean counters.
99 	 */
100 	(void) pthread_mutex_lock(&fmd.d_err_lock);
101 	fmd.d_errstats[EFMD_MODULE - EFMD_UNKNOWN].fmds_value.ui64++;
102 
103 	if (err > EFMD_UNKNOWN && err < EFMD_END)
104 		fmd.d_errstats[err - EFMD_UNKNOWN].fmds_value.ui64++;
105 
106 	(void) pthread_mutex_unlock(&fmd.d_err_lock);
107 
108 	/*
109 	 * Format the message using vsnprintf().  As usual, if the format has a
110 	 * newline in it, it is printed alone; otherwise strerror() is added.
111 	 */
112 	if (strchr(format, '\n') != NULL)
113 		err = 0; /* err is not relevant in the message */
114 
115 	len1 = vsnprintf(&c, 1, format, ap);
116 	len2 = err != 0 ? snprintf(&c, 1, ": %s\n", fmd_strerror(err)) : 0;
117 
118 	msg = fmd_alloc(len1 + len2 + 1, FMD_SLEEP);
119 	(void) vsnprintf(msg, len1 + 1, format, ap);
120 
121 	if (err != 0) {
122 		(void) snprintf(&msg[len1], len2 + 1,
123 		    ": %s\n", fmd_strerror(err));
124 	}
125 
126 	/*
127 	 * Create an error event corresponding to the error, insert it into the
128 	 * error log, and dispatch it to the fmd-self-diagnosis engine.
129 	 */
130 	if (mp != fmd.d_self && (raw_err != EFMD_HDL_ABORT || fmd.d_running)) {
131 		if ((c = msg[len1 + len2 - 1]) == '\n')
132 			msg[len1 + len2 - 1] = '\0'; /* strip \n for event */
133 
134 		nvl = fmd_protocol_moderror(mp, err, msg);
135 
136 		if (c == '\n')
137 			msg[len1 + len2 - 1] = c;
138 
139 		(void) nvlist_lookup_string(nvl, FM_CLASS, &class);
140 		e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
141 
142 		(void) pthread_rwlock_rdlock(&fmd.d_log_lock);
143 		fmd_log_append(fmd.d_errlog, e, NULL);
144 		(void) pthread_rwlock_unlock(&fmd.d_log_lock);
145 
146 		fmd_event_transition(e, FMD_EVS_ACCEPTED);
147 		fmd_event_commit(e);
148 
149 		fmd_dispq_dispatch(fmd.d_disp, e, class);
150 	}
151 
152 	/*
153 	 * Similar to fmd_vdebug(), if the debugging switches are enabled we
154 	 * echo the module name and message to stderr and/or syslog.  Unlike
155 	 * fmd_vdebug(), we also print to stderr if foreground mode is enabled.
156 	 * We also print the message if a built-in module is aborting before
157 	 * fmd has detached from its parent (e.g. default transport failure).
158 	 */
159 	if (fmd.d_fg || (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) || (
160 	    raw_err == EFMD_HDL_ABORT && !fmd.d_running)) {
161 		(void) pthread_mutex_lock(&fmd.d_err_lock);
162 		(void) fprintf(stderr, "%s: %s: %s",
163 		    fmd.d_pname, mp->mod_name, msg);
164 		(void) pthread_mutex_unlock(&fmd.d_err_lock);
165 	}
166 
167 	if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) {
168 		syslog(LOG_ERR | LOG_DAEMON, "%s ERROR: %s: %s",
169 		    fmd.d_pname, mp->mod_name, msg);
170 	}
171 
172 	fmd_free(msg, len1 + len2 + 1);
173 }
174 
175 /*PRINTFLIKE3*/
176 static void
177 fmd_api_xerror(fmd_module_t *mp, int err, const char *format, ...)
178 {
179 	va_list ap;
180 
181 	va_start(ap, format);
182 	fmd_api_vxerror(mp, err, format, ap);
183 	va_end(ap);
184 }
185 
186 /*
187  * fmd_api_verror() is a wrapper around fmd_api_vxerror() for API subroutines.
188  * It calls fmd_module_unlock() on behalf of its caller, logs the error, and
189  * then aborts the API call and the surrounding module entry point by doing an
190  * fmd_module_abort(), which longjmps to the place where we entered the module.
191  */
192 static void
193 fmd_api_verror(fmd_module_t *mp, int err, const char *format, va_list ap)
194 {
195 	if (fmd_module_locked(mp))
196 		fmd_module_unlock(mp);
197 
198 	fmd_api_vxerror(mp, err, format, ap);
199 	fmd_module_abort(mp, err);
200 }
201 
202 /*PRINTFLIKE3*/
203 static void
204 fmd_api_error(fmd_module_t *mp, int err, const char *format, ...)
205 {
206 	va_list ap;
207 
208 	va_start(ap, format);
209 	fmd_api_verror(mp, err, format, ap);
210 	va_end(ap);
211 }
212 
213 /*
214  * Common code for fmd_api_module_lock() and fmd_api_transport_impl().  This
215  * code verifies that the handle is valid and associated with a proper thread.
216  */
217 static fmd_module_t *
218 fmd_api_module(fmd_hdl_t *hdl)
219 {
220 	fmd_thread_t *tp;
221 	fmd_module_t *mp;
222 
223 	/*
224 	 * If our TSD is not present at all, this is either a serious bug or
225 	 * someone has created a thread behind our back and is using fmd's API.
226 	 * We can't call fmd_api_error() because we can't be sure that we can
227 	 * unwind our state back to an enclosing fmd_module_dispatch(), so we
228 	 * must panic instead.  This is likely a module design or coding error.
229 	 */
230 	if ((tp = pthread_getspecific(fmd.d_key)) == NULL) {
231 		fmd_panic("fmd module api call made using "
232 		    "client handle %p from unknown thread\n", (void *)hdl);
233 	}
234 
235 	/*
236 	 * If our TSD refers to the root module and is a door server thread,
237 	 * then it was created asynchronously at the request of a module but
238 	 * is using now the module API as an auxiliary module thread.  We reset
239 	 * tp->thr_mod to the module handle so it can act as a module thread.
240 	 */
241 	if (tp->thr_mod == fmd.d_rmod && tp->thr_func == &fmd_door_server)
242 		tp->thr_mod = (fmd_module_t *)hdl;
243 
244 	if ((mp = tp->thr_mod) != (fmd_module_t *)hdl) {
245 		fmd_api_error(mp, EFMD_HDL_INVAL,
246 		    "client handle %p is not valid\n", (void *)hdl);
247 	}
248 
249 	if (mp->mod_flags & FMD_MOD_FAIL) {
250 		fmd_api_error(mp, EFMD_MOD_FAIL,
251 		    "module has experienced an unrecoverable error\n");
252 	}
253 
254 	return (mp);
255 }
256 
257 /*
258  * fmd_api_module_lock() is used as a wrapper around fmd_module_lock() and a
259  * common prologue to each fmd_api.c routine.  It verifies that the handle is
260  * valid and owned by the current server thread, locks the handle, and then
261  * verifies that the caller is performing an operation on a registered handle.
262  * If any tests fail, the entire API call is aborted by fmd_api_error().
263  */
264 static fmd_module_t *
265 fmd_api_module_lock(fmd_hdl_t *hdl)
266 {
267 	fmd_module_t *mp = fmd_api_module(hdl);
268 
269 	fmd_module_lock(mp);
270 
271 	if (mp->mod_info == NULL) {
272 		fmd_api_error(mp, EFMD_HDL_NOTREG,
273 		    "client handle %p has not been registered\n", (void *)hdl);
274 	}
275 
276 	return (mp);
277 }
278 
279 /*
280  * Utility function for API entry points that accept fmd_case_t's.  We cast cp
281  * to fmd_case_impl_t and check to make sure the case is owned by the caller.
282  */
283 static fmd_case_impl_t *
284 fmd_api_case_impl(fmd_module_t *mp, fmd_case_t *cp)
285 {
286 	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
287 
288 	if (cip == NULL || cip->ci_mod != mp) {
289 		fmd_api_error(mp, EFMD_CASE_OWNER,
290 		    "case %p is invalid or not owned by caller\n", (void *)cip);
291 	}
292 
293 	return (cip);
294 }
295 
296 /*
297  * Utility function for API entry points that accept fmd_xprt_t's.  We cast xp
298  * to fmd_transport_t and check to make sure the case is owned by the caller.
299  * Note that we could make this check safer by actually walking mp's transport
300  * list, but that requires holding the module lock and this routine needs to be
301  * MT-hot w.r.t. auxiliary module threads.  Ultimately any loadable module can
302  * cause us to crash anyway, so we optimize for scalability over safety here.
303  */
304 static fmd_xprt_impl_t *
305 fmd_api_transport_impl(fmd_hdl_t *hdl, fmd_xprt_t *xp)
306 {
307 	fmd_module_t *mp = fmd_api_module(hdl);
308 	fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
309 
310 	if (xip == NULL || xip->xi_queue->eq_mod != mp) {
311 		fmd_api_error(mp, EFMD_XPRT_OWNER,
312 		    "xprt %p is invalid or not owned by caller\n", (void *)xp);
313 	}
314 
315 	return (xip);
316 }
317 
318 /*
319  * fmd_hdl_register() is the one function which cannot use fmd_api_error() to
320  * report errors, because that routine causes the module to abort.  Failure to
321  * register is instead handled by having fmd_hdl_register() return an error to
322  * the _fmd_init() function and then detecting no registration when it returns.
323  * So we use this routine for fmd_hdl_register() error paths instead.
324  */
325 static int
326 fmd_hdl_register_error(fmd_module_t *mp, int err)
327 {
328 	if (fmd_module_locked(mp))
329 		fmd_module_unlock(mp);
330 
331 	fmd_api_xerror(mp, err, "failed to register");
332 	return (fmd_set_errno(err));
333 }
334 
335 static void
336 fmd_hdl_nop(void)
337 {
338 	/* empty function for use with unspecified module entry points */
339 }
340 
341 int
342 fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
343 {
344 	fmd_thread_t *tp = pthread_getspecific(fmd.d_key);
345 	fmd_module_t *mp = tp->thr_mod;
346 
347 	const fmd_prop_t *prop;
348 	const fmd_conf_path_t *pap;
349 	fmd_conf_formal_t *cfp;
350 	fmd_hdl_ops_t ops;
351 
352 	const char *conf = NULL;
353 	char buf[PATH_MAX];
354 	int i;
355 
356 	if (mp != (fmd_module_t *)hdl)
357 		return (fmd_hdl_register_error(mp, EFMD_HDL_INVAL));
358 
359 	fmd_module_lock(mp);
360 
361 	/*
362 	 * First perform some sanity checks on our input.  The API version must
363 	 * be supported by FMD and the handle can only be registered once by
364 	 * the module thread to which we assigned this client handle.  The info
365 	 * provided for the handle must be valid and have the minimal settings.
366 	 */
367 	if (version > FMD_API_VERSION_3)
368 		return (fmd_hdl_register_error(mp, EFMD_VER_NEW));
369 
370 	if (version < FMD_API_VERSION_1)
371 		return (fmd_hdl_register_error(mp, EFMD_VER_OLD));
372 
373 	if (mp->mod_conf != NULL)
374 		return (fmd_hdl_register_error(mp, EFMD_HDL_REG));
375 
376 	if (pthread_self() != mp->mod_thread->thr_tid)
377 		return (fmd_hdl_register_error(mp, EFMD_HDL_TID));
378 
379 	if (mip == NULL || mip->fmdi_desc == NULL ||
380 	    mip->fmdi_vers == NULL || mip->fmdi_ops == NULL)
381 		return (fmd_hdl_register_error(mp, EFMD_HDL_INFO));
382 
383 	/*
384 	 * Copy the module's ops vector into a local variable to account for
385 	 * changes in the module ABI.  Then if any of the optional entry points
386 	 * are NULL, set them to nop so we don't have to check before calling.
387 	 */
388 	bzero(&ops, sizeof (ops));
389 
390 	if (version < FMD_API_VERSION_3)
391 		bcopy(mip->fmdi_ops, &ops, sizeof (ops) - sizeof (void *));
392 	else
393 		bcopy(mip->fmdi_ops, &ops, sizeof (ops));
394 
395 	if (ops.fmdo_recv == NULL)
396 		ops.fmdo_recv = (void (*)())fmd_hdl_nop;
397 	if (ops.fmdo_timeout == NULL)
398 		ops.fmdo_timeout = (void (*)())fmd_hdl_nop;
399 	if (ops.fmdo_close == NULL)
400 		ops.fmdo_close = (void (*)())fmd_hdl_nop;
401 	if (ops.fmdo_stats == NULL)
402 		ops.fmdo_stats = (void (*)())fmd_hdl_nop;
403 	if (ops.fmdo_gc == NULL)
404 		ops.fmdo_gc = (void (*)())fmd_hdl_nop;
405 	if (ops.fmdo_send == NULL)
406 		ops.fmdo_send = (int (*)())fmd_hdl_nop;
407 
408 	/*
409 	 * Make two passes through the property array to initialize the formals
410 	 * to use for processing the module's .conf file.  In the first pass,
411 	 * we validate the types and count the number of properties.  In the
412 	 * second pass we copy the strings and fill in the appropriate ops.
413 	 */
414 	for (prop = mip->fmdi_props, i = 0; prop != NULL &&
415 	    prop->fmdp_name != NULL; prop++, i++) {
416 		if (prop->fmdp_type >=
417 		    sizeof (_fmd_prop_ops) / sizeof (_fmd_prop_ops[0])) {
418 			fmd_api_xerror(mp, EFMD_HDL_PROP,
419 			    "property %s uses invalid type %u\n",
420 			    prop->fmdp_name, prop->fmdp_type);
421 			return (fmd_hdl_register_error(mp, EFMD_HDL_PROP));
422 		}
423 	}
424 
425 	mp->mod_argc = i;
426 	mp->mod_argv = fmd_zalloc(sizeof (fmd_conf_formal_t) * i, FMD_SLEEP);
427 
428 	prop = mip->fmdi_props;
429 	cfp = mp->mod_argv;
430 
431 	for (i = 0; i < mp->mod_argc; i++, prop++, cfp++) {
432 		cfp->cf_name = fmd_strdup(prop->fmdp_name, FMD_SLEEP);
433 		cfp->cf_ops = _fmd_prop_ops[prop->fmdp_type];
434 		cfp->cf_default = fmd_strdup(prop->fmdp_defv, FMD_SLEEP);
435 	}
436 
437 	/*
438 	 * If this module came from an on-disk file, compute the name of the
439 	 * corresponding .conf file and parse properties from it if it exists.
440 	 */
441 	if (mp->mod_path != NULL) {
442 		(void) strlcpy(buf, mp->mod_path, sizeof (buf));
443 		(void) fmd_strdirname(buf);
444 
445 		(void) strlcat(buf, "/", sizeof (buf));
446 		(void) strlcat(buf, mp->mod_name, sizeof (buf));
447 		(void) strlcat(buf, ".conf", sizeof (buf));
448 
449 		if (access(buf, F_OK) == 0)
450 			conf = buf;
451 	}
452 
453 	if ((mp->mod_conf = fmd_conf_open(conf,
454 	    mp->mod_argc, mp->mod_argv, 0)) == NULL)
455 		return (fmd_hdl_register_error(mp, EFMD_MOD_CONF));
456 
457 	fmd_conf_propagate(fmd.d_conf, mp->mod_conf, mp->mod_name);
458 
459 	/*
460 	 * Look up the list of the libdiagcode dictionaries associated with the
461 	 * module.  If none were specified, use the value from daemon's config.
462 	 * We only fail if the module specified an explicit dictionary.
463 	 */
464 	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap);
465 	if (pap->cpa_argc == 0 && mp->mod_ops == &fmd_bltin_ops)
466 		(void) fmd_conf_getprop(fmd.d_conf, "self.dict", &pap);
467 
468 	for (i = 0; i < pap->cpa_argc; i++) {
469 		if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) {
470 			fmd_api_xerror(mp, errno,
471 			    "failed to open dictionary %s", pap->cpa_argv[i]);
472 			return (fmd_hdl_register_error(mp, EFMD_MOD_CONF));
473 		}
474 	}
475 
476 	/*
477 	 * Make a copy of the handle information and store it in mod_info.  We
478 	 * do not need to bother copying fmdi_props since they're already read.
479 	 */
480 	mp->mod_info = fmd_alloc(sizeof (fmd_hdl_info_t), FMD_SLEEP);
481 	mp->mod_info->fmdi_desc = fmd_strdup(mip->fmdi_desc, FMD_SLEEP);
482 	mp->mod_info->fmdi_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP);
483 	mp->mod_info->fmdi_ops = fmd_alloc(sizeof (fmd_hdl_ops_t), FMD_SLEEP);
484 	bcopy(&ops, (void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t));
485 	mp->mod_info->fmdi_props = NULL;
486 
487 	/*
488 	 * Allocate an FMRI representing this module.  We'll use this later
489 	 * if the module decides to publish any events (e.g. list.suspects).
490 	 */
491 	mp->mod_fmri = fmd_protocol_fmri_module(mp);
492 
493 	/*
494 	 * Any subscriptions specified in the conf file are now stored in the
495 	 * corresponding property.  Add all of these to the dispatch queue.
496 	 */
497 	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap);
498 
499 	for (i = 0; i < pap->cpa_argc; i++) {
500 		fmd_dispq_insert(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]);
501 		fmd_xprt_subscribe_all(pap->cpa_argv[i]);
502 	}
503 
504 	/*
505 	 * Unlock the module and restore any pre-existing module checkpoint.
506 	 * If the checkpoint is missing or corrupt, we just keep going.
507 	 */
508 	fmd_module_unlock(mp);
509 	fmd_ckpt_restore(mp);
510 	return (0);
511 }
512 
513 /*
514  * If an auxiliary thread exists for the specified module at unregistration
515  * time, send it an asynchronous cancellation to force it to exit and then
516  * join with it (we expect this to either succeed quickly or return ESRCH).
517  * Once this is complete we can destroy the associated fmd_thread_t data.
518  */
519 static void
520 fmd_module_thrcancel(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
521 {
522 	fmd_thread_t *tp = fmd_idspace_getspecific(ids, id);
523 
524 	fmd_dprintf(FMD_DBG_MOD, "cancelling %s auxiliary thread %u\n",
525 	    mp->mod_name, tp->thr_tid);
526 
527 	ASSERT(tp->thr_tid == id);
528 	(void) pthread_cancel(tp->thr_tid);
529 	(void) pthread_join(tp->thr_tid, NULL);
530 
531 	fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
532 }
533 
534 void
535 fmd_module_unregister(fmd_module_t *mp)
536 {
537 	fmd_conf_formal_t *cfp = mp->mod_argv;
538 	const fmd_conf_path_t *pap;
539 	fmd_case_t *cp;
540 	fmd_xprt_t *xp;
541 	int i;
542 
543 	TRACE((FMD_DBG_MOD, "unregister %p (%s)", (void *)mp, mp->mod_name));
544 	ASSERT(fmd_module_locked(mp));
545 
546 	/*
547 	 * If any transports are still open, they have send threads that are
548 	 * using the module handle: shut them down and join with these threads.
549 	 */
550 	while ((xp = fmd_list_next(&mp->mod_transports)) != NULL)
551 		fmd_xprt_destroy(xp);
552 
553 	/*
554 	 * If any auxiliary threads exist, they may be using our module handle,
555 	 * and therefore could cause a fault as soon as we start destroying it.
556 	 * Module writers should clean up any threads before unregistering: we
557 	 * forcibly cancel any remaining auxiliary threads before proceeding.
558 	 */
559 	fmd_idspace_apply(mp->mod_threads,
560 	    (void (*)())fmd_module_thrcancel, mp);
561 
562 	if (mp->mod_error == 0)
563 		fmd_ckpt_save(mp); /* take one more checkpoint if needed */
564 
565 	/*
566 	 * Delete any cases associated with the module (UNSOLVED, SOLVED, or
567 	 * CLOSE_WAIT) as if fmdo_close() has finished processing them.
568 	 */
569 	while ((cp = fmd_list_next(&mp->mod_cases)) != NULL)
570 		fmd_case_delete(cp);
571 
572 	fmd_ustat_delete_references(mp->mod_ustat);
573 	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap);
574 
575 	for (i = 0; i < pap->cpa_argc; i++) {
576 		fmd_xprt_unsubscribe_all(pap->cpa_argv[i]);
577 		fmd_dispq_delete(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]);
578 	}
579 
580 	fmd_conf_close(mp->mod_conf);
581 	mp->mod_conf = NULL;
582 
583 	for (i = 0; i < mp->mod_argc; i++, cfp++) {
584 		fmd_strfree((char *)cfp->cf_name);
585 		fmd_strfree((char *)cfp->cf_default);
586 	}
587 
588 	fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc);
589 	mp->mod_argv = NULL;
590 	mp->mod_argc = 0;
591 
592 	nvlist_free(mp->mod_fmri);
593 	mp->mod_fmri = NULL;
594 
595 	fmd_strfree((char *)mp->mod_info->fmdi_desc);
596 	fmd_strfree((char *)mp->mod_info->fmdi_vers);
597 	fmd_free((void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t));
598 	fmd_free(mp->mod_info, sizeof (fmd_hdl_info_t));
599 	mp->mod_info = NULL;
600 
601 	fmd_eventq_abort(mp->mod_queue);
602 }
603 
604 void
605 fmd_hdl_unregister(fmd_hdl_t *hdl)
606 {
607 	fmd_module_t *mp = fmd_api_module_lock(hdl);
608 	fmd_module_unregister(mp);
609 	fmd_module_unlock(mp);
610 }
611 
612 void
613 fmd_hdl_subscribe(fmd_hdl_t *hdl, const char *class)
614 {
615 	fmd_module_t *mp = fmd_api_module_lock(hdl);
616 
617 	if (fmd_conf_setprop(mp->mod_conf,
618 	    FMD_PROP_SUBSCRIPTIONS, class) == 0) {
619 		fmd_dispq_insert(fmd.d_disp, mp->mod_queue, class);
620 		fmd_xprt_subscribe_all(class);
621 	}
622 
623 	fmd_module_unlock(mp);
624 }
625 
626 
627 void
628 fmd_hdl_unsubscribe(fmd_hdl_t *hdl, const char *class)
629 {
630 	fmd_module_t *mp = fmd_api_module_lock(hdl);
631 
632 	if (fmd_conf_delprop(mp->mod_conf,
633 	    FMD_PROP_SUBSCRIPTIONS, class) == 0) {
634 		fmd_xprt_unsubscribe_all(class);
635 		fmd_dispq_delete(fmd.d_disp, mp->mod_queue, class);
636 	}
637 
638 	fmd_module_unlock(mp);
639 	fmd_eventq_cancel(mp->mod_queue, FMD_EVT_PROTOCOL, (void *)class);
640 }
641 
642 void
643 fmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec)
644 {
645 	fmd_module_t *mp = fmd_api_module_lock(hdl);
646 
647 	mp->mod_spec = spec;
648 	fmd_module_unlock(mp);
649 }
650 
651 void *
652 fmd_hdl_getspecific(fmd_hdl_t *hdl)
653 {
654 	fmd_module_t *mp = fmd_api_module_lock(hdl);
655 	void *spec = mp->mod_spec;
656 
657 	fmd_module_unlock(mp);
658 	return (spec);
659 }
660 
661 void
662 fmd_hdl_opendict(fmd_hdl_t *hdl, const char *dict)
663 {
664 	fmd_module_t *mp = fmd_api_module_lock(hdl);
665 	const fmd_conf_path_t *pap;
666 	int i;
667 
668 	/*
669 	 * Update the dictionary property in order to preserve the list of
670 	 * pathnames and expand any % tokens in the path.  Then retrieve the
671 	 * new dictionary names from cpa_argv[] and open them one at a time.
672 	 */
673 	(void) fmd_conf_setprop(mp->mod_conf, FMD_PROP_DICTIONARIES, dict);
674 	(void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap);
675 
676 	ASSERT(pap->cpa_argc > mp->mod_dictc);
677 
678 	for (i = mp->mod_dictc; i < pap->cpa_argc; i++) {
679 		if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) {
680 			fmd_api_error(mp, EFMD_MOD_DICT,
681 			    "failed to open dictionary %s for module %s",
682 			    pap->cpa_argv[i], mp->mod_name);
683 		}
684 	}
685 
686 	fmd_module_unlock(mp);
687 }
688 
689 void *
690 fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags)
691 {
692 	fmd_module_t *mp = fmd_api_module_lock(hdl);
693 	void *data;
694 
695 	if (mp->mod_stats->ms_memlimit.fmds_value.ui64 -
696 	    mp->mod_stats->ms_memtotal.fmds_value.ui64 < size) {
697 		fmd_api_error(mp, EFMD_HDL_NOMEM, "%s's allocation of %lu "
698 		    "bytes exceeds module memory limit (%llu)\n",
699 		    mp->mod_name, (ulong_t)size, (u_longlong_t)
700 		    mp->mod_stats->ms_memtotal.fmds_value.ui64);
701 	}
702 
703 	if ((data = fmd_alloc(size, flags)) != NULL)
704 		mp->mod_stats->ms_memtotal.fmds_value.ui64 += size;
705 
706 	fmd_module_unlock(mp);
707 	return (data);
708 }
709 
710 void *
711 fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags)
712 {
713 	void *data = fmd_hdl_alloc(hdl, size, flags);
714 
715 	if (data != NULL)
716 		bzero(data, size);
717 
718 	return (data);
719 }
720 
721 void
722 fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size)
723 {
724 	fmd_module_t *mp = fmd_api_module_lock(hdl);
725 
726 	fmd_free(data, size);
727 	mp->mod_stats->ms_memtotal.fmds_value.ui64 -= size;
728 
729 	fmd_module_unlock(mp);
730 }
731 
732 char *
733 fmd_hdl_strdup(fmd_hdl_t *hdl, const char *s, int flags)
734 {
735 	char *p;
736 
737 	if (s != NULL)
738 		p = fmd_hdl_alloc(hdl, strlen(s) + 1, flags);
739 	else
740 		p = NULL;
741 
742 	if (p != NULL)
743 		(void) strcpy(p, s);
744 
745 	return (p);
746 }
747 
748 void
749 fmd_hdl_strfree(fmd_hdl_t *hdl, char *s)
750 {
751 	if (s != NULL)
752 		fmd_hdl_free(hdl, s, strlen(s) + 1);
753 }
754 
755 void
756 fmd_hdl_vabort(fmd_hdl_t *hdl, const char *format, va_list ap)
757 {
758 	fmd_api_verror(fmd_api_module_lock(hdl), EFMD_HDL_ABORT, format, ap);
759 }
760 
761 /*PRINTFLIKE2*/
762 void
763 fmd_hdl_abort(fmd_hdl_t *hdl, const char *format, ...)
764 {
765 	fmd_module_t *mp = fmd_api_module_lock(hdl);
766 	va_list ap;
767 
768 	va_start(ap, format);
769 	fmd_api_verror(mp, EFMD_HDL_ABORT, format, ap);
770 	va_end(ap);
771 }
772 
773 void
774 fmd_hdl_verror(fmd_hdl_t *hdl, const char *format, va_list ap)
775 {
776 	fmd_module_t *mp = fmd_api_module_lock(hdl);
777 	fmd_api_vxerror(mp, errno, format, ap);
778 	fmd_module_unlock(mp);
779 }
780 
781 /*PRINTFLIKE2*/
782 void
783 fmd_hdl_error(fmd_hdl_t *hdl, const char *format, ...)
784 {
785 	va_list ap;
786 
787 	va_start(ap, format);
788 	fmd_hdl_verror(hdl, format, ap);
789 	va_end(ap);
790 }
791 
792 void
793 fmd_hdl_vdebug(fmd_hdl_t *hdl, const char *format, va_list ap)
794 {
795 	fmd_module_t *mp = fmd_api_module_lock(hdl);
796 
797 	char *msg;
798 	size_t len;
799 	char c;
800 
801 	if (!(fmd.d_hdl_debug)) {
802 		mp->mod_stats->ms_debugdrop.fmds_value.ui64++;
803 		fmd_module_unlock(mp);
804 		return;
805 	}
806 
807 	len = vsnprintf(&c, 1, format, ap);
808 
809 	if ((msg = fmd_alloc(len + 2, FMD_NOSLEEP)) == NULL) {
810 		mp->mod_stats->ms_debugdrop.fmds_value.ui64++;
811 		fmd_module_unlock(mp);
812 		return;
813 	}
814 
815 	(void) vsnprintf(msg, len + 1, format, ap);
816 
817 	if (msg[len - 1] != '\n')
818 		(void) strcpy(&msg[len], "\n");
819 
820 	if (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) {
821 		(void) pthread_mutex_lock(&fmd.d_err_lock);
822 		(void) fprintf(stderr, "%s DEBUG: %s: %s",
823 		    fmd.d_pname, mp->mod_name, msg);
824 		(void) pthread_mutex_unlock(&fmd.d_err_lock);
825 	}
826 
827 	if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) {
828 		syslog(LOG_DEBUG | LOG_DAEMON, "%s DEBUG: %s: %s",
829 		    fmd.d_pname, mp->mod_name, msg);
830 	}
831 
832 	fmd_free(msg, len + 2);
833 	fmd_module_unlock(mp);
834 }
835 
836 /*PRINTFLIKE2*/
837 void
838 fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...)
839 {
840 	va_list ap;
841 
842 	va_start(ap, format);
843 	fmd_hdl_vdebug(hdl, format, ap);
844 	va_end(ap);
845 }
846 
847 int32_t
848 fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)
849 {
850 	fmd_module_t *mp = fmd_api_module_lock(hdl);
851 	const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name);
852 	int32_t value = 0;
853 
854 	if (ops == &fmd_conf_bool || ops == &fmd_conf_int32 ||
855 	    ops == &fmd_conf_uint32)
856 		(void) fmd_conf_getprop(mp->mod_conf, name, &value);
857 	else if (ops != NULL) {
858 		fmd_api_error(mp, EFMD_PROP_TYPE,
859 		    "property %s is not of int32 type\n", name);
860 	} else {
861 		fmd_api_error(mp, EFMD_PROP_DEFN,
862 		    "property %s is not defined\n", name);
863 	}
864 
865 	fmd_module_unlock(mp);
866 	return (value);
867 }
868 
869 int64_t
870 fmd_prop_get_int64(fmd_hdl_t *hdl, const char *name)
871 {
872 	fmd_module_t *mp = fmd_api_module_lock(hdl);
873 	const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name);
874 	int64_t value = 0;
875 
876 	if (ops == &fmd_conf_int64 || ops == &fmd_conf_uint64 ||
877 	    ops == &fmd_conf_time || ops == &fmd_conf_size)
878 		(void) fmd_conf_getprop(mp->mod_conf, name, &value);
879 	else if (ops != NULL) {
880 		fmd_api_error(mp, EFMD_PROP_TYPE,
881 		    "property %s is not of int64 type\n", name);
882 	} else {
883 		fmd_api_error(mp, EFMD_PROP_DEFN,
884 		    "property %s is not defined\n", name);
885 	}
886 
887 	fmd_module_unlock(mp);
888 	return (value);
889 }
890 
891 char *
892 fmd_prop_get_string(fmd_hdl_t *hdl, const char *name)
893 {
894 	fmd_module_t *mp = fmd_api_module_lock(hdl);
895 	const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name);
896 	char *value = NULL;
897 	const char *s;
898 
899 	if (ops == &fmd_conf_string) {
900 		(void) fmd_conf_getprop(mp->mod_conf, name, &s);
901 		value = fmd_strdup(s, FMD_SLEEP);
902 	} else if (ops != NULL) {
903 		fmd_api_error(mp, EFMD_PROP_TYPE,
904 		    "property %s is not of string type\n", name);
905 	} else {
906 		fmd_api_error(mp, EFMD_PROP_DEFN,
907 		    "property %s is not defined\n", name);
908 	}
909 
910 	fmd_module_unlock(mp);
911 	return (value);
912 }
913 
914 void
915 fmd_prop_free_string(fmd_hdl_t *hdl, char *s)
916 {
917 	fmd_module_t *mp = fmd_api_module_lock(hdl);
918 	fmd_strfree(s);
919 	fmd_module_unlock(mp);
920 }
921 
922 fmd_stat_t *
923 fmd_stat_create(fmd_hdl_t *hdl, uint_t flags, uint_t argc, fmd_stat_t *argv)
924 {
925 	fmd_module_t *mp = fmd_api_module_lock(hdl);
926 	fmd_stat_t *ep, *sp;
927 
928 	if (flags & ~FMD_STAT_ALLOC) {
929 		fmd_api_error(mp, EFMD_STAT_FLAGS,
930 		    "invalid flags 0x%x passed to fmd_stat_create\n", flags);
931 	}
932 
933 	if ((sp = fmd_ustat_insert(mp->mod_ustat,
934 	    flags | FMD_USTAT_VALIDATE, argc, argv, &ep)) == NULL) {
935 		fmd_api_error(mp, errno,
936 		    "failed to publish stat '%s'", ep->fmds_name);
937 	}
938 
939 	fmd_module_unlock(mp);
940 	return (sp);
941 }
942 
943 void
944 fmd_stat_destroy(fmd_hdl_t *hdl, uint_t argc, fmd_stat_t *argv)
945 {
946 	fmd_module_t *mp = fmd_api_module_lock(hdl);
947 	fmd_ustat_delete(mp->mod_ustat, argc, argv);
948 	fmd_module_unlock(mp);
949 }
950 
951 void
952 fmd_stat_setstr(fmd_hdl_t *hdl, fmd_stat_t *sp, const char *s)
953 {
954 	char *str = fmd_strdup(s, FMD_SLEEP);
955 	fmd_module_t *mp = fmd_api_module_lock(hdl);
956 
957 	if (sp->fmds_type != FMD_TYPE_STRING) {
958 		fmd_strfree(str);
959 		fmd_api_error(mp, EFMD_STAT_TYPE,
960 		    "stat '%s' is not a string\n", sp->fmds_name);
961 	}
962 
963 	fmd_strfree(sp->fmds_value.str);
964 	sp->fmds_value.str = str;
965 
966 	fmd_module_unlock(mp);
967 }
968 
969 fmd_case_t *
970 fmd_case_open(fmd_hdl_t *hdl, void *data)
971 {
972 	fmd_module_t *mp = fmd_api_module_lock(hdl);
973 	fmd_case_t *cp = fmd_case_create(mp, data);
974 	fmd_module_unlock(mp);
975 	return (cp);
976 }
977 
978 void
979 fmd_case_reset(fmd_hdl_t *hdl, fmd_case_t *cp)
980 {
981 	fmd_module_t *mp = fmd_api_module_lock(hdl);
982 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
983 
984 	if (cip->ci_state >= FMD_CASE_SOLVED) {
985 		fmd_api_error(mp, EFMD_CASE_STATE, "cannot solve %s: "
986 		    "case is already solved or closed\n", cip->ci_uuid);
987 	}
988 
989 	fmd_case_reset_suspects(cp);
990 	fmd_module_unlock(mp);
991 }
992 
993 void
994 fmd_case_solve(fmd_hdl_t *hdl, fmd_case_t *cp)
995 {
996 	fmd_module_t *mp = fmd_api_module_lock(hdl);
997 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
998 
999 	if (cip->ci_state >= FMD_CASE_SOLVED) {
1000 		fmd_api_error(mp, EFMD_CASE_STATE, "cannot solve %s: "
1001 		    "case is already solved or closed\n", cip->ci_uuid);
1002 	}
1003 
1004 	fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED);
1005 	fmd_module_unlock(mp);
1006 }
1007 
1008 void
1009 fmd_case_close(fmd_hdl_t *hdl, fmd_case_t *cp)
1010 {
1011 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1012 
1013 	(void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1014 	fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED);
1015 
1016 	fmd_module_unlock(mp);
1017 }
1018 
1019 const char *
1020 fmd_case_uuid(fmd_hdl_t *hdl, fmd_case_t *cp)
1021 {
1022 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1023 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1024 	const char *uuid = cip->ci_uuid;
1025 
1026 	fmd_module_unlock(mp);
1027 	return (uuid);
1028 }
1029 
1030 fmd_case_t *
1031 fmd_case_uulookup(fmd_hdl_t *hdl, const char *uuid)
1032 {
1033 	fmd_module_t *cmp, *mp = fmd_api_module_lock(hdl);
1034 	fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1035 
1036 	if (cp != NULL) {
1037 		cmp = ((fmd_case_impl_t *)cp)->ci_mod;
1038 		fmd_case_rele(cp);
1039 	} else
1040 		cmp = NULL;
1041 
1042 	fmd_module_unlock(mp);
1043 	return (cmp == mp ? cp : NULL);
1044 }
1045 
1046 void
1047 fmd_case_uuclose(fmd_hdl_t *hdl, const char *uuid)
1048 {
1049 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1050 	fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1051 
1052 	if (cp != NULL) {
1053 		fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED);
1054 		fmd_case_rele(cp);
1055 	}
1056 
1057 	fmd_module_unlock(mp);
1058 }
1059 
1060 int
1061 fmd_case_uuclosed(fmd_hdl_t *hdl, const char *uuid)
1062 {
1063 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1064 	fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1065 	fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1066 	int rv = FMD_B_TRUE;
1067 
1068 	if (cip != NULL) {
1069 		rv = cip->ci_state >= FMD_CASE_CLOSE_WAIT;
1070 		fmd_case_rele(cp);
1071 	}
1072 
1073 	fmd_module_unlock(mp);
1074 	return (rv);
1075 }
1076 
1077 static int
1078 fmd_case_instate(fmd_hdl_t *hdl, fmd_case_t *cp, uint_t state)
1079 {
1080 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1081 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1082 	int rv = cip->ci_state >= state;
1083 
1084 	fmd_module_unlock(mp);
1085 	return (rv);
1086 }
1087 
1088 int
1089 fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp)
1090 {
1091 	return (fmd_case_instate(hdl, cp, FMD_CASE_SOLVED));
1092 }
1093 
1094 int
1095 fmd_case_closed(fmd_hdl_t *hdl, fmd_case_t *cp)
1096 {
1097 	return (fmd_case_instate(hdl, cp, FMD_CASE_CLOSE_WAIT));
1098 }
1099 
1100 void
1101 fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
1102 {
1103 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1104 
1105 	(void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1106 	fmd_case_insert_event(cp, ep);
1107 	mp->mod_stats->ms_accepted.fmds_value.ui64++;
1108 
1109 	fmd_module_unlock(mp);
1110 }
1111 
1112 void
1113 fmd_case_add_serd(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name)
1114 {
1115 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1116 	fmd_serd_elem_t *sep;
1117 	fmd_serd_eng_t *sgp;
1118 
1119 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1120 		fmd_api_error(mp, EFMD_SERD_NAME,
1121 		    "failed to add events from serd engine '%s'", name);
1122 	}
1123 
1124 	(void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1125 
1126 	for (sep = fmd_list_next(&sgp->sg_list);
1127 	    sep != NULL; sep = fmd_list_next(sep))
1128 		fmd_case_insert_event(cp, sep->se_event);
1129 
1130 	mp->mod_stats->ms_accepted.fmds_value.ui64 += sgp->sg_count;
1131 	fmd_module_unlock(mp);
1132 }
1133 
1134 void
1135 fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *nvl)
1136 {
1137 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1138 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1139 	char *class;
1140 
1141 	if (cip->ci_state >= FMD_CASE_SOLVED) {
1142 		fmd_api_error(mp, EFMD_CASE_STATE, "cannot add suspect to "
1143 		    "%s: case is already solved or closed\n", cip->ci_uuid);
1144 	}
1145 
1146 	if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0 ||
1147 	    class == NULL || *class == '\0') {
1148 		fmd_api_error(mp, EFMD_CASE_EVENT, "cannot add suspect to "
1149 		    "%s: suspect event is missing a class\n", cip->ci_uuid);
1150 	}
1151 
1152 	fmd_case_insert_suspect(cp, nvl);
1153 	fmd_module_unlock(mp);
1154 }
1155 
1156 void
1157 fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data)
1158 {
1159 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1160 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1161 
1162 	(void) pthread_mutex_lock(&cip->ci_lock);
1163 	cip->ci_data = data;
1164 	(void) pthread_mutex_unlock(&cip->ci_lock);
1165 
1166 	fmd_module_unlock(mp);
1167 }
1168 
1169 void *
1170 fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp)
1171 {
1172 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1173 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1174 	void *data;
1175 
1176 	(void) pthread_mutex_lock(&cip->ci_lock);
1177 	data = cip->ci_data;
1178 	(void) pthread_mutex_unlock(&cip->ci_lock);
1179 
1180 	fmd_module_unlock(mp);
1181 	return (data);
1182 }
1183 
1184 void
1185 fmd_case_setprincipal(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
1186 {
1187 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1188 
1189 	(void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1190 	fmd_case_insert_principal(cp, ep);
1191 	mp->mod_stats->ms_accepted.fmds_value.ui64++;
1192 
1193 	fmd_module_unlock(mp);
1194 }
1195 
1196 fmd_event_t *
1197 fmd_case_getprincipal(fmd_hdl_t *hdl, fmd_case_t *cp)
1198 {
1199 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1200 	fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1201 	fmd_event_t *ep;
1202 
1203 	(void) pthread_mutex_lock(&cip->ci_lock);
1204 	ep = cip->ci_principal;
1205 	(void) pthread_mutex_unlock(&cip->ci_lock);
1206 
1207 	fmd_module_unlock(mp);
1208 	return (ep);
1209 }
1210 
1211 fmd_case_t *
1212 fmd_case_next(fmd_hdl_t *hdl, fmd_case_t *cp)
1213 {
1214 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1215 
1216 	if (cp != NULL)
1217 		cp = fmd_list_next(fmd_api_case_impl(mp, cp));
1218 	else
1219 		cp = fmd_list_next(&mp->mod_cases);
1220 
1221 	fmd_module_unlock(mp);
1222 	return (cp);
1223 }
1224 
1225 fmd_case_t *
1226 fmd_case_prev(fmd_hdl_t *hdl, fmd_case_t *cp)
1227 {
1228 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1229 
1230 	if (cp != NULL)
1231 		cp = fmd_list_prev(fmd_api_case_impl(mp, cp));
1232 	else
1233 		cp = fmd_list_prev(&mp->mod_cases);
1234 
1235 	fmd_module_unlock(mp);
1236 	return (cp);
1237 }
1238 
1239 /*
1240  * Utility function for fmd_buf_* routines.  If a case is specified, use the
1241  * case's ci_bufs hash; otherwise use the module's global mod_bufs hash.
1242  */
1243 static fmd_buf_hash_t *
1244 fmd_buf_gethash(fmd_module_t *mp, fmd_case_t *cp)
1245 {
1246 	return (cp ? &fmd_api_case_impl(mp, cp)->ci_bufs : &mp->mod_bufs);
1247 }
1248 
1249 void
1250 fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size)
1251 {
1252 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1253 	fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1254 	fmd_buf_t *bp = fmd_buf_lookup(bhp, name);
1255 
1256 	if (bp == NULL) {
1257 		if (fmd_strbadid(name, FMD_B_TRUE) != NULL || size == 0) {
1258 			fmd_api_error(mp, EFMD_BUF_INVAL, "cannot create '%s' "
1259 			    "(size %lu): %s\n", name, (ulong_t)size,
1260 			    fmd_strerror(EFMD_BUF_INVAL));
1261 		}
1262 
1263 		if (mp->mod_stats->ms_buflimit.fmds_value.ui64 -
1264 		    mp->mod_stats->ms_buftotal.fmds_value.ui64 < size) {
1265 			fmd_api_error(mp, EFMD_BUF_LIMIT, "cannot create '%s': "
1266 			    "buf limit exceeded (%llu)\n", name, (u_longlong_t)
1267 			    mp->mod_stats->ms_buflimit.fmds_value.ui64);
1268 		}
1269 
1270 		mp->mod_stats->ms_buftotal.fmds_value.ui64 += size;
1271 		bp = fmd_buf_insert(bhp, name, size);
1272 
1273 	} else {
1274 		fmd_api_error(mp, EFMD_BUF_EXISTS,
1275 		    "cannot create '%s': buffer already exists\n", name);
1276 	}
1277 
1278 	if (cp != NULL)
1279 		fmd_case_setdirty(cp);
1280 	else
1281 		fmd_module_setdirty(mp);
1282 
1283 	fmd_module_unlock(mp);
1284 }
1285 
1286 void
1287 fmd_buf_destroy(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name)
1288 {
1289 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1290 	fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1291 	fmd_buf_t *bp = fmd_buf_lookup(bhp, name);
1292 
1293 	if (bp != NULL) {
1294 		mp->mod_stats->ms_buftotal.fmds_value.ui64 -= bp->buf_size;
1295 		fmd_buf_delete(bhp, name);
1296 
1297 		if (cp != NULL)
1298 			fmd_case_setdirty(cp);
1299 		else
1300 			fmd_module_setdirty(mp);
1301 	}
1302 
1303 	fmd_module_unlock(mp);
1304 }
1305 
1306 void
1307 fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp,
1308     const char *name, void *buf, size_t size)
1309 {
1310 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1311 	fmd_buf_t *bp = fmd_buf_lookup(fmd_buf_gethash(mp, cp), name);
1312 
1313 	if (bp == NULL) {
1314 		fmd_api_error(mp, EFMD_BUF_NOENT, "no buf named '%s' is "
1315 		    "associated with %s\n", name, cp ? "case" : "module");
1316 	}
1317 
1318 	bcopy(bp->buf_data, buf, MIN(bp->buf_size, size));
1319 	if (size > bp->buf_size)
1320 		bzero((char *)buf + bp->buf_size, size - bp->buf_size);
1321 
1322 	fmd_module_unlock(mp);
1323 }
1324 
1325 void
1326 fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp,
1327     const char *name, const void *buf, size_t size)
1328 {
1329 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1330 	fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1331 	fmd_buf_t *bp = fmd_buf_lookup(bhp, name);
1332 
1333 	if (bp == NULL) {
1334 		if (fmd_strbadid(name, FMD_B_TRUE) != NULL || size == 0) {
1335 			fmd_api_error(mp, EFMD_BUF_INVAL, "cannot write '%s' "
1336 			    "(size %lu): %s\n", name, (ulong_t)size,
1337 			    fmd_strerror(EFMD_BUF_INVAL));
1338 		}
1339 
1340 		if (mp->mod_stats->ms_buflimit.fmds_value.ui64 -
1341 		    mp->mod_stats->ms_buftotal.fmds_value.ui64 < size) {
1342 			fmd_api_error(mp, EFMD_BUF_LIMIT, "cannot write '%s': "
1343 			    "buf limit exceeded (%llu)\n", name, (u_longlong_t)
1344 			    mp->mod_stats->ms_buflimit.fmds_value.ui64);
1345 		}
1346 
1347 		mp->mod_stats->ms_buftotal.fmds_value.ui64 += size;
1348 		bp = fmd_buf_insert(bhp, name, size);
1349 
1350 	} else if (size > bp->buf_size) {
1351 		fmd_api_error(mp, EFMD_BUF_OFLOW,
1352 		    "write to buf '%s' overflows buf size (%lu > %lu)\n",
1353 		    name, (ulong_t)size, (ulong_t)bp->buf_size);
1354 	}
1355 
1356 	bcopy(buf, bp->buf_data, MIN(bp->buf_size, size));
1357 	bp->buf_flags |= FMD_BUF_DIRTY;
1358 
1359 	if (cp != NULL)
1360 		fmd_case_setdirty(cp);
1361 	else
1362 		fmd_module_setdirty(mp);
1363 
1364 	fmd_module_unlock(mp);
1365 }
1366 
1367 size_t
1368 fmd_buf_size(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name)
1369 {
1370 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1371 	fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1372 
1373 	fmd_buf_t *bp;
1374 	size_t size;
1375 
1376 	if ((bp = fmd_buf_lookup(bhp, name)) != NULL)
1377 		size = bp->buf_size;
1378 	else
1379 		size = 0;
1380 
1381 	fmd_module_unlock(mp);
1382 	return (size);
1383 }
1384 
1385 void
1386 fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t)
1387 {
1388 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1389 
1390 	if (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL) {
1391 		fmd_api_error(mp, EFMD_SERD_EXISTS,
1392 		    "failed to create serd engine '%s': %s\n",
1393 		    name, fmd_strerror(EFMD_SERD_EXISTS));
1394 	}
1395 
1396 	(void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t);
1397 	fmd_module_setdirty(mp);
1398 	fmd_module_unlock(mp);
1399 }
1400 
1401 void
1402 fmd_serd_destroy(fmd_hdl_t *hdl, const char *name)
1403 {
1404 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1405 
1406 	fmd_serd_eng_delete(&mp->mod_serds, name);
1407 	fmd_module_setdirty(mp);
1408 	fmd_module_unlock(mp);
1409 }
1410 
1411 int
1412 fmd_serd_exists(fmd_hdl_t *hdl, const char *name)
1413 {
1414 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1415 	int rv = (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL);
1416 	fmd_module_unlock(mp);
1417 
1418 	return (rv);
1419 }
1420 
1421 void
1422 fmd_serd_reset(fmd_hdl_t *hdl, const char *name)
1423 {
1424 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1425 	fmd_serd_eng_t *sgp;
1426 
1427 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1428 		fmd_api_error(mp, EFMD_SERD_NAME,
1429 		    "serd engine '%s' does not exist\n", name);
1430 	}
1431 
1432 	fmd_serd_eng_reset(sgp);
1433 	fmd_module_setdirty(mp);
1434 	fmd_module_unlock(mp);
1435 }
1436 
1437 int
1438 fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep)
1439 {
1440 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1441 	fmd_serd_eng_t *sgp;
1442 	int err;
1443 
1444 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1445 		fmd_api_error(mp, EFMD_SERD_NAME,
1446 		    "failed to add record to serd engine '%s'", name);
1447 	}
1448 
1449 	err = fmd_serd_eng_record(sgp, ep);
1450 
1451 	if (sgp->sg_flags & FMD_SERD_DIRTY)
1452 		fmd_module_setdirty(mp);
1453 
1454 	fmd_module_unlock(mp);
1455 	return (err);
1456 }
1457 
1458 int
1459 fmd_serd_fired(fmd_hdl_t *hdl, const char *name)
1460 {
1461 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1462 	fmd_serd_eng_t *sgp;
1463 	int err;
1464 
1465 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1466 		fmd_api_error(mp, EFMD_SERD_NAME,
1467 		    "serd engine '%s' does not exist\n", name);
1468 	}
1469 
1470 	err = fmd_serd_eng_fired(sgp);
1471 	fmd_module_unlock(mp);
1472 	return (err);
1473 }
1474 
1475 int
1476 fmd_serd_empty(fmd_hdl_t *hdl, const char *name)
1477 {
1478 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1479 	fmd_serd_eng_t *sgp;
1480 	int empty;
1481 
1482 	if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1483 		fmd_api_error(mp, EFMD_SERD_NAME,
1484 		    "serd engine '%s' does not exist\n", name);
1485 	}
1486 
1487 	empty = fmd_serd_eng_empty(sgp);
1488 	fmd_module_unlock(mp);
1489 	return (empty);
1490 }
1491 
1492 pthread_t
1493 fmd_thr_create(fmd_hdl_t *hdl, void (*func)(void *), void *arg)
1494 {
1495 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1496 	fmd_thread_t *tp;
1497 	pthread_t tid;
1498 
1499 	if (mp->mod_stats->ms_thrtotal.fmds_value.ui32 >=
1500 	    mp->mod_stats->ms_thrlimit.fmds_value.ui32) {
1501 		fmd_api_error(mp, EFMD_THR_LIMIT, "%s request to create an "
1502 		    "auxiliary thread exceeds module thread limit (%u)\n",
1503 		    mp->mod_name, mp->mod_stats->ms_thrlimit.fmds_value.ui32);
1504 	}
1505 
1506 	if ((tp = fmd_thread_create(mp, func, arg)) == NULL) {
1507 		fmd_api_error(mp, EFMD_THR_CREATE,
1508 		    "failed to create auxiliary thread");
1509 	}
1510 
1511 	tid = tp->thr_tid;
1512 	mp->mod_stats->ms_thrtotal.fmds_value.ui32++;
1513 	(void) fmd_idspace_xalloc(mp->mod_threads, tid, tp);
1514 
1515 	fmd_module_unlock(mp);
1516 	return (tid);
1517 }
1518 
1519 void
1520 fmd_thr_destroy(fmd_hdl_t *hdl, pthread_t tid)
1521 {
1522 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1523 	fmd_thread_t *tp;
1524 	int err;
1525 
1526 	if (pthread_self() == tid) {
1527 		fmd_api_error(mp, EFMD_THR_INVAL, "auxiliary thread tried to "
1528 		    "destroy itself (tid %u)\n", tid);
1529 	}
1530 
1531 	if ((tp = fmd_idspace_getspecific(mp->mod_threads, tid)) == NULL) {
1532 		fmd_api_error(mp, EFMD_THR_INVAL, "auxiliary thread tried to "
1533 		    "destroy an invalid thread (tid %u)\n", tid);
1534 	}
1535 
1536 	/*
1537 	 * Wait for the specified thread to exit and then join with it.  Since
1538 	 * the thread may need to make API calls in order to complete its work
1539 	 * we must sleep with the module lock unheld, and then reacquire it.
1540 	 */
1541 	fmd_module_unlock(mp);
1542 	err = pthread_join(tid, NULL);
1543 	mp = fmd_api_module_lock(hdl);
1544 
1545 	/*
1546 	 * Since pthread_join() was called without the module lock held, if
1547 	 * multiple callers attempted to destroy the same auxiliary thread
1548 	 * simultaneously, one will succeed and the others will get ESRCH.
1549 	 * Therefore we silently ignore ESRCH but only allow the caller who
1550 	 * succeessfully joined with the auxiliary thread to destroy it.
1551 	 */
1552 	if (err != 0 && err != ESRCH) {
1553 		fmd_api_error(mp, EFMD_THR_JOIN,
1554 		    "failed to join with auxiliary thread %u\n", tid);
1555 	}
1556 
1557 	if (err == 0) {
1558 		fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
1559 		mp->mod_stats->ms_thrtotal.fmds_value.ui32--;
1560 		(void) fmd_idspace_free(mp->mod_threads, tid);
1561 	}
1562 
1563 	fmd_module_unlock(mp);
1564 }
1565 
1566 void
1567 fmd_thr_signal(fmd_hdl_t *hdl, pthread_t tid)
1568 {
1569 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1570 
1571 	if (tid != mp->mod_thread->thr_tid &&
1572 	    fmd_idspace_getspecific(mp->mod_threads, tid) == NULL) {
1573 		fmd_api_error(mp, EFMD_THR_INVAL, "tid %u is not a valid "
1574 		    "thread id for module %s\n", tid, mp->mod_name);
1575 	}
1576 
1577 	(void) pthread_kill(tid, fmd.d_thr_sig);
1578 	fmd_module_unlock(mp);
1579 }
1580 
1581 id_t
1582 fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
1583 {
1584 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1585 	fmd_modtimer_t *t;
1586 	id_t id;
1587 
1588 	if (delta < 0) {
1589 		fmd_api_error(mp, EFMD_TIMER_INVAL,
1590 		    "timer delta %lld is not a valid interval\n", delta);
1591 	}
1592 
1593 	t = fmd_alloc(sizeof (fmd_modtimer_t), FMD_SLEEP);
1594 	t->mt_mod = mp;
1595 	t->mt_arg = arg;
1596 	t->mt_id = -1;
1597 
1598 	if ((id = fmd_timerq_install(fmd.d_timers, mp->mod_timerids,
1599 	    (fmd_timer_f *)fmd_module_timeout, t, ep, delta)) == -1) {
1600 		fmd_free(t, sizeof (fmd_modtimer_t));
1601 		fmd_api_error(mp, EFMD_TIMER_LIMIT,
1602 		    "failed to install timer +%lld", delta);
1603 	}
1604 
1605 	fmd_module_unlock(mp);
1606 	return (id);
1607 }
1608 
1609 void
1610 fmd_timer_remove(fmd_hdl_t *hdl, id_t id)
1611 {
1612 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1613 	fmd_modtimer_t *t;
1614 
1615 	if (!fmd_idspace_valid(mp->mod_timerids, id)) {
1616 		fmd_api_error(mp, EFMD_TIMER_INVAL,
1617 		    "id %ld is not a valid timer id\n", id);
1618 	}
1619 
1620 	t = fmd_timerq_remove(fmd.d_timers, mp->mod_timerids, id);
1621 	fmd_module_unlock(mp);
1622 
1623 	if (t != NULL) {
1624 		fmd_eventq_cancel(mp->mod_queue, FMD_EVT_TIMEOUT, t);
1625 		fmd_free(t, sizeof (fmd_modtimer_t));
1626 	}
1627 }
1628 
1629 nvlist_t *
1630 fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class,
1631     uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
1632 {
1633 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1634 	nvlist_t *nvl;
1635 
1636 	if (class == NULL || class[0] == '\0')
1637 		fmd_api_error(mp, EFMD_NVL_INVAL, "invalid fault class\n");
1638 
1639 	nvl = fmd_protocol_fault(class, certainty, asru, fru, rsrc);
1640 	fmd_module_unlock(mp);
1641 	return (nvl);
1642 }
1643 
1644 int
1645 fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
1646 {
1647 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1648 	char *class;
1649 	int rv;
1650 
1651 	rv = (nvl != NULL && nvlist_lookup_string(nvl,
1652 	    FM_CLASS, &class) == 0 && fmd_strmatch(class, pattern));
1653 
1654 	fmd_module_unlock(mp);
1655 	return (rv);
1656 }
1657 
1658 int
1659 fmd_nvl_fmri_expand(fmd_hdl_t *hdl, nvlist_t *nvl)
1660 {
1661 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1662 	int rv;
1663 
1664 	if (nvl == NULL) {
1665 		fmd_api_error(mp, EFMD_NVL_INVAL,
1666 		    "invalid nvlist %p\n", (void *)nvl);
1667 	}
1668 
1669 	rv = fmd_fmri_expand(nvl);
1670 	fmd_module_unlock(mp);
1671 	return (rv);
1672 }
1673 
1674 int
1675 fmd_nvl_fmri_present(fmd_hdl_t *hdl, nvlist_t *nvl)
1676 {
1677 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1678 	int rv;
1679 
1680 	if (nvl == NULL) {
1681 		fmd_api_error(mp, EFMD_NVL_INVAL,
1682 		    "invalid nvlist %p\n", (void *)nvl);
1683 	}
1684 
1685 	rv = fmd_fmri_present(nvl);
1686 	fmd_module_unlock(mp);
1687 
1688 	if (rv < 0) {
1689 		fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
1690 		    "fmd_nvl_fmri_present\n");
1691 	}
1692 
1693 	return (rv);
1694 }
1695 
1696 int
1697 fmd_nvl_fmri_unusable(fmd_hdl_t *hdl, nvlist_t *nvl)
1698 {
1699 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1700 	int rv;
1701 
1702 	if (nvl == NULL) {
1703 		fmd_api_error(mp, EFMD_NVL_INVAL,
1704 		    "invalid nvlist %p\n", (void *)nvl);
1705 	}
1706 
1707 	rv = fmd_fmri_unusable(nvl);
1708 	fmd_module_unlock(mp);
1709 
1710 	if (rv < 0) {
1711 		fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
1712 		    "fmd_nvl_fmri_unusable\n");
1713 	}
1714 
1715 	return (rv);
1716 }
1717 
1718 int
1719 fmd_nvl_fmri_contains(fmd_hdl_t *hdl, nvlist_t *n1, nvlist_t *n2)
1720 {
1721 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1722 	int rv;
1723 
1724 	if (n1 == NULL || n2 == NULL) {
1725 		fmd_api_error(mp, EFMD_NVL_INVAL,
1726 		    "invalid nvlist(s): %p, %p\n", (void *)n1, (void *)n2);
1727 	}
1728 
1729 	rv = fmd_fmri_contains(n1, n2);
1730 	fmd_module_unlock(mp);
1731 
1732 	if (rv < 0) {
1733 		fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
1734 		    "fmd_nvl_fmri_contains\n");
1735 	}
1736 
1737 	return (rv);
1738 }
1739 
1740 nvlist_t *
1741 fmd_nvl_fmri_translate(fmd_hdl_t *hdl, nvlist_t *fmri, nvlist_t *auth)
1742 {
1743 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1744 	nvlist_t *xfmri;
1745 
1746 	if (fmri == NULL || auth == NULL) {
1747 		fmd_api_error(mp, EFMD_NVL_INVAL,
1748 		    "invalid nvlist(s): %p, %p\n", (void *)fmri, (void *)auth);
1749 	}
1750 
1751 	xfmri = fmd_fmri_translate(fmri, auth);
1752 	fmd_module_unlock(mp);
1753 	return (xfmri);
1754 }
1755 
1756 int
1757 fmd_event_local(fmd_hdl_t *hdl, fmd_event_t *ep)
1758 {
1759 	if (hdl == NULL || ep == NULL) {
1760 		fmd_api_error(fmd_api_module_lock(hdl), EFMD_EVENT_INVAL,
1761 		    "NULL parameter specified to fmd_event_local\n");
1762 	}
1763 
1764 	return (((fmd_event_impl_t *)ep)->ev_flags & FMD_EVF_LOCAL);
1765 }
1766 
1767 fmd_xprt_t *
1768 fmd_xprt_open(fmd_hdl_t *hdl, uint_t flags, nvlist_t *auth, void *data)
1769 {
1770 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1771 	fmd_xprt_t *xp;
1772 
1773 	if (flags & ~FMD_XPRT_CMASK) {
1774 		fmd_api_error(mp, EFMD_XPRT_INVAL,
1775 		    "invalid transport flags 0x%x\n", flags);
1776 	}
1777 
1778 	if ((flags & FMD_XPRT_RDWR) != FMD_XPRT_RDWR &&
1779 	    (flags & FMD_XPRT_RDWR) != FMD_XPRT_RDONLY) {
1780 		fmd_api_error(mp, EFMD_XPRT_INVAL,
1781 		    "cannot open write-only transport\n");
1782 	}
1783 
1784 	if (mp->mod_stats->ms_xprtopen.fmds_value.ui32 >=
1785 	    mp->mod_stats->ms_xprtlimit.fmds_value.ui32) {
1786 		fmd_api_error(mp, EFMD_XPRT_LIMIT, "%s request to create a "
1787 		    "transport exceeds module transport limit (%u)\n",
1788 		    mp->mod_name, mp->mod_stats->ms_xprtlimit.fmds_value.ui32);
1789 	}
1790 
1791 	if ((xp = fmd_xprt_create(mp, flags, auth, data)) == NULL)
1792 		fmd_api_error(mp, errno, "cannot create transport");
1793 
1794 	fmd_module_unlock(mp);
1795 	return (xp);
1796 }
1797 
1798 void
1799 fmd_xprt_close(fmd_hdl_t *hdl, fmd_xprt_t *xp)
1800 {
1801 	fmd_module_t *mp = fmd_api_module_lock(hdl);
1802 	fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
1803 
1804 	/*
1805 	 * Although this could be supported, it doesn't seem necessary or worth
1806 	 * the trouble.  For now, just detect this and trigger a module abort.
1807 	 * If it is needed, transports should grow reference counts and a new
1808 	 * event type will need to be enqueued for the main thread to reap it.
1809 	 */
1810 	if (xip->xi_thread != NULL &&
1811 	    xip->xi_thread->thr_tid == pthread_self()) {
1812 		fmd_api_error(mp, EFMD_XPRT_INVAL,
1813 		    "fmd_xprt_close() cannot be called from fmdo_send()\n");
1814 	}
1815 
1816 	fmd_xprt_destroy(xp);
1817 	fmd_module_unlock(mp);
1818 }
1819 
1820 void
1821 fmd_xprt_post(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt)
1822 {
1823 	fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
1824 
1825 	/*
1826 	 * fmd_xprt_recv() must block during startup waiting for fmd to globally
1827 	 * clear FMD_XPRT_DSUSPENDED.  As such, we can't allow it to be called
1828 	 * from a module's _fmd_init() routine, because that would block
1829 	 * fmd from completing initial module loading, resulting in a deadlock.
1830 	 */
1831 	if ((xip->xi_flags & FMD_XPRT_ISUSPENDED) &&
1832 	    (pthread_self() == xip->xi_queue->eq_mod->mod_thread->thr_tid)) {
1833 		fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL,
1834 		    "fmd_xprt_post() cannot be called from _fmd_init()\n");
1835 	}
1836 
1837 	fmd_xprt_recv(xp, nvl, hrt);
1838 }
1839 
1840 void
1841 fmd_xprt_suspend(fmd_hdl_t *hdl, fmd_xprt_t *xp)
1842 {
1843 	(void) fmd_api_transport_impl(hdl, xp); /* validate 'xp' */
1844 	fmd_xprt_xsuspend(xp, FMD_XPRT_SUSPENDED);
1845 }
1846 
1847 void
1848 fmd_xprt_resume(fmd_hdl_t *hdl, fmd_xprt_t *xp)
1849 {
1850 	(void) fmd_api_transport_impl(hdl, xp); /* validate 'xp' */
1851 	fmd_xprt_xresume(xp, FMD_XPRT_SUSPENDED);
1852 }
1853 
1854 int
1855 fmd_xprt_error(fmd_hdl_t *hdl, fmd_xprt_t *xp)
1856 {
1857 	fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
1858 	return (xip->xi_state == _fmd_xprt_state_err);
1859 }
1860 
1861 /*
1862  * Translate all FMRIs in the specified name-value pair list for the specified
1863  * FMRI authority, and return a new name-value pair list for the translation.
1864  * This function is the recursive engine used by fmd_xprt_translate(), below.
1865  */
1866 static nvlist_t *
1867 fmd_xprt_xtranslate(nvlist_t *nvl, nvlist_t *auth)
1868 {
1869 	uint_t i, j, n;
1870 	nvpair_t *nvp, **nvps;
1871 	uint_t nvpslen = 0;
1872 	char *name;
1873 	size_t namelen = 0;
1874 
1875 	nvlist_t **a, **b;
1876 	nvlist_t *l, *r;
1877 	data_type_t type;
1878 	char *s;
1879 	int err;
1880 
1881 	(void) nvlist_xdup(nvl, &nvl, &fmd.d_nva);
1882 
1883 	/*
1884 	 * Count up the number of name-value pairs in 'nvl' and compute the
1885 	 * maximum length of a name used in this list for use below.
1886 	 */
1887 	for (nvp = nvlist_next_nvpair(nvl, NULL);
1888 	    nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp), nvpslen++) {
1889 		size_t len = strlen(nvpair_name(nvp));
1890 		namelen = MAX(namelen, len);
1891 	}
1892 
1893 	nvps = alloca(sizeof (nvpair_t *) * nvpslen);
1894 	name = alloca(namelen + 1);
1895 
1896 	/*
1897 	 * Store a snapshot of the name-value pairs in 'nvl' into nvps[] so
1898 	 * that we can iterate over the original pairs in the loop below while
1899 	 * performing arbitrary insert and delete operations on 'nvl' itself.
1900 	 */
1901 	for (i = 0, nvp = nvlist_next_nvpair(nvl, NULL);
1902 	    nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp))
1903 		nvps[i++] = nvp;
1904 
1905 	/*
1906 	 * Now iterate over the snapshot of the name-value pairs.  If we find a
1907 	 * value that is of type NVLIST or NVLIST_ARRAY, we translate that
1908 	 * object by either calling ourself recursively on it, or calling into
1909 	 * fmd_fmri_translate() if the object is an FMRI.  We then rip out the
1910 	 * original name-value pair and replace it with the translated one.
1911 	 */
1912 	for (i = 0; i < nvpslen; i++) {
1913 		nvp = nvps[i];
1914 		type = nvpair_type(nvp);
1915 
1916 		switch (type) {
1917 		case DATA_TYPE_NVLIST_ARRAY:
1918 			if (nvpair_value_nvlist_array(nvp, &a, &n) != 0 ||
1919 			    a == NULL || n == 0)
1920 				continue; /* array is zero-sized; skip it */
1921 
1922 			b = fmd_alloc(sizeof (nvlist_t *) * n, FMD_SLEEP);
1923 
1924 			/*
1925 			 * If the first array nvlist element looks like an FMRI
1926 			 * then assume the other elements are FMRIs as well.
1927 			 * If any b[j]'s can't be translated, then EINVAL will
1928 			 * be returned from nvlist_add_nvlist_array() below.
1929 			 */
1930 			if (nvlist_lookup_string(*a, FM_FMRI_SCHEME, &s) == 0) {
1931 				for (j = 0; j < n; j++)
1932 					b[j] = fmd_fmri_translate(a[j], auth);
1933 			} else {
1934 				for (j = 0; j < n; j++)
1935 					b[j] = fmd_xprt_xtranslate(a[j], auth);
1936 			}
1937 
1938 			(void) strcpy(name, nvpair_name(nvp));
1939 			(void) nvlist_remove(nvl, name, type);
1940 			err = nvlist_add_nvlist_array(nvl, name, b, n);
1941 
1942 			for (j = 0; j < n; j++)
1943 				nvlist_free(b[j]);
1944 
1945 			fmd_free(b, sizeof (nvlist_t *) * n);
1946 
1947 			if (err != 0) {
1948 				nvlist_free(nvl);
1949 				errno = err;
1950 				return (NULL);
1951 			}
1952 			break;
1953 
1954 		case DATA_TYPE_NVLIST:
1955 			if (nvpair_value_nvlist(nvp, &l) == 0 &&
1956 			    nvlist_lookup_string(l, FM_FMRI_SCHEME, &s) == 0)
1957 				r = fmd_fmri_translate(l, auth);
1958 			else
1959 				r = fmd_xprt_xtranslate(l, auth);
1960 
1961 			if (r == NULL) {
1962 				nvlist_free(nvl);
1963 				return (NULL);
1964 			}
1965 
1966 			(void) strcpy(name, nvpair_name(nvp));
1967 			(void) nvlist_remove(nvl, name, type);
1968 			(void) nvlist_add_nvlist(nvl, name, r);
1969 
1970 			nvlist_free(r);
1971 			break;
1972 		}
1973 	}
1974 
1975 	return (nvl);
1976 }
1977 
1978 nvlist_t *
1979 fmd_xprt_translate(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep)
1980 {
1981 	fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
1982 
1983 	if (xip->xi_auth == NULL) {
1984 		fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL,
1985 		    "no authority defined for transport %p\n", (void *)xp);
1986 	}
1987 
1988 	return (fmd_xprt_xtranslate(FMD_EVENT_NVL(ep), xip->xi_auth));
1989 }
1990 
1991 void
1992 fmd_xprt_setspecific(fmd_hdl_t *hdl, fmd_xprt_t *xp, void *data)
1993 {
1994 	fmd_api_transport_impl(hdl, xp)->xi_data = data;
1995 }
1996 
1997 void *
1998 fmd_xprt_getspecific(fmd_hdl_t *hdl, fmd_xprt_t *xp)
1999 {
2000 	return (fmd_api_transport_impl(hdl, xp)->xi_data);
2001 }
2002