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) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Receive (on GPEC channels) raw events published by a few select producers 28 * using the private libfmevent publication interfaces, and massage those 29 * raw events into full protocol events. Each raw event selects a "ruleset" 30 * by which to perform the transformation into a protocol event. 31 * 32 * Only publication from userland running privileged is supported; two 33 * channels are used - one for high-value and one for low-value events. 34 * There is some planning in the implementation below for kernel hi and low 35 * value channels, and for non-privileged userland low and hi value channels. 36 */ 37 38 #include <fm/fmd_api.h> 39 #include <fm/libfmevent.h> 40 #include <uuid/uuid.h> 41 #include <libsysevent.h> 42 #include <pthread.h> 43 #include <libnvpair.h> 44 #include <strings.h> 45 #include <zone.h> 46 47 #include "fmevt.h" 48 49 static struct fmevt_inbound_stats { 50 fmd_stat_t raw_callbacks; 51 fmd_stat_t raw_noattrlist; 52 fmd_stat_t raw_nodetector; 53 fmd_stat_t pp_bad_ruleset; 54 fmd_stat_t pp_explicitdrop; 55 fmd_stat_t pp_fallthrurule; 56 fmd_stat_t pp_fanoutmax; 57 fmd_stat_t pp_intldrop; 58 fmd_stat_t pp_badclass; 59 fmd_stat_t pp_nvlallocfail; 60 fmd_stat_t pp_nvlbuildfail; 61 fmd_stat_t pp_badreturn; 62 fmd_stat_t xprt_posted; 63 } inbound_stats = { 64 { "raw_callbacks", FMD_TYPE_UINT64, 65 "total raw event callbacks from producers" }, 66 { "raw_noattrlist", FMD_TYPE_UINT64, 67 "missing attribute list" }, 68 { "raw_nodetector", FMD_TYPE_UINT64, 69 "unable to add detector" }, 70 { "pp_bad_ruleset", FMD_TYPE_UINT64, 71 "post-process bad ruleset" }, 72 { "pp_explicitdrop", FMD_TYPE_UINT64, 73 "ruleset drops event with NULL func" }, 74 { "pp_fanoutmax", FMD_TYPE_UINT64, 75 "post-processing produced too many events" }, 76 { "pp_intldrop", FMD_TYPE_UINT64, 77 "post-processing requested event drop" }, 78 { "pp_badclass", FMD_TYPE_UINT64, 79 "post-processing produced invalid event class" }, 80 { "pp_nvlallocfail", FMD_TYPE_UINT64, 81 "fmd_nvl_alloc failed" }, 82 { "pp_nvlbuildfail", FMD_TYPE_UINT64, 83 "nvlist_add_foo failed in building event" }, 84 { "pp_badreturn", FMD_TYPE_UINT64, 85 "inconsistent number of events returned" }, 86 { "xprt_posted", FMD_TYPE_UINT64, 87 "protocol events posted with fmd_xprt_post" }, 88 }; 89 90 static int isglobalzone; 91 static char zonename[ZONENAME_MAX]; 92 93 #define BUMPSTAT(stat) inbound_stats.stat.fmds_value.ui64++ 94 95 #define CBF_USER 0x1U 96 #define CBF_PRIV 0x2U 97 #define CBF_LV 0x4U 98 #define CBF_HV 0x8U 99 #define CBF_ALL (CBF_USER | CBF_PRIV | CBF_LV | CBF_HV) 100 101 static struct fmevt_chaninfo { 102 const char *ci_propname; /* property to get channel name */ 103 evchan_t *ci_binding; /* GPEC binding for this channel */ 104 char ci_sid[MAX_SUBID_LEN]; /* subscriber id */ 105 uint32_t ci_cbarg; /* callback cookie */ 106 uint32_t ci_sflags; /* subscription flags to use */ 107 } chaninfo[] = { 108 { "user_priv_highval_channel", NULL, { 0 }, 109 CBF_USER | CBF_PRIV | CBF_HV, EVCH_SUB_KEEP }, 110 { "user_priv_lowval_channel", NULL, { 0 }, 111 CBF_USER | CBF_PRIV | CBF_LV, EVCH_SUB_KEEP }, 112 }; 113 114 static pthread_cond_t fmevt_cv = PTHREAD_COND_INITIALIZER; 115 static pthread_mutex_t fmevt_lock = PTHREAD_MUTEX_INITIALIZER; 116 static int fmevt_exiting; 117 118 static fmd_xprt_t *fmevt_xprt; 119 static uint32_t fmevt_xprt_refcnt; 120 static sysevent_subattr_t *subattr; 121 122 /* 123 * Rulesets we recognize and who handles them. Additions and changes 124 * must follow the Portfolio Review process. At ths time only 125 * the FMEV_RULESET_ON_SUNOS and FMEVT_RULESET_SMF rulesets are 126 * formally recognized by that process - the others here are experimental. 127 */ 128 static struct fmevt_rs { 129 char *rs_pat; 130 fmevt_pp_func_t *rs_ppfunc; 131 char *rs_namespace; 132 char *rs_subsys; 133 } rulelist[] = { 134 { FMEV_RULESET_SMF, fmevt_pp_smf }, 135 { FMEV_RULESET_ON_EREPORT, fmevt_pp_on_ereport }, 136 { FMEV_RULESET_ON_SUNOS, fmevt_pp_on_sunos }, 137 { FMEV_RULESET_ON_PRIVATE, fmevt_pp_on_private }, 138 { FMEV_RULESET_UNREGISTERED, fmevt_pp_unregistered } 139 }; 140 141 /* 142 * Take a ruleset specification string and separate it into namespace 143 * and subsystem components. 144 */ 145 static int 146 fmevt_rs_burst(fmd_hdl_t *hdl, char *ruleset, char **nsp, char **subsysp, 147 boolean_t alloc) 148 { 149 char *ns, *s; 150 size_t len; 151 152 if (ruleset == NULL || *ruleset == '\0' || 153 strnlen(ruleset, FMEV_MAX_RULESET_LEN) == FMEV_MAX_RULESET_LEN) 154 return (0); 155 156 if (alloc == B_FALSE) { 157 s = ruleset; 158 ns = strsep(&s, FMEV_RS_SEPARATOR); 159 160 if (s == NULL || s == ns + 1) 161 return (0); 162 } else { 163 if ((s = strstr(ruleset, FMEV_RS_SEPARATOR)) == NULL || 164 s == ruleset + strlen(ruleset) - 1) 165 return (0); 166 167 len = s - ruleset; 168 169 ns = fmd_hdl_alloc(hdl, len + 1, FMD_SLEEP); 170 (void) strncpy(ns, ruleset, len); 171 ns[len] = '\0'; 172 173 s++; 174 } 175 176 if (nsp) 177 *nsp = ns; /* caller must free if alloc == B_TRUE */ 178 179 if (subsysp) 180 *subsysp = s; /* always within original ruleset string */ 181 182 return (1); 183 } 184 185 static int 186 fmevt_rs_init(fmd_hdl_t *hdl) 187 { 188 int i; 189 190 for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) { 191 struct fmevt_rs *rsp = &rulelist[i]; 192 193 if (!fmevt_rs_burst(hdl, rsp->rs_pat, &rsp->rs_namespace, 194 &rsp->rs_subsys, B_TRUE)) 195 return (0); 196 } 197 198 return (1); 199 } 200 201 /* 202 * Construct a "sw" scheme detector FMRI. 203 * 204 * We make no use of priv or pri. 205 */ 206 /*ARGSUSED3*/ 207 static nvlist_t * 208 fmevt_detector(nvlist_t *attr, char *ruleset, int user, int priv, 209 fmev_pri_t pri) 210 { 211 char buf[FMEV_MAX_RULESET_LEN + 1]; 212 char *ns, *subsys; 213 nvlist_t *obj, *dtcr, *site, *ctxt; 214 char *execname = NULL; 215 int32_t i32; 216 int64_t i64; 217 int err = 0; 218 char *str; 219 220 (void) strncpy(buf, ruleset, sizeof (buf)); 221 if (!fmevt_rs_burst(NULL, buf, &ns, &subsys, B_FALSE)) 222 return (NULL); 223 224 obj = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP); 225 dtcr = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP); 226 site = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP); 227 ctxt = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP); 228 229 if (obj == NULL || dtcr == NULL || site == NULL || ctxt == NULL) { 230 err++; 231 goto done; 232 } 233 234 /* 235 * Build up 'object' nvlist. 236 */ 237 if (nvlist_lookup_string(attr, "__fmev_execname", &execname) == 0) 238 err += nvlist_add_string(obj, FM_FMRI_SW_OBJ_PATH, execname); 239 240 /* 241 * Build up 'site' nvlist. We should have source file and line 242 * number and, if the producer was compiled with C99, function name. 243 */ 244 if (nvlist_lookup_string(attr, "__fmev_file", &str) == 0) { 245 err += nvlist_add_string(site, FM_FMRI_SW_SITE_FILE, str); 246 (void) nvlist_remove(attr, "__fmev_file", DATA_TYPE_STRING); 247 } 248 249 if (nvlist_lookup_string(attr, "__fmev_func", &str) == 0) { 250 err += nvlist_add_string(site, FM_FMRI_SW_SITE_FUNC, str); 251 (void) nvlist_remove(attr, "__fmev_func", DATA_TYPE_STRING); 252 } 253 254 if (nvlist_lookup_int64(attr, "__fmev_line", &i64) == 0) { 255 err += nvlist_add_int64(site, FM_FMRI_SW_SITE_LINE, i64); 256 (void) nvlist_remove(attr, "__fmev_line", DATA_TYPE_INT64); 257 } 258 259 /* 260 * Build up 'context' nvlist. We do not include contract id at 261 * this time. 262 */ 263 264 err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_ORIGIN, 265 user ? "userland" : "kernel"); 266 267 if (execname) { 268 err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_EXECNAME, 269 execname); 270 (void) nvlist_remove(attr, "__fmev_execname", DATA_TYPE_STRING); 271 } 272 273 if (nvlist_lookup_int32(attr, "__fmev_pid", &i32) == 0) { 274 err += nvlist_add_int32(ctxt, FM_FMRI_SW_CTXT_PID, i32); 275 (void) nvlist_remove(attr, "__fmev_pid", DATA_TYPE_INT32); 276 } 277 278 if (!isglobalzone) 279 err += nvlist_add_string(ctxt, FM_FMRI_SW_CTXT_ZONE, zonename); 280 281 /* Put it all together */ 282 283 err += nvlist_add_uint8(dtcr, FM_VERSION, SW_SCHEME_VERSION0); 284 err += nvlist_add_string(dtcr, FM_FMRI_SCHEME, FM_FMRI_SCHEME_SW); 285 err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_OBJ, obj); 286 err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_SITE, site); 287 err += nvlist_add_nvlist(dtcr, FM_FMRI_SW_CTXT, ctxt); 288 289 done: 290 if (obj != NULL) 291 nvlist_free(obj); 292 if (site != NULL) 293 nvlist_free(site); 294 if (ctxt != NULL) 295 nvlist_free(ctxt); 296 297 if (err == 0) { 298 return (dtcr); 299 } else { 300 nvlist_free(dtcr); 301 return (NULL); 302 } 303 } 304 305 static int 306 class_ok(char *class) 307 { 308 static const char *approved[] = { 309 FM_IREPORT_CLASS ".", 310 FM_EREPORT_CLASS "." 311 }; 312 313 int i; 314 315 for (i = 0; i < sizeof (approved) / sizeof (approved[0]); i++) { 316 if (strncmp(class, approved[i], strlen(approved[i])) == 0) 317 return (1); 318 } 319 320 return (0); 321 } 322 323 static void 324 fmevt_postprocess(char *ruleset, nvlist_t *dtcr, nvlist_t *rawattr, 325 struct fmevt_ppargs *eap) 326 { 327 uint_t expected = 0, processed = 0; 328 char rs2burst[FMEV_MAX_RULESET_LEN + 1]; 329 char *class[FMEVT_FANOUT_MAX]; 330 nvlist_t *attr[FMEVT_FANOUT_MAX]; 331 fmevt_pp_func_t *dispf = NULL; 332 char buf[FMEV_MAX_CLASS]; 333 char *ns, *subsys; 334 int i, found = 0; 335 uuid_t uu; 336 337 (void) strncpy(rs2burst, ruleset, sizeof (rs2burst)); 338 if (!fmevt_rs_burst(NULL, rs2burst, &ns, &subsys, B_FALSE)) { 339 BUMPSTAT(pp_bad_ruleset); 340 return; 341 } 342 343 /* 344 * Lookup a matching rule in our table. 345 */ 346 for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) { 347 struct fmevt_rs *rsp = &rulelist[i]; 348 349 if (*ns != '*' && *rsp->rs_namespace != '*' && 350 strcmp(ns, rsp->rs_namespace) != 0) 351 continue; 352 353 if (*subsys != '*' && *rsp->rs_subsys != '*' && 354 strcmp(subsys, rsp->rs_subsys) != 0) 355 continue; 356 357 dispf = rsp->rs_ppfunc; 358 found = 1; 359 break; 360 361 } 362 363 /* 364 * If a ruleset matches but specifies a NULL function then 365 * it's electing to drop the event. If no rule was matched 366 * then default to unregistered processing. 367 */ 368 if (dispf == NULL) { 369 if (found) { 370 BUMPSTAT(pp_explicitdrop); 371 return; 372 } else { 373 BUMPSTAT(pp_fallthrurule); 374 dispf = fmevt_pp_unregistered; 375 } 376 } 377 378 /* 379 * Clear the arrays in which class strings and attribute 380 * nvlists can be returned. Pass a pointer to our stack buffer 381 * that the callee can use for the first event class (for others 382 * it must fmd_hdl_alloc and we'll free below). We will free 383 * and nvlists that are returned. 384 */ 385 bzero(class, sizeof (class)); 386 bzero(attr, sizeof (attr)); 387 class[0] = buf; 388 389 /* 390 * Generate an event UUID which will be used for the first 391 * event generated by post-processing; if post-processing 392 * fans out into more than one event the additional events 393 * can reference this uuid (but we don't generate their 394 * UUIDs until later). 395 */ 396 uuid_generate(uu); 397 uuid_unparse(uu, eap->pp_uuidstr); 398 399 /* 400 * Call selected post-processing function. See block comment 401 * in fmevt.h for a description of this process. 402 */ 403 expected = (*dispf)(class, attr, ruleset, 404 (const nvlist_t *)dtcr, rawattr, 405 (const struct fmevt_ppargs *)eap); 406 407 if (expected > FMEVT_FANOUT_MAX) { 408 BUMPSTAT(pp_fanoutmax); 409 return; /* without freeing class and nvl - could leak */ 410 } else if (expected == 0) { 411 BUMPSTAT(pp_intldrop); 412 return; 413 } 414 415 /* 416 * Post as many events as the callback completed. 417 */ 418 for (i = 0; i < FMEVT_FANOUT_MAX; i++) { 419 char uuidstr[36 + 1]; 420 char *uuidstrp; 421 nvlist_t *nvl; 422 int err = 0; 423 424 if (class[i] == NULL) 425 continue; 426 427 if (!class_ok(class[i])) { 428 BUMPSTAT(pp_badclass); 429 continue; 430 } 431 432 if (processed++ == 0) { 433 uuidstrp = eap->pp_uuidstr; 434 } else { 435 uuid_generate(uu); 436 uuid_unparse(uu, uuidstr); 437 uuidstrp = uuidstr; 438 } 439 440 if ((nvl = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP)) == NULL) { 441 BUMPSTAT(pp_nvlallocfail); 442 continue; 443 } 444 445 err += nvlist_add_uint8(nvl, FM_VERSION, 0); 446 err += nvlist_add_string(nvl, FM_CLASS, (const char *)class[i]); 447 err += nvlist_add_string(nvl, FM_IREPORT_UUID, uuidstrp); 448 err += nvlist_add_nvlist(nvl, FM_IREPORT_DETECTOR, dtcr); 449 err += nvlist_add_string(nvl, FM_IREPORT_PRIORITY, 450 fmev_pri_string(eap->pp_pri) ? 451 fmev_pri_string(eap->pp_pri) : "?"); 452 453 if (attr[i] != NULL) 454 err += nvlist_add_nvlist(nvl, FM_IREPORT_ATTRIBUTES, 455 attr[i]); 456 457 /* 458 * If we post the event into fmd_xport_post then the 459 * transport code is responsible for freeing the nvl we 460 * posted. 461 */ 462 if (err == 0) { 463 fmd_xprt_post(fmevt_hdl, fmevt_xprt, nvl, 464 eap->pp_hrt); 465 } else { 466 BUMPSTAT(pp_nvlbuildfail); 467 nvlist_free(nvl); 468 } 469 } 470 471 if (processed != expected) 472 BUMPSTAT(pp_badreturn); 473 474 for (i = 0; i < FMEVT_FANOUT_MAX; i++) { 475 /* 476 * We provided storage for class[0] but any 477 * additional events have allocated a string. 478 */ 479 if (i > 0 && class[i] != NULL) 480 fmd_hdl_strfree(fmevt_hdl, class[i]); 481 482 /* 483 * Free all attribute lists passed in if they are not 484 * just a pointer to the raw attributes 485 */ 486 if (attr[i] != NULL && attr[i] != rawattr) 487 nvlist_free(attr[i]); 488 } 489 } 490 491 static int 492 fmevt_cb(sysevent_t *sep, void *arg) 493 { 494 char *ruleset = NULL, *rawclass, *rawsubclass; 495 uint32_t cbarg = (uint32_t)arg; 496 nvlist_t *rawattr = NULL; 497 struct fmevt_ppargs ea; 498 nvlist_t *dtcr; 499 int user, priv; 500 fmev_pri_t pri; 501 502 BUMPSTAT(raw_callbacks); 503 504 if (cbarg & ~CBF_ALL) 505 fmd_hdl_abort(fmevt_hdl, "event receipt callback with " 506 "invalid flags\n"); 507 508 user = (cbarg & CBF_USER) != 0; 509 priv = (cbarg & CBF_PRIV) != 0; 510 pri = (cbarg & CBF_HV ? FMEV_HIPRI : FMEV_LOPRI); 511 512 (void) pthread_mutex_lock(&fmevt_lock); 513 514 if (fmevt_exiting) { 515 while (fmevt_xprt_refcnt > 0) 516 (void) pthread_cond_wait(&fmevt_cv, &fmevt_lock); 517 (void) pthread_mutex_unlock(&fmevt_lock); 518 return (0); /* discard event */ 519 } 520 521 fmevt_xprt_refcnt++; 522 (void) pthread_mutex_unlock(&fmevt_lock); 523 524 ruleset = sysevent_get_vendor_name(sep); /* must free */ 525 rawclass = sysevent_get_class_name(sep); /* valid with sep */ 526 rawsubclass = sysevent_get_subclass_name(sep); /* valid with sep */ 527 528 if (sysevent_get_attr_list(sep, &rawattr) != 0) { 529 BUMPSTAT(raw_noattrlist); 530 goto done; 531 } 532 533 if ((dtcr = fmevt_detector(rawattr, ruleset, user, priv, 534 pri)) == NULL) { 535 BUMPSTAT(raw_nodetector); 536 goto done; 537 } 538 539 ea.pp_rawclass = rawclass; 540 ea.pp_rawsubclass = rawsubclass; 541 sysevent_get_time(sep, &ea.pp_hrt); 542 ea.pp_user = user; 543 ea.pp_priv = priv; 544 ea.pp_pri = pri; 545 546 fmevt_postprocess(ruleset, dtcr, rawattr, &ea); 547 nvlist_free(dtcr); 548 done: 549 (void) pthread_mutex_lock(&fmevt_lock); 550 551 if (--fmevt_xprt_refcnt == 0 && fmevt_exiting) 552 (void) pthread_cond_broadcast(&fmevt_cv); 553 554 (void) pthread_mutex_unlock(&fmevt_lock); 555 556 if (ruleset) 557 free(ruleset); 558 559 if (rawattr) 560 nvlist_free(rawattr); 561 562 return (0); /* in all cases consider the event delivered */ 563 } 564 565 void 566 fmevt_init_inbound(fmd_hdl_t *hdl) 567 { 568 char *sidpfx; 569 zoneid_t zoneid; 570 int i; 571 572 if (!fmevt_rs_init(hdl)) 573 fmd_hdl_abort(hdl, "error in fmevt_rs_init\n"); 574 575 (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (inbound_stats) / 576 sizeof (fmd_stat_t), (fmd_stat_t *)&inbound_stats); 577 578 zoneid = getzoneid(); 579 isglobalzone = (zoneid == GLOBAL_ZONEID); 580 if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) == -1) 581 fmd_hdl_abort(hdl, "getzonenamebyid failed"); 582 583 if ((subattr = sysevent_subattr_alloc()) == NULL) 584 fmd_hdl_abort(hdl, "failed to allocate subscription " 585 "attributes: %s"); 586 587 sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL); 588 sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL); 589 590 sidpfx = fmd_prop_get_string(hdl, "sidprefix"); 591 fmevt_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL); 592 593 for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) { 594 struct fmevt_chaninfo *cip = &chaninfo[i]; 595 char *channel = fmd_prop_get_string(hdl, cip->ci_propname); 596 int err; 597 598 if (sysevent_evc_bind(channel, &cip->ci_binding, 599 EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0) 600 fmd_hdl_abort(hdl, "failed to bind GPEC channel for " 601 "channel %s", channel); 602 603 (void) snprintf(cip->ci_sid, sizeof (cip->ci_sid), 604 "%s_%c%c%c", sidpfx, 605 cip->ci_cbarg & CBF_USER ? 'u' : 'k', 606 cip->ci_cbarg & CBF_PRIV ? 'p' : 'n', 607 cip->ci_cbarg & CBF_HV ? 'h' : 'l'); 608 609 err = sysevent_evc_xsubscribe(cip->ci_binding, cip->ci_sid, 610 EC_ALL, fmevt_cb, (void *)cip->ci_cbarg, 611 cip->ci_sflags, subattr); 612 613 if (err == EEXIST) 614 fmd_hdl_abort(hdl, "another fmd is active on " 615 "channel %s\n", channel); 616 else if (err != 0) 617 fmd_hdl_abort(hdl, "failed to subscribe to channel %s", 618 channel); 619 620 fmd_prop_free_string(hdl, channel); 621 } 622 623 fmd_prop_free_string(hdl, sidpfx); 624 } 625 626 void 627 fmevt_fini_inbound(fmd_hdl_t *hdl) 628 { 629 int i; 630 631 for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) { 632 struct fmevt_chaninfo *cip = &chaninfo[i]; 633 634 if (cip->ci_binding) { 635 (void) sysevent_evc_unsubscribe(cip->ci_binding, 636 cip->ci_sid); 637 (void) sysevent_evc_unbind(cip->ci_binding); 638 cip->ci_binding = NULL; 639 } 640 } 641 642 if (subattr) { 643 sysevent_subattr_free(subattr); 644 subattr = NULL; 645 } 646 647 if (fmevt_xprt) { 648 /* drain before destruction */ 649 (void) pthread_mutex_lock(&fmevt_lock); 650 fmevt_exiting = 1; 651 while (fmevt_xprt_refcnt > 0) 652 (void) pthread_cond_wait(&fmevt_cv, &fmevt_lock); 653 (void) pthread_mutex_unlock(&fmevt_lock); 654 655 fmd_xprt_close(hdl, fmevt_xprt); 656 } 657 658 }