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 }