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
fmevt_rs_burst(fmd_hdl_t * hdl,char * ruleset,char ** nsp,char ** subsysp,boolean_t alloc)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
fmevt_rs_init(fmd_hdl_t * hdl)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 *
fmevt_detector(nvlist_t * attr,char * ruleset,int user,int priv,fmev_pri_t pri)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
class_ok(char * class)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
fmevt_postprocess(char * ruleset,nvlist_t * dtcr,nvlist_t * rawattr,struct fmevt_ppargs * eap)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
fmevt_cb(sysevent_t * sep,void * arg)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
fmevt_init_inbound(fmd_hdl_t * hdl)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
fmevt_fini_inbound(fmd_hdl_t * hdl)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 }
655