xref: /onnv-gate/usr/src/cmd/fm/fmd/common/fmd_sysevent.c (revision 12967:ab9ae749152f)
11193Smws /*
21193Smws  * CDDL HEADER START
31193Smws  *
41193Smws  * The contents of this file are subject to the terms of the
52914Sstephh  * Common Development and Distribution License (the "License").
62914Sstephh  * You may not use this file except in compliance with the License.
71193Smws  *
81193Smws  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91193Smws  * or http://www.opensolaris.org/os/licensing.
101193Smws  * See the License for the specific language governing permissions
111193Smws  * and limitations under the License.
121193Smws  *
131193Smws  * When distributing Covered Code, include this CDDL HEADER in each
141193Smws  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151193Smws  * If applicable, add the following below this CDDL HEADER, with the
161193Smws  * fields enclosed by brackets "[]" replaced with your own identifying
171193Smws  * information: Portions Copyright [yyyy] [name of copyright owner]
181193Smws  *
191193Smws  * CDDL HEADER END
201193Smws  */
211193Smws 
221193Smws /*
23*12967Sgavin.maltby@oracle.com  * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
241193Smws  */
251193Smws 
261193Smws #include <sys/sysevent/eventdefs.h>
271193Smws #include <sys/sysevent.h>
281193Smws #include <sys/sysevent_impl.h>
291193Smws #include <sys/fm/protocol.h>
301193Smws #include <sys/sysmacros.h>
311193Smws #include <sys/dumphdr.h>
321193Smws #include <sys/dumpadm.h>
331414Scindi #include <sys/fm/util.h>
341193Smws 
351193Smws #include <libsysevent.h>
361193Smws #include <libnvpair.h>
371193Smws #include <alloca.h>
381193Smws #include <limits.h>
391193Smws #include <strings.h>
401193Smws #include <unistd.h>
411193Smws #include <fcntl.h>
421193Smws #include <errno.h>
43*12967Sgavin.maltby@oracle.com #include <zone.h>
441193Smws 
451193Smws #undef MUTEX_HELD
461193Smws #undef RW_READ_HELD
471193Smws #undef RW_WRITE_HELD
481193Smws 
491193Smws #include <fmd_api.h>
501193Smws #include <fmd_log.h>
511193Smws #include <fmd_subr.h>
521193Smws #include <fmd_dispq.h>
537171Seschrock #include <fmd_dr.h>
541193Smws #include <fmd_module.h>
557171Seschrock #include <fmd_protocol.h>
561193Smws #include <fmd_scheme.h>
571193Smws #include <fmd_error.h>
581193Smws 
591193Smws #include <fmd.h>
601193Smws 
611193Smws static char *sysev_channel;	/* event channel to which we are subscribed */
621193Smws static char *sysev_class;	/* event class to which we are subscribed */
631193Smws static char *sysev_device;	/* device path to use for replaying events */
641193Smws static char *sysev_sid;		/* event channel subscriber identifier */
651193Smws static void *sysev_evc;		/* event channel cookie from evc_bind */
661193Smws 
671193Smws static fmd_xprt_t *sysev_xprt;
686081Scy152378 static int sysev_xprt_refcnt;
691193Smws static fmd_hdl_t *sysev_hdl;
701193Smws 
711193Smws static struct sysev_stats {
721193Smws 	fmd_stat_t dump_replay;
731193Smws 	fmd_stat_t dump_lost;
741193Smws 	fmd_stat_t bad_class;
751193Smws 	fmd_stat_t bad_attr;
761193Smws 	fmd_stat_t eagain;
771193Smws } sysev_stats = {
781193Smws 	{ "dump_replay", FMD_TYPE_UINT64, "events replayed from dump device" },
791193Smws 	{ "dump_lost", FMD_TYPE_UINT64, "events lost from dump device" },
801193Smws 	{ "bad_class", FMD_TYPE_UINT64, "events dropped due to invalid class" },
811193Smws 	{ "bad_attr", FMD_TYPE_UINT64, "events dropped due to invalid nvlist" },
821193Smws 	{ "eagain", FMD_TYPE_UINT64, "events retried due to low memory" },
831193Smws };
841193Smws 
856081Scy152378 static pthread_cond_t sysev_cv = PTHREAD_COND_INITIALIZER;
866081Scy152378 static pthread_mutex_t sysev_mutex = PTHREAD_MUTEX_INITIALIZER;
872914Sstephh static int sysev_replay_wait = 1;
886081Scy152378 static int sysev_exiting;
896081Scy152378 
90*12967Sgavin.maltby@oracle.com static sysevent_subattr_t *subattr;
91*12967Sgavin.maltby@oracle.com 
927171Seschrock /*
937171Seschrock  * Entry point for legacy sysevents.  This function is responsible for two
947171Seschrock  * things: passing off interesting events to the DR handler, and converting
957171Seschrock  * sysevents into resource events that modules can then subscribe to.
967171Seschrock  */
977171Seschrock static void
sysev_legacy(sysevent_t * sep)987171Seschrock sysev_legacy(sysevent_t *sep)
997171Seschrock {
1007171Seschrock 	const char *class = sysevent_get_class_name(sep);
1017171Seschrock 	const char *subclass = sysevent_get_subclass_name(sep);
1027171Seschrock 	char *fullclass;
1037171Seschrock 	size_t len;
1047171Seschrock 	nvlist_t *attr, *nvl;
1057171Seschrock 	hrtime_t hrt;
1067171Seschrock 
1077171Seschrock 	/* notify the DR subsystem of the event */
1087171Seschrock 	fmd_dr_event(sep);
1097171Seschrock 
1107171Seschrock 	/* get the matching sysevent name */
1117171Seschrock 	len = snprintf(NULL, 0, "%s%s.%s", SYSEVENT_RSRC_CLASS,
1127171Seschrock 	    class, subclass);
1137171Seschrock 	fullclass = alloca(len + 1);
1147171Seschrock 	(void) snprintf(fullclass, len + 1, "%s%s.%s",
1157171Seschrock 	    SYSEVENT_RSRC_CLASS, class, subclass);
1167171Seschrock 
1177171Seschrock 	/* construct the event payload */
1187171Seschrock 	(void) nvlist_xalloc(&nvl, NV_UNIQUE_NAME, &fmd.d_nva);
1197243Srobj 	if (sysevent_get_attr_list(sep, &attr) == 0) {
1207171Seschrock 		(void) nvlist_merge(nvl, attr, 0);
1217243Srobj 		nvlist_free(attr);
1227243Srobj 	}
1237171Seschrock 
1247171Seschrock 	/*
1259967SStephen.Hanson@Sun.COM 	 * Add class and version after the nvlist_merge() just in case
1269967SStephen.Hanson@Sun.COM 	 * the sysevent has an attribute called class or version.
1279967SStephen.Hanson@Sun.COM 	 */
1289967SStephen.Hanson@Sun.COM 	(void) nvlist_add_string(nvl, FM_CLASS, fullclass);
1299967SStephen.Hanson@Sun.COM 	(void) nvlist_add_uint8(nvl, FM_VERSION, FM_RSRC_VERSION);
1309967SStephen.Hanson@Sun.COM 
1319967SStephen.Hanson@Sun.COM 	/*
132*12967Sgavin.maltby@oracle.com 	 * Dispatch the event.  Because we have used sysevent_bind_xhandle
133*12967Sgavin.maltby@oracle.com 	 * the delivery thread is blessed as a proper fmd thread so
134*12967Sgavin.maltby@oracle.com 	 * we may use regular fmd api calls.
1357171Seschrock 	 */
1367171Seschrock 	sysevent_get_time(sep, &hrt);
137*12967Sgavin.maltby@oracle.com 	fmd_xprt_post(sysev_hdl, sysev_xprt, nvl, hrt);
1387171Seschrock }
1392914Sstephh 
1401193Smws /*
1411193Smws  * Receive an event from the SysEvent channel and post it to our transport.
1421193Smws  * Under extreme low-memory situations where we cannot event unpack the event,
1431193Smws  * we can request that SysEvent redeliver the event later by returning EAGAIN.
1441193Smws  * If we do this too many times, the kernel will drop the event.  Rather than
1451193Smws  * keeping state per-event, we simply attempt a garbage-collect, hoping that
1461193Smws  * enough free memory will be available by the time the event is redelivered.
1471193Smws  */
1481193Smws static int
sysev_recv(sysevent_t * sep,void * arg)1491193Smws sysev_recv(sysevent_t *sep, void *arg)
1501193Smws {
1511193Smws 	uint64_t seq = sysevent_get_seq(sep);
1521193Smws 	fmd_xprt_t *xp = arg;
1531193Smws 	nvlist_t *nvl;
1541193Smws 	hrtime_t hrt;
1556081Scy152378 	int rc = 0;
1561193Smws 
1576081Scy152378 	(void) pthread_mutex_lock(&sysev_mutex);
1586081Scy152378 	if (sysev_exiting == 1) {
1596081Scy152378 		while (sysev_xprt_refcnt > 0)
1606081Scy152378 			(void) pthread_cond_wait(&sysev_cv, &sysev_mutex);
1616081Scy152378 		(void) pthread_mutex_unlock(&sysev_mutex);
1626081Scy152378 		return (EAGAIN);
1636081Scy152378 	}
1646081Scy152378 	sysev_xprt_refcnt++;
1652914Sstephh 	while (sysev_replay_wait)
1666081Scy152378 		(void) pthread_cond_wait(&sysev_cv, &sysev_mutex);
1676081Scy152378 	(void) pthread_mutex_unlock(&sysev_mutex);
1682914Sstephh 
1691193Smws 	if (strcmp(sysevent_get_class_name(sep), EC_FM) != 0) {
1701193Smws 		fmd_hdl_error(sysev_hdl, "discarding event 0x%llx: unexpected"
1711193Smws 		    " transport class %s\n", seq, sysevent_get_class_name(sep));
1721193Smws 		sysev_stats.bad_class.fmds_value.ui64++;
1736081Scy152378 	} else if (sysevent_get_attr_list(sep, &nvl) != 0) {
1741193Smws 		if (errno == EAGAIN || errno == ENOMEM) {
1751193Smws 			fmd_modhash_tryapply(fmd.d_mod_hash, fmd_module_trygc);
1761193Smws 			fmd_scheme_hash_trygc(fmd.d_schemes);
1771193Smws 			sysev_stats.eagain.fmds_value.ui64++;
1786081Scy152378 			rc = EAGAIN;
1796081Scy152378 		} else {
1806081Scy152378 			fmd_hdl_error(sysev_hdl, "discarding event 0x%llx: "
1816081Scy152378 			    "missing or invalid payload", seq);
1826081Scy152378 			sysev_stats.bad_attr.fmds_value.ui64++;
1831193Smws 		}
1846081Scy152378 	} else {
1856081Scy152378 		sysevent_get_time(sep, &hrt);
1866081Scy152378 		fmd_xprt_post(sysev_hdl, xp, nvl, hrt);
1871193Smws 	}
1881193Smws 
1896081Scy152378 	(void) pthread_mutex_lock(&sysev_mutex);
1906081Scy152378 	if (--sysev_xprt_refcnt == 0 && sysev_exiting == 1)
1916081Scy152378 		(void) pthread_cond_broadcast(&sysev_cv);
1926081Scy152378 	(void) pthread_mutex_unlock(&sysev_mutex);
1936081Scy152378 
1946081Scy152378 	return (rc);
1951193Smws }
1961193Smws 
1971193Smws /*
1981193Smws  * Checksum algorithm used by the dump transport for verifying the content of
1991193Smws  * error reports saved on the dump device (copy of the kernel's checksum32()).
2001193Smws  */
2011193Smws static uint32_t
sysev_checksum(void * cp_arg,size_t length)2021193Smws sysev_checksum(void *cp_arg, size_t length)
2031193Smws {
2041193Smws 	uchar_t *cp, *ep;
2051193Smws 	uint32_t sum = 0;
2061193Smws 
2071193Smws 	for (cp = cp_arg, ep = cp + length; cp < ep; cp++)
2081193Smws 		sum = ((sum >> 1) | (sum << 31)) + *cp;
2091193Smws 
2101193Smws 	return (sum);
2111193Smws }
2121193Smws 
2131193Smws /*
2141193Smws  * Replay saved events from the dump transport.  This function is installed as
2151193Smws  * the timer callback and is called only once during the module's lifetime.
2161193Smws  */
2171193Smws /*ARGSUSED*/
2181193Smws static void
sysev_replay(fmd_hdl_t * hdl,id_t id,void * arg)2191193Smws sysev_replay(fmd_hdl_t *hdl, id_t id, void *arg)
2201193Smws {
2211193Smws 	char *dumpdev;
2221193Smws 	off64_t off, off0;
2231193Smws 	int fd, err;
2241193Smws 
2251193Smws 	/*
2261193Smws 	 * Determine the appropriate dump device to use for replaying pending
2271193Smws 	 * error reports.  If the device property is NULL (default), we
2281193Smws 	 * open and query /dev/dump to determine the current dump device.
2291193Smws 	 */
2301193Smws 	if ((dumpdev = sysev_device) == NULL) {
2311193Smws 		if ((fd = open("/dev/dump", O_RDONLY)) == -1) {
2321193Smws 			fmd_hdl_error(hdl, "failed to open /dev/dump "
2331193Smws 			    "to locate dump device for event replay");
2342914Sstephh 			goto done;
2351193Smws 		}
2361193Smws 
2371193Smws 		dumpdev = alloca(PATH_MAX);
2381193Smws 		err = ioctl(fd, DIOCGETDEV, dumpdev);
2391193Smws 		(void) close(fd);
2401193Smws 
2411193Smws 		if (err == -1) {
2421193Smws 			if (errno != ENODEV) {
2431193Smws 				fmd_hdl_error(hdl, "failed to obtain "
2441193Smws 				    "path to dump device for event replay");
2451193Smws 			}
2462914Sstephh 			goto done;
2471193Smws 		}
2481193Smws 	}
2491193Smws 
2501193Smws 	if (strcmp(dumpdev, "/dev/null") == 0)
2512914Sstephh 		goto done; /* return silently and skip replay for /dev/null */
2521193Smws 
2531193Smws 	/*
2541193Smws 	 * Open the appropriate device and then determine the offset of the
2551193Smws 	 * start of the ereport dump region located at the end of the device.
2561193Smws 	 */
2571193Smws 	if ((fd = open64(dumpdev, O_RDWR | O_DSYNC)) == -1) {
2581193Smws 		fmd_hdl_error(hdl, "failed to open dump transport %s "
2591193Smws 		    "(pending events will not be replayed)", dumpdev);
2602914Sstephh 		goto done;
2611193Smws 	}
2621193Smws 
2631193Smws 	off = DUMP_OFFSET + DUMP_LOGSIZE + DUMP_ERPTSIZE;
2641193Smws 	off = off0 = lseek64(fd, -off, SEEK_END) & -DUMP_OFFSET;
2651193Smws 
2661193Smws 	if (off == (off64_t)-1LL) {
2671193Smws 		fmd_hdl_error(hdl, "failed to seek dump transport %s "
2681193Smws 		    "(pending events will not be replayed)", dumpdev);
2691193Smws 		(void) close(fd);
2702914Sstephh 		goto done;
2711193Smws 	}
2721193Smws 
2731193Smws 	/*
2741193Smws 	 * The ereport dump region is a sequence of erpt_dump_t headers each of
2751193Smws 	 * which is followed by packed nvlist data.  We iterate over them in
2761193Smws 	 * order, unpacking and dispatching each one to our dispatch queue.
2771193Smws 	 */
2781193Smws 	for (;;) {
2791193Smws 		char nvbuf[ERPT_DATA_SZ];
2801193Smws 		uint32_t chksum;
2811193Smws 		erpt_dump_t ed;
2821193Smws 		nvlist_t *nvl;
2831193Smws 
2841193Smws 		fmd_timeval_t ftv, tod;
2851193Smws 		hrtime_t hrt;
2861193Smws 		uint64_t ena;
2871193Smws 
2881193Smws 		if (pread64(fd, &ed, sizeof (ed), off) != sizeof (ed)) {
2891193Smws 			fmd_hdl_error(hdl, "failed to read from dump "
2901193Smws 			    "transport %s (pending events lost)", dumpdev);
2911193Smws 			break;
2921193Smws 		}
2931193Smws 
2941193Smws 		if (ed.ed_magic == 0 && ed.ed_size == 0)
2951193Smws 			break; /* end of list: all zero */
2961193Smws 
2971193Smws 		if (ed.ed_magic == 0) {
2981193Smws 			off += sizeof (ed) + ed.ed_size;
2991193Smws 			continue; /* continue searching */
3001193Smws 		}
3011193Smws 
3021193Smws 		if (ed.ed_magic != ERPT_MAGIC) {
3031193Smws 			/*
3041193Smws 			 * Stop reading silently if the first record has the
3051193Smws 			 * wrong magic number; this likely indicates that we
3061193Smws 			 * rebooted from non-FMA bits or paged over the dump.
3071193Smws 			 */
3081193Smws 			if (off == off0)
3091193Smws 				break;
3101193Smws 
3111193Smws 			fmd_hdl_error(hdl, "invalid dump transport "
3121193Smws 			    "record at %llx (magic number %x, expected %x)\n",
3131193Smws 			    (u_longlong_t)off, ed.ed_magic, ERPT_MAGIC);
3141193Smws 			break;
3151193Smws 		}
3161193Smws 
3171193Smws 		if (ed.ed_size > ERPT_DATA_SZ) {
3181193Smws 			fmd_hdl_error(hdl, "invalid dump transport "
3191193Smws 			    "record at %llx size (%u exceeds limit)\n",
3201193Smws 			    (u_longlong_t)off, ed.ed_size);
3211193Smws 			break;
3221193Smws 		}
3231193Smws 
3241193Smws 		if (pread64(fd, nvbuf, ed.ed_size,
3251193Smws 		    off + sizeof (ed)) != ed.ed_size) {
3261193Smws 			fmd_hdl_error(hdl, "failed to read dump "
3271193Smws 			    "transport event (offset %llx)", (u_longlong_t)off);
3281193Smws 
3291193Smws 			sysev_stats.dump_lost.fmds_value.ui64++;
3301193Smws 			goto next;
3311193Smws 		}
3321193Smws 
3331193Smws 		if ((chksum = sysev_checksum(nvbuf,
3341193Smws 		    ed.ed_size)) != ed.ed_chksum) {
3351193Smws 			fmd_hdl_error(hdl, "dump transport event at "
3361193Smws 			    "offset %llx is corrupt (checksum %x != %x)\n",
3371193Smws 			    (u_longlong_t)off, chksum, ed.ed_chksum);
3381193Smws 
3391193Smws 			sysev_stats.dump_lost.fmds_value.ui64++;
3401193Smws 			goto next;
3411193Smws 		}
3421193Smws 
3431193Smws 		if ((err = nvlist_xunpack(nvbuf,
3441193Smws 		    ed.ed_size, &nvl, &fmd.d_nva)) != 0) {
3451193Smws 			fmd_hdl_error(hdl, "failed to unpack dump "
3461193Smws 			    "transport event at offset %llx: %s\n",
3471193Smws 			    (u_longlong_t)off, fmd_strerror(err));
3481193Smws 
3491193Smws 			sysev_stats.dump_lost.fmds_value.ui64++;
3501193Smws 			goto next;
3511193Smws 		}
3521193Smws 
3531193Smws 		/*
3541193Smws 		 * If ed_hrt_nsec is set it contains the gethrtime() value from
3551193Smws 		 * when the event was originally enqueued for the transport.
3561193Smws 		 * If it is zero, we use the weaker bound ed_hrt_base instead.
3571193Smws 		 */
3581193Smws 		if (ed.ed_hrt_nsec != 0)
3591193Smws 			hrt = ed.ed_hrt_nsec;
3601193Smws 		else
3611193Smws 			hrt = ed.ed_hrt_base;
3621193Smws 
3631193Smws 		/*
3641193Smws 		 * If this is an FMA protocol event of class "ereport.*" that
3651193Smws 		 * contains valid ENA, we can improve the precision of 'hrt'.
3661193Smws 		 */
3671193Smws 		if (nvlist_lookup_uint64(nvl, FM_EREPORT_ENA, &ena) == 0)
3681193Smws 			hrt = fmd_time_ena2hrt(hrt, ena);
3691193Smws 
3701193Smws 		/*
3711193Smws 		 * Now convert 'hrt' to an adjustable TOD based on the values
3721193Smws 		 * in ed_tod_base which correspond to one another and are
3731193Smws 		 * sampled before reboot using the old gethrtime() clock.
3741193Smws 		 * fmd_event_recreate() will use this TOD value to re-assign
3751193Smws 		 * the event an updated gethrtime() value based on the current
3761193Smws 		 * value of the non-adjustable gethrtime() clock.  Phew.
3771193Smws 		 */
3781193Smws 		tod.ftv_sec = ed.ed_tod_base.sec;
3791193Smws 		tod.ftv_nsec = ed.ed_tod_base.nsec;
3801193Smws 		fmd_time_hrt2tod(ed.ed_hrt_base, &tod, hrt, &ftv);
3811193Smws 
3821193Smws 		(void) nvlist_remove_all(nvl, FMD_EVN_TOD);
3831193Smws 		(void) nvlist_add_uint64_array(nvl,
3841193Smws 		    FMD_EVN_TOD, (uint64_t *)&ftv, 2);
3851193Smws 
3861193Smws 		fmd_xprt_post(hdl, sysev_xprt, nvl, 0);
3871193Smws 		sysev_stats.dump_replay.fmds_value.ui64++;
3881193Smws 
3891193Smws next:
3901193Smws 		/*
3911193Smws 		 * Reset the magic number for the event record to zero so that
3921193Smws 		 * we do not replay the same event multiple times.
3931193Smws 		 */
3941193Smws 		ed.ed_magic = 0;
3951193Smws 
3961193Smws 		if (pwrite64(fd, &ed, sizeof (ed), off) != sizeof (ed)) {
3971193Smws 			fmd_hdl_error(hdl, "failed to mark dump "
3981193Smws 			    "transport event (offset %llx)", (u_longlong_t)off);
3991193Smws 		}
4001193Smws 
4011193Smws 		off += sizeof (ed) + ed.ed_size;
4021193Smws 	}
4031193Smws 
4041193Smws 	(void) close(fd);
4052914Sstephh done:
4066081Scy152378 	(void) pthread_mutex_lock(&sysev_mutex);
4072914Sstephh 	sysev_replay_wait = 0;
4086081Scy152378 	(void) pthread_cond_broadcast(&sysev_cv);
4096081Scy152378 	(void) pthread_mutex_unlock(&sysev_mutex);
4101193Smws }
4111193Smws 
4121193Smws static const fmd_prop_t sysev_props[] = {
4131193Smws 	{ "class", FMD_TYPE_STRING, EC_ALL },		/* event class */
4141193Smws 	{ "device", FMD_TYPE_STRING, NULL },		/* replay device */
4151193Smws 	{ "channel", FMD_TYPE_STRING, FM_ERROR_CHAN },	/* channel name */
4161193Smws 	{ "sid", FMD_TYPE_STRING, "fmd" },		/* subscriber id */
4171193Smws 	{ NULL, 0, NULL }
4181193Smws };
4191193Smws 
4201193Smws static const fmd_hdl_ops_t sysev_ops = {
4211193Smws 	NULL,		/* fmdo_recv */
4221193Smws 	sysev_replay,	/* fmdo_timeout */
4231193Smws 	NULL,		/* fmdo_close */
4241193Smws 	NULL,		/* fmdo_stats */
4251193Smws 	NULL,		/* fmdo_gc */
4261193Smws 	NULL,		/* fmdo_send */
4271193Smws };
4281193Smws 
4291193Smws static const fmd_hdl_info_t sysev_info = {
4301193Smws 	"SysEvent Transport Agent", "1.0", &sysev_ops, sysev_props
4311193Smws };
4321193Smws 
4331193Smws /*
4341193Smws  * Bind to the sysevent channel we use for listening for error events and then
4357171Seschrock  * subscribe to appropriate events received over this channel.  Setup the
4367171Seschrock  * legacy sysevent handler for creating sysevent resources and forwarding DR
4377171Seschrock  * events.
4381193Smws  */
4391193Smws void
sysev_init(fmd_hdl_t * hdl)4401193Smws sysev_init(fmd_hdl_t *hdl)
4411193Smws {
4421193Smws 	uint_t flags;
4437171Seschrock 	const char *subclasses[] = { EC_SUB_ALL };
4441193Smws 
445*12967Sgavin.maltby@oracle.com 	/* This builtin is for the global zone only */
446*12967Sgavin.maltby@oracle.com 	if (getzoneid() != GLOBAL_ZONEID)
447*12967Sgavin.maltby@oracle.com 		return;
448*12967Sgavin.maltby@oracle.com 
4491193Smws 	if (fmd_hdl_register(hdl, FMD_API_VERSION, &sysev_info) != 0)
4501193Smws 		return; /* invalid property settings */
4511193Smws 
4521193Smws 	(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (sysev_stats) /
4531193Smws 	    sizeof (fmd_stat_t), (fmd_stat_t *)&sysev_stats);
4541193Smws 
4551193Smws 	sysev_channel = fmd_prop_get_string(hdl, "channel");
4561193Smws 	sysev_class = fmd_prop_get_string(hdl, "class");
4571193Smws 	sysev_device = fmd_prop_get_string(hdl, "device");
4581193Smws 	sysev_sid = fmd_prop_get_string(hdl, "sid");
4591193Smws 
4601193Smws 	if (sysev_channel == NULL)
4611193Smws 		fmd_hdl_abort(hdl, "channel property must be defined\n");
4621193Smws 
4631193Smws 	if (sysev_sid == NULL)
4641193Smws 		fmd_hdl_abort(hdl, "sid property must be defined\n");
4651193Smws 
4661193Smws 	if ((errno = sysevent_evc_bind(sysev_channel, &sysev_evc,
4671193Smws 	    EVCH_CREAT | EVCH_HOLD_PEND)) != 0) {
4681193Smws 		fmd_hdl_abort(hdl, "failed to bind to event transport "
4691193Smws 		    "channel %s", sysev_channel);
4701193Smws 	}
4711193Smws 
4729120SStephen.Hanson@Sun.COM 	sysev_xprt = fmd_xprt_open(hdl, FMD_XPRT_RDONLY |
4739120SStephen.Hanson@Sun.COM 	    FMD_XPRT_CACHE_AS_LOCAL, NULL, NULL);
4741193Smws 	sysev_hdl = hdl;
4751193Smws 
4761193Smws 	/*
4771193Smws 	 * If we're subscribing to the default channel, keep our subscription
4781193Smws 	 * active even if we die unexpectedly so we continue queuing events.
4791193Smws 	 * If we're not (e.g. running under fmsim), do not specify SUB_KEEP so
4801193Smws 	 * that our event channel will be destroyed if we die unpleasantly.
4811193Smws 	 */
4821193Smws 	if (strcmp(sysev_channel, FM_ERROR_CHAN) == 0)
4831193Smws 		flags = EVCH_SUB_KEEP | EVCH_SUB_DUMP;
4841193Smws 	else
4851193Smws 		flags = EVCH_SUB_DUMP;
4861193Smws 
487*12967Sgavin.maltby@oracle.com 	if ((subattr = sysevent_subattr_alloc()) == NULL)
488*12967Sgavin.maltby@oracle.com 		fmd_hdl_abort(hdl, "failed to allocate subscription "
489*12967Sgavin.maltby@oracle.com 		    "attributes");
490*12967Sgavin.maltby@oracle.com 
491*12967Sgavin.maltby@oracle.com 	sysevent_subattr_thrcreate(subattr, fmd_doorthr_create, NULL);
492*12967Sgavin.maltby@oracle.com 	sysevent_subattr_thrsetup(subattr, fmd_doorthr_setup, NULL);
493*12967Sgavin.maltby@oracle.com 
494*12967Sgavin.maltby@oracle.com 	errno = sysevent_evc_xsubscribe(sysev_evc,
495*12967Sgavin.maltby@oracle.com 	    sysev_sid, sysev_class, sysev_recv, sysev_xprt, flags, subattr);
4961193Smws 
4971193Smws 	if (errno != 0) {
4981193Smws 		if (errno == EEXIST) {
4991193Smws 			fmd_hdl_abort(hdl, "another fault management daemon is "
5001193Smws 			    "active on transport channel %s\n", sysev_channel);
5011193Smws 		} else {
502*12967Sgavin.maltby@oracle.com 			fmd_hdl_abort(hdl, "failed to xsubscribe to %s on "
5031193Smws 			    "transport channel %s", sysev_class, sysev_channel);
5041193Smws 		}
5051193Smws 	}
5061193Smws 
5071193Smws 	/*
5081193Smws 	 * Once the transport is open, install a single timer to fire at once
5091193Smws 	 * in the context of the module's thread to run sysev_replay().  This
5101193Smws 	 * thread will block in its first fmd_xprt_post() until fmd is ready.
5111193Smws 	 */
5121193Smws 	fmd_hdl_debug(hdl, "transport '%s' open\n", sysev_channel);
5131193Smws 	(void) fmd_timer_install(hdl, NULL, NULL, 0);
5147171Seschrock 
5157171Seschrock 	/*
5167171Seschrock 	 * Open the legacy sysevent handle and subscribe to all events.  These
5177171Seschrock 	 * are automatically converted to "resource.sysevent.*" events so that
5187171Seschrock 	 * modules can manage these events without additional infrastructure.
5197171Seschrock 	 */
5207171Seschrock 	if (geteuid() != 0)
5217171Seschrock 		return;
5227171Seschrock 
5237171Seschrock 	if ((fmd.d_sysev_hdl =
524*12967Sgavin.maltby@oracle.com 	    sysevent_bind_xhandle(sysev_legacy, subattr)) == NULL)
5257171Seschrock 		fmd_hdl_abort(hdl, "failed to bind to legacy sysevent channel");
5267171Seschrock 
5277171Seschrock 	if (sysevent_subscribe_event(fmd.d_sysev_hdl, EC_ALL,
5287171Seschrock 	    subclasses, 1) != 0)
5297171Seschrock 		fmd_hdl_abort(hdl, "failed to subscribe to legacy sysevents");
5301193Smws }
5311193Smws 
5321193Smws /*
5331193Smws  * Close the channel by unsubscribing and unbinding.  We only do this when a
5341193Smws  * a non-default channel has been selected.  If we're using FM_ERROR_CHAN,
5351193Smws  * the system default, we do *not* want to unsubscribe because the kernel will
5361193Smws  * remove the subscriber queue and any events published in our absence will
5371193Smws  * therefore be lost.  This scenario may occur when, for example, fmd is sent
5381193Smws  * a SIGTERM by init(1M) during reboot but an error is detected and makes it
5391193Smws  * into the sysevent channel queue before init(1M) manages to call uadmin(2).
5401193Smws  */
5411193Smws void
sysev_fini(fmd_hdl_t * hdl)5421193Smws sysev_fini(fmd_hdl_t *hdl)
5431193Smws {
5441193Smws 	if (strcmp(sysev_channel, FM_ERROR_CHAN) != 0) {
54511102SGavin.Maltby@Sun.COM 		(void) sysevent_evc_unsubscribe(sysev_evc, sysev_sid);
54611102SGavin.Maltby@Sun.COM 		(void) sysevent_evc_unbind(sysev_evc);
5471193Smws 	}
5481193Smws 
5497171Seschrock 	if (fmd.d_sysev_hdl != NULL)
5507171Seschrock 		sysevent_unbind_handle(fmd.d_sysev_hdl);
5517171Seschrock 
552*12967Sgavin.maltby@oracle.com 	if (subattr != NULL) {
553*12967Sgavin.maltby@oracle.com 		sysevent_subattr_free(subattr);
554*12967Sgavin.maltby@oracle.com 		subattr = NULL;
555*12967Sgavin.maltby@oracle.com 	}
556*12967Sgavin.maltby@oracle.com 
5576081Scy152378 	if (sysev_xprt != NULL) {
5586081Scy152378 		/*
5596081Scy152378 		 * Wait callback returns before destroy the transport.
5606081Scy152378 		 */
5616081Scy152378 		(void) pthread_mutex_lock(&sysev_mutex);
5626081Scy152378 		sysev_exiting = 1;
5636081Scy152378 		while (sysev_xprt_refcnt > 0)
5646081Scy152378 			(void) pthread_cond_wait(&sysev_cv, &sysev_mutex);
5656081Scy152378 		(void) pthread_mutex_unlock(&sysev_mutex);
5661193Smws 		fmd_xprt_close(hdl, sysev_xprt);
5676081Scy152378 	}
5681193Smws 
5691193Smws 	fmd_prop_free_string(hdl, sysev_class);
5701193Smws 	fmd_prop_free_string(hdl, sysev_channel);
5711193Smws 	fmd_prop_free_string(hdl, sysev_device);
5721193Smws 	fmd_prop_free_string(hdl, sysev_sid);
5731193Smws }
574