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) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 /*
  27  * FMA event subscription interfaces - subscribe to FMA protocol
  28  * from outside the fault manager.
  29  */
  30 
  31 #include <sys/types.h>
  32 #include <atomic.h>
  33 #include <libsysevent.h>
  34 #include <libuutil.h>
  35 #include <pthread.h>
  36 #include <stdarg.h>
  37 #include <stdlib.h>
  38 #include <string.h>
  39 #include <strings.h>
  40 #include <unistd.h>
  41 #include <fm/libtopo.h>
  42 
  43 #include <fm/libfmevent.h>
  44 
  45 #include "fmev_impl.h"
  46 
  47 static topo_hdl_t *g_topohdl;
  48 
  49 typedef struct {
  50         struct fmev_hdl_cmn sh_cmn;
  51         evchan_t *sh_binding;
  52         uu_avl_pool_t *sh_pool;
  53         uu_avl_t *sh_avl;
  54         uint32_t sh_subcnt;
  55         uint32_t sh_flags;
  56         sysevent_subattr_t *sh_attr;
  57         pthread_mutex_t sh_lock;
  58         pthread_mutex_t sh_srlz_lock;
  59 } fmev_shdl_impl_t;
  60 
  61 #define HDL2IHDL(hdl)   ((fmev_shdl_impl_t *)(hdl))
  62 #define IHDL2HDL(ihdl)  ((fmev_shdl_t)(ihdl))
  63 
  64 #define _FMEV_SHMAGIC   0x5368446c      /* ShDl */
  65 #define FMEV_SHDL_VALID(ihdl)   ((ihdl)->sh_cmn.hc_magic == _FMEV_SHMAGIC)
  66 
  67 #define SHDL_FL_SERIALIZE       0x1
  68 
  69 #define FMEV_API_ENTER(hdl, v) \
  70         fmev_api_enter(&HDL2IHDL(hdl)->sh_cmn, LIBFMEVENT_VERSION_##v)
  71 
  72 /*
  73  * For each subscription on a handle we add a node to an avl tree
  74  * to track subscriptions.
  75  */
  76 
  77 #define FMEV_SID_SZ     (16 + 1)        /* Matches MAX_SUBID_LEN */
  78 
  79 struct fmev_subinfo {
  80         uu_avl_node_t si_node;
  81         fmev_shdl_impl_t *si_ihdl;
  82         char si_pat[FMEV_MAX_CLASS];
  83         char si_sid[FMEV_SID_SZ];
  84         fmev_cbfunc_t *si_cb;
  85         void *si_cbarg;
  86 };
  87 
  88 struct fmev_hdl_cmn *
  89 fmev_shdl_cmn(fmev_shdl_t hdl)
  90 {
  91         return (&HDL2IHDL(hdl)->sh_cmn);
  92 }
  93 
  94 static int
  95 shdlctl_start(fmev_shdl_impl_t *ihdl)
  96 {
  97         (void) pthread_mutex_lock(&ihdl->sh_lock);
  98 
  99         if (ihdl->sh_subcnt == 0) {
 100                 return (1);     /* lock still held */
 101         } else {
 102                 (void) pthread_mutex_unlock(&ihdl->sh_lock);
 103                 return (0);
 104         }
 105 }
 106 
 107 static void
 108 shdlctl_end(fmev_shdl_impl_t *ihdl)
 109 {
 110         (void) pthread_mutex_unlock(&ihdl->sh_lock);
 111 }
 112 
 113 fmev_err_t
 114 fmev_shdlctl_serialize(fmev_shdl_t hdl)
 115 {
 116         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 117 
 118         if (!FMEV_API_ENTER(hdl, 1))
 119                 return (fmev_errno);
 120 
 121         if (!shdlctl_start(ihdl))
 122                 return (fmev_seterr(FMEVERR_BUSY));
 123 
 124         if (!(ihdl->sh_flags & SHDL_FL_SERIALIZE)) {
 125                 (void) pthread_mutex_init(&ihdl->sh_srlz_lock, NULL);
 126                 ihdl->sh_flags |= SHDL_FL_SERIALIZE;
 127         }
 128 
 129         shdlctl_end(ihdl);
 130         return (fmev_seterr(FMEV_SUCCESS));
 131 }
 132 
 133 fmev_err_t
 134 fmev_shdlctl_thrattr(fmev_shdl_t hdl, pthread_attr_t *attr)
 135 {
 136         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 137 
 138         if (!FMEV_API_ENTER(hdl, 1))
 139                 return (fmev_errno);
 140 
 141         if (!shdlctl_start(ihdl))
 142                 return (fmev_seterr(FMEVERR_BUSY));
 143 
 144         sysevent_subattr_thrattr(ihdl->sh_attr, attr);
 145 
 146         shdlctl_end(ihdl);
 147         return (fmev_seterr(FMEV_SUCCESS));
 148 }
 149 
 150 fmev_err_t
 151 fmev_shdlctl_sigmask(fmev_shdl_t hdl, sigset_t *set)
 152 {
 153         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 154 
 155         if (!FMEV_API_ENTER(hdl, 1))
 156                 return (fmev_errno);
 157 
 158         if (!shdlctl_start(ihdl))
 159                 return (fmev_seterr(FMEVERR_BUSY));
 160 
 161         sysevent_subattr_sigmask(ihdl->sh_attr, set);
 162 
 163         shdlctl_end(ihdl);
 164         return (fmev_seterr(FMEV_SUCCESS));
 165 }
 166 
 167 fmev_err_t
 168 fmev_shdlctl_thrsetup(fmev_shdl_t hdl, door_xcreate_thrsetup_func_t *func,
 169     void *cookie)
 170 {
 171         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 172 
 173         if (!FMEV_API_ENTER(hdl, 1))
 174                 return (fmev_errno);
 175 
 176         if (!shdlctl_start(ihdl))
 177                 return (fmev_seterr(FMEVERR_BUSY));
 178 
 179         sysevent_subattr_thrsetup(ihdl->sh_attr, func, cookie);
 180 
 181         shdlctl_end(ihdl);
 182         return (fmev_seterr(FMEV_SUCCESS));
 183 }
 184 
 185 fmev_err_t
 186 fmev_shdlctl_thrcreate(fmev_shdl_t hdl, door_xcreate_server_func_t *func,
 187     void *cookie)
 188 {
 189         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 190 
 191         if (!FMEV_API_ENTER(hdl, 1))
 192                 return (fmev_errno);
 193 
 194         if (!shdlctl_start(ihdl))
 195                 return (fmev_seterr(FMEVERR_BUSY));
 196 
 197         sysevent_subattr_thrcreate(ihdl->sh_attr, func, cookie);
 198 
 199         shdlctl_end(ihdl);
 200         return (fmev_seterr(FMEV_SUCCESS));
 201 }
 202 
 203 /*
 204  * Our door service function.  We return 0 regardless so that the kernel
 205  * does not keep either retrying (EAGAIN) or bleat to cmn_err.
 206  */
 207 
 208 uint64_t fmev_proxy_cb_inval;
 209 uint64_t fmev_proxy_cb_enomem;
 210 
 211 int
 212 fmev_proxy_cb(sysevent_t *sep, void *arg)
 213 {
 214         struct fmev_subinfo *sip = arg;
 215         fmev_shdl_impl_t *ihdl = sip->si_ihdl;
 216         nvlist_t *nvl;
 217         char *class;
 218         fmev_t ev;
 219 
 220         if (sip == NULL || sip->si_cb == NULL) {
 221                 fmev_proxy_cb_inval++;
 222                 return (0);
 223         }
 224 
 225         if ((ev = fmev_sysev2fmev(IHDL2HDL(ihdl), sep, &class, &nvl)) == NULL) {
 226                 fmev_proxy_cb_enomem++;
 227                 return (0);
 228         }
 229 
 230         if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
 231                 (void) pthread_mutex_lock(&ihdl->sh_srlz_lock);
 232 
 233         sip->si_cb(ev, class, nvl, sip->si_cbarg);
 234 
 235         if (ihdl->sh_flags & SHDL_FL_SERIALIZE)
 236                 (void) pthread_mutex_unlock(&ihdl->sh_srlz_lock);
 237 
 238         fmev_rele(ev);  /* release hold obtained in fmev_sysev2fmev */
 239 
 240         return (0);
 241 }
 242 
 243 static volatile uint32_t fmev_subid;
 244 
 245 fmev_err_t
 246 fmev_shdl_subscribe(fmev_shdl_t hdl, const char *pat, fmev_cbfunc_t func,
 247     void *funcarg)
 248 {
 249         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 250         struct fmev_subinfo *sip;
 251         uu_avl_index_t idx;
 252         uint64_t nsid;
 253         int serr;
 254 
 255         if (!FMEV_API_ENTER(hdl, 1))
 256                 return (fmev_errno);
 257 
 258         if (pat == NULL || func == NULL)
 259                 return (fmev_seterr(FMEVERR_API));
 260 
 261         /*
 262          * Empty class patterns are illegal, as is the sysevent magic for
 263          * all classes.  Also validate class length.
 264          */
 265         if (*pat == '\0' || strncmp(pat, EC_ALL, sizeof (EC_ALL)) == 0 ||
 266             strncmp(pat, EC_SUB_ALL, sizeof (EC_SUB_ALL)) == 0 ||
 267             strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
 268                 return (fmev_seterr(FMEVERR_BADCLASS));
 269 
 270         if ((sip = fmev_shdl_zalloc(hdl, sizeof (*sip))) == NULL)
 271                 return (fmev_seterr(FMEVERR_ALLOC));
 272 
 273         (void) strncpy(sip->si_pat, pat, sizeof (sip->si_pat));
 274 
 275         uu_avl_node_init(sip, &sip->si_node, ihdl->sh_pool);
 276 
 277         (void) pthread_mutex_lock(&ihdl->sh_lock);
 278 
 279         if (uu_avl_find(ihdl->sh_avl, sip, NULL, &idx) != NULL) {
 280                 (void) pthread_mutex_unlock(&ihdl->sh_lock);
 281                 fmev_shdl_free(hdl, sip, sizeof (*sip));
 282                 return (fmev_seterr(FMEVERR_DUPLICATE));
 283         }
 284 
 285         /*
 286          * Generate a subscriber id for GPEC that is unique to this
 287          * subscription.  There is no provision for persistent
 288          * subscribers.  The subscriber id must be unique within
 289          * this zone.
 290          */
 291         nsid = (uint64_t)getpid() << 32 | atomic_inc_32_nv(&fmev_subid);
 292         (void) snprintf(sip->si_sid, sizeof (sip->si_sid), "%llx", nsid);
 293 
 294         sip->si_ihdl = ihdl;
 295         sip->si_cb = func;
 296         sip->si_cbarg = funcarg;
 297 
 298         if ((serr = sysevent_evc_xsubscribe(ihdl->sh_binding, sip->si_sid,
 299             sip->si_pat, fmev_proxy_cb, sip, 0, ihdl->sh_attr)) != 0) {
 300                 fmev_err_t err;
 301 
 302                 (void) pthread_mutex_unlock(&ihdl->sh_lock);
 303                 fmev_shdl_free(hdl, sip, sizeof (*sip));
 304 
 305                 switch (serr) {
 306                 case ENOMEM:
 307                         err = FMEVERR_MAX_SUBSCRIBERS;
 308                         break;
 309 
 310                 default:
 311                         err = FMEVERR_INTERNAL;
 312                         break;
 313                 }
 314 
 315                 return (fmev_seterr(err));
 316         }
 317 
 318         uu_avl_insert(ihdl->sh_avl, sip, idx);
 319         ihdl->sh_subcnt++;
 320 
 321         (void) pthread_mutex_unlock(&ihdl->sh_lock);
 322 
 323         return (fmev_seterr(FMEV_SUCCESS));
 324 }
 325 
 326 static int
 327 fmev_subinfo_fini(fmev_shdl_impl_t *ihdl, struct fmev_subinfo *sip,
 328     boolean_t doavl)
 329 {
 330         int err;
 331 
 332         ASSERT(sip->si_ihdl == ihdl);
 333 
 334         err = sysevent_evc_unsubscribe(ihdl->sh_binding, sip->si_sid);
 335 
 336         if (err == 0) {
 337                 if (doavl) {
 338                         uu_avl_remove(ihdl->sh_avl, sip);
 339                         uu_avl_node_fini(sip, &sip->si_node, ihdl->sh_pool);
 340                 }
 341                 fmev_shdl_free(IHDL2HDL(ihdl), sip, sizeof (*sip));
 342                 ihdl->sh_subcnt--;
 343         }
 344 
 345         return (err);
 346 }
 347 
 348 fmev_err_t
 349 fmev_shdl_unsubscribe(fmev_shdl_t hdl, const char *pat)
 350 {
 351         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 352         fmev_err_t rv = FMEVERR_NOMATCH;
 353         struct fmev_subinfo *sip;
 354         struct fmev_subinfo si;
 355         int err;
 356 
 357         if (!FMEV_API_ENTER(hdl, 1))
 358                 return (fmev_errno);
 359 
 360         if (pat == NULL)
 361                 return (fmev_seterr(FMEVERR_API));
 362 
 363         if (*pat == '\0' || strncmp(pat, EVCH_ALLSUB, sizeof (EC_ALL)) == 0 ||
 364             strnlen(pat, FMEV_MAX_CLASS) == FMEV_MAX_CLASS)
 365                 return (fmev_seterr(FMEVERR_BADCLASS));
 366 
 367         (void) strncpy(si.si_pat, pat, sizeof (si.si_pat));
 368 
 369         (void) pthread_mutex_lock(&ihdl->sh_lock);
 370 
 371         if ((sip = uu_avl_find(ihdl->sh_avl, &si, NULL, NULL)) != NULL) {
 372                 if ((err = fmev_subinfo_fini(ihdl, sip, B_TRUE)) == 0) {
 373                         rv = FMEV_SUCCESS;
 374                 } else {
 375                         /*
 376                          * Return an API error if the unsubscribe was
 377                          * attempted from within a door callback invocation;
 378                          * other errors should not happen.
 379                          */
 380                         rv = (err == EDEADLK) ? FMEVERR_API : FMEVERR_INTERNAL;
 381                 }
 382         }
 383 
 384         (void) pthread_mutex_unlock(&ihdl->sh_lock);
 385 
 386         return (fmev_seterr(rv));
 387 }
 388 
 389 void *
 390 fmev_shdl_alloc(fmev_shdl_t hdl, size_t sz)
 391 {
 392         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 393 
 394         if (!FMEV_API_ENTER(hdl, 1))
 395                 return (NULL);
 396 
 397         return (ihdl->sh_cmn.hc_alloc(sz));
 398 }
 399 
 400 void *
 401 fmev_shdl_zalloc(fmev_shdl_t hdl, size_t sz)
 402 {
 403         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 404 
 405         if (!FMEV_API_ENTER(hdl, 1))
 406                 return (NULL);
 407 
 408         return (ihdl->sh_cmn.hc_zalloc(sz));
 409 }
 410 
 411 void
 412 fmev_shdl_free(fmev_shdl_t hdl, void *buf, size_t sz)
 413 {
 414         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 415 
 416         if (!FMEV_API_ENTER(hdl, 1))
 417                 return;
 418 
 419         ihdl->sh_cmn.hc_free(buf, sz);
 420 }
 421 
 422 char *
 423 fmev_shdl_strdup(fmev_shdl_t hdl, char *src)
 424 {
 425         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 426         size_t srclen;
 427         char *dst;
 428 
 429         if (!FMEV_API_ENTER(hdl, 2))
 430                 return (NULL);
 431 
 432         srclen = strlen(src);
 433 
 434         if ((dst = ihdl->sh_cmn.hc_alloc(srclen + 1)) == NULL) {
 435                 (void) fmev_seterr(FMEVERR_ALLOC);
 436                 return (NULL);
 437         }
 438 
 439         (void) strncpy(dst, src, srclen);
 440         dst[srclen] = '\0';
 441         return (dst);
 442 }
 443 
 444 void
 445 fmev_shdl_strfree(fmev_shdl_t hdl, char *buf)
 446 {
 447         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 448 
 449         (void) FMEV_API_ENTER(hdl, 2);
 450 
 451         ihdl->sh_cmn.hc_free(buf, strlen(buf) + 1);
 452 }
 453 
 454 int
 455 fmev_shdl_valid(fmev_shdl_t hdl)
 456 {
 457         return (FMEV_SHDL_VALID(HDL2IHDL(hdl)));
 458 }
 459 
 460 /*ARGSUSED*/
 461 static int
 462 fmev_keycmp(const void *l, const void *r, void *arg)
 463 {
 464         struct fmev_subinfo *left = (struct fmev_subinfo *)l;
 465         struct fmev_subinfo *right = (struct fmev_subinfo *)r;
 466 
 467         return (strncmp(left->si_pat, right->si_pat, FMEV_MAX_CLASS));
 468 }
 469 
 470 fmev_shdl_t
 471 fmev_shdl_init(uint32_t caller_version, void *(*hdlalloc)(size_t),
 472     void *(*hdlzalloc)(size_t), void (*hdlfree)(void *, size_t))
 473 {
 474         fmev_shdl_impl_t *ihdl;
 475         struct fmev_hdl_cmn hc;
 476         const char *chan_name;
 477         int err;
 478 
 479         hc.hc_magic = _FMEV_SHMAGIC;
 480         hc.hc_api_vers = caller_version;
 481         hc.hc_alloc = hdlalloc ? hdlalloc : dflt_alloc;
 482         hc.hc_zalloc = hdlzalloc ? hdlzalloc : dflt_zalloc;
 483         hc.hc_free = hdlfree ? hdlfree : dflt_free;
 484 
 485         if (!fmev_api_init(&hc))
 486                 return (NULL);  /* error type set */
 487 
 488         if (!((hdlalloc == NULL && hdlzalloc == NULL && hdlfree == NULL) ||
 489             (hdlalloc != NULL && hdlzalloc != NULL && hdlfree != NULL))) {
 490                 (void) fmev_seterr(FMEVERR_API);
 491                 return (NULL);
 492         }
 493 
 494         if (hdlzalloc == NULL)
 495                 ihdl = dflt_zalloc(sizeof (*ihdl));
 496         else
 497                 ihdl = hdlzalloc(sizeof (*ihdl));
 498 
 499         if (ihdl == NULL) {
 500                 (void) fmev_seterr(FMEVERR_ALLOC);
 501                 return (NULL);
 502         }
 503 
 504         ihdl->sh_cmn = hc;
 505 
 506         if ((ihdl->sh_attr = sysevent_subattr_alloc()) == NULL) {
 507                 err = FMEVERR_ALLOC;
 508                 goto error;
 509         }
 510 
 511         (void) pthread_mutex_init(&ihdl->sh_lock, NULL);
 512 
 513         /*
 514          * For simulation purposes we allow an environment variable
 515          * to provide a different channel name.
 516          */
 517         if ((chan_name = getenv("FMD_SNOOP_CHANNEL")) == NULL)
 518                 chan_name = FMD_SNOOP_CHANNEL;
 519 
 520         /*
 521          * Try to bind to the event channel. If it's not already present,
 522          * attempt to create the channel so that we can startup before
 523          * the event producer (who will also apply choices such as
 524          * channel depth when they bind to the channel).
 525          */
 526         if (sysevent_evc_bind(chan_name, &ihdl->sh_binding,
 527             EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) {
 528                 switch (errno) {
 529                 case EINVAL:
 530                 default:
 531                         err = FMEVERR_INTERNAL;
 532                         break;
 533                 case ENOMEM:
 534                         err = FMEVERR_ALLOC;
 535                         break;
 536                 case EPERM:
 537                         err = FMEVERR_NOPRIV;
 538                         break;
 539                 }
 540                 goto error;
 541         }
 542 
 543         if ((ihdl->sh_pool = uu_avl_pool_create("subinfo_pool",
 544             sizeof (struct fmev_subinfo),
 545             offsetof(struct fmev_subinfo, si_node), fmev_keycmp,
 546             UU_AVL_POOL_DEBUG)) == NULL) {
 547                 err = FMEVERR_INTERNAL;
 548                 goto error;
 549         }
 550 
 551         if ((ihdl->sh_avl = uu_avl_create(ihdl->sh_pool, NULL,
 552             UU_DEFAULT)) == NULL) {
 553                 err = FMEVERR_INTERNAL;
 554                 goto error;
 555         }
 556 
 557         return (IHDL2HDL(ihdl));
 558 
 559 error:
 560         (void) fmev_shdl_fini(IHDL2HDL(ihdl));
 561         (void) fmev_seterr(err);
 562         return (NULL);
 563 }
 564 
 565 fmev_err_t
 566 fmev_shdl_getauthority(fmev_shdl_t hdl, nvlist_t **nvlp)
 567 {
 568         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 569         nvlist_t *propnvl;
 570         fmev_err_t rc;
 571 
 572         if (!FMEV_API_ENTER(hdl, 2))
 573                 return (fmev_errno);
 574 
 575         (void) pthread_mutex_lock(&ihdl->sh_lock);
 576 
 577         if (sysevent_evc_getpropnvl(ihdl->sh_binding, &propnvl) != 0) {
 578                 *nvlp = NULL;
 579                 (void) pthread_mutex_unlock(&ihdl->sh_lock);
 580                 return (fmev_seterr(FMEVERR_UNKNOWN));
 581         }
 582 
 583         if (propnvl == NULL) {
 584                 rc = FMEVERR_BUSY;      /* Other end has not bound */
 585         } else {
 586                 nvlist_t *auth;
 587 
 588                 if (nvlist_lookup_nvlist(propnvl, "fmdauth", &auth) == 0) {
 589                         rc = (nvlist_dup(auth, nvlp, 0) == 0) ? FMEV_SUCCESS :
 590                             FMEVERR_ALLOC;
 591                 } else {
 592                         rc = FMEVERR_INTERNAL;
 593                 }
 594                 nvlist_free(propnvl);
 595         }
 596 
 597         (void) pthread_mutex_unlock(&ihdl->sh_lock);
 598 
 599         if (rc != FMEV_SUCCESS) {
 600                 *nvlp = NULL;
 601                 (void) fmev_seterr(rc);
 602         }
 603 
 604         return (rc);
 605 }
 606 
 607 char *
 608 fmev_shdl_nvl2str(fmev_shdl_t hdl, nvlist_t *nvl)
 609 {
 610         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 611         char *fmri, *fmricp;
 612         fmev_err_t err;
 613         int topoerr;
 614 
 615         if (!FMEV_API_ENTER(hdl, 2))
 616                 return (NULL);
 617 
 618         if (g_topohdl == NULL) {
 619                 (void) pthread_mutex_lock(&ihdl->sh_lock);
 620                 if (g_topohdl == NULL)
 621                         g_topohdl = topo_open(TOPO_VERSION, NULL, &topoerr);
 622                 (void) pthread_mutex_unlock(&ihdl->sh_lock);
 623 
 624                 if (g_topohdl == NULL) {
 625                         (void) fmev_seterr(FMEVERR_INTERNAL);
 626                         return (NULL);
 627                 }
 628         }
 629 
 630         if (topo_fmri_nvl2str(g_topohdl, nvl, &fmri, &topoerr) == 0) {
 631                 fmricp = fmev_shdl_strdup(hdl, fmri);
 632                 topo_hdl_strfree(g_topohdl, fmri);
 633                 return (fmricp);        /* fmev_errno set if strdup failed */
 634         }
 635 
 636         switch (topoerr) {
 637         case ETOPO_FMRI_NOMEM:
 638                 err = FMEVERR_ALLOC;
 639                 break;
 640 
 641         case ETOPO_FMRI_MALFORM:
 642         case ETOPO_METHOD_NOTSUP:
 643         case ETOPO_METHOD_INVAL:
 644         default:
 645                 err = FMEVERR_INVALIDARG;
 646                 break;
 647         }
 648 
 649         (void) fmev_seterr(err);
 650         return (NULL);
 651 }
 652 
 653 fmev_err_t
 654 fmev_shdl_fini(fmev_shdl_t hdl)
 655 {
 656         fmev_shdl_impl_t *ihdl = HDL2IHDL(hdl);
 657 
 658         if (!FMEV_API_ENTER(hdl, 1))
 659                 return (fmev_errno);
 660 
 661         (void) pthread_mutex_lock(&ihdl->sh_lock);
 662 
 663         /*
 664          * Verify that we are not in callback context - return an API
 665          * error if we are.
 666          */
 667         if (sysevent_evc_unsubscribe(ihdl->sh_binding, "invalidsid") ==
 668             EDEADLK) {
 669                 (void) pthread_mutex_unlock(&ihdl->sh_lock);
 670                 return (fmev_seterr(FMEVERR_API));
 671         }
 672 
 673         if (ihdl->sh_avl) {
 674                 void *cookie = NULL;
 675                 struct fmev_subinfo *sip;
 676 
 677                 while ((sip = uu_avl_teardown(ihdl->sh_avl, &cookie)) != NULL)
 678                         (void) fmev_subinfo_fini(ihdl, sip, B_FALSE);
 679 
 680                 uu_avl_destroy(ihdl->sh_avl);
 681                 ihdl->sh_avl = NULL;
 682         }
 683 
 684         ASSERT(ihdl->sh_subcnt == 0);
 685 
 686         if (ihdl->sh_binding) {
 687                 (void) sysevent_evc_unbind(ihdl->sh_binding);
 688                 ihdl->sh_binding = NULL;
 689         }
 690 
 691         if (ihdl->sh_pool) {
 692                 uu_avl_pool_destroy(ihdl->sh_pool);
 693                 ihdl->sh_pool = NULL;
 694         }
 695 
 696         if (ihdl->sh_attr) {
 697                 sysevent_subattr_free(ihdl->sh_attr);
 698                 ihdl->sh_attr = NULL;
 699         }
 700 
 701         ihdl->sh_cmn.hc_magic = 0;
 702 
 703         if (g_topohdl) {
 704                 topo_close(g_topohdl);
 705                 g_topohdl = NULL;
 706         }
 707 
 708         (void) pthread_mutex_unlock(&ihdl->sh_lock);
 709         (void) pthread_mutex_destroy(&ihdl->sh_lock);
 710 
 711         fmev_shdl_free(hdl, hdl, sizeof (*ihdl));
 712 
 713         fmev_api_freetsd();
 714 
 715         return (fmev_seterr(FMEV_SUCCESS));
 716 }