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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
25 */
26
27 #include <sys/types.h>
28 #include <sys/fm/protocol.h>
29 #include <fm/topo_hc.h>
30 #include <uuid/uuid.h>
31
32 #include <unistd.h>
33 #include <signal.h>
34 #include <limits.h>
35 #include <syslog.h>
36 #include <alloca.h>
37 #include <stddef.h>
38 #include <door.h>
39
40 #include <fmd_module.h>
41 #include <fmd_api.h>
42 #include <fmd_string.h>
43 #include <fmd_subr.h>
44 #include <fmd_error.h>
45 #include <fmd_event.h>
46 #include <fmd_eventq.h>
47 #include <fmd_dispq.h>
48 #include <fmd_timerq.h>
49 #include <fmd_thread.h>
50 #include <fmd_ustat.h>
51 #include <fmd_case.h>
52 #include <fmd_protocol.h>
53 #include <fmd_buf.h>
54 #include <fmd_asru.h>
55 #include <fmd_fmri.h>
56 #include <fmd_topo.h>
57 #include <fmd_ckpt.h>
58 #include <fmd_xprt.h>
59
60 #include <fmd.h>
61
62 /*
63 * Table of configuration file variable types ops-vector pointers. We use this
64 * to convert from the property description array specified by the module to an
65 * array of fmd_conf_formal_t's. The order of this array must match the order
66 * of #define values specified in <fmd_api.h> (i.e. FMD_TYPE_BOOL must be 0).
67 * For now, the fmd_conf_list and fmd_conf_path types are not supported as we
68 * do not believe modules need them and they would require more complexity.
69 */
70 static const fmd_conf_ops_t *const _fmd_prop_ops[] = {
71 &fmd_conf_bool, /* FMD_TYPE_BOOL */
72 &fmd_conf_int32, /* FMD_TYPE_INT32 */
73 &fmd_conf_uint32, /* FMD_TYPE_UINT32 */
74 &fmd_conf_int64, /* FMD_TYPE_INT64 */
75 &fmd_conf_uint64, /* FMD_TYPE_UINT64 */
76 &fmd_conf_string, /* FMD_TYPE_STRING */
77 &fmd_conf_time, /* FMD_TYPE_TIME */
78 &fmd_conf_size, /* FMD_TYPE_SIZE */
79 };
80
81 static void fmd_api_verror(fmd_module_t *, int, const char *, va_list)
82 __NORETURN;
83 static void fmd_api_error(fmd_module_t *, int, const char *, ...) __NORETURN;
84
85 /*
86 * fmd_api_vxerror() provides the engine underlying the fmd_hdl_[v]error() API
87 * calls and the fmd_api_[v]error() utility routine defined below. The routine
88 * formats the error, optionally associated with a particular errno code 'err',
89 * and logs it as an ereport associated with the calling module. Depending on
90 * other optional properties, we also emit a message to stderr and to syslog.
91 */
92 static void
93 fmd_api_vxerror(fmd_module_t *mp, int err, const char *format, va_list ap)
94 {
95 int raw_err = err;
96 nvlist_t *nvl;
97 fmd_event_t *e;
98 char *class, *msg;
99 size_t len1, len2;
100 char c;
101
102 /*
103 * fmd_api_vxerror() counts as both an error of class EFMD_MODULE
104 * as well as an instance of 'err' w.r.t. our internal bean counters.
105 */
106 (void) pthread_mutex_lock(&fmd.d_err_lock);
107 fmd.d_errstats[EFMD_MODULE - EFMD_UNKNOWN].fmds_value.ui64++;
108
109 if (err > EFMD_UNKNOWN && err < EFMD_END)
110 fmd.d_errstats[err - EFMD_UNKNOWN].fmds_value.ui64++;
111
112 (void) pthread_mutex_unlock(&fmd.d_err_lock);
113
114 /*
115 * Format the message using vsnprintf(). As usual, if the format has a
116 * newline in it, it is printed alone; otherwise strerror() is added.
117 */
118 if (strchr(format, '\n') != NULL)
119 err = 0; /* err is not relevant in the message */
120
121 len1 = vsnprintf(&c, 1, format, ap);
122 len2 = err != 0 ? snprintf(&c, 1, ": %s\n", fmd_strerror(err)) : 0;
123
124 msg = fmd_alloc(len1 + len2 + 1, FMD_SLEEP);
125 (void) vsnprintf(msg, len1 + 1, format, ap);
126
127 if (err != 0) {
128 (void) snprintf(&msg[len1], len2 + 1,
129 ": %s\n", fmd_strerror(err));
130 }
131
132 /*
133 * Create an error event corresponding to the error, insert it into the
134 * error log, and dispatch it to the fmd-self-diagnosis engine.
135 */
136 if (mp != fmd.d_self && (raw_err != EFMD_HDL_ABORT || fmd.d_running)) {
137 if ((c = msg[len1 + len2 - 1]) == '\n')
138 msg[len1 + len2 - 1] = '\0'; /* strip \n for event */
139
140 nvl = fmd_protocol_moderror(mp, err, msg);
141
142 if (c == '\n')
143 msg[len1 + len2 - 1] = c;
144
145 (void) nvlist_lookup_string(nvl, FM_CLASS, &class);
146 e = fmd_event_create(FMD_EVT_PROTOCOL, FMD_HRT_NOW, nvl, class);
147
148 (void) pthread_rwlock_rdlock(&fmd.d_log_lock);
149 fmd_log_append(fmd.d_errlog, e, NULL);
150 (void) pthread_rwlock_unlock(&fmd.d_log_lock);
151
152 fmd_event_transition(e, FMD_EVS_ACCEPTED);
153 fmd_event_commit(e);
154
155 fmd_dispq_dispatch(fmd.d_disp, e, class);
156 }
157
158 /*
159 * Similar to fmd_vdebug(), if the debugging switches are enabled we
160 * echo the module name and message to stderr and/or syslog. Unlike
161 * fmd_vdebug(), we also print to stderr if foreground mode is enabled.
162 * We also print the message if a built-in module is aborting before
163 * fmd has detached from its parent (e.g. default transport failure).
164 */
165 if (fmd.d_fg || (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) || (
166 raw_err == EFMD_HDL_ABORT && !fmd.d_running)) {
167 (void) pthread_mutex_lock(&fmd.d_err_lock);
168 (void) fprintf(stderr, "%s: %s: %s",
169 fmd.d_pname, mp->mod_name, msg);
170 (void) pthread_mutex_unlock(&fmd.d_err_lock);
171 }
172
173 if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) {
174 syslog(LOG_ERR | LOG_DAEMON, "%s ERROR: %s: %s",
175 fmd.d_pname, mp->mod_name, msg);
176 }
177
178 fmd_free(msg, len1 + len2 + 1);
179 }
180
181 /*PRINTFLIKE3*/
182 static void
183 fmd_api_xerror(fmd_module_t *mp, int err, const char *format, ...)
184 {
185 va_list ap;
186
187 va_start(ap, format);
188 fmd_api_vxerror(mp, err, format, ap);
189 va_end(ap);
190 }
191
192 /*
193 * fmd_api_verror() is a wrapper around fmd_api_vxerror() for API subroutines.
194 * It calls fmd_module_unlock() on behalf of its caller, logs the error, and
195 * then aborts the API call and the surrounding module entry point by doing an
196 * fmd_module_abort(), which longjmps to the place where we entered the module.
197 */
198 static void
199 fmd_api_verror(fmd_module_t *mp, int err, const char *format, va_list ap)
200 {
201 if (fmd_module_locked(mp))
202 fmd_module_unlock(mp);
203
204 fmd_api_vxerror(mp, err, format, ap);
205 fmd_module_abort(mp, err);
206 }
207
208 /*PRINTFLIKE3*/
209 static void
210 fmd_api_error(fmd_module_t *mp, int err, const char *format, ...)
211 {
212 va_list ap;
213
214 va_start(ap, format);
215 fmd_api_verror(mp, err, format, ap);
216 va_end(ap);
217 }
218
219 /*
220 * Common code for fmd_api_module_lock() and fmd_api_transport_impl(). This
221 * code verifies that the handle is valid and associated with a proper thread.
222 */
223 static fmd_module_t *
224 fmd_api_module(fmd_hdl_t *hdl)
225 {
226 fmd_thread_t *tp;
227 fmd_module_t *mp;
228
229 /*
230 * If our TSD is not present at all, this is either a serious bug or
231 * someone has created a thread behind our back and is using fmd's API.
232 * We can't call fmd_api_error() because we can't be sure that we can
233 * unwind our state back to an enclosing fmd_module_dispatch(), so we
234 * must panic instead. This is likely a module design or coding error.
235 */
236 if ((tp = pthread_getspecific(fmd.d_key)) == NULL) {
237 fmd_panic("fmd module api call made using "
238 "client handle %p from unknown thread\n", (void *)hdl);
239 }
240
241 /*
242 * If our TSD refers to the root module and is a non-private
243 * door server thread, then it was created asynchronously at the
244 * request of a module but is using now the module API as an
245 * auxiliary module thread. We reset tp->thr_mod to the module
246 * handle so it can act as a module thread.
247 *
248 * If more than one module uses non-private doors then the
249 * "client handle is not valid" check below can fail since
250 * door server threads for such doors can service *any*
251 * non-private door. We use non-private door for legacy sysevent
252 * alone.
253 */
254 if (tp->thr_mod == fmd.d_rmod && tp->thr_func == &fmd_door_server)
255 tp->thr_mod = (fmd_module_t *)hdl;
256
257 if ((mp = tp->thr_mod) != (fmd_module_t *)hdl) {
258 fmd_api_error(mp, EFMD_HDL_INVAL,
259 "client handle %p is not valid\n", (void *)hdl);
260 }
261
262 if (mp->mod_flags & FMD_MOD_FAIL) {
263 fmd_api_error(mp, EFMD_MOD_FAIL,
264 "module has experienced an unrecoverable error\n");
265 }
266
267 return (mp);
268 }
269
270 /*
271 * fmd_api_module_lock() is used as a wrapper around fmd_module_lock() and a
272 * common prologue to each fmd_api.c routine. It verifies that the handle is
273 * valid and owned by the current server thread, locks the handle, and then
274 * verifies that the caller is performing an operation on a registered handle.
275 * If any tests fail, the entire API call is aborted by fmd_api_error().
276 */
277 static fmd_module_t *
278 fmd_api_module_lock(fmd_hdl_t *hdl)
279 {
280 fmd_module_t *mp = fmd_api_module(hdl);
281
282 fmd_module_lock(mp);
283
284 if (mp->mod_info == NULL) {
285 fmd_api_error(mp, EFMD_HDL_NOTREG,
286 "client handle %p has not been registered\n", (void *)hdl);
287 }
288
289 return (mp);
290 }
291
292 /*
293 * Utility function for API entry points that accept fmd_case_t's. We cast cp
294 * to fmd_case_impl_t and check to make sure the case is owned by the caller.
295 */
296 static fmd_case_impl_t *
297 fmd_api_case_impl(fmd_module_t *mp, fmd_case_t *cp)
298 {
299 fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
300
301 if (cip == NULL || cip->ci_mod != mp) {
302 fmd_api_error(mp, EFMD_CASE_OWNER,
303 "case %p is invalid or not owned by caller\n", (void *)cip);
304 }
305
306 return (cip);
307 }
308
309 /*
310 * Utility function for API entry points that accept fmd_xprt_t's. We cast xp
311 * to fmd_transport_t and check to make sure the case is owned by the caller.
312 * Note that we could make this check safer by actually walking mp's transport
313 * list, but that requires holding the module lock and this routine needs to be
314 * MT-hot w.r.t. auxiliary module threads. Ultimately any loadable module can
315 * cause us to crash anyway, so we optimize for scalability over safety here.
316 */
317 static fmd_xprt_impl_t *
318 fmd_api_transport_impl(fmd_hdl_t *hdl, fmd_xprt_t *xp)
319 {
320 fmd_module_t *mp = fmd_api_module(hdl);
321 fmd_xprt_impl_t *xip = (fmd_xprt_impl_t *)xp;
322
323 if (xip == NULL || xip->xi_queue->eq_mod != mp) {
324 fmd_api_error(mp, EFMD_XPRT_OWNER,
325 "xprt %p is invalid or not owned by caller\n", (void *)xp);
326 }
327
328 return (xip);
329 }
330
331 /*
332 * fmd_hdl_register() is the one function which cannot use fmd_api_error() to
333 * report errors, because that routine causes the module to abort. Failure to
334 * register is instead handled by having fmd_hdl_register() return an error to
335 * the _fmd_init() function and then detecting no registration when it returns.
336 * So we use this routine for fmd_hdl_register() error paths instead.
337 */
338 static int
339 fmd_hdl_register_error(fmd_module_t *mp, int err)
340 {
341 if (fmd_module_locked(mp))
342 fmd_module_unlock(mp);
343
344 fmd_api_xerror(mp, err, "failed to register");
345 return (fmd_set_errno(err));
346 }
347
348 static void
349 fmd_hdl_nop(void)
350 {
351 /* empty function for use with unspecified module entry points */
352 }
353
354 int
355 fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
356 {
357 fmd_thread_t *tp = pthread_getspecific(fmd.d_key);
358 fmd_module_t *mp = tp->thr_mod;
359
360 const fmd_prop_t *prop;
361 const fmd_conf_path_t *pap;
362 fmd_conf_formal_t *cfp;
363 fmd_hdl_ops_t ops;
364
365 const char *conf = NULL;
366 char buf[PATH_MAX];
367 int i;
368
369 if (mp != (fmd_module_t *)hdl)
370 return (fmd_hdl_register_error(mp, EFMD_HDL_INVAL));
371
372 fmd_module_lock(mp);
373
374 /*
375 * First perform some sanity checks on our input. The API version must
376 * be supported by FMD and the handle can only be registered once by
377 * the module thread to which we assigned this client handle. The info
378 * provided for the handle must be valid and have the minimal settings.
379 */
380 if (version > FMD_API_VERSION_5)
381 return (fmd_hdl_register_error(mp, EFMD_VER_NEW));
382
383 if (version < FMD_API_VERSION_1)
384 return (fmd_hdl_register_error(mp, EFMD_VER_OLD));
385
386 if (mp->mod_conf != NULL)
387 return (fmd_hdl_register_error(mp, EFMD_HDL_REG));
388
389 if (pthread_self() != mp->mod_thread->thr_tid)
390 return (fmd_hdl_register_error(mp, EFMD_HDL_TID));
391
392 if (mip == NULL || mip->fmdi_desc == NULL ||
393 mip->fmdi_vers == NULL || mip->fmdi_ops == NULL)
394 return (fmd_hdl_register_error(mp, EFMD_HDL_INFO));
395
396 /*
397 * Copy the module's ops vector into a local variable to account for
398 * changes in the module ABI. Then if any of the optional entry points
399 * are NULL, set them to nop so we don't have to check before calling.
400 */
401 bzero(&ops, sizeof (ops));
402
403 if (version < FMD_API_VERSION_3)
404 bcopy(mip->fmdi_ops, &ops, offsetof(fmd_hdl_ops_t, fmdo_send));
405 else if (version < FMD_API_VERSION_4)
406 bcopy(mip->fmdi_ops, &ops,
407 offsetof(fmd_hdl_ops_t, fmdo_topo));
408 else
409 bcopy(mip->fmdi_ops, &ops, sizeof (ops));
410
411 if (ops.fmdo_recv == NULL)
412 ops.fmdo_recv = (void (*)())fmd_hdl_nop;
413 if (ops.fmdo_timeout == NULL)
414 ops.fmdo_timeout = (void (*)())fmd_hdl_nop;
415 if (ops.fmdo_close == NULL)
416 ops.fmdo_close = (void (*)())fmd_hdl_nop;
417 if (ops.fmdo_stats == NULL)
418 ops.fmdo_stats = (void (*)())fmd_hdl_nop;
419 if (ops.fmdo_gc == NULL)
420 ops.fmdo_gc = (void (*)())fmd_hdl_nop;
421 if (ops.fmdo_send == NULL)
422 ops.fmdo_send = (int (*)())fmd_hdl_nop;
423 if (ops.fmdo_topo == NULL)
424 ops.fmdo_topo = (void (*)())fmd_hdl_nop;
425
426 /*
427 * Make two passes through the property array to initialize the formals
428 * to use for processing the module's .conf file. In the first pass,
429 * we validate the types and count the number of properties. In the
430 * second pass we copy the strings and fill in the appropriate ops.
431 */
432 for (prop = mip->fmdi_props, i = 0; prop != NULL &&
433 prop->fmdp_name != NULL; prop++, i++) {
434 if (prop->fmdp_type >=
435 sizeof (_fmd_prop_ops) / sizeof (_fmd_prop_ops[0])) {
436 fmd_api_xerror(mp, EFMD_HDL_PROP,
437 "property %s uses invalid type %u\n",
438 prop->fmdp_name, prop->fmdp_type);
439 return (fmd_hdl_register_error(mp, EFMD_HDL_PROP));
440 }
441 }
442
443 mp->mod_argc = i;
444 mp->mod_argv = fmd_zalloc(sizeof (fmd_conf_formal_t) * i, FMD_SLEEP);
445
446 prop = mip->fmdi_props;
447 cfp = mp->mod_argv;
448
449 for (i = 0; i < mp->mod_argc; i++, prop++, cfp++) {
450 cfp->cf_name = fmd_strdup(prop->fmdp_name, FMD_SLEEP);
451 cfp->cf_ops = _fmd_prop_ops[prop->fmdp_type];
452 cfp->cf_default = fmd_strdup(prop->fmdp_defv, FMD_SLEEP);
453 }
454
455 /*
456 * If this module came from an on-disk file, compute the name of the
457 * corresponding .conf file and parse properties from it if it exists.
458 */
459 if (mp->mod_path != NULL) {
460 (void) strlcpy(buf, mp->mod_path, sizeof (buf));
461 (void) fmd_strdirname(buf);
462
463 (void) strlcat(buf, "/", sizeof (buf));
464 (void) strlcat(buf, mp->mod_name, sizeof (buf));
465 (void) strlcat(buf, ".conf", sizeof (buf));
466
467 if (access(buf, F_OK) == 0)
468 conf = buf;
469 }
470
471 if ((mp->mod_conf = fmd_conf_open(conf,
472 mp->mod_argc, mp->mod_argv, 0)) == NULL)
473 return (fmd_hdl_register_error(mp, EFMD_MOD_CONF));
474
475 fmd_conf_propagate(fmd.d_conf, mp->mod_conf, mp->mod_name);
476
477 /*
478 * Look up the list of the libdiagcode dictionaries associated with the
479 * module. If none were specified, use the value from daemon's config.
480 * We only fail if the module specified an explicit dictionary.
481 */
482 (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap);
483 if (pap->cpa_argc == 0 && mp->mod_ops == &fmd_bltin_ops)
484 (void) fmd_conf_getprop(fmd.d_conf, "self.dict", &pap);
485
486 for (i = 0; i < pap->cpa_argc; i++) {
487 if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) {
488 fmd_api_xerror(mp, errno,
489 "failed to open dictionary %s", pap->cpa_argv[i]);
490 return (fmd_hdl_register_error(mp, EFMD_MOD_CONF));
491 }
492 }
493
494 /*
495 * Make a copy of the handle information and store it in mod_info. We
496 * do not need to bother copying fmdi_props since they're already read.
497 */
498 mp->mod_info = fmd_alloc(sizeof (fmd_hdl_info_t), FMD_SLEEP);
499 mp->mod_info->fmdi_desc = fmd_strdup(mip->fmdi_desc, FMD_SLEEP);
500 mp->mod_info->fmdi_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP);
501 mp->mod_info->fmdi_ops = fmd_alloc(sizeof (fmd_hdl_ops_t), FMD_SLEEP);
502 bcopy(&ops, (void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t));
503 mp->mod_info->fmdi_props = NULL;
504
505 /*
506 * Store a copy of module version in mp for fmd_scheme_fmd_present()
507 */
508 if (mp->mod_vers == NULL)
509 mp->mod_vers = fmd_strdup(mip->fmdi_vers, FMD_SLEEP);
510
511 /*
512 * Allocate an FMRI representing this module. We'll use this later
513 * if the module decides to publish any events (e.g. list.suspects).
514 */
515 mp->mod_fmri = fmd_protocol_fmri_module(mp);
516
517 /*
518 * Any subscriptions specified in the conf file are now stored in the
519 * corresponding property. Add all of these to the dispatch queue.
520 */
521 (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap);
522
523 for (i = 0; i < pap->cpa_argc; i++) {
524 fmd_dispq_insert(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]);
525 fmd_xprt_subscribe_all(pap->cpa_argv[i]);
526 }
527
528 /*
529 * Unlock the module and restore any pre-existing module checkpoint.
530 * If the checkpoint is missing or corrupt, we just keep going.
531 */
532 fmd_module_unlock(mp);
533 fmd_ckpt_restore(mp);
534 return (0);
535 }
536
537 /*
538 * If an auxiliary thread exists for the specified module at unregistration
539 * time, send it an asynchronous cancellation to force it to exit and then
540 * join with it (we expect this to either succeed quickly or return ESRCH).
541 * Once this is complete we can destroy the associated fmd_thread_t data.
542 */
543 static void
544 fmd_module_thrcancel(fmd_idspace_t *ids, id_t id, fmd_module_t *mp)
545 {
546 fmd_thread_t *tp = fmd_idspace_getspecific(ids, id);
547
548 /*
549 * Door service threads are not cancellable (worse - if they're
550 * waiting in door_return then that is interrupted, but they then spin
551 * endlessly!). Non-private door service threads are not tracked
552 * in the module thread idspace so it's only private server threads
553 * created via fmd_doorthr_create that we'll encounter. In most
554 * cases the module _fini should have tidied up (e.g., calling
555 * sysevent_evc_unbind which will cleanup door threads if
556 * sysevent_evc_xsubscribe was used). One case that does not
557 * clean up is sysev_fini which explicitly does not unbind the
558 * channel, so we must skip any remaining door threads here.
559 */
560 if (tp->thr_isdoor) {
561 fmd_dprintf(FMD_DBG_MOD, "not cancelling %s private door "
562 "thread %u\n", mp->mod_name, tp->thr_tid);
563 fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
564 return;
565 }
566
567 fmd_dprintf(FMD_DBG_MOD, "cancelling %s auxiliary thread %u\n",
568 mp->mod_name, tp->thr_tid);
569
570 ASSERT(tp->thr_tid == id);
571 (void) pthread_cancel(tp->thr_tid);
572 (void) pthread_join(tp->thr_tid, NULL);
573
574 fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
575 }
576
577 void
578 fmd_module_unregister(fmd_module_t *mp)
579 {
580 fmd_conf_formal_t *cfp = mp->mod_argv;
581 const fmd_conf_path_t *pap;
582 fmd_case_t *cp;
583 fmd_xprt_t *xp;
584 int i;
585
586 TRACE((FMD_DBG_MOD, "unregister %p (%s)", (void *)mp, mp->mod_name));
587 ASSERT(fmd_module_locked(mp));
588
589 /*
590 * If any transports are still open, they have send threads that are
591 * using the module handle: shut them down and join with these threads.
592 */
593 while ((xp = fmd_list_next(&mp->mod_transports)) != NULL)
594 fmd_xprt_destroy(xp);
595
596 /*
597 * If any auxiliary threads exist, they may be using our module handle,
598 * and therefore could cause a fault as soon as we start destroying it.
599 * Module writers should clean up any threads before unregistering: we
600 * forcibly cancel any remaining auxiliary threads before proceeding.
601 */
602 fmd_idspace_apply(mp->mod_threads,
603 (void (*)())fmd_module_thrcancel, mp);
604
605 if (mp->mod_error == 0)
606 fmd_ckpt_save(mp); /* take one more checkpoint if needed */
607
608 /*
609 * Delete any cases associated with the module (UNSOLVED, SOLVED, or
610 * CLOSE_WAIT) as if fmdo_close() has finished processing them.
611 */
612 while ((cp = fmd_list_next(&mp->mod_cases)) != NULL)
613 fmd_case_delete(cp);
614
615 fmd_ustat_delete_references(mp->mod_ustat);
616 (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_SUBSCRIPTIONS, &pap);
617
618 for (i = 0; i < pap->cpa_argc; i++) {
619 fmd_xprt_unsubscribe_all(pap->cpa_argv[i]);
620 fmd_dispq_delete(fmd.d_disp, mp->mod_queue, pap->cpa_argv[i]);
621 }
622
623 fmd_conf_close(mp->mod_conf);
624 mp->mod_conf = NULL;
625
626 for (i = 0; i < mp->mod_argc; i++, cfp++) {
627 fmd_strfree((char *)cfp->cf_name);
628 fmd_strfree((char *)cfp->cf_default);
629 }
630
631 fmd_free(mp->mod_argv, sizeof (fmd_conf_formal_t) * mp->mod_argc);
632 mp->mod_argv = NULL;
633 mp->mod_argc = 0;
634
635 nvlist_free(mp->mod_fmri);
636 mp->mod_fmri = NULL;
637
638 fmd_strfree((char *)mp->mod_info->fmdi_desc);
639 fmd_strfree((char *)mp->mod_info->fmdi_vers);
640 fmd_free((void *)mp->mod_info->fmdi_ops, sizeof (fmd_hdl_ops_t));
641 fmd_free(mp->mod_info, sizeof (fmd_hdl_info_t));
642 mp->mod_info = NULL;
643
644 fmd_eventq_abort(mp->mod_queue);
645 }
646
647 void
648 fmd_hdl_unregister(fmd_hdl_t *hdl)
649 {
650 fmd_module_t *mp = fmd_api_module_lock(hdl);
651 fmd_module_unregister(mp);
652 fmd_module_unlock(mp);
653 }
654
655 void
656 fmd_hdl_subscribe(fmd_hdl_t *hdl, const char *class)
657 {
658 fmd_module_t *mp = fmd_api_module_lock(hdl);
659
660 if (fmd_conf_setprop(mp->mod_conf,
661 FMD_PROP_SUBSCRIPTIONS, class) == 0) {
662 fmd_dispq_insert(fmd.d_disp, mp->mod_queue, class);
663 fmd_xprt_subscribe_all(class);
664 }
665
666 fmd_module_unlock(mp);
667 }
668
669
670 void
671 fmd_hdl_unsubscribe(fmd_hdl_t *hdl, const char *class)
672 {
673 fmd_module_t *mp = fmd_api_module_lock(hdl);
674
675 if (fmd_conf_delprop(mp->mod_conf,
676 FMD_PROP_SUBSCRIPTIONS, class) == 0) {
677 fmd_xprt_unsubscribe_all(class);
678 fmd_dispq_delete(fmd.d_disp, mp->mod_queue, class);
679 }
680
681 fmd_module_unlock(mp);
682 fmd_eventq_cancel(mp->mod_queue, FMD_EVT_PROTOCOL, (void *)class);
683 }
684
685 void
686 fmd_hdl_setspecific(fmd_hdl_t *hdl, void *spec)
687 {
688 fmd_module_t *mp = fmd_api_module_lock(hdl);
689
690 mp->mod_spec = spec;
691 fmd_module_unlock(mp);
692 }
693
694 void *
695 fmd_hdl_getspecific(fmd_hdl_t *hdl)
696 {
697 fmd_module_t *mp = fmd_api_module_lock(hdl);
698 void *spec = mp->mod_spec;
699
700 fmd_module_unlock(mp);
701 return (spec);
702 }
703
704 void
705 fmd_hdl_opendict(fmd_hdl_t *hdl, const char *dict)
706 {
707 fmd_module_t *mp = fmd_api_module_lock(hdl);
708 const fmd_conf_path_t *pap;
709 int i;
710
711 /*
712 * Update the dictionary property in order to preserve the list of
713 * pathnames and expand any % tokens in the path. Then retrieve the
714 * new dictionary names from cpa_argv[] and open them one at a time.
715 */
716 (void) fmd_conf_setprop(mp->mod_conf, FMD_PROP_DICTIONARIES, dict);
717 (void) fmd_conf_getprop(mp->mod_conf, FMD_PROP_DICTIONARIES, &pap);
718
719 ASSERT(pap->cpa_argc > mp->mod_dictc);
720
721 for (i = mp->mod_dictc; i < pap->cpa_argc; i++) {
722 if (fmd_module_dc_opendict(mp, pap->cpa_argv[i]) != 0) {
723 fmd_api_error(mp, EFMD_MOD_DICT,
724 "failed to open dictionary %s for module %s",
725 pap->cpa_argv[i], mp->mod_name);
726 }
727 }
728
729 fmd_module_unlock(mp);
730 }
731
732 topo_hdl_t *
733 fmd_hdl_topo_hold(fmd_hdl_t *hdl, int v)
734 {
735 fmd_module_t *mp = fmd_api_module_lock(hdl);
736 topo_hdl_t *thp;
737
738 if (v != TOPO_VERSION) {
739 fmd_api_error(mp, EFMD_MOD_TOPO, "libtopo version mismatch: "
740 "fmd version %d != client version %d\n", TOPO_VERSION, v);
741 }
742
743 thp = fmd_module_topo_hold(mp);
744 ASSERT(thp != NULL);
745
746 fmd_module_unlock(mp);
747 return (thp);
748 }
749
750 void
751 fmd_hdl_topo_rele(fmd_hdl_t *hdl, topo_hdl_t *thp)
752 {
753 fmd_module_t *mp = fmd_api_module_lock(hdl);
754
755 if (fmd_module_topo_rele(mp, thp) != 0)
756 fmd_api_error(mp, EFMD_MOD_TOPO, "failed to release invalid "
757 "topo handle: %p\n", (void *)thp);
758
759 fmd_module_unlock(mp);
760 }
761
762 /*
763 * Visit a libtopo node trying to find a disk with the specified devid.
764 * If we find it, copy it's FRU and resource fields.
765 */
766 static int
767 fmd_hdl_topo_walk_cb(topo_hdl_t *thp, tnode_t *tn, void *arg)
768 {
769
770 fmd_hdl_topo_node_info_t *node = (fmd_hdl_topo_node_info_t *)arg;
771 char *cur_devid;
772 nvlist_t *fru;
773 nvlist_t *resource;
774 int err = 0;
775 _NOTE(ARGUNUSED(thp));
776
777 if (strcmp(topo_node_name(tn), "disk") != 0)
778 return (TOPO_WALK_NEXT);
779
780 if (topo_prop_get_string(tn, "io", "devid", &cur_devid, &err) != 0)
781 return (TOPO_WALK_NEXT);
782
783 if (strcmp(cur_devid, node->device) == 0) {
784 if (topo_node_fru(tn, &fru, NULL, &err) == 0 && err == 0 &&
785 topo_node_resource(tn, &resource, &err) == 0 && err == 0) {
786 node->fru = fnvlist_dup(fru);
787 node->resource = fnvlist_dup(resource);
788 return (TOPO_WALK_TERMINATE);
789 }
790 }
791
792 return (TOPO_WALK_NEXT);
793 }
794
795 /*
796 * Extract FRU and resource values from a libtopo node with the matching devid
797 */
798 fmd_hdl_topo_node_info_t *
799 fmd_hdl_topo_node_get_by_devid(fmd_hdl_t *hdl, char *device)
800 {
801
802 int err = 0;
803 topo_hdl_t *thp;
804 topo_walk_t *twp;
805
806 fmd_hdl_topo_node_info_t *node = (fmd_hdl_topo_node_info_t *)
807 fmd_hdl_zalloc(hdl, sizeof (fmd_hdl_topo_node_info_t), FMD_SLEEP);
808
809 thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
810
811 node->device = device;
812
813 if ((twp = topo_walk_init(thp, FM_FMRI_SCHEME_HC, fmd_hdl_topo_walk_cb,
814 node, &err)) == NULL) {
815 fmd_hdl_error(hdl, "failed to get topology: %s",
816 topo_strerror(err));
817 fmd_hdl_topo_rele(hdl, thp);
818 return (NULL);
819 }
820
821 (void) topo_walk_step(twp, TOPO_WALK_CHILD);
822 topo_walk_fini(twp);
823 fmd_hdl_topo_rele(hdl, thp);
824
825 if (node->fru == NULL || node->resource == NULL) {
826 fmd_hdl_debug(hdl, "Could not find device with matching FRU");
827 fmd_hdl_free(hdl, node, sizeof (fmd_hdl_topo_node_info_t));
828 return (NULL);
829 } else {
830 fmd_hdl_debug(hdl, "Found FRU for device %s", device);
831 return (node);
832 }
833 }
834
835 static void *
836 fmd_hdl_alloc_locked(fmd_module_t *mp, size_t size, int flags)
837 {
838 void *data;
839
840 if (mp->mod_stats->ms_memlimit.fmds_value.ui64 -
841 mp->mod_stats->ms_memtotal.fmds_value.ui64 < size) {
842 fmd_api_error(mp, EFMD_HDL_NOMEM, "%s's allocation of %lu "
843 "bytes exceeds module memory limit (%llu)\n",
844 mp->mod_name, (ulong_t)size, (u_longlong_t)
845 mp->mod_stats->ms_memtotal.fmds_value.ui64);
846 }
847
848 if ((data = fmd_alloc(size, flags)) != NULL)
849 mp->mod_stats->ms_memtotal.fmds_value.ui64 += size;
850
851 return (data);
852 }
853
854 void *
855 fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags)
856 {
857 fmd_module_t *mp = fmd_api_module_lock(hdl);
858 void *data;
859
860 data = fmd_hdl_alloc_locked(mp, size, flags);
861
862 fmd_module_unlock(mp);
863 return (data);
864 }
865
866 void *
867 fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags)
868 {
869 void *data = fmd_hdl_alloc(hdl, size, flags);
870
871 if (data != NULL)
872 bzero(data, size);
873
874 return (data);
875 }
876
877 static void
878 fmd_hdl_free_locked(fmd_module_t *mp, void *data, size_t size)
879 {
880 fmd_free(data, size);
881 mp->mod_stats->ms_memtotal.fmds_value.ui64 -= size;
882 }
883
884 void
885 fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size)
886 {
887 fmd_module_t *mp = fmd_api_module_lock(hdl);
888
889 fmd_hdl_free_locked(mp, data, size);
890
891 fmd_module_unlock(mp);
892 }
893
894 char *
895 fmd_hdl_strdup(fmd_hdl_t *hdl, const char *s, int flags)
896 {
897 char *p;
898
899 if (s != NULL)
900 p = fmd_hdl_alloc(hdl, strlen(s) + 1, flags);
901 else
902 p = NULL;
903
904 if (p != NULL)
905 (void) strcpy(p, s);
906
907 return (p);
908 }
909
910 void
911 fmd_hdl_strfree(fmd_hdl_t *hdl, char *s)
912 {
913 if (s != NULL)
914 fmd_hdl_free(hdl, s, strlen(s) + 1);
915 }
916
917 void
918 fmd_hdl_vabort(fmd_hdl_t *hdl, const char *format, va_list ap)
919 {
920 fmd_api_verror(fmd_api_module_lock(hdl), EFMD_HDL_ABORT, format, ap);
921 }
922
923 /*PRINTFLIKE2*/
924 void
925 fmd_hdl_abort(fmd_hdl_t *hdl, const char *format, ...)
926 {
927 fmd_module_t *mp = fmd_api_module_lock(hdl);
928 va_list ap;
929
930 va_start(ap, format);
931 fmd_api_verror(mp, EFMD_HDL_ABORT, format, ap);
932 va_end(ap);
933 }
934
935 void
936 fmd_hdl_verror(fmd_hdl_t *hdl, const char *format, va_list ap)
937 {
938 fmd_module_t *mp = fmd_api_module_lock(hdl);
939 fmd_api_vxerror(mp, errno, format, ap);
940 fmd_module_unlock(mp);
941 }
942
943 /*PRINTFLIKE2*/
944 void
945 fmd_hdl_error(fmd_hdl_t *hdl, const char *format, ...)
946 {
947 va_list ap;
948
949 va_start(ap, format);
950 fmd_hdl_verror(hdl, format, ap);
951 va_end(ap);
952 }
953
954 void
955 fmd_hdl_vdebug(fmd_hdl_t *hdl, const char *format, va_list ap)
956 {
957 fmd_module_t *mp = fmd_api_module_lock(hdl);
958
959 char *msg;
960 size_t len;
961 char c;
962
963 if (!(fmd.d_hdl_debug)) {
964 mp->mod_stats->ms_debugdrop.fmds_value.ui64++;
965 fmd_module_unlock(mp);
966 return;
967 }
968
969 len = vsnprintf(&c, 1, format, ap);
970
971 if ((msg = fmd_alloc(len + 2, FMD_NOSLEEP)) == NULL) {
972 mp->mod_stats->ms_debugdrop.fmds_value.ui64++;
973 fmd_module_unlock(mp);
974 return;
975 }
976
977 (void) vsnprintf(msg, len + 1, format, ap);
978
979 if (msg[len - 1] != '\n')
980 (void) strcpy(&msg[len], "\n");
981
982 if (fmd.d_hdl_dbout & FMD_DBOUT_STDERR) {
983 (void) pthread_mutex_lock(&fmd.d_err_lock);
984 (void) fprintf(stderr, "%s DEBUG: %s: %s",
985 fmd.d_pname, mp->mod_name, msg);
986 (void) pthread_mutex_unlock(&fmd.d_err_lock);
987 }
988
989 if (fmd.d_hdl_dbout & FMD_DBOUT_SYSLOG) {
990 syslog(LOG_DEBUG | LOG_DAEMON, "%s DEBUG: %s: %s",
991 fmd.d_pname, mp->mod_name, msg);
992 }
993
994 fmd_free(msg, len + 2);
995 fmd_module_unlock(mp);
996 }
997
998 /*PRINTFLIKE2*/
999 void
1000 fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...)
1001 {
1002 va_list ap;
1003
1004 va_start(ap, format);
1005 fmd_hdl_vdebug(hdl, format, ap);
1006 va_end(ap);
1007 }
1008
1009 int32_t
1010 fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)
1011 {
1012 fmd_module_t *mp = fmd_api_module_lock(hdl);
1013 const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name);
1014 int32_t value = 0;
1015
1016 if (ops == &fmd_conf_bool || ops == &fmd_conf_int32 ||
1017 ops == &fmd_conf_uint32)
1018 (void) fmd_conf_getprop(mp->mod_conf, name, &value);
1019 else if (ops != NULL) {
1020 fmd_api_error(mp, EFMD_PROP_TYPE,
1021 "property %s is not of int32 type\n", name);
1022 } else {
1023 fmd_api_error(mp, EFMD_PROP_DEFN,
1024 "property %s is not defined\n", name);
1025 }
1026
1027 fmd_module_unlock(mp);
1028 return (value);
1029 }
1030
1031 int64_t
1032 fmd_prop_get_int64(fmd_hdl_t *hdl, const char *name)
1033 {
1034 fmd_module_t *mp = fmd_api_module_lock(hdl);
1035 const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name);
1036 int64_t value = 0;
1037
1038 if (ops == &fmd_conf_int64 || ops == &fmd_conf_uint64 ||
1039 ops == &fmd_conf_time || ops == &fmd_conf_size)
1040 (void) fmd_conf_getprop(mp->mod_conf, name, &value);
1041 else if (ops != NULL) {
1042 fmd_api_error(mp, EFMD_PROP_TYPE,
1043 "property %s is not of int64 type\n", name);
1044 } else {
1045 fmd_api_error(mp, EFMD_PROP_DEFN,
1046 "property %s is not defined\n", name);
1047 }
1048
1049 fmd_module_unlock(mp);
1050 return (value);
1051 }
1052
1053 char *
1054 fmd_prop_get_string(fmd_hdl_t *hdl, const char *name)
1055 {
1056 fmd_module_t *mp = fmd_api_module_lock(hdl);
1057 const fmd_conf_ops_t *ops = fmd_conf_gettype(mp->mod_conf, name);
1058 char *value = NULL;
1059 const char *s;
1060
1061 if (ops == &fmd_conf_string) {
1062 (void) fmd_conf_getprop(mp->mod_conf, name, &s);
1063 value = fmd_strdup(s, FMD_SLEEP);
1064 } else if (ops != NULL) {
1065 fmd_api_error(mp, EFMD_PROP_TYPE,
1066 "property %s is not of string type\n", name);
1067 } else {
1068 fmd_api_error(mp, EFMD_PROP_DEFN,
1069 "property %s is not defined\n", name);
1070 }
1071
1072 fmd_module_unlock(mp);
1073 return (value);
1074 }
1075
1076 void
1077 fmd_prop_free_string(fmd_hdl_t *hdl, char *s)
1078 {
1079 fmd_module_t *mp = fmd_api_module_lock(hdl);
1080 fmd_strfree(s);
1081 fmd_module_unlock(mp);
1082 }
1083
1084 fmd_stat_t *
1085 fmd_stat_create(fmd_hdl_t *hdl, uint_t flags, uint_t argc, fmd_stat_t *argv)
1086 {
1087 fmd_module_t *mp = fmd_api_module_lock(hdl);
1088 fmd_stat_t *ep, *sp;
1089
1090 if (flags & ~FMD_STAT_ALLOC) {
1091 fmd_api_error(mp, EFMD_STAT_FLAGS,
1092 "invalid flags 0x%x passed to fmd_stat_create\n", flags);
1093 }
1094
1095 if ((sp = fmd_ustat_insert(mp->mod_ustat,
1096 flags | FMD_USTAT_VALIDATE, argc, argv, &ep)) == NULL) {
1097 fmd_api_error(mp, errno,
1098 "failed to publish stat '%s'", ep->fmds_name);
1099 }
1100
1101 fmd_module_unlock(mp);
1102 return (sp);
1103 }
1104
1105 void
1106 fmd_stat_destroy(fmd_hdl_t *hdl, uint_t argc, fmd_stat_t *argv)
1107 {
1108 fmd_module_t *mp = fmd_api_module_lock(hdl);
1109 fmd_ustat_delete(mp->mod_ustat, argc, argv);
1110 fmd_module_unlock(mp);
1111 }
1112
1113 void
1114 fmd_stat_setstr(fmd_hdl_t *hdl, fmd_stat_t *sp, const char *s)
1115 {
1116 char *str = fmd_strdup(s, FMD_SLEEP);
1117 fmd_module_t *mp = fmd_api_module_lock(hdl);
1118
1119 if (sp->fmds_type != FMD_TYPE_STRING) {
1120 fmd_strfree(str);
1121 fmd_api_error(mp, EFMD_STAT_TYPE,
1122 "stat '%s' is not a string\n", sp->fmds_name);
1123 }
1124
1125 fmd_strfree(sp->fmds_value.str);
1126 sp->fmds_value.str = str;
1127
1128 fmd_module_unlock(mp);
1129 }
1130
1131 fmd_case_t *
1132 fmd_case_open(fmd_hdl_t *hdl, void *data)
1133 {
1134 fmd_module_t *mp = fmd_api_module_lock(hdl);
1135 fmd_case_t *cp = fmd_case_create(mp, NULL, data);
1136 fmd_module_unlock(mp);
1137 return (cp);
1138 }
1139
1140 fmd_case_t *
1141 fmd_case_open_uuid(fmd_hdl_t *hdl, const char *uuidstr, void *data)
1142 {
1143 fmd_module_t *mp;
1144 fmd_case_t *cp;
1145 uint_t uuidlen;
1146 uuid_t uuid;
1147
1148 mp = fmd_api_module_lock(hdl);
1149
1150 (void) fmd_conf_getprop(fmd.d_conf, "uuidlen", &uuidlen);
1151
1152 if (uuidstr == NULL) {
1153 fmd_api_error(mp, EFMD_CASE_INVAL, "NULL uuid string\n");
1154 } else if (strnlen(uuidstr, uuidlen + 1) != uuidlen) {
1155 fmd_api_error(mp, EFMD_CASE_INVAL, "invalid uuid string: '%s' "
1156 "(expected length %d)\n", uuidstr, uuidlen);
1157 } else if (uuid_parse((char *)uuidstr, uuid) == -1) {
1158 fmd_api_error(mp, EFMD_CASE_INVAL, "cannot parse uuid string: "
1159 "'%s'\n", uuidstr);
1160 }
1161
1162 if ((cp = fmd_case_hash_lookup(fmd.d_cases, uuidstr)) == NULL) {
1163 cp = fmd_case_create(mp, uuidstr, data);
1164 } else {
1165 fmd_case_rele(cp);
1166 cp = NULL;
1167 }
1168
1169 fmd_module_unlock(mp);
1170 return (cp); /* May be NULL iff case already exists */
1171 }
1172
1173 void
1174 fmd_case_reset(fmd_hdl_t *hdl, fmd_case_t *cp)
1175 {
1176 fmd_module_t *mp = fmd_api_module_lock(hdl);
1177 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1178
1179 if (cip->ci_state >= FMD_CASE_SOLVED) {
1180 fmd_api_error(mp, EFMD_CASE_STATE, "cannot solve %s: "
1181 "case is already solved or closed\n", cip->ci_uuid);
1182 }
1183
1184 fmd_case_reset_suspects(cp);
1185 fmd_module_unlock(mp);
1186 }
1187
1188 void
1189 fmd_case_solve(fmd_hdl_t *hdl, fmd_case_t *cp)
1190 {
1191 fmd_module_t *mp = fmd_api_module_lock(hdl);
1192 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1193
1194 if (cip->ci_state >= FMD_CASE_SOLVED) {
1195 fmd_api_error(mp, EFMD_CASE_STATE, "cannot solve %s: "
1196 "case is already solved or closed\n", cip->ci_uuid);
1197 }
1198
1199 fmd_case_transition(cp, FMD_CASE_SOLVED, FMD_CF_SOLVED);
1200 fmd_module_unlock(mp);
1201 }
1202
1203 void
1204 fmd_case_close(fmd_hdl_t *hdl, fmd_case_t *cp)
1205 {
1206 fmd_module_t *mp = fmd_api_module_lock(hdl);
1207
1208 (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1209 fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED);
1210
1211 fmd_module_unlock(mp);
1212 }
1213
1214 const char *
1215 fmd_case_uuid(fmd_hdl_t *hdl, fmd_case_t *cp)
1216 {
1217 fmd_module_t *mp = fmd_api_module_lock(hdl);
1218 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1219 const char *uuid = cip->ci_uuid;
1220
1221 fmd_module_unlock(mp);
1222 return (uuid);
1223 }
1224
1225 fmd_case_t *
1226 fmd_case_uulookup(fmd_hdl_t *hdl, const char *uuid)
1227 {
1228 fmd_module_t *cmp, *mp = fmd_api_module_lock(hdl);
1229 fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1230
1231 if (cp != NULL) {
1232 cmp = ((fmd_case_impl_t *)cp)->ci_mod;
1233 fmd_case_rele(cp);
1234 } else
1235 cmp = NULL;
1236
1237 fmd_module_unlock(mp);
1238 return (cmp == mp ? cp : NULL);
1239 }
1240
1241 void
1242 fmd_case_uuclose(fmd_hdl_t *hdl, const char *uuid)
1243 {
1244 fmd_module_t *mp = fmd_api_module_lock(hdl);
1245 fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1246
1247 if (cp != NULL) {
1248 fmd_case_transition(cp, FMD_CASE_CLOSE_WAIT, FMD_CF_ISOLATED);
1249 fmd_case_rele(cp);
1250 }
1251
1252 fmd_module_unlock(mp);
1253 }
1254
1255 int
1256 fmd_case_uuclosed(fmd_hdl_t *hdl, const char *uuid)
1257 {
1258 fmd_module_t *mp = fmd_api_module_lock(hdl);
1259 fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1260 fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1261 int rv = FMD_B_TRUE;
1262
1263 if (cip != NULL) {
1264 rv = cip->ci_state >= FMD_CASE_CLOSE_WAIT;
1265 fmd_case_rele(cp);
1266 }
1267
1268 fmd_module_unlock(mp);
1269 return (rv);
1270 }
1271
1272 void
1273 fmd_case_uuresolved(fmd_hdl_t *hdl, const char *uuid)
1274 {
1275 fmd_module_t *mp = fmd_api_module_lock(hdl);
1276 fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1277
1278 if (cp != NULL) {
1279 fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1280 /*
1281 * For a proxy, we notify the diagnosing side, and then
1282 * wait for it to send us back a list.resolved.
1283 */
1284 if (cip->ci_xprt != NULL)
1285 fmd_xprt_uuresolved(cip->ci_xprt, cip->ci_uuid);
1286 else
1287 fmd_case_transition(cp, FMD_CASE_RESOLVED, 0);
1288 fmd_case_rele(cp);
1289 }
1290
1291 fmd_module_unlock(mp);
1292 }
1293
1294 int
1295 fmd_case_uuisresolved(fmd_hdl_t *hdl, const char *uuid)
1296 {
1297 fmd_module_t *mp = fmd_api_module_lock(hdl);
1298 fmd_case_t *cp = fmd_case_hash_lookup(fmd.d_cases, uuid);
1299 fmd_case_impl_t *cip = (fmd_case_impl_t *)cp;
1300 int rv = FMD_B_FALSE;
1301
1302 if (cip != NULL) {
1303 rv = (cip->ci_state >= FMD_CASE_RESOLVED);
1304 fmd_case_rele(cp);
1305 }
1306
1307 fmd_module_unlock(mp);
1308 return (rv);
1309 }
1310
1311 static int
1312 fmd_case_instate(fmd_hdl_t *hdl, fmd_case_t *cp, uint_t state)
1313 {
1314 fmd_module_t *mp = fmd_api_module_lock(hdl);
1315 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1316 int rv = cip->ci_state >= state;
1317
1318 fmd_module_unlock(mp);
1319 return (rv);
1320 }
1321
1322 int
1323 fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp)
1324 {
1325 return (fmd_case_instate(hdl, cp, FMD_CASE_SOLVED));
1326 }
1327
1328 int
1329 fmd_case_closed(fmd_hdl_t *hdl, fmd_case_t *cp)
1330 {
1331 return (fmd_case_instate(hdl, cp, FMD_CASE_CLOSE_WAIT));
1332 }
1333
1334 void
1335 fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
1336 {
1337 fmd_module_t *mp = fmd_api_module_lock(hdl);
1338
1339 (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1340
1341 if (fmd_case_insert_event(cp, ep))
1342 mp->mod_stats->ms_accepted.fmds_value.ui64++;
1343
1344 fmd_module_unlock(mp);
1345 }
1346
1347 void
1348 fmd_case_add_serd(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name)
1349 {
1350 fmd_module_t *mp = fmd_api_module_lock(hdl);
1351 fmd_serd_elem_t *sep;
1352 fmd_serd_eng_t *sgp;
1353
1354 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1355 fmd_api_error(mp, EFMD_SERD_NAME,
1356 "failed to add events from serd engine '%s'", name);
1357 }
1358
1359 (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1360
1361 for (sep = fmd_list_next(&sgp->sg_list);
1362 sep != NULL; sep = fmd_list_next(sep)) {
1363 if (fmd_case_insert_event(cp, sep->se_event))
1364 mp->mod_stats->ms_accepted.fmds_value.ui64++;
1365 }
1366
1367 fmd_module_unlock(mp);
1368 }
1369
1370 void
1371 fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *nvl)
1372 {
1373 fmd_module_t *mp = fmd_api_module_lock(hdl);
1374 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1375 char *class;
1376 topo_hdl_t *thp;
1377 int err;
1378 nvlist_t *rsrc = NULL, *asru_prop = NULL, *asru = NULL, *fru = NULL;
1379 char *loc = NULL, *serial = NULL;
1380
1381 if (cip->ci_state >= FMD_CASE_SOLVED) {
1382 fmd_api_error(mp, EFMD_CASE_STATE, "cannot add suspect to "
1383 "%s: case is already solved or closed\n", cip->ci_uuid);
1384 }
1385
1386 if (nvlist_lookup_string(nvl, FM_CLASS, &class) != 0 ||
1387 class == NULL || *class == '\0') {
1388 fmd_api_error(mp, EFMD_CASE_EVENT, "cannot add suspect to "
1389 "%s: suspect event is missing a class\n", cip->ci_uuid);
1390 }
1391
1392 thp = fmd_module_topo_hold(mp);
1393 (void) nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc);
1394 (void) nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU, &asru);
1395 (void) nvlist_lookup_nvlist(nvl, FM_FAULT_FRU, &fru);
1396 if (rsrc != NULL) {
1397 if (strncmp(class, "defect", 6) == 0) {
1398 if (asru == NULL && topo_fmri_getprop(thp, rsrc,
1399 TOPO_PGROUP_IO, TOPO_IO_MODULE, rsrc,
1400 &asru_prop, &err) == 0 &&
1401 nvlist_lookup_nvlist(asru_prop, TOPO_PROP_VAL_VAL,
1402 &asru) == 0) {
1403 (void) nvlist_add_nvlist(nvl, FM_FAULT_ASRU,
1404 asru);
1405 nvlist_free(asru_prop);
1406 (void) nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU,
1407 &asru);
1408 }
1409 } else {
1410 if (topo_fmri_asru(thp, rsrc, &asru, &err) == 0) {
1411 (void) nvlist_remove(nvl, FM_FAULT_ASRU,
1412 DATA_TYPE_NVLIST);
1413 (void) nvlist_add_nvlist(nvl, FM_FAULT_ASRU,
1414 asru);
1415 nvlist_free(asru);
1416 (void) nvlist_lookup_nvlist(nvl, FM_FAULT_ASRU,
1417 &asru);
1418 }
1419 if (topo_fmri_fru(thp, rsrc, &fru, &err) == 0) {
1420 (void) nvlist_remove(nvl, FM_FAULT_FRU,
1421 DATA_TYPE_NVLIST);
1422 (void) nvlist_add_nvlist(nvl, FM_FAULT_FRU,
1423 fru);
1424 nvlist_free(fru);
1425 (void) nvlist_lookup_nvlist(nvl, FM_FAULT_FRU,
1426 &fru);
1427 }
1428 }
1429 }
1430
1431 /*
1432 * Try to find the location label for this resource
1433 */
1434 if (strncmp(class, "defect", 6) != 0) {
1435 if (fru != NULL)
1436 (void) topo_fmri_label(thp, fru, &loc, &err);
1437 else if (rsrc != NULL)
1438 (void) topo_fmri_label(thp, rsrc, &loc, &err);
1439 if (loc != NULL) {
1440 (void) nvlist_remove(nvl, FM_FAULT_LOCATION,
1441 DATA_TYPE_STRING);
1442 (void) nvlist_add_string(nvl, FM_FAULT_LOCATION, loc);
1443 topo_hdl_strfree(thp, loc);
1444 }
1445 }
1446
1447 /*
1448 * In some cases, serial information for the resource will not be
1449 * available at enumeration but may instead be available by invoking
1450 * a dynamic property method on the FRU. In order to ensure the serial
1451 * number is persisted properly in the ASRU cache, we'll fetch the
1452 * property, if it exists, and add it to the resource and fru fmris.
1453 * If the DE has not listed a fru in the suspect, see if we can
1454 * retrieve the serial from the resource instead.
1455 */
1456 if (fru != NULL) {
1457 (void) topo_fmri_serial(thp, fru, &serial, &err);
1458 if (serial != NULL) {
1459 (void) nvlist_add_string(fru, "serial", serial);
1460 topo_hdl_strfree(thp, serial);
1461 }
1462 }
1463
1464 err = fmd_module_topo_rele(mp, thp);
1465 ASSERT(err == 0);
1466
1467 fmd_case_insert_suspect(cp, nvl);
1468 fmd_module_unlock(mp);
1469 }
1470
1471 void
1472 fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data)
1473 {
1474 fmd_module_t *mp = fmd_api_module_lock(hdl);
1475 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1476
1477 (void) pthread_mutex_lock(&cip->ci_lock);
1478 cip->ci_data = data;
1479 (void) pthread_mutex_unlock(&cip->ci_lock);
1480
1481 fmd_module_unlock(mp);
1482 }
1483
1484 void *
1485 fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp)
1486 {
1487 fmd_module_t *mp = fmd_api_module_lock(hdl);
1488 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1489 void *data;
1490
1491 (void) pthread_mutex_lock(&cip->ci_lock);
1492 data = cip->ci_data;
1493 (void) pthread_mutex_unlock(&cip->ci_lock);
1494
1495 fmd_module_unlock(mp);
1496 return (data);
1497 }
1498
1499 void
1500 fmd_case_setprincipal(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
1501 {
1502 fmd_module_t *mp = fmd_api_module_lock(hdl);
1503
1504 (void) fmd_api_case_impl(mp, cp); /* validate 'cp' */
1505
1506 if (fmd_case_insert_principal(cp, ep))
1507 mp->mod_stats->ms_accepted.fmds_value.ui64++;
1508
1509 fmd_module_unlock(mp);
1510 }
1511
1512 fmd_event_t *
1513 fmd_case_getprincipal(fmd_hdl_t *hdl, fmd_case_t *cp)
1514 {
1515 fmd_module_t *mp = fmd_api_module_lock(hdl);
1516 fmd_case_impl_t *cip = fmd_api_case_impl(mp, cp);
1517 fmd_event_t *ep;
1518
1519 (void) pthread_mutex_lock(&cip->ci_lock);
1520 ep = cip->ci_principal;
1521 (void) pthread_mutex_unlock(&cip->ci_lock);
1522
1523 fmd_module_unlock(mp);
1524 return (ep);
1525 }
1526
1527 fmd_case_t *
1528 fmd_case_next(fmd_hdl_t *hdl, fmd_case_t *cp)
1529 {
1530 fmd_module_t *mp = fmd_api_module_lock(hdl);
1531
1532 if (cp != NULL)
1533 cp = fmd_list_next(fmd_api_case_impl(mp, cp));
1534 else
1535 cp = fmd_list_next(&mp->mod_cases);
1536
1537 fmd_module_unlock(mp);
1538 return (cp);
1539 }
1540
1541 fmd_case_t *
1542 fmd_case_prev(fmd_hdl_t *hdl, fmd_case_t *cp)
1543 {
1544 fmd_module_t *mp = fmd_api_module_lock(hdl);
1545
1546 if (cp != NULL)
1547 cp = fmd_list_prev(fmd_api_case_impl(mp, cp));
1548 else
1549 cp = fmd_list_prev(&mp->mod_cases);
1550
1551 fmd_module_unlock(mp);
1552 return (cp);
1553 }
1554
1555 /*
1556 * Utility function for fmd_buf_* routines. If a case is specified, use the
1557 * case's ci_bufs hash; otherwise use the module's global mod_bufs hash.
1558 */
1559 static fmd_buf_hash_t *
1560 fmd_buf_gethash(fmd_module_t *mp, fmd_case_t *cp)
1561 {
1562 return (cp ? &fmd_api_case_impl(mp, cp)->ci_bufs : &mp->mod_bufs);
1563 }
1564
1565 void
1566 fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size)
1567 {
1568 fmd_module_t *mp = fmd_api_module_lock(hdl);
1569 fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1570 fmd_buf_t *bp = fmd_buf_lookup(bhp, name);
1571
1572 if (bp == NULL) {
1573 if (fmd_strbadid(name, FMD_B_TRUE) != NULL || size == 0) {
1574 fmd_api_error(mp, EFMD_BUF_INVAL, "cannot create '%s' "
1575 "(size %lu): %s\n", name, (ulong_t)size,
1576 fmd_strerror(EFMD_BUF_INVAL));
1577 }
1578
1579 if (mp->mod_stats->ms_buflimit.fmds_value.ui64 -
1580 mp->mod_stats->ms_buftotal.fmds_value.ui64 < size) {
1581 fmd_api_error(mp, EFMD_BUF_LIMIT, "cannot create '%s': "
1582 "buf limit exceeded (%llu)\n", name, (u_longlong_t)
1583 mp->mod_stats->ms_buflimit.fmds_value.ui64);
1584 }
1585
1586 mp->mod_stats->ms_buftotal.fmds_value.ui64 += size;
1587 bp = fmd_buf_insert(bhp, name, size);
1588
1589 } else {
1590 fmd_api_error(mp, EFMD_BUF_EXISTS,
1591 "cannot create '%s': buffer already exists\n", name);
1592 }
1593
1594 if (cp != NULL)
1595 fmd_case_setdirty(cp);
1596 else
1597 fmd_module_setdirty(mp);
1598
1599 fmd_module_unlock(mp);
1600 }
1601
1602 void
1603 fmd_buf_destroy(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name)
1604 {
1605 fmd_module_t *mp = fmd_api_module_lock(hdl);
1606 fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1607 fmd_buf_t *bp = fmd_buf_lookup(bhp, name);
1608
1609 if (bp != NULL) {
1610 mp->mod_stats->ms_buftotal.fmds_value.ui64 -= bp->buf_size;
1611 fmd_buf_delete(bhp, name);
1612
1613 if (cp != NULL)
1614 fmd_case_setdirty(cp);
1615 else
1616 fmd_module_setdirty(mp);
1617 }
1618
1619 fmd_module_unlock(mp);
1620 }
1621
1622 void
1623 fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp,
1624 const char *name, void *buf, size_t size)
1625 {
1626 fmd_module_t *mp = fmd_api_module_lock(hdl);
1627 fmd_buf_t *bp = fmd_buf_lookup(fmd_buf_gethash(mp, cp), name);
1628
1629 if (bp == NULL) {
1630 fmd_api_error(mp, EFMD_BUF_NOENT, "no buf named '%s' is "
1631 "associated with %s\n", name, cp ? "case" : "module");
1632 }
1633
1634 bcopy(bp->buf_data, buf, MIN(bp->buf_size, size));
1635 if (size > bp->buf_size)
1636 bzero((char *)buf + bp->buf_size, size - bp->buf_size);
1637
1638 fmd_module_unlock(mp);
1639 }
1640
1641 void
1642 fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp,
1643 const char *name, const void *buf, size_t size)
1644 {
1645 fmd_module_t *mp = fmd_api_module_lock(hdl);
1646 fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1647 fmd_buf_t *bp = fmd_buf_lookup(bhp, name);
1648
1649 if (bp == NULL) {
1650 if (fmd_strbadid(name, FMD_B_TRUE) != NULL || size == 0) {
1651 fmd_api_error(mp, EFMD_BUF_INVAL, "cannot write '%s' "
1652 "(size %lu): %s\n", name, (ulong_t)size,
1653 fmd_strerror(EFMD_BUF_INVAL));
1654 }
1655
1656 if (mp->mod_stats->ms_buflimit.fmds_value.ui64 -
1657 mp->mod_stats->ms_buftotal.fmds_value.ui64 < size) {
1658 fmd_api_error(mp, EFMD_BUF_LIMIT, "cannot write '%s': "
1659 "buf limit exceeded (%llu)\n", name, (u_longlong_t)
1660 mp->mod_stats->ms_buflimit.fmds_value.ui64);
1661 }
1662
1663 mp->mod_stats->ms_buftotal.fmds_value.ui64 += size;
1664 bp = fmd_buf_insert(bhp, name, size);
1665
1666 } else if (size > bp->buf_size) {
1667 fmd_api_error(mp, EFMD_BUF_OFLOW,
1668 "write to buf '%s' overflows buf size (%lu > %lu)\n",
1669 name, (ulong_t)size, (ulong_t)bp->buf_size);
1670 }
1671
1672 bcopy(buf, bp->buf_data, MIN(bp->buf_size, size));
1673 bp->buf_flags |= FMD_BUF_DIRTY;
1674
1675 if (cp != NULL)
1676 fmd_case_setdirty(cp);
1677 else
1678 fmd_module_setdirty(mp);
1679
1680 fmd_module_unlock(mp);
1681 }
1682
1683 size_t
1684 fmd_buf_size(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name)
1685 {
1686 fmd_module_t *mp = fmd_api_module_lock(hdl);
1687 fmd_buf_hash_t *bhp = fmd_buf_gethash(mp, cp);
1688
1689 fmd_buf_t *bp;
1690 size_t size;
1691
1692 if ((bp = fmd_buf_lookup(bhp, name)) != NULL)
1693 size = bp->buf_size;
1694 else
1695 size = 0;
1696
1697 fmd_module_unlock(mp);
1698 return (size);
1699 }
1700
1701 void
1702 fmd_serd_create(fmd_hdl_t *hdl, const char *name, uint_t n, hrtime_t t)
1703 {
1704 fmd_module_t *mp = fmd_api_module_lock(hdl);
1705
1706 if (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL) {
1707 fmd_api_error(mp, EFMD_SERD_EXISTS,
1708 "failed to create serd engine '%s': %s\n",
1709 name, fmd_strerror(EFMD_SERD_EXISTS));
1710 }
1711
1712 (void) fmd_serd_eng_insert(&mp->mod_serds, name, n, t);
1713 fmd_module_setdirty(mp);
1714 fmd_module_unlock(mp);
1715 }
1716
1717 void
1718 fmd_serd_destroy(fmd_hdl_t *hdl, const char *name)
1719 {
1720 fmd_module_t *mp = fmd_api_module_lock(hdl);
1721
1722 fmd_serd_eng_delete(&mp->mod_serds, name);
1723 fmd_module_setdirty(mp);
1724 fmd_module_unlock(mp);
1725 }
1726
1727 int
1728 fmd_serd_exists(fmd_hdl_t *hdl, const char *name)
1729 {
1730 fmd_module_t *mp = fmd_api_module_lock(hdl);
1731 int rv = (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL);
1732 fmd_module_unlock(mp);
1733
1734 return (rv);
1735 }
1736
1737 void
1738 fmd_serd_reset(fmd_hdl_t *hdl, const char *name)
1739 {
1740 fmd_module_t *mp = fmd_api_module_lock(hdl);
1741 fmd_serd_eng_t *sgp;
1742
1743 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1744 fmd_api_error(mp, EFMD_SERD_NAME,
1745 "serd engine '%s' does not exist\n", name);
1746 }
1747
1748 fmd_serd_eng_reset(sgp);
1749 fmd_module_setdirty(mp);
1750 fmd_module_unlock(mp);
1751 }
1752
1753 int
1754 fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep)
1755 {
1756 fmd_module_t *mp = fmd_api_module_lock(hdl);
1757 fmd_serd_eng_t *sgp;
1758 int err;
1759
1760 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1761 fmd_api_error(mp, EFMD_SERD_NAME,
1762 "failed to add record to serd engine '%s'", name);
1763 }
1764
1765 err = fmd_serd_eng_record(sgp, ep);
1766
1767 if (sgp->sg_flags & FMD_SERD_DIRTY)
1768 fmd_module_setdirty(mp);
1769
1770 fmd_module_unlock(mp);
1771 return (err);
1772 }
1773
1774 int
1775 fmd_serd_fired(fmd_hdl_t *hdl, const char *name)
1776 {
1777 fmd_module_t *mp = fmd_api_module_lock(hdl);
1778 fmd_serd_eng_t *sgp;
1779 int err;
1780
1781 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1782 fmd_api_error(mp, EFMD_SERD_NAME,
1783 "serd engine '%s' does not exist\n", name);
1784 }
1785
1786 err = fmd_serd_eng_fired(sgp);
1787 fmd_module_unlock(mp);
1788 return (err);
1789 }
1790
1791 int
1792 fmd_serd_empty(fmd_hdl_t *hdl, const char *name)
1793 {
1794 fmd_module_t *mp = fmd_api_module_lock(hdl);
1795 fmd_serd_eng_t *sgp;
1796 int empty;
1797
1798 if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
1799 fmd_api_error(mp, EFMD_SERD_NAME,
1800 "serd engine '%s' does not exist\n", name);
1801 }
1802
1803 empty = fmd_serd_eng_empty(sgp);
1804 fmd_module_unlock(mp);
1805 return (empty);
1806 }
1807
1808 pthread_t
1809 fmd_thr_create(fmd_hdl_t *hdl, void (*func)(void *), void *arg)
1810 {
1811 fmd_module_t *mp = fmd_api_module_lock(hdl);
1812 fmd_thread_t *tp;
1813 pthread_t tid;
1814
1815 if (mp->mod_stats->ms_thrtotal.fmds_value.ui32 >=
1816 mp->mod_stats->ms_thrlimit.fmds_value.ui32) {
1817 fmd_api_error(mp, EFMD_THR_LIMIT, "%s request to create an "
1818 "auxiliary thread exceeds module thread limit (%u)\n",
1819 mp->mod_name, mp->mod_stats->ms_thrlimit.fmds_value.ui32);
1820 }
1821
1822 if ((tp = fmd_thread_create(mp, func, arg)) == NULL) {
1823 fmd_api_error(mp, EFMD_THR_CREATE,
1824 "failed to create auxiliary thread");
1825 }
1826
1827 tid = tp->thr_tid;
1828 mp->mod_stats->ms_thrtotal.fmds_value.ui32++;
1829 (void) fmd_idspace_xalloc(mp->mod_threads, tid, tp);
1830
1831 fmd_module_unlock(mp);
1832 return (tid);
1833 }
1834
1835 void
1836 fmd_thr_destroy(fmd_hdl_t *hdl, pthread_t tid)
1837 {
1838 fmd_module_t *mp = fmd_api_module_lock(hdl);
1839 fmd_thread_t *tp;
1840 int err;
1841
1842 if (pthread_self() == tid) {
1843 fmd_api_error(mp, EFMD_THR_INVAL, "auxiliary thread tried to "
1844 "destroy itself (tid %u)\n", tid);
1845 }
1846
1847 if ((tp = fmd_idspace_getspecific(mp->mod_threads, tid)) == NULL) {
1848 fmd_api_error(mp, EFMD_THR_INVAL, "auxiliary thread tried to "
1849 "destroy an invalid thread (tid %u)\n", tid);
1850 }
1851
1852 /*
1853 * Wait for the specified thread to exit and then join with it. Since
1854 * the thread may need to make API calls in order to complete its work
1855 * we must sleep with the module lock unheld, and then reacquire it.
1856 */
1857 fmd_module_unlock(mp);
1858 err = pthread_join(tid, NULL);
1859 mp = fmd_api_module_lock(hdl);
1860
1861 /*
1862 * Since pthread_join() was called without the module lock held, if
1863 * multiple callers attempted to destroy the same auxiliary thread
1864 * simultaneously, one will succeed and the others will get ESRCH.
1865 * Therefore we silently ignore ESRCH but only allow the caller who
1866 * succeessfully joined with the auxiliary thread to destroy it.
1867 */
1868 if (err != 0 && err != ESRCH) {
1869 fmd_api_error(mp, EFMD_THR_JOIN,
1870 "failed to join with auxiliary thread %u\n", tid);
1871 }
1872
1873 if (err == 0) {
1874 fmd_thread_destroy(tp, FMD_THREAD_NOJOIN);
1875 mp->mod_stats->ms_thrtotal.fmds_value.ui32--;
1876 (void) fmd_idspace_free(mp->mod_threads, tid);
1877 }
1878
1879 fmd_module_unlock(mp);
1880 }
1881
1882 void
1883 fmd_thr_signal(fmd_hdl_t *hdl, pthread_t tid)
1884 {
1885 fmd_module_t *mp = fmd_api_module_lock(hdl);
1886
1887 if (tid != mp->mod_thread->thr_tid &&
1888 fmd_idspace_getspecific(mp->mod_threads, tid) == NULL) {
1889 fmd_api_error(mp, EFMD_THR_INVAL, "tid %u is not a valid "
1890 "thread id for module %s\n", tid, mp->mod_name);
1891 }
1892
1893 (void) pthread_kill(tid, fmd.d_thr_sig);
1894 fmd_module_unlock(mp);
1895 }
1896
1897 void
1898 fmd_thr_checkpoint(fmd_hdl_t *hdl)
1899 {
1900 fmd_module_t *mp = fmd_api_module_lock(hdl);
1901 pthread_t tid = pthread_self();
1902
1903 if (tid == mp->mod_thread->thr_tid ||
1904 fmd_idspace_getspecific(mp->mod_threads, tid) == NULL) {
1905 fmd_api_error(mp, EFMD_THR_INVAL, "tid %u is not a valid "
1906 "auxiliary thread id for module %s\n", tid, mp->mod_name);
1907 }
1908
1909 fmd_ckpt_save(mp);
1910
1911 fmd_module_unlock(mp);
1912 }
1913
1914 /*ARGSUSED3*/
1915 int
1916 fmd_doorthr_create(door_info_t *dip, void *(*crf)(void *), void *crarg,
1917 void *cookie)
1918 {
1919 fmd_thread_t *old_tp, *new_tp;
1920 fmd_module_t *mp;
1921 pthread_t tid;
1922
1923 /*
1924 * We're called either during initial door_xcreate or during
1925 * a depletion callback. In both cases the current thread
1926 * is already setup so we can retrieve the fmd_thread_t.
1927 * If not then we panic. The new thread will be associated with
1928 * the same module as the old.
1929 *
1930 * If dip == NULL we're being called as part of the
1931 * sysevent_bind_subscriber hack - see comments there.
1932 */
1933 if ((old_tp = pthread_getspecific(fmd.d_key)) == NULL)
1934 fmd_panic("fmd_doorthr_create from unrecognized thread\n");
1935
1936 mp = old_tp->thr_mod;
1937 (void) fmd_api_module_lock((fmd_hdl_t *)mp);
1938
1939 if (dip && mp->mod_stats->ms_doorthrtotal.fmds_value.ui32 >=
1940 mp->mod_stats->ms_doorthrlimit.fmds_value.ui32) {
1941 fmd_module_unlock(mp);
1942 (void) fmd_dprintf(FMD_DBG_XPRT, "door server %s for %p "
1943 "not attemped - at max\n",
1944 dip->di_attributes & DOOR_DEPLETION_CB ?
1945 "depletion callback" : "startup", (void *)dip);
1946 return (0);
1947 }
1948
1949 if ((new_tp = fmd_doorthread_create(mp, (fmd_thread_f *)crf,
1950 crarg)) != NULL) {
1951 tid = new_tp->thr_tid;
1952 mp->mod_stats->ms_doorthrtotal.fmds_value.ui32++;
1953 (void) fmd_idspace_xalloc(mp->mod_threads, tid, new_tp);
1954 }
1955
1956 fmd_module_unlock(mp);
1957
1958 if (dip) {
1959 fmd_dprintf(FMD_DBG_XPRT, "door server startup for %p %s\n",
1960 (void *)dip, new_tp ? "successful" : "failed");
1961 }
1962
1963 return (new_tp ? 1 : -1);
1964 }
1965
1966 /*ARGSUSED*/
1967 void
1968 fmd_doorthr_setup(void *cookie)
1969 {
1970 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
1971 }
1972
1973 id_t
1974 fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
1975 {
1976 fmd_module_t *mp = fmd_api_module_lock(hdl);
1977 fmd_modtimer_t *t;
1978 id_t id;
1979
1980 if (delta < 0) {
1981 fmd_api_error(mp, EFMD_TIMER_INVAL,
1982 "timer delta %lld is not a valid interval\n", delta);
1983 }
1984
1985 t = fmd_alloc(sizeof (fmd_modtimer_t), FMD_SLEEP);
1986 t->mt_mod = mp;
1987 t->mt_arg = arg;
1988 t->mt_id = -1;
1989
1990 if ((id = fmd_timerq_install(fmd.d_timers, mp->mod_timerids,
1991 (fmd_timer_f *)fmd_module_timeout, t, ep, delta)) == -1) {
1992 fmd_free(t, sizeof (fmd_modtimer_t));
1993 fmd_api_error(mp, EFMD_TIMER_LIMIT,
1994 "failed to install timer +%lld", delta);
1995 }
1996
1997 fmd_module_unlock(mp);
1998 return (id);
1999 }
2000
2001 void
2002 fmd_timer_remove(fmd_hdl_t *hdl, id_t id)
2003 {
2004 fmd_module_t *mp = fmd_api_module_lock(hdl);
2005 fmd_modtimer_t *t;
2006
2007 if (!fmd_idspace_valid(mp->mod_timerids, id)) {
2008 fmd_api_error(mp, EFMD_TIMER_INVAL,
2009 "id %ld is not a valid timer id\n", id);
2010 }
2011
2012 /*
2013 * If the timer has not fired (t != NULL), remove it from the timer
2014 * queue. If the timer has fired (t == NULL), we could be in one of
2015 * two situations: a) we are processing the timer callback or b)
2016 * the timer event is on the module queue awaiting dispatch. For a),
2017 * fmd_timerq_remove() will wait for the timer callback function
2018 * to complete and queue an event for dispatch. For a) and b),
2019 * we cancel the outstanding timer event from the module's dispatch
2020 * queue.
2021 */
2022 if ((t = fmd_timerq_remove(fmd.d_timers, mp->mod_timerids, id)) != NULL)
2023 fmd_free(t, sizeof (fmd_modtimer_t));
2024 fmd_module_unlock(mp);
2025
2026 fmd_eventq_cancel(mp->mod_queue, FMD_EVT_TIMEOUT, (void *)id);
2027 }
2028
2029 static nvlist_t *
2030 fmd_nvl_create_suspect(fmd_hdl_t *hdl, const char *class,
2031 uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc,
2032 const char *pfx, boolean_t chkpfx)
2033 {
2034 fmd_module_t *mp;
2035 nvlist_t *nvl;
2036
2037 mp = fmd_api_module_lock(hdl);
2038 if (class == NULL || class[0] == '\0' ||
2039 chkpfx == B_TRUE && strncmp(class, pfx, strlen(pfx)) != 0)
2040 fmd_api_error(mp, EFMD_NVL_INVAL, "invalid %s class: '%s'\n",
2041 pfx, class ? class : "(empty)");
2042
2043 nvl = fmd_protocol_fault(class, certainty, asru, fru, rsrc, NULL);
2044
2045 fmd_module_unlock(mp);
2046
2047 return (nvl);
2048 }
2049
2050 nvlist_t *
2051 fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class,
2052 uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
2053 {
2054 /*
2055 * We can't enforce that callers only specifiy classes matching
2056 * fault.* since there are already a number of modules that
2057 * use fmd_nvl_create_fault to create a defect event. Since
2058 * fmd_nvl_create_{fault,defect} are equivalent, for now anyway,
2059 * no harm is done. So call fmd_nvl_create_suspect with last
2060 * argument B_FALSE.
2061 */
2062 return (fmd_nvl_create_suspect(hdl, class, certainty, asru,
2063 fru, rsrc, FM_FAULT_CLASS ".", B_FALSE));
2064 }
2065
2066 nvlist_t *
2067 fmd_nvl_create_defect(fmd_hdl_t *hdl, const char *class,
2068 uint8_t certainty, nvlist_t *asru, nvlist_t *fru, nvlist_t *rsrc)
2069 {
2070 return (fmd_nvl_create_suspect(hdl, class, certainty, asru,
2071 fru, rsrc, FM_DEFECT_CLASS ".", B_TRUE));
2072 }
2073
2074 const nvlist_t *
2075 fmd_hdl_fmauth(fmd_hdl_t *hdl)
2076 {
2077 fmd_module_t *mp = fmd_api_module_lock(hdl);
2078 const nvlist_t *auth;
2079
2080 auth = (const nvlist_t *)fmd.d_rmod->mod_fmri;
2081
2082 fmd_module_unlock(mp);
2083
2084 return (auth);
2085 }
2086
2087 const nvlist_t *
2088 fmd_hdl_modauth(fmd_hdl_t *hdl)
2089 {
2090 fmd_module_t *mp = fmd_api_module_lock(hdl);
2091 const nvlist_t *auth;
2092
2093 auth = (const nvlist_t *)mp->mod_fmri;
2094
2095 fmd_module_unlock(mp);
2096
2097 return (auth);
2098 }
2099
2100
2101 int
2102 fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
2103 {
2104 fmd_module_t *mp = fmd_api_module_lock(hdl);
2105 char *class;
2106 int rv;
2107
2108 rv = (nvl != NULL && nvlist_lookup_string(nvl,
2109 FM_CLASS, &class) == 0 && fmd_strmatch(class, pattern));
2110
2111 fmd_module_unlock(mp);
2112 return (rv);
2113 }
2114
2115 int
2116 fmd_nvl_fmri_expand(fmd_hdl_t *hdl, nvlist_t *nvl)
2117 {
2118 fmd_module_t *mp = fmd_api_module_lock(hdl);
2119 int rv;
2120
2121 if (nvl == NULL) {
2122 fmd_api_error(mp, EFMD_NVL_INVAL,
2123 "invalid nvlist %p\n", (void *)nvl);
2124 }
2125
2126 rv = fmd_fmri_expand(nvl);
2127 fmd_module_unlock(mp);
2128 return (rv);
2129 }
2130
2131 int
2132 fmd_nvl_fmri_present(fmd_hdl_t *hdl, nvlist_t *nvl)
2133 {
2134 fmd_module_t *mp = fmd_api_module_lock(hdl);
2135 int rv;
2136
2137 if (nvl == NULL) {
2138 fmd_api_error(mp, EFMD_NVL_INVAL,
2139 "invalid nvlist %p\n", (void *)nvl);
2140 }
2141
2142 rv = fmd_fmri_present(nvl);
2143 fmd_module_unlock(mp);
2144
2145 if (rv < 0) {
2146 fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
2147 "fmd_nvl_fmri_present\n");
2148 }
2149
2150 return (rv);
2151 }
2152
2153 int
2154 fmd_nvl_fmri_replaced(fmd_hdl_t *hdl, nvlist_t *nvl)
2155 {
2156 fmd_module_t *mp = fmd_api_module_lock(hdl);
2157 int rv;
2158
2159 if (nvl == NULL) {
2160 fmd_api_error(mp, EFMD_NVL_INVAL,
2161 "invalid nvlist %p\n", (void *)nvl);
2162 }
2163
2164 rv = fmd_fmri_replaced(nvl);
2165 fmd_module_unlock(mp);
2166
2167 return (rv);
2168 }
2169
2170 int
2171 fmd_nvl_fmri_unusable(fmd_hdl_t *hdl, nvlist_t *nvl)
2172 {
2173 fmd_module_t *mp = fmd_api_module_lock(hdl);
2174 int rv;
2175
2176 if (nvl == NULL) {
2177 fmd_api_error(mp, EFMD_NVL_INVAL,
2178 "invalid nvlist %p\n", (void *)nvl);
2179 }
2180
2181 rv = fmd_fmri_unusable(nvl);
2182 fmd_module_unlock(mp);
2183
2184 if (rv < 0) {
2185 fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
2186 "fmd_nvl_fmri_unusable\n");
2187 }
2188
2189 return (rv);
2190 }
2191
2192 int
2193 fmd_nvl_fmri_retire(fmd_hdl_t *hdl, nvlist_t *nvl)
2194 {
2195 fmd_module_t *mp = fmd_api_module_lock(hdl);
2196 int rv;
2197
2198 if (nvl == NULL) {
2199 fmd_api_error(mp, EFMD_NVL_INVAL,
2200 "invalid nvlist %p\n", (void *)nvl);
2201 }
2202
2203 rv = fmd_fmri_retire(nvl);
2204 fmd_module_unlock(mp);
2205
2206 return (rv);
2207 }
2208
2209 int
2210 fmd_nvl_fmri_unretire(fmd_hdl_t *hdl, nvlist_t *nvl)
2211 {
2212 fmd_module_t *mp = fmd_api_module_lock(hdl);
2213 int rv;
2214
2215 if (nvl == NULL) {
2216 fmd_api_error(mp, EFMD_NVL_INVAL,
2217 "invalid nvlist %p\n", (void *)nvl);
2218 }
2219
2220 rv = fmd_fmri_unretire(nvl);
2221 fmd_module_unlock(mp);
2222
2223 return (rv);
2224 }
2225
2226 int
2227 fmd_nvl_fmri_service_state(fmd_hdl_t *hdl, nvlist_t *nvl)
2228 {
2229 fmd_module_t *mp = fmd_api_module_lock(hdl);
2230 int rv;
2231
2232 if (nvl == NULL) {
2233 fmd_api_error(mp, EFMD_NVL_INVAL,
2234 "invalid nvlist %p\n", (void *)nvl);
2235 }
2236
2237 rv = fmd_fmri_service_state(nvl);
2238 if (rv < 0)
2239 rv = fmd_fmri_unusable(nvl) ? FMD_SERVICE_STATE_UNUSABLE :
2240 FMD_SERVICE_STATE_OK;
2241 fmd_module_unlock(mp);
2242
2243 if (rv < 0) {
2244 fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
2245 "fmd_nvl_fmri_service_state\n");
2246 }
2247
2248 return (rv);
2249 }
2250
2251 typedef struct {
2252 const char *class;
2253 int *rvp;
2254 } fmd_has_fault_arg_t;
2255
2256 static void
2257 fmd_rsrc_has_fault(fmd_asru_link_t *alp, void *arg)
2258 {
2259 fmd_has_fault_arg_t *fhfp = (fmd_has_fault_arg_t *)arg;
2260 char *class;
2261
2262 if (fhfp->class == NULL) {
2263 if (alp->al_flags & FMD_ASRU_FAULTY)
2264 *fhfp->rvp = 1;
2265 } else {
2266 if ((alp->al_flags & FMD_ASRU_FAULTY) &&
2267 alp->al_event != NULL && nvlist_lookup_string(alp->al_event,
2268 FM_CLASS, &class) == 0 && fmd_strmatch(class, fhfp->class))
2269 *fhfp->rvp = 1;
2270 }
2271 }
2272
2273 int
2274 fmd_nvl_fmri_has_fault(fmd_hdl_t *hdl, nvlist_t *nvl, int type, char *class)
2275 {
2276 fmd_module_t *mp = fmd_api_module_lock(hdl);
2277 fmd_asru_hash_t *ahp = fmd.d_asrus;
2278 int rv = 0;
2279 char *name;
2280 int namelen;
2281 fmd_has_fault_arg_t fhf;
2282
2283 if (nvl == NULL) {
2284 fmd_api_error(mp, EFMD_NVL_INVAL,
2285 "invalid nvlist %p\n", (void *)nvl);
2286 }
2287 if ((namelen = fmd_fmri_nvl2str(nvl, NULL, 0)) == -1)
2288 fmd_api_error(mp, EFMD_NVL_INVAL,
2289 "invalid nvlist: %p\n", (void *)nvl);
2290 name = fmd_alloc(namelen + 1, FMD_SLEEP);
2291 if (fmd_fmri_nvl2str(nvl, name, namelen + 1) == -1) {
2292 if (name != NULL)
2293 fmd_free(name, namelen + 1);
2294 fmd_api_error(mp, EFMD_NVL_INVAL,
2295 "invalid nvlist: %p\n", (void *)nvl);
2296 }
2297
2298 fhf.class = class;
2299 fhf.rvp = &rv;
2300 if (type == FMD_HAS_FAULT_RESOURCE)
2301 fmd_asru_hash_apply_by_rsrc(ahp, name, fmd_rsrc_has_fault,
2302 &fhf);
2303 else if (type == FMD_HAS_FAULT_ASRU)
2304 fmd_asru_hash_apply_by_asru(ahp, name, fmd_rsrc_has_fault,
2305 &fhf);
2306 else if (type == FMD_HAS_FAULT_FRU)
2307 fmd_asru_hash_apply_by_fru(ahp, name, fmd_rsrc_has_fault,
2308 &fhf);
2309
2310 if (name != NULL)
2311 fmd_free(name, namelen + 1);
2312 fmd_module_unlock(mp);
2313 return (rv);
2314 }
2315
2316 int
2317 fmd_nvl_fmri_contains(fmd_hdl_t *hdl, nvlist_t *n1, nvlist_t *n2)
2318 {
2319 fmd_module_t *mp = fmd_api_module_lock(hdl);
2320 int rv;
2321
2322 if (n1 == NULL || n2 == NULL) {
2323 fmd_api_error(mp, EFMD_NVL_INVAL,
2324 "invalid nvlist(s): %p, %p\n", (void *)n1, (void *)n2);
2325 }
2326
2327 rv = fmd_fmri_contains(n1, n2);
2328 fmd_module_unlock(mp);
2329
2330 if (rv < 0) {
2331 fmd_api_error(mp, EFMD_FMRI_OP, "invalid fmri for "
2332 "fmd_nvl_fmri_contains\n");
2333 }
2334
2335 return (rv);
2336 }
2337
2338 nvlist_t *
2339 fmd_nvl_fmri_translate(fmd_hdl_t *hdl, nvlist_t *fmri, nvlist_t *auth)
2340 {
2341 fmd_module_t *mp = fmd_api_module_lock(hdl);
2342 nvlist_t *xfmri;
2343
2344 if (fmri == NULL || auth == NULL) {
2345 fmd_api_error(mp, EFMD_NVL_INVAL,
2346 "invalid nvlist(s): %p, %p\n", (void *)fmri, (void *)auth);
2347 }
2348
2349 xfmri = fmd_fmri_translate(fmri, auth);
2350 fmd_module_unlock(mp);
2351 return (xfmri);
2352 }
2353
2354 static int
2355 fmd_nvl_op_init(nv_alloc_t *ops, va_list ap)
2356 {
2357 fmd_module_t *mp = va_arg(ap, fmd_module_t *);
2358
2359 ops->nva_arg = mp;
2360
2361 return (0);
2362 }
2363
2364 static void *
2365 fmd_nvl_op_alloc_sleep(nv_alloc_t *ops, size_t size)
2366 {
2367 fmd_module_t *mp = ops->nva_arg;
2368
2369 return (fmd_hdl_alloc_locked(mp, size, FMD_SLEEP));
2370 }
2371
2372 static void *
2373 fmd_nvl_op_alloc_nosleep(nv_alloc_t *ops, size_t size)
2374 {
2375 fmd_module_t *mp = ops->nva_arg;
2376
2377 return (fmd_hdl_alloc_locked(mp, size, FMD_NOSLEEP));
2378 }
2379
2380 static void
2381 fmd_nvl_op_free(nv_alloc_t *ops, void *data, size_t size)
2382 {
2383 fmd_module_t *mp = ops->nva_arg;
2384
2385 fmd_hdl_free_locked(mp, data, size);
2386 }
2387
2388 nv_alloc_ops_t fmd_module_nva_ops_sleep = {
2389 fmd_nvl_op_init,
2390 NULL,
2391 fmd_nvl_op_alloc_sleep,
2392 fmd_nvl_op_free,
2393 NULL
2394 };
2395
2396 nv_alloc_ops_t fmd_module_nva_ops_nosleep = {
2397 fmd_nvl_op_init,
2398 NULL,
2399 fmd_nvl_op_alloc_nosleep,
2400 fmd_nvl_op_free,
2401 NULL
2402 };
2403
2404 nvlist_t *
2405 fmd_nvl_alloc(fmd_hdl_t *hdl, int flags)
2406 {
2407 fmd_module_t *mp = fmd_api_module_lock(hdl);
2408 nv_alloc_t *nva;
2409 nvlist_t *nvl;
2410 int ret;
2411
2412 if (flags == FMD_SLEEP)
2413 nva = &mp->mod_nva_sleep;
2414 else
2415 nva = &mp->mod_nva_nosleep;
2416
2417 ret = nvlist_xalloc(&nvl, NV_UNIQUE_NAME, nva);
2418
2419 fmd_module_unlock(mp);
2420
2421 if (ret != 0)
2422 return (NULL);
2423 else
2424 return (nvl);
2425 }
2426
2427 nvlist_t *
2428 fmd_nvl_dup(fmd_hdl_t *hdl, nvlist_t *src, int flags)
2429 {
2430 fmd_module_t *mp = fmd_api_module_lock(hdl);
2431 nv_alloc_t *nva;
2432 nvlist_t *nvl;
2433 int ret;
2434
2435 if (flags == FMD_SLEEP)
2436 nva = &mp->mod_nva_sleep;
2437 else
2438 nva = &mp->mod_nva_nosleep;
2439
2440 ret = nvlist_xdup(src, &nvl, nva);
2441
2442 fmd_module_unlock(mp);
2443
2444 if (ret != 0)
2445 return (NULL);
2446 else
2447 return (nvl);
2448 }
2449
2450 /*ARGSUSED*/
2451 void
2452 fmd_repair_fru(fmd_hdl_t *hdl, const char *fmri)
2453 {
2454 int err;
2455 fmd_asru_rep_arg_t fara;
2456
2457 fara.fara_reason = FMD_ASRU_REPAIRED;
2458 fara.fara_bywhat = FARA_BY_FRU;
2459 fara.fara_rval = &err;
2460 fmd_asru_hash_apply_by_fru(fmd.d_asrus, (char *)fmri,
2461 fmd_asru_repaired, &fara);
2462 }
2463
2464 /*ARGSUSED*/
2465 int
2466 fmd_repair_asru(fmd_hdl_t *hdl, const char *fmri)
2467 {
2468 int err = FARA_ERR_RSRCNOTF;
2469 fmd_asru_rep_arg_t fara;
2470
2471 fara.fara_reason = FMD_ASRU_REPAIRED;
2472 fara.fara_rval = &err;
2473 fara.fara_uuid = NULL;
2474 fara.fara_bywhat = FARA_BY_ASRU;
2475 fmd_asru_hash_apply_by_asru(fmd.d_asrus, fmri,
2476 fmd_asru_repaired, &fara);
2477 return (err);
2478 }
2479
2480 int
2481 fmd_event_local(fmd_hdl_t *hdl, fmd_event_t *ep)
2482 {
2483 if (hdl == NULL || ep == NULL) {
2484 fmd_api_error(fmd_api_module_lock(hdl), EFMD_EVENT_INVAL,
2485 "NULL parameter specified to fmd_event_local\n");
2486 }
2487
2488 return (((fmd_event_impl_t *)ep)->ev_flags & FMD_EVF_LOCAL);
2489 }
2490
2491 /*ARGSUSED*/
2492 uint64_t
2493 fmd_event_ena_create(fmd_hdl_t *hdl)
2494 {
2495 return (fmd_ena());
2496 }
2497
2498 fmd_xprt_t *
2499 fmd_xprt_open(fmd_hdl_t *hdl, uint_t flags, nvlist_t *auth, void *data)
2500 {
2501 fmd_module_t *mp = fmd_api_module_lock(hdl);
2502 fmd_xprt_t *xp;
2503
2504 if (flags & ~FMD_XPRT_CMASK) {
2505 fmd_api_error(mp, EFMD_XPRT_INVAL,
2506 "invalid transport flags 0x%x\n", flags);
2507 }
2508
2509 if ((flags & FMD_XPRT_RDWR) != FMD_XPRT_RDWR &&
2510 (flags & FMD_XPRT_RDWR) != FMD_XPRT_RDONLY) {
2511 fmd_api_error(mp, EFMD_XPRT_INVAL,
2512 "cannot open write-only transport\n");
2513 }
2514
2515 if (mp->mod_stats->ms_xprtopen.fmds_value.ui32 >=
2516 mp->mod_stats->ms_xprtlimit.fmds_value.ui32) {
2517 fmd_api_error(mp, EFMD_XPRT_LIMIT, "%s request to create a "
2518 "transport exceeds module transport limit (%u)\n",
2519 mp->mod_name, mp->mod_stats->ms_xprtlimit.fmds_value.ui32);
2520 }
2521
2522 if ((xp = fmd_xprt_create(mp, flags, auth, data)) == NULL)
2523 fmd_api_error(mp, errno, "cannot create transport");
2524
2525 fmd_module_unlock(mp);
2526 return (xp);
2527 }
2528
2529 void
2530 fmd_xprt_close(fmd_hdl_t *hdl, fmd_xprt_t *xp)
2531 {
2532 fmd_module_t *mp = fmd_api_module_lock(hdl);
2533 fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
2534
2535 /*
2536 * Although this could be supported, it doesn't seem necessary or worth
2537 * the trouble. For now, just detect this and trigger a module abort.
2538 * If it is needed, transports should grow reference counts and a new
2539 * event type will need to be enqueued for the main thread to reap it.
2540 */
2541 if (xip->xi_thread != NULL &&
2542 xip->xi_thread->thr_tid == pthread_self()) {
2543 fmd_api_error(mp, EFMD_XPRT_INVAL,
2544 "fmd_xprt_close() cannot be called from fmdo_send()\n");
2545 }
2546
2547 fmd_xprt_destroy(xp);
2548 fmd_module_unlock(mp);
2549 }
2550
2551 void
2552 fmd_xprt_post(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt)
2553 {
2554 nv_alloc_t *nva = nvlist_lookup_nv_alloc(nvl);
2555 fmd_module_t *mp = fmd_api_module(hdl);
2556 fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
2557 nvlist_t *tmp;
2558
2559 /*
2560 * If this event was allocated using the module-specific nvlist ops, we
2561 * need to create a copy using the standard fmd nvlist ops. Otherwise,
2562 * the event may persist after the module has been unloaded and we'll
2563 * die when attempting to free the nvlist.
2564 */
2565 if (nva == &mp->mod_nva_sleep || nva == &mp->mod_nva_nosleep) {
2566 (void) nvlist_xdup(nvl, &tmp, &fmd.d_nva);
2567 nvlist_free(nvl);
2568 nvl = tmp;
2569 }
2570
2571 /*
2572 * fmd_xprt_recv() must block during startup waiting for fmd to globally
2573 * clear FMD_XPRT_DSUSPENDED. As such, we can't allow it to be called
2574 * from a module's _fmd_init() routine, because that would block
2575 * fmd from completing initial module loading, resulting in a deadlock.
2576 */
2577 if ((xip->xi_flags & FMD_XPRT_ISUSPENDED) &&
2578 (pthread_self() == xip->xi_queue->eq_mod->mod_thread->thr_tid)) {
2579 fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL,
2580 "fmd_xprt_post() cannot be called from _fmd_init()\n");
2581 }
2582
2583 fmd_xprt_recv(xp, nvl, hrt, FMD_B_FALSE);
2584 }
2585
2586 void
2587 fmd_xprt_log(fmd_hdl_t *hdl, fmd_xprt_t *xp, nvlist_t *nvl, hrtime_t hrt)
2588 {
2589 fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
2590
2591 /*
2592 * fmd_xprt_recv() must block during startup waiting for fmd to globally
2593 * clear FMD_XPRT_DSUSPENDED. As such, we can't allow it to be called
2594 * from a module's _fmd_init() routine, because that would block
2595 * fmd from completing initial module loading, resulting in a deadlock.
2596 */
2597 if ((xip->xi_flags & FMD_XPRT_ISUSPENDED) &&
2598 (pthread_self() == xip->xi_queue->eq_mod->mod_thread->thr_tid)) {
2599 fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL,
2600 "fmd_xprt_log() cannot be called from _fmd_init()\n");
2601 }
2602
2603 fmd_xprt_recv(xp, nvl, hrt, FMD_B_TRUE);
2604 }
2605
2606 void
2607 fmd_xprt_suspend(fmd_hdl_t *hdl, fmd_xprt_t *xp)
2608 {
2609 (void) fmd_api_transport_impl(hdl, xp); /* validate 'xp' */
2610 fmd_xprt_xsuspend(xp, FMD_XPRT_SUSPENDED);
2611 }
2612
2613 void
2614 fmd_xprt_resume(fmd_hdl_t *hdl, fmd_xprt_t *xp)
2615 {
2616 (void) fmd_api_transport_impl(hdl, xp); /* validate 'xp' */
2617 fmd_xprt_xresume(xp, FMD_XPRT_SUSPENDED);
2618 }
2619
2620 int
2621 fmd_xprt_error(fmd_hdl_t *hdl, fmd_xprt_t *xp)
2622 {
2623 fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
2624 return (xip->xi_state == _fmd_xprt_state_err);
2625 }
2626
2627 /*
2628 * Translate all FMRIs in the specified name-value pair list for the specified
2629 * FMRI authority, and return a new name-value pair list for the translation.
2630 * This function is the recursive engine used by fmd_xprt_translate(), below.
2631 */
2632 static nvlist_t *
2633 fmd_xprt_xtranslate(nvlist_t *nvl, nvlist_t *auth)
2634 {
2635 uint_t i, j, n;
2636 nvpair_t *nvp, **nvps;
2637 uint_t nvpslen = 0;
2638 char *name;
2639 size_t namelen = 0;
2640
2641 nvlist_t **a, **b;
2642 nvlist_t *l, *r;
2643 data_type_t type;
2644 char *s;
2645 int err;
2646
2647 (void) nvlist_xdup(nvl, &nvl, &fmd.d_nva);
2648
2649 /*
2650 * Count up the number of name-value pairs in 'nvl' and compute the
2651 * maximum length of a name used in this list for use below.
2652 */
2653 for (nvp = nvlist_next_nvpair(nvl, NULL);
2654 nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp), nvpslen++) {
2655 size_t len = strlen(nvpair_name(nvp));
2656 namelen = MAX(namelen, len);
2657 }
2658
2659 nvps = alloca(sizeof (nvpair_t *) * nvpslen);
2660 name = alloca(namelen + 1);
2661
2662 /*
2663 * Store a snapshot of the name-value pairs in 'nvl' into nvps[] so
2664 * that we can iterate over the original pairs in the loop below while
2665 * performing arbitrary insert and delete operations on 'nvl' itself.
2666 */
2667 for (i = 0, nvp = nvlist_next_nvpair(nvl, NULL);
2668 nvp != NULL; nvp = nvlist_next_nvpair(nvl, nvp))
2669 nvps[i++] = nvp;
2670
2671 /*
2672 * Now iterate over the snapshot of the name-value pairs. If we find a
2673 * value that is of type NVLIST or NVLIST_ARRAY, we translate that
2674 * object by either calling ourself recursively on it, or calling into
2675 * fmd_fmri_translate() if the object is an FMRI. We then rip out the
2676 * original name-value pair and replace it with the translated one.
2677 */
2678 for (i = 0; i < nvpslen; i++) {
2679 nvp = nvps[i];
2680 type = nvpair_type(nvp);
2681
2682 switch (type) {
2683 case DATA_TYPE_NVLIST_ARRAY:
2684 if (nvpair_value_nvlist_array(nvp, &a, &n) != 0 ||
2685 a == NULL || n == 0)
2686 continue; /* array is zero-sized; skip it */
2687
2688 b = fmd_alloc(sizeof (nvlist_t *) * n, FMD_SLEEP);
2689
2690 /*
2691 * If the first array nvlist element looks like an FMRI
2692 * then assume the other elements are FMRIs as well.
2693 * If any b[j]'s can't be translated, then EINVAL will
2694 * be returned from nvlist_add_nvlist_array() below.
2695 */
2696 if (nvlist_lookup_string(*a, FM_FMRI_SCHEME, &s) == 0) {
2697 for (j = 0; j < n; j++)
2698 b[j] = fmd_fmri_translate(a[j], auth);
2699 } else {
2700 for (j = 0; j < n; j++)
2701 b[j] = fmd_xprt_xtranslate(a[j], auth);
2702 }
2703
2704 (void) strcpy(name, nvpair_name(nvp));
2705 (void) nvlist_remove(nvl, name, type);
2706 err = nvlist_add_nvlist_array(nvl, name, b, n);
2707
2708 for (j = 0; j < n; j++)
2709 nvlist_free(b[j]);
2710
2711 fmd_free(b, sizeof (nvlist_t *) * n);
2712
2713 if (err != 0) {
2714 nvlist_free(nvl);
2715 errno = err;
2716 return (NULL);
2717 }
2718 break;
2719
2720 case DATA_TYPE_NVLIST:
2721 if (nvpair_value_nvlist(nvp, &l) == 0 &&
2722 nvlist_lookup_string(l, FM_FMRI_SCHEME, &s) == 0)
2723 r = fmd_fmri_translate(l, auth);
2724 else
2725 r = fmd_xprt_xtranslate(l, auth);
2726
2727 if (r == NULL) {
2728 nvlist_free(nvl);
2729 return (NULL);
2730 }
2731
2732 (void) strcpy(name, nvpair_name(nvp));
2733 (void) nvlist_remove(nvl, name, type);
2734 (void) nvlist_add_nvlist(nvl, name, r);
2735
2736 nvlist_free(r);
2737 break;
2738 }
2739 }
2740
2741 return (nvl);
2742 }
2743
2744 nvlist_t *
2745 fmd_xprt_translate(fmd_hdl_t *hdl, fmd_xprt_t *xp, fmd_event_t *ep)
2746 {
2747 fmd_xprt_impl_t *xip = fmd_api_transport_impl(hdl, xp);
2748
2749 if (xip->xi_auth == NULL) {
2750 fmd_api_error(fmd_api_module_lock(hdl), EFMD_XPRT_INVAL,
2751 "no authority defined for transport %p\n", (void *)xp);
2752 }
2753
2754 return (fmd_xprt_xtranslate(FMD_EVENT_NVL(ep), xip->xi_auth));
2755 }
2756
2757 /*ARGSUSED*/
2758 void
2759 fmd_xprt_add_domain(fmd_hdl_t *hdl, nvlist_t *nvl, char *domain)
2760 {
2761 nvpair_t *nvp, *nvp2;
2762 nvlist_t *nvl2, *nvl3;
2763 char *class;
2764
2765 if (nvl == NULL || domain == NULL)
2766 return;
2767 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
2768 nvp = nvlist_next_nvpair(nvl, nvp)) {
2769 if (strcmp(nvpair_name(nvp), FM_CLASS) == 0) {
2770 (void) nvpair_value_string(nvp, &class);
2771 if (strcmp(class, FM_LIST_SUSPECT_CLASS) != 0)
2772 return;
2773 }
2774 }
2775 for (nvp = nvlist_next_nvpair(nvl, NULL); nvp != NULL;
2776 nvp = nvlist_next_nvpair(nvl, nvp)) {
2777 if (strcmp(nvpair_name(nvp), FM_SUSPECT_DE) == 0) {
2778 (void) nvpair_value_nvlist(nvp, &nvl2);
2779 for (nvp2 = nvlist_next_nvpair(nvl2, NULL);
2780 nvp2 != NULL;
2781 nvp2 = nvlist_next_nvpair(nvl2, nvp2)) {
2782 if (strcmp(nvpair_name(nvp2),
2783 FM_FMRI_AUTHORITY) == 0) {
2784 (void) nvpair_value_nvlist(nvp2, &nvl3);
2785 (void) nvlist_add_string(nvl3,
2786 FM_FMRI_AUTH_DOMAIN, domain);
2787 break;
2788 }
2789 }
2790 break;
2791 }
2792 }
2793 }
2794
2795 void
2796 fmd_xprt_setspecific(fmd_hdl_t *hdl, fmd_xprt_t *xp, void *data)
2797 {
2798 fmd_api_transport_impl(hdl, xp)->xi_data = data;
2799 }
2800
2801 void *
2802 fmd_xprt_getspecific(fmd_hdl_t *hdl, fmd_xprt_t *xp)
2803 {
2804 return (fmd_api_transport_impl(hdl, xp)->xi_data);
2805 }