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         nvlist_free(obj);
 291         nvlist_free(site);
 292         nvlist_free(ctxt);
 293 
 294         if (err == 0) {
 295                 return (dtcr);
 296         } else {
 297                 nvlist_free(dtcr);
 298                 return (NULL);
 299         }
 300 }
 301 
 302 static int
 303 class_ok(char *class)
 304 {
 305         static const char *approved[] = {
 306                 FM_IREPORT_CLASS ".",
 307                 FM_EREPORT_CLASS "."
 308         };
 309 
 310         int i;
 311 
 312         for (i = 0; i < sizeof (approved) / sizeof (approved[0]); i++) {
 313                 if (strncmp(class, approved[i], strlen(approved[i])) == 0)
 314                         return (1);
 315         }
 316 
 317         return (0);
 318 }
 319 
 320 static void
 321 fmevt_postprocess(char *ruleset, nvlist_t *dtcr, nvlist_t *rawattr,
 322     struct fmevt_ppargs *eap)
 323 {
 324         uint_t expected = 0, processed = 0;
 325         char rs2burst[FMEV_MAX_RULESET_LEN + 1];
 326         char *class[FMEVT_FANOUT_MAX];
 327         nvlist_t *attr[FMEVT_FANOUT_MAX];
 328         fmevt_pp_func_t *dispf = NULL;
 329         char buf[FMEV_MAX_CLASS];
 330         char *ns, *subsys;
 331         int i, found = 0;
 332         uuid_t uu;
 333 
 334         (void) strncpy(rs2burst, ruleset, sizeof (rs2burst));
 335         if (!fmevt_rs_burst(NULL, rs2burst, &ns, &subsys, B_FALSE)) {
 336                 BUMPSTAT(pp_bad_ruleset);
 337                 return;
 338         }
 339 
 340         /*
 341          * Lookup a matching rule in our table.
 342          */
 343         for (i = 0; i < sizeof (rulelist) / sizeof (rulelist[0]); i++) {
 344                 struct fmevt_rs *rsp = &rulelist[i];
 345 
 346                 if (*ns != '*' && *rsp->rs_namespace != '*' &&
 347                     strcmp(ns, rsp->rs_namespace) != 0)
 348                         continue;
 349 
 350                 if (*subsys != '*' && *rsp->rs_subsys != '*' &&
 351                     strcmp(subsys, rsp->rs_subsys) != 0)
 352                         continue;
 353 
 354                 dispf = rsp->rs_ppfunc;
 355                 found = 1;
 356                 break;
 357 
 358         }
 359 
 360         /*
 361          * If a ruleset matches but specifies a NULL function then
 362          * it's electing to drop the event.  If no rule was matched
 363          * then default to unregistered processing.
 364          */
 365         if (dispf == NULL) {
 366                 if (found) {
 367                         BUMPSTAT(pp_explicitdrop);
 368                         return;
 369                 } else {
 370                         BUMPSTAT(pp_fallthrurule);
 371                         dispf = fmevt_pp_unregistered;
 372                 }
 373         }
 374 
 375         /*
 376          * Clear the arrays in which class strings and attribute
 377          * nvlists can be returned.  Pass a pointer to our stack buffer
 378          * that the callee can use for the first event class (for others
 379          * it must fmd_hdl_alloc and we'll free below).  We will free
 380          * and nvlists that are returned.
 381          */
 382         bzero(class, sizeof (class));
 383         bzero(attr, sizeof (attr));
 384         class[0] = buf;
 385 
 386         /*
 387          * Generate an event UUID which will be used for the first
 388          * event generated by post-processing; if post-processing
 389          * fans out into more than one event the additional events
 390          * can reference this uuid (but we don't generate their
 391          * UUIDs until later).
 392          */
 393         uuid_generate(uu);
 394         uuid_unparse(uu, eap->pp_uuidstr);
 395 
 396         /*
 397          * Call selected post-processing function.  See block comment
 398          * in fmevt.h for a description of this process.
 399          */
 400         expected = (*dispf)(class, attr, ruleset,
 401             (const nvlist_t *)dtcr, rawattr,
 402             (const struct fmevt_ppargs *)eap);
 403 
 404         if (expected > FMEVT_FANOUT_MAX) {
 405                 BUMPSTAT(pp_fanoutmax);
 406                 return; /* without freeing class and nvl - could leak */
 407         } else if (expected == 0) {
 408                 BUMPSTAT(pp_intldrop);
 409                 return;
 410         }
 411 
 412         /*
 413          * Post as many events as the callback completed.
 414          */
 415         for (i = 0; i < FMEVT_FANOUT_MAX; i++) {
 416                 char uuidstr[36 + 1];
 417                 char *uuidstrp;
 418                 nvlist_t *nvl;
 419                 int err = 0;
 420 
 421                 if (class[i] == NULL)
 422                         continue;
 423 
 424                 if (!class_ok(class[i])) {
 425                         BUMPSTAT(pp_badclass);
 426                         continue;
 427                 }
 428 
 429                 if (processed++ == 0) {
 430                         uuidstrp = eap->pp_uuidstr;
 431                 } else {
 432                         uuid_generate(uu);
 433                         uuid_unparse(uu, uuidstr);
 434                         uuidstrp = uuidstr;
 435                 }
 436 
 437                 if ((nvl = fmd_nvl_alloc(fmevt_hdl, FMD_SLEEP)) == NULL) {
 438                         BUMPSTAT(pp_nvlallocfail);
 439                         continue;
 440                 }
 441 
 442                 err += nvlist_add_uint8(nvl, FM_VERSION, 0);
 443                 err += nvlist_add_string(nvl, FM_CLASS, (const char *)class[i]);
 444                 err += nvlist_add_string(nvl, FM_IREPORT_UUID, uuidstrp);
 445                 err += nvlist_add_nvlist(nvl, FM_IREPORT_DETECTOR, dtcr);
 446                 err += nvlist_add_string(nvl, FM_IREPORT_PRIORITY,
 447                     fmev_pri_string(eap->pp_pri) ?
 448                     fmev_pri_string(eap->pp_pri) : "?");
 449 
 450                 if (attr[i] != NULL)
 451                         err += nvlist_add_nvlist(nvl, FM_IREPORT_ATTRIBUTES,
 452                             attr[i]);
 453 
 454                 /*
 455                  * If we post the event into fmd_xport_post then the
 456                  * transport code is responsible for freeing the nvl we
 457                  * posted.
 458                  */
 459                 if (err == 0) {
 460                         fmd_xprt_post(fmevt_hdl, fmevt_xprt, nvl,
 461                             eap->pp_hrt);
 462                 } else {
 463                         BUMPSTAT(pp_nvlbuildfail);
 464                         nvlist_free(nvl);
 465                 }
 466         }
 467 
 468         if (processed != expected)
 469                 BUMPSTAT(pp_badreturn);
 470 
 471         for (i = 0; i < FMEVT_FANOUT_MAX; i++) {
 472                 /*
 473                  * We provided storage for class[0] but any
 474                  * additional events have allocated a string.
 475                  */
 476                 if (i > 0 && class[i] != NULL)
 477                         fmd_hdl_strfree(fmevt_hdl, class[i]);
 478 
 479                 /*
 480                  * Free all attribute lists passed in if they are not
 481                  * just a pointer to the raw attributes
 482                  */
 483                 if (attr[i] != NULL && attr[i] != rawattr)
 484                         nvlist_free(attr[i]);
 485         }
 486 }
 487 
 488 static int
 489 fmevt_cb(sysevent_t *sep, void *arg)
 490 {
 491         char *ruleset = NULL, *rawclass, *rawsubclass;
 492         uint32_t cbarg = (uint32_t)arg;
 493         nvlist_t *rawattr = NULL;
 494         struct fmevt_ppargs ea;
 495         nvlist_t *dtcr;
 496         int user, priv;
 497         fmev_pri_t pri;
 498 
 499         BUMPSTAT(raw_callbacks);
 500 
 501         if (cbarg & ~CBF_ALL)
 502                 fmd_hdl_abort(fmevt_hdl, "event receipt callback with "
 503                     "invalid flags\n");
 504 
 505         user = (cbarg & CBF_USER) != 0;
 506         priv = (cbarg & CBF_PRIV) != 0;
 507         pri = (cbarg & CBF_HV ? FMEV_HIPRI : FMEV_LOPRI);
 508 
 509         (void) pthread_mutex_lock(&fmevt_lock);
 510 
 511         if (fmevt_exiting) {
 512                 while (fmevt_xprt_refcnt > 0)
 513                         (void) pthread_cond_wait(&fmevt_cv, &fmevt_lock);
 514                 (void) pthread_mutex_unlock(&fmevt_lock);
 515                 return (0);     /* discard event */
 516         }
 517 
 518         fmevt_xprt_refcnt++;
 519         (void) pthread_mutex_unlock(&fmevt_lock);
 520 
 521         ruleset = sysevent_get_vendor_name(sep);        /* must free */
 522         rawclass = sysevent_get_class_name(sep);        /* valid with sep */
 523         rawsubclass = sysevent_get_subclass_name(sep);  /* valid with sep */
 524 
 525         if (sysevent_get_attr_list(sep, &rawattr) != 0) {
 526                 BUMPSTAT(raw_noattrlist);
 527                 goto done;
 528         }
 529 
 530         if ((dtcr = fmevt_detector(rawattr, ruleset, user, priv,
 531             pri)) == NULL) {
 532                 BUMPSTAT(raw_nodetector);
 533                 goto done;
 534         }
 535 
 536         ea.pp_rawclass = rawclass;
 537         ea.pp_rawsubclass = rawsubclass;
 538         sysevent_get_time(sep, &ea.pp_hrt);
 539         ea.pp_user = user;
 540         ea.pp_priv = priv;
 541         ea.pp_pri = pri;
 542 
 543         fmevt_postprocess(ruleset, dtcr, rawattr, &ea);
 544         nvlist_free(dtcr);
 545 done:
 546         (void) pthread_mutex_lock(&fmevt_lock);
 547 
 548         if (--fmevt_xprt_refcnt == 0 && fmevt_exiting)
 549                 (void) pthread_cond_broadcast(&fmevt_cv);
 550 
 551         (void) pthread_mutex_unlock(&fmevt_lock);
 552 
 553         if (ruleset)
 554                 free(ruleset);
 555 
 556         nvlist_free(rawattr);
 557 
 558         return (0);     /* in all cases consider the event delivered */
 559 }
 560 
 561 void
 562 fmevt_init_inbound(fmd_hdl_t *hdl)
 563 {
 564         char *sidpfx;
 565         zoneid_t zoneid;
 566         int i;
 567 
 568         if (!fmevt_rs_init(hdl))
 569                 fmd_hdl_abort(hdl, "error in fmevt_rs_init\n");
 570 
 571         (void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (inbound_stats) /
 572             sizeof (fmd_stat_t), (fmd_stat_t *)&inbound_stats);
 573 
 574         zoneid = getzoneid();
 575         isglobalzone = (zoneid == GLOBAL_ZONEID);
 576         if (getzonenamebyid(zoneid, zonename, sizeof (zonename)) == -1)
 577                 fmd_hdl_abort(hdl, "getzonenamebyid failed");
 578 
 579         if ((subattr = sysevent_subattr_alloc()) == NULL)
 580                 fmd_hdl_abort(hdl, "failed to allocate subscription "
 581                     "attributes: %s");
 582 
 583         sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL);
 584         sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL);
 585 
 586         sidpfx = fmd_prop_get_string(hdl, "sidprefix");
 587         fmevt_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY, NULL, NULL);
 588 
 589         for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) {
 590                 struct fmevt_chaninfo *cip = &chaninfo[i];
 591                 char *channel = fmd_prop_get_string(hdl, cip->ci_propname);
 592                 int err;
 593 
 594                 if (sysevent_evc_bind(channel, &cip->ci_binding,
 595                     EVCH_CREAT | EVCH_HOLD_PEND_INDEF) != 0)
 596                         fmd_hdl_abort(hdl, "failed to bind GPEC channel for "
 597                             "channel %s", channel);
 598 
 599                 (void) snprintf(cip->ci_sid, sizeof (cip->ci_sid),
 600                     "%s_%c%c%c", sidpfx,
 601                     cip->ci_cbarg & CBF_USER ? 'u' : 'k',
 602                     cip->ci_cbarg & CBF_PRIV ? 'p' : 'n',
 603                     cip->ci_cbarg & CBF_HV ? 'h' : 'l');
 604 
 605                 err = sysevent_evc_xsubscribe(cip->ci_binding, cip->ci_sid,
 606                     EC_ALL, fmevt_cb, (void *)cip->ci_cbarg,
 607                     cip->ci_sflags, subattr);
 608 
 609                 if (err == EEXIST)
 610                         fmd_hdl_abort(hdl, "another fmd is active on "
 611                             "channel %s\n", channel);
 612                 else if (err != 0)
 613                         fmd_hdl_abort(hdl, "failed to subscribe to channel %s",
 614                             channel);
 615 
 616                 fmd_prop_free_string(hdl, channel);
 617         }
 618 
 619         fmd_prop_free_string(hdl, sidpfx);
 620 }
 621 
 622 void
 623 fmevt_fini_inbound(fmd_hdl_t *hdl)
 624 {
 625         int i;
 626 
 627         for (i = 0; i < sizeof (chaninfo) / sizeof (chaninfo[0]); i++) {
 628                 struct fmevt_chaninfo *cip = &chaninfo[i];
 629 
 630                 if (cip->ci_binding) {
 631                         (void) sysevent_evc_unsubscribe(cip->ci_binding,
 632                             cip->ci_sid);
 633                         (void) sysevent_evc_unbind(cip->ci_binding);
 634                         cip->ci_binding = NULL;
 635                 }
 636         }
 637 
 638         if (subattr) {
 639                 sysevent_subattr_free(subattr);
 640                 subattr = NULL;
 641         }
 642 
 643         if (fmevt_xprt) {
 644                 /* drain before destruction */
 645                 (void) pthread_mutex_lock(&fmevt_lock);
 646                 fmevt_exiting = 1;
 647                 while (fmevt_xprt_refcnt > 0)
 648                         (void) pthread_cond_wait(&fmevt_cv, &fmevt_lock);
 649                 (void) pthread_mutex_unlock(&fmevt_lock);
 650 
 651                 fmd_xprt_close(hdl, fmevt_xprt);
 652         }
 653 
 654 }