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