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 }