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