summaryrefslogtreecommitdiff
path: root/usr/src/lib/libsysevent
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libsysevent')
-rw-r--r--usr/src/lib/libsysevent/Makefile68
-rw-r--r--usr/src/lib/libsysevent/Makefile.com69
-rw-r--r--usr/src/lib/libsysevent/amd64/Makefile36
-rw-r--r--usr/src/lib/libsysevent/i386/Makefile36
-rw-r--r--usr/src/lib/libsysevent/libevchannel.c529
-rw-r--r--usr/src/lib/libsysevent/libsysevent.c2538
-rw-r--r--usr/src/lib/libsysevent/libsysevent.h119
-rw-r--r--usr/src/lib/libsysevent/libsysevent_impl.h163
-rw-r--r--usr/src/lib/libsysevent/llib-lsysevent134
-rw-r--r--usr/src/lib/libsysevent/sparc/Makefile36
-rw-r--r--usr/src/lib/libsysevent/sparcv9/Makefile37
-rw-r--r--usr/src/lib/libsysevent/spec/Makefile30
-rw-r--r--usr/src/lib/libsysevent/spec/Makefile.targ35
-rw-r--r--usr/src/lib/libsysevent/spec/amd64/Makefile42
-rw-r--r--usr/src/lib/libsysevent/spec/i386/Makefile42
-rw-r--r--usr/src/lib/libsysevent/spec/sparc/Makefile44
-rw-r--r--usr/src/lib/libsysevent/spec/sparcv9/Makefile43
-rw-r--r--usr/src/lib/libsysevent/spec/sysevent.spec286
-rw-r--r--usr/src/lib/libsysevent/spec/versions57
19 files changed, 4344 insertions, 0 deletions
diff --git a/usr/src/lib/libsysevent/Makefile b/usr/src/lib/libsysevent/Makefile
new file mode 100644
index 0000000000..7edc64ff6f
--- /dev/null
+++ b/usr/src/lib/libsysevent/Makefile
@@ -0,0 +1,68 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright 2000,2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# lib/libsysevent/Makefile
+#
+#
+
+# include library definitions
+include ../Makefile.lib
+
+SUBDIRS = spec .WAIT $(MACH) $(BUILD64) $(MACH64)
+
+# conditional assignments
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+test := TARGET= test
+
+# definitions for install_h target
+HDRS= libsysevent.h libsysevent_impl.h
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDRS:%=$(ROOTHDRDIR)/%)
+CHECKHDRS= $(HDRS:%.h=%.check)
+
+.KEEP_STATE:
+
+all install clean clobber lint: $(SUBDIRS)
+
+# install rule for install_h target
+
+$(ROOTHDRDIR)/%: %
+ $(INS.file)
+
+install_h: $(ROOTHDRS)
+
+check: $(CHECKHDRS)
+
+$(MACH) $(MACH64) spec: FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
diff --git a/usr/src/lib/libsysevent/Makefile.com b/usr/src/lib/libsysevent/Makefile.com
new file mode 100644
index 0000000000..5abbe900e7
--- /dev/null
+++ b/usr/src/lib/libsysevent/Makefile.com
@@ -0,0 +1,69 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY= libsysevent.a
+
+VERS= .1
+
+OBJECTS= libsysevent.o \
+ libevchannel.o
+
+# include library definitions
+include ../../Makefile.lib
+include ../../Makefile.rootfs
+
+SRCS= $(OBJECTS:%.o=../%.c)
+
+MAPDIR= ../spec/$(TRANSMACH)
+SPECMAPFILE= $(MAPDIR)/mapfile
+
+LIBS = $(DYNLIB) $(LINTLIB)
+
+LINTSRC = $(LINTLIB:%.ln=%)
+
+CPPFLAGS += -D_REENTRANT -I..
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lnvpair -lc
+
+$(LINTLIB) := SRCS = ../llib-lsysevent
+
+.KEEP_STATE:
+
+all : $(LIBS)
+
+lint : lintcheck
+
+# include library targets
+include ../../Makefile.targ
+
+pics/%.o: ../%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
+
+$(ROOTLINTDIR)/%: ../%
+ $(INS.file)
diff --git a/usr/src/lib/libsysevent/amd64/Makefile b/usr/src/lib/libsysevent/amd64/Makefile
new file mode 100644
index 0000000000..9e109c2863
--- /dev/null
+++ b/usr/src/lib/libsysevent/amd64/Makefile
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libsysevent/i386/Makefile b/usr/src/lib/libsysevent/i386/Makefile
new file mode 100644
index 0000000000..a27d61f99f
--- /dev/null
+++ b/usr/src/lib/libsysevent/i386/Makefile
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2000,2002-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/i386/Makefile
+
+include ../Makefile.com
+
+all: $(LIBS)
+
+.KEEP_STATE:
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libsysevent/libevchannel.c b/usr/src/lib/libsysevent/libevchannel.c
new file mode 100644
index 0000000000..a18a62b0dd
--- /dev/null
+++ b/usr/src/lib/libsysevent/libevchannel.c
@@ -0,0 +1,529 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <door.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/varargs.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent_impl.h>
+
+#include "libsysevent.h"
+#include "libsysevent_impl.h"
+
+/*
+ * The functions below deal with the General Purpose Event Handling framework
+ *
+ * sysevent_evc_bind - create/bind application to named channel
+ * sysevent_evc_unbind - unbind from previously bound/created channel
+ * sysevent_evc_subscribe - subscribe to existing event channel
+ * sysevent_evc_unsubscribe - unsubscribe from existing event channel
+ * sysevent_evc_publish - generate a system event via an event channel
+ * sysevent_evc_control - various channel based control operation
+ */
+
+#define misaligned(p) ((uintptr_t)(p) & 3) /* 4-byte alignment required */
+
+/*
+ * Check syntax of a channel name
+ */
+static int
+sysevent_is_chan_name(const char *str)
+{
+ for (; *str != '\0'; str++) {
+ if (!EVCH_ISCHANCHAR(*str))
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * Check for printable characters
+ */
+static int
+strisprint(const char *s)
+{
+ for (; *s != '\0'; s++) {
+ if (*s < ' ' || *s > '~')
+ return (0);
+ }
+
+ return (1);
+}
+
+/*
+ * sysevent_evc_bind - Create/bind application to named channel
+ */
+int
+sysevent_evc_bind(const char *channel, evchan_t **scpp, uint32_t flags)
+{
+ int chanlen;
+ evchan_t *scp;
+ sev_bind_args_t uargs;
+ int ec;
+
+ if (scpp == NULL || misaligned(scpp)) {
+ return (errno = EINVAL);
+ }
+
+ /* Provide useful value in error case */
+ *scpp = NULL;
+
+ if (channel == NULL ||
+ (chanlen = strlen(channel) + 1) > MAX_CHNAME_LEN) {
+ return (errno = EINVAL);
+ }
+
+ /* Check channel syntax */
+ if (!sysevent_is_chan_name(channel)) {
+ return (errno = EINVAL);
+ }
+
+ if (flags & ~EVCH_B_FLAGS) {
+ return (errno = EINVAL);
+ }
+
+ scp = calloc(1, sizeof (evchan_impl_hdl_t));
+ if (scp == NULL) {
+ return (errno = ENOMEM);
+ }
+
+ /*
+ * Enable sysevent driver. Fallback if the device link doesn't exist;
+ * this situation can arise if a channel is bound early in system
+ * startup, prior to devfsadm(1M) being invoked.
+ */
+ EV_FD(scp) = open(DEVSYSEVENT, O_RDWR);
+ if (EV_FD(scp) == -1) {
+ if (errno != ENOENT) {
+ ec = errno == EACCES ? EPERM : errno;
+ free(scp);
+ return (errno = ec);
+ }
+
+ EV_FD(scp) = open(DEVICESYSEVENT, O_RDWR);
+ if (EV_FD(scp) == -1) {
+ ec = errno == EACCES ? EPERM : errno;
+ free(scp);
+ return (errno = ec);
+ }
+ }
+
+ /*
+ * Force to close the fd's when process is doing exec.
+ * The driver will then release stale binding handles.
+ * The driver will release also the associated subscriptions
+ * if EVCH_SUB_KEEP flag was not set.
+ */
+ (void) fcntl(EV_FD(scp), F_SETFD, FD_CLOEXEC);
+
+ uargs.chan_name.name = (uintptr_t)channel;
+ uargs.chan_name.len = chanlen;
+ uargs.flags = flags;
+
+ if (ioctl(EV_FD(scp), SEV_CHAN_OPEN, &uargs) != 0) {
+ ec = errno;
+ (void) close(EV_FD(scp));
+ free(scp);
+ return (errno = ec);
+ }
+
+ /* Needed to detect a fork() */
+ EV_PID(scp) = getpid();
+ (void) mutex_init(EV_LOCK(scp), USYNC_THREAD, NULL);
+
+ *scpp = scp;
+
+ return (0);
+}
+
+/*
+ * sysevent_evc_unbind - Unbind from previously bound/created channel
+ */
+void
+sysevent_evc_unbind(evchan_t *scp)
+{
+ sev_unsubscribe_args_t uargs;
+ evchan_subscr_t *subp, *tofree;
+
+ if (scp == NULL || misaligned(scp))
+ return;
+
+ (void) mutex_lock(EV_LOCK(scp));
+
+ /*
+ * Unsubscribe, if we are in the process which did the bind.
+ */
+ if (EV_PID(scp) == getpid()) {
+ uargs.sid.name = NULL;
+ uargs.sid.len = 0;
+ /*
+ * The unsubscribe ioctl will block until all door upcalls have
+ * drained.
+ */
+ if (ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs) != 0) {
+ (void) mutex_unlock(EV_LOCK(scp));
+ return;
+ }
+ }
+
+ subp = (evchan_subscr_t *)(void*)EV_SUB(scp);
+ while (subp->evsub_next != NULL) {
+ tofree = subp->evsub_next;
+ subp->evsub_next = tofree->evsub_next;
+ if (door_revoke(tofree->evsub_door_desc) != 0 && errno == EPERM)
+ (void) close(tofree->evsub_door_desc);
+ free(tofree->evsub_sid);
+ free(tofree);
+ }
+
+ (void) mutex_unlock(EV_LOCK(scp));
+
+ /*
+ * The close of the driver will do the unsubscribe if a) it is the last
+ * close and b) we are in a child which inherited subscriptions.
+ */
+ (void) close(EV_FD(scp));
+ (void) mutex_destroy(EV_LOCK(scp));
+ free(scp);
+}
+
+/*
+ * sysevent_evc_publish - Generate a system event via an event channel
+ */
+int
+sysevent_evc_publish(evchan_t *scp, const char *class,
+ const char *subclass, const char *vendor,
+ const char *pub_name, nvlist_t *attr_list,
+ uint32_t flags)
+{
+ sysevent_t *ev;
+ sev_publish_args_t uargs;
+ int rc;
+ int ec;
+
+ if (scp == NULL || misaligned(scp)) {
+ return (errno = EINVAL);
+ }
+
+ /* No inheritance of binding handles via fork() */
+ if (EV_PID(scp) != getpid()) {
+ return (errno = EINVAL);
+ }
+
+ ev = sysevent_alloc_event((char *)class, (char *)subclass,
+ (char *)vendor, (char *)pub_name, attr_list);
+ if (ev == NULL) {
+ return (errno);
+ }
+
+ uargs.ev.name = (uintptr_t)ev;
+ uargs.ev.len = SE_SIZE(ev);
+ uargs.flags = flags;
+
+ (void) mutex_lock(EV_LOCK(scp));
+
+ rc = ioctl(EV_FD(scp), SEV_PUBLISH, (intptr_t)&uargs);
+ ec = errno;
+
+ (void) mutex_unlock(EV_LOCK(scp));
+
+ sysevent_free(ev);
+
+ if (rc != 0) {
+ return (ec);
+ }
+ return (0);
+}
+
+/*
+ * Generic callback which catches events from the kernel and calls
+ * subscribers call back routine.
+ *
+ * Kernel guarantees that door_upcalls are disabled when unsubscription
+ * was issued that's why cookie points always to a valid evchan_subscr_t *.
+ *
+ * Furthermore it's not necessary to lock subp because the sysevent
+ * framework guarantees no unsubscription until door_return.
+ */
+/*ARGSUSED3*/
+static void
+door_upcall(void *cookie, char *args, size_t alen,
+ door_desc_t *ddp, uint_t ndid)
+{
+ evchan_subscr_t *subp = EVCHAN_SUBSCR(cookie);
+ int rval = 0;
+
+ if (args == NULL || alen <= (size_t)0) {
+ /* Skip callback execution */
+ rval = EINVAL;
+ } else {
+ rval = subp->evsub_func((sysevent_t *)(void *)args,
+ subp->evsub_cookie);
+ }
+
+ /*
+ * Fill in return values for door_return
+ */
+ alen = sizeof (rval);
+ bcopy(&rval, args, alen);
+
+ (void) door_return(args, alen, NULL, 0);
+}
+
+/*
+ * sysevent_evc_subscribe - Subscribe to an existing event channel
+ */
+int
+sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *class,
+ int (*event_handler)(sysevent_t *ev, void *cookie),
+ void *cookie, uint32_t flags)
+{
+ evchan_subscr_t *subp;
+ int upcall_door;
+ sev_subscribe_args_t uargs;
+ uint32_t sid_len;
+ uint32_t class_len;
+ int ec;
+
+ if (scp == NULL || misaligned(scp) || sid == NULL || class == NULL) {
+ return (errno = EINVAL);
+ }
+
+ /* No inheritance of binding handles via fork() */
+ if (EV_PID(scp) != getpid()) {
+ return (errno = EINVAL);
+ }
+
+ if ((sid_len = strlen(sid) + 1) > MAX_SUBID_LEN || sid_len == 1 ||
+ (class_len = strlen(class) + 1) > MAX_CLASS_LEN) {
+ return (errno = EINVAL);
+ }
+
+ /* Check for printable characters */
+ if (!strisprint(sid)) {
+ return (errno = EINVAL);
+ }
+
+ if (event_handler == NULL) {
+ return (errno = EINVAL);
+ }
+
+ /* Create subscriber data */
+ if ((subp = calloc(1, sizeof (evchan_subscr_t))) == NULL) {
+ return (errno);
+ }
+
+ if ((subp->evsub_sid = strdup(sid)) == NULL) {
+ ec = errno;
+ free(subp);
+ return (ec);
+ }
+
+ /*
+ * EC_ALL string will not be copied to kernel - NULL is assumed
+ */
+ if (strcmp(class, EC_ALL) == 0) {
+ class = NULL;
+ class_len = 0;
+ }
+
+ upcall_door = door_create(door_upcall, (void *)subp,
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+ if (upcall_door == -1) {
+ ec = errno;
+ free(subp->evsub_sid);
+ free(subp);
+ return (ec);
+ }
+
+ /* Complete subscriber information */
+ subp->evsub_door_desc = upcall_door;
+ subp->evsub_func = event_handler;
+ subp->evsub_cookie = cookie;
+
+ (void) mutex_lock(EV_LOCK(scp));
+
+ subp->ev_subhead = EVCHAN_IMPL_HNDL(scp);
+
+ uargs.sid.name = (uintptr_t)sid;
+ uargs.sid.len = sid_len;
+ uargs.class_info.name = (uintptr_t)class;
+ uargs.class_info.len = class_len;
+ uargs.door_desc = subp->evsub_door_desc;
+ uargs.flags = flags;
+ if (ioctl(EV_FD(scp), SEV_SUBSCRIBE, (intptr_t)&uargs) != 0) {
+ ec = errno;
+ (void) mutex_unlock(EV_LOCK(scp));
+ (void) door_revoke(upcall_door);
+ free(subp->evsub_sid);
+ free(subp);
+ return (ec);
+ }
+
+ /* Attach to subscriber list */
+ subp->evsub_next = EV_SUB_NEXT(scp);
+ EV_SUB_NEXT(scp) = subp;
+
+ (void) mutex_unlock(EV_LOCK(scp));
+
+ return (0);
+}
+
+/*
+ * sysevent_evc_unsubscribe - Unsubscribe from an existing event channel
+ */
+void
+sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
+{
+ int all_subscribers = 0;
+ sev_unsubscribe_args_t uargs;
+ evchan_subscr_t *subp, *tofree;
+ int rc;
+
+ if (scp == NULL || misaligned(scp))
+ return;
+
+ if (sid == NULL || strlen(sid) == 0 ||
+ (strlen(sid) >= MAX_SUBID_LEN))
+ return;
+
+ /* No inheritance of binding handles via fork() */
+ if (EV_PID(scp) != getpid()) {
+ return;
+ }
+
+ if (strcmp(sid, EVCH_ALLSUB) == 0) {
+ all_subscribers++;
+ /* Indicates all subscriber id's for this channel */
+ uargs.sid.name = NULL;
+ uargs.sid.len = 0;
+ } else {
+ uargs.sid.name = (uintptr_t)sid;
+ uargs.sid.len = strlen(sid) + 1;
+ }
+
+ (void) mutex_lock(EV_LOCK(scp));
+
+ /*
+ * The unsubscribe ioctl will block until all door upcalls have drained.
+ */
+ rc = ioctl(EV_FD(scp), SEV_UNSUBSCRIBE, (intptr_t)&uargs);
+
+ if (rc != 0) {
+ (void) mutex_unlock(EV_LOCK(scp));
+ return;
+ }
+
+ /* Search for the matching subscriber */
+ subp = (evchan_subscr_t *)(void*)EV_SUB(scp);
+ while (subp->evsub_next != NULL) {
+
+ if (all_subscribers ||
+ (strcmp(subp->evsub_next->evsub_sid, sid) == 0)) {
+
+ tofree = subp->evsub_next;
+ subp->evsub_next = tofree->evsub_next;
+ (void) door_revoke(tofree->evsub_door_desc);
+ free(tofree->evsub_sid);
+ free(tofree);
+ /* Freed single subscriber already */
+ if (all_subscribers == 0) {
+ break;
+ }
+ } else
+ subp = subp->evsub_next;
+ }
+
+ (void) mutex_unlock(EV_LOCK(scp));
+}
+
+/*
+ * sysevent_evc_control - Various channel based control operation
+ */
+int
+sysevent_evc_control(evchan_t *scp, int cmd, /* arg */ ...)
+{
+ va_list ap;
+ uint32_t *chlenp;
+ sev_control_args_t uargs;
+ int rc = 0;
+
+ if (scp == NULL || misaligned(scp)) {
+ return (errno = EINVAL);
+ }
+
+ /* No inheritance of binding handles via fork() */
+ if (EV_PID(scp) != getpid()) {
+ return (errno = EINVAL);
+ }
+
+ va_start(ap, cmd);
+
+ uargs.cmd = cmd;
+
+ (void) mutex_lock(EV_LOCK(scp));
+
+ switch (cmd) {
+ case EVCH_GET_CHAN_LEN:
+ case EVCH_GET_CHAN_LEN_MAX:
+ chlenp = va_arg(ap, uint32_t *);
+ if (chlenp == NULL || misaligned(chlenp)) {
+ rc = EINVAL;
+ break;
+ }
+ rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
+ *chlenp = uargs.value;
+ break;
+ case EVCH_SET_CHAN_LEN:
+ /* Range change will be handled in framework */
+ uargs.value = va_arg(ap, uint32_t);
+ rc = ioctl(EV_FD(scp), SEV_CHAN_CONTROL, (intptr_t)&uargs);
+ break;
+ default:
+ rc = EINVAL;
+ }
+
+ (void) mutex_unlock(EV_LOCK(scp));
+
+ if (rc == -1) {
+ rc = errno;
+ }
+
+ va_end(ap);
+
+ return (errno = rc);
+}
diff --git a/usr/src/lib/libsysevent/libsysevent.c b/usr/src/lib/libsysevent/libsysevent.c
new file mode 100644
index 0000000000..6dff4538df
--- /dev/null
+++ b/usr/src/lib/libsysevent/libsysevent.c
@@ -0,0 +1,2538 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <door.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <synch.h>
+#include <pthread.h>
+#include <thread.h>
+#include <libnvpair.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/modctl.h>
+#include <sys/mnttab.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent_impl.h>
+
+#include "libsysevent.h"
+#include "libsysevent_impl.h"
+
+/*
+ * libsysevent - The system event framework library
+ *
+ * This library provides routines to help with marshalling
+ * and unmarshalling of data contained in a sysevent event
+ * buffer.
+ */
+
+#define SE_ENCODE_METHOD NV_ENCODE_NATIVE
+
+#define dprint if (libsysevent_debug) (void) printf
+static int libsysevent_debug = 0;
+
+static sysevent_t *se_unpack(sysevent_t *);
+static int cleanup_id(sysevent_handle_t *shp, uint32_t id, int type);
+
+/*
+ * The following routines allow system event publication to the sysevent
+ * framework.
+ */
+
+/*
+ * sysevent_alloc - allocate a sysevent buffer
+ */
+static sysevent_t *
+sysevent_alloc(char *class, int class_sz, char *subclass, int subclass_sz,
+ char *pub, int pub_sz, nvlist_t *attr_list)
+{
+ int payload_sz;
+ int aligned_class_sz, aligned_subclass_sz, aligned_pub_sz;
+ size_t nvlist_sz = 0;
+ char *attr;
+ uint64_t attr_offset;
+ sysevent_t *ev;
+
+ if (attr_list != NULL) {
+ if (nvlist_size(attr_list, &nvlist_sz, SE_ENCODE_METHOD)
+ != 0) {
+ return (NULL);
+ }
+ }
+
+ /*
+ * Calculate and reserve space for the class, subclass and
+ * publisher strings in the event buffer
+ */
+
+ /* String sizes must be 64-bit aligned in the event buffer */
+ aligned_class_sz = SE_ALIGN(class_sz);
+ aligned_subclass_sz = SE_ALIGN(subclass_sz);
+ aligned_pub_sz = SE_ALIGN(pub_sz);
+
+ payload_sz = (aligned_class_sz - sizeof (uint64_t)) +
+ (aligned_subclass_sz - sizeof (uint64_t)) +
+ (aligned_pub_sz - sizeof (uint64_t)) - sizeof (uint64_t) +
+ nvlist_sz;
+
+ /*
+ * Allocate event buffer plus additional payload overhead.
+ */
+ ev = calloc(1, sizeof (sysevent_impl_t) + payload_sz);
+ if (ev == NULL) {
+ return (NULL);
+ }
+
+ /* Initialize the event buffer data */
+ SE_VERSION(ev) = SYS_EVENT_VERSION;
+ (void) bcopy(class, SE_CLASS_NAME(ev), class_sz);
+
+ SE_SUBCLASS_OFF(ev) = SE_ALIGN(offsetof(sysevent_impl_t, se_class_name))
+ + aligned_class_sz;
+ (void) bcopy(subclass, SE_SUBCLASS_NAME(ev), subclass_sz);
+
+ SE_PUB_OFF(ev) = SE_SUBCLASS_OFF(ev) + aligned_subclass_sz;
+ (void) bcopy(pub, SE_PUB_NAME(ev), pub_sz);
+
+ SE_PAYLOAD_SZ(ev) = payload_sz;
+ SE_ATTR_PTR(ev) = (uint64_t)0;
+
+ /* Check for attribute list */
+ if (attr_list == NULL) {
+ return (ev);
+ }
+
+ /* Copy attribute data to contiguous memory */
+ SE_FLAG(ev) = SE_PACKED_BUF;
+ attr_offset = SE_ATTR_OFF(ev);
+ attr = (char *)((caddr_t)ev + attr_offset);
+ if (nvlist_pack(attr_list, &attr, &nvlist_sz, SE_ENCODE_METHOD,
+ 0) != 0) {
+ free(ev);
+ return (NULL);
+ }
+
+ return (ev);
+}
+
+/*
+ * sysevent_post_event - generate a system event via the sysevent framework
+ */
+int
+sysevent_post_event(char *class, char *subclass, char *vendor, char *pub_name,
+ nvlist_t *attr_list, sysevent_id_t *eid)
+{
+ int error;
+ sysevent_t *ev;
+
+ ev = sysevent_alloc_event(class, subclass, vendor, pub_name, attr_list);
+ if (ev == NULL) {
+ return (-1);
+ }
+
+ error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_POST_EVENT,
+ (uintptr_t)ev, (uintptr_t)SE_SIZE(ev),
+ (uintptr_t)eid, 0);
+
+ sysevent_free(ev);
+
+ if (error) {
+ errno = EIO;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * The following routines are used to free or duplicate a
+ * sysevent event buffer.
+ */
+
+/*
+ * sysevent_dup - Allocate and copy an event buffer
+ * Copies both packed and unpacked to unpacked sysevent.
+ */
+sysevent_t *
+sysevent_dup(sysevent_t *ev)
+{
+ nvlist_t *nvl, *cnvl = NULL;
+ uint64_t attr_offset;
+ sysevent_t *copy;
+
+ if (SE_FLAG(ev) == SE_PACKED_BUF)
+ return (se_unpack(ev));
+
+ /* Copy event header information */
+ attr_offset = SE_ATTR_OFF(ev);
+ copy = calloc(1, attr_offset);
+ if (copy == NULL)
+ return (NULL);
+ bcopy(ev, copy, attr_offset);
+
+ nvl = (nvlist_t *)SE_ATTR_PTR(ev);
+ if (nvl && nvlist_dup(nvl, &cnvl, 0) != 0) {
+ free(copy);
+ return (NULL);
+ }
+
+ SE_ATTR_PTR(copy) = (uintptr_t)cnvl;
+ SE_FLAG(copy) = 0; /* unpacked */
+ return (copy);
+}
+
+/*
+ * sysevent_free - Free memory allocated for an event buffer
+ */
+void
+sysevent_free(sysevent_t *ev)
+{
+ nvlist_t *attr_list = (nvlist_t *)SE_ATTR_PTR(ev);
+
+ if (attr_list)
+ nvlist_free(attr_list);
+ free(ev);
+}
+
+/*
+ * The following routines are used to extract attribute data from a sysevent
+ * handle.
+ */
+
+/*
+ * sysevent_get_attr_list - allocate and return an attribute associated with
+ * the given sysevent buffer.
+ */
+int
+sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
+{
+ int error;
+ caddr_t attr;
+ size_t attr_len;
+ uint64_t attr_offset;
+ nvlist_t *nvl;
+
+ *nvlist = NULL;
+
+ /* Duplicate attribute for an unpacked sysevent buffer */
+ if (SE_FLAG(ev) != SE_PACKED_BUF) {
+ nvl = (nvlist_t *)SE_ATTR_PTR(ev);
+ if (nvl == NULL) {
+ return (0);
+ }
+ if ((error = nvlist_dup(nvl, nvlist, 0)) != 0) {
+ if (error == ENOMEM) {
+ errno = error;
+ } else {
+ errno = EINVAL;
+ }
+ return (-1);
+ }
+ return (0);
+ }
+
+ attr_offset = SE_ATTR_OFF(ev);
+ if (SE_SIZE(ev) == attr_offset) {
+ return (0);
+ }
+
+ /* unpack nvlist */
+ attr = (caddr_t)ev + attr_offset;
+ attr_len = SE_SIZE(ev) - attr_offset;
+ if ((error = nvlist_unpack(attr, attr_len, nvlist, 0)) != 0) {
+ if (error == ENOMEM) {
+ errno = error;
+ } else {
+ errno = EINVAL;
+ }
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * sysevent_attr_name - Get name of attribute
+ */
+char *
+sysevent_attr_name(sysevent_attr_t *attr)
+{
+ if (attr == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ return (nvpair_name((nvpair_t *)attr));
+}
+
+/*
+ * sysevent_attr_value - Get attribute value data and type
+ */
+int
+sysevent_attr_value(sysevent_attr_t *attr, sysevent_value_t *se_value)
+{
+ nvpair_t *nvp = attr;
+
+ if (nvp == NULL)
+ return (EINVAL);
+
+ /* Convert DATA_TYPE_* to SE_DATA_TYPE_* */
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_BYTE:
+ se_value->value_type = SE_DATA_TYPE_BYTE;
+ (void) nvpair_value_byte(nvp, &se_value->value.sv_byte);
+ break;
+ case DATA_TYPE_INT16:
+ se_value->value_type = SE_DATA_TYPE_INT16;
+ (void) nvpair_value_int16(nvp, &se_value->value.sv_int16);
+ break;
+ case DATA_TYPE_UINT16:
+ se_value->value_type = SE_DATA_TYPE_UINT16;
+ (void) nvpair_value_uint16(nvp, &se_value->value.sv_uint16);
+ break;
+ case DATA_TYPE_INT32:
+ se_value->value_type = SE_DATA_TYPE_INT32;
+ (void) nvpair_value_int32(nvp, &se_value->value.sv_int32);
+ break;
+ case DATA_TYPE_UINT32:
+ se_value->value_type = SE_DATA_TYPE_UINT32;
+ (void) nvpair_value_uint32(nvp, &se_value->value.sv_uint32);
+ break;
+ case DATA_TYPE_INT64:
+ se_value->value_type = SE_DATA_TYPE_INT64;
+ (void) nvpair_value_int64(nvp, &se_value->value.sv_int64);
+ break;
+ case DATA_TYPE_UINT64:
+ se_value->value_type = SE_DATA_TYPE_UINT64;
+ (void) nvpair_value_uint64(nvp, &se_value->value.sv_uint64);
+ break;
+ case DATA_TYPE_STRING:
+ se_value->value_type = SE_DATA_TYPE_STRING;
+ (void) nvpair_value_string(nvp, &se_value->value.sv_string);
+ break;
+ case DATA_TYPE_BYTE_ARRAY:
+ se_value->value_type = SE_DATA_TYPE_BYTES;
+ (void) nvpair_value_byte_array(nvp,
+ &se_value->value.sv_bytes.data,
+ (uint_t *)&se_value->value.sv_bytes.size);
+ break;
+ case DATA_TYPE_HRTIME:
+ se_value->value_type = SE_DATA_TYPE_TIME;
+ (void) nvpair_value_hrtime(nvp, &se_value->value.sv_time);
+ break;
+ default:
+ return (ENOTSUP);
+ }
+ return (0);
+}
+
+/*
+ * sysevent_attr_next - Get next attribute in event attribute list
+ */
+sysevent_attr_t *
+sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr)
+{
+ nvlist_t *nvl;
+ nvpair_t *nvp = attr;
+
+ /* all user visible sysevent_t's are unpacked */
+ assert(SE_FLAG(ev) != SE_PACKED_BUF);
+
+ if (SE_ATTR_PTR(ev) == (uint64_t)0) {
+ return (NULL);
+ }
+
+ nvl = (nvlist_t *)SE_ATTR_PTR(ev);
+ return (nvlist_next_nvpair(nvl, nvp));
+}
+
+/*
+ * sysevent_lookup_attr - Lookup attribute by name and datatype.
+ */
+int
+sysevent_lookup_attr(sysevent_t *ev, char *name, int datatype,
+ sysevent_value_t *se_value)
+{
+ nvpair_t *nvp;
+ nvlist_t *nvl;
+
+ assert(SE_FLAG(ev) != SE_PACKED_BUF);
+
+ if (SE_ATTR_PTR(ev) == (uint64_t)0) {
+ return (ENOENT);
+ }
+
+ /*
+ * sysevent matches on both name and datatype
+ * nvlist_look mataches name only. So we walk
+ * nvlist manually here.
+ */
+ nvl = (nvlist_t *)SE_ATTR_PTR(ev);
+ nvp = nvlist_next_nvpair(nvl, NULL);
+ while (nvp) {
+ if ((strcmp(name, nvpair_name(nvp)) == 0) &&
+ (sysevent_attr_value(nvp, se_value) == 0) &&
+ (se_value->value_type == datatype))
+ return (0);
+ nvp = nvlist_next_nvpair(nvl, nvp);
+ }
+ return (ENOENT);
+}
+
+/* Routines to extract event header information */
+
+/*
+ * sysevent_get_class - Get class id
+ */
+int
+sysevent_get_class(sysevent_t *ev)
+{
+ return (SE_CLASS(ev));
+}
+
+/*
+ * sysevent_get_subclass - Get subclass id
+ */
+int
+sysevent_get_subclass(sysevent_t *ev)
+{
+ return (SE_SUBCLASS(ev));
+}
+
+/*
+ * sysevent_get_class_name - Get class name string
+ */
+char *
+sysevent_get_class_name(sysevent_t *ev)
+{
+ return (SE_CLASS_NAME(ev));
+}
+
+typedef enum {
+ PUB_VEND,
+ PUB_KEYWD,
+ PUB_NAME,
+ PUB_PID
+} se_pub_id_t;
+
+/*
+ * sysevent_get_pub - Get publisher name string
+ */
+char *
+sysevent_get_pub(sysevent_t *ev)
+{
+ return (SE_PUB_NAME(ev));
+}
+
+/*
+ * Get the requested string pointed by the token.
+ *
+ * Return NULL if not found or for insufficient memory.
+ */
+static char *
+parse_pub_id(sysevent_t *ev, se_pub_id_t token)
+{
+ int i;
+ char *pub_id, *pub_element, *str, *next;
+
+ next = pub_id = strdup(sysevent_get_pub(ev));
+ for (i = 0; i <= token; ++i) {
+ str = strtok_r(next, ":", &next);
+ if (str == NULL) {
+ free(pub_id);
+ return (NULL);
+ }
+ }
+
+ pub_element = strdup(str);
+ free(pub_id);
+ return (pub_element);
+}
+
+/*
+ * Return a pointer to the string following the token
+ *
+ * Note: This is a dedicated function for parsing
+ * publisher strings and not for general purpose.
+ */
+static const char *
+pub_idx(const char *pstr, int token)
+{
+ int i;
+
+ for (i = 1; i <= token; i++) {
+ if ((pstr = index(pstr, ':')) == NULL)
+ return (NULL);
+ pstr++;
+ }
+
+ /* String might be empty */
+ if (pstr) {
+ if (*pstr == '\0' || *pstr == ':')
+ return (NULL);
+ }
+ return (pstr);
+}
+
+char *
+sysevent_get_vendor_name(sysevent_t *ev)
+{
+ return (parse_pub_id(ev, PUB_VEND));
+}
+
+char *
+sysevent_get_pub_name(sysevent_t *ev)
+{
+ return (parse_pub_id(ev, PUB_NAME));
+}
+
+/*
+ * Provide the pid encoded in the publisher string
+ * w/o allocating any resouces.
+ */
+void
+sysevent_get_pid(sysevent_t *ev, pid_t *pid)
+{
+ const char *part_str;
+ const char *pub_str = sysevent_get_pub(ev);
+
+ *pid = (pid_t)SE_KERN_PID;
+
+ part_str = pub_idx(pub_str, PUB_KEYWD);
+ if (part_str != NULL && strstr(part_str, SE_KERN_PUB) != NULL)
+ return;
+
+ if ((part_str = pub_idx(pub_str, PUB_PID)) == NULL)
+ return;
+
+ *pid = (pid_t)atoi(part_str);
+}
+
+/*
+ * sysevent_get_subclass_name - Get subclass name string
+ */
+char *
+sysevent_get_subclass_name(sysevent_t *ev)
+{
+ return (SE_SUBCLASS_NAME(ev));
+}
+
+/*
+ * sysevent_get_seq - Get event sequence id
+ */
+uint64_t
+sysevent_get_seq(sysevent_t *ev)
+{
+ return (SE_SEQ(ev));
+}
+
+/*
+ * sysevent_get_time - Get event timestamp
+ */
+void
+sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
+{
+ *etime = SE_TIME(ev);
+}
+
+/*
+ * sysevent_get_size - Get event buffer size
+ */
+size_t
+sysevent_get_size(sysevent_t *ev)
+{
+ return ((size_t)SE_SIZE(ev));
+}
+
+/*
+ * The following routines are used by devfsadm_mod.c to propagate event
+ * buffers to devfsadmd. These routines will serve as the basis for
+ * event channel publication and subscription.
+ */
+
+/*
+ * sysevent_alloc_event -
+ * allocate a sysevent buffer for sending through an established event
+ * channel.
+ */
+sysevent_t *
+sysevent_alloc_event(char *class, char *subclass, char *vendor, char *pub_name,
+ nvlist_t *attr_list)
+{
+ int class_sz, subclass_sz, pub_sz;
+ char *pub_id;
+ sysevent_t *ev;
+
+ if ((class == NULL) || (subclass == NULL) || (vendor == NULL) ||
+ (pub_name == NULL)) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ class_sz = strlen(class) + 1;
+ subclass_sz = strlen(subclass) + 1;
+ if ((class_sz > MAX_CLASS_LEN) ||
+ (subclass_sz > MAX_SUBCLASS_LEN)) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * Calculate the publisher size plus string seperators and maximum
+ * pid characters
+ */
+ pub_sz = strlen(vendor) + sizeof (SE_USR_PUB) + strlen(pub_name) + 14;
+ if (pub_sz > MAX_PUB_LEN) {
+ errno = EINVAL;
+ return (NULL);
+ }
+ pub_id = malloc(pub_sz);
+ if (pub_id == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if (snprintf(pub_id, pub_sz, "%s:%s%s:%d", vendor, SE_USR_PUB,
+ pub_name, (int)getpid()) >= pub_sz) {
+ free(pub_id);
+ errno = EINVAL;
+ return (NULL);
+ }
+ pub_sz = strlen(pub_id) + 1;
+
+ ev = sysevent_alloc(class, class_sz, subclass, subclass_sz,
+ pub_id, pub_sz, attr_list);
+ free(pub_id);
+ if (ev == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ return (ev);
+}
+
+/*
+ * se_unpack - unpack nvlist to a searchable list.
+ * If already unpacked, will do a dup.
+ */
+static sysevent_t *
+se_unpack(sysevent_t *ev)
+{
+ caddr_t attr;
+ size_t attr_len;
+ nvlist_t *attrp = NULL;
+ uint64_t attr_offset;
+ sysevent_t *copy;
+
+ assert(SE_FLAG(ev) == SE_PACKED_BUF);
+
+ /* Copy event header information */
+ attr_offset = SE_ATTR_OFF(ev);
+ copy = calloc(1, attr_offset);
+ if (copy == NULL)
+ return (NULL);
+ bcopy(ev, copy, attr_offset);
+ SE_FLAG(copy) = 0; /* unpacked */
+
+ /* unpack nvlist */
+ attr = (caddr_t)ev + attr_offset;
+ attr_len = SE_SIZE(ev) - attr_offset;
+ if (attr_len == 0) {
+ return (copy);
+ }
+ if (nvlist_unpack(attr, attr_len, &attrp, 0) != 0) {
+ free(copy);
+ return (NULL);
+ }
+
+ SE_ATTR_PTR(copy) = (uintptr_t)attrp;
+ return (copy);
+}
+
+/*
+ * se_print - Prints elements in an event buffer
+ */
+void
+se_print(FILE *fp, sysevent_t *ev)
+{
+ char *vendor, *pub;
+ pid_t pid;
+ hrtime_t hrt;
+ nvlist_t *attr_list = NULL;
+
+ (void) sysevent_get_time(ev, &hrt);
+ (void) fprintf(fp, "received sysevent id = 0X%llx:%llx\n",
+ hrt, (longlong_t)sysevent_get_seq(ev));
+ (void) fprintf(fp, "\tclass = %s\n", sysevent_get_class_name(ev));
+ (void) fprintf(fp, "\tsubclass = %s\n", sysevent_get_subclass_name(ev));
+ if ((vendor = sysevent_get_vendor_name(ev)) != NULL) {
+ (void) fprintf(fp, "\tvendor = %s\n", vendor);
+ free(vendor);
+ }
+ if ((pub = sysevent_get_pub_name(ev)) != NULL) {
+ sysevent_get_pid(ev, &pid);
+ (void) fprintf(fp, "\tpublisher = %s:%d\n", pub, (int)pid);
+ free(pub);
+ }
+
+ if (sysevent_get_attr_list(ev, &attr_list) == 0 && attr_list != NULL) {
+ nvlist_print(fp, attr_list);
+ nvlist_free(attr_list);
+ }
+}
+
+/*
+ * The following routines are provided to support establishment and use
+ * of sysevent channels. A sysevent channel is established between
+ * publishers and subscribers of sysevents for an agreed upon channel name.
+ * These routines currently support sysevent channels between user-level
+ * applications running on the same system.
+ *
+ * Sysevent channels may be created by a single publisher or subscriber process.
+ * Once established, up to MAX_SUBSRCIBERS subscribers may subscribe interest in
+ * receiving sysevent notifications on the named channel. At present, only
+ * one publisher is allowed per sysevent channel.
+ *
+ * The registration information for each channel is kept in the kernel. A
+ * kernel-based registration was chosen for persistence and reliability reasons.
+ * If either a publisher or a subscriber exits for any reason, the channel
+ * properties are maintained until all publishers and subscribers have exited.
+ * Additionally, an in-kernel registration allows the API to be extended to
+ * include kernel subscribers as well as userland subscribers in the future.
+ *
+ * To insure fast lookup of subscriptions, a cached copy of the registration
+ * is kept and maintained for the publisher process. Updates are made
+ * everytime a change is made in the kernel. Changes to the registration are
+ * expected to be infrequent.
+ *
+ * Channel communication between publisher and subscriber processes is
+ * implemented primarily via doors. Each publisher creates a door for
+ * registration notifications and each subscriber creates a door for event
+ * delivery.
+ *
+ * Most of these routines are used by syseventd(1M), the sysevent publisher
+ * for the syseventd channel. Processes wishing to receive sysevent
+ * notifications from syseventd may use a set of public
+ * APIs designed to subscribe to syseventd sysevents. The subscription
+ * APIs are implemented in accordance with PSARC/2001/076.
+ *
+ */
+
+/*
+ * Door handlers for the channel subscribers
+ */
+
+/*
+ * subscriber_event_handler - generic event handling wrapper for subscribers
+ * This handler is used to process incoming sysevent
+ * notifications from channel publishers.
+ * It is created as a seperate thread in each subscriber
+ * process per subscription.
+ */
+static void
+subscriber_event_handler(sysevent_handle_t *shp)
+{
+ subscriber_priv_t *sub_info;
+ sysevent_queue_t *evqp;
+
+ sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
+
+ (void) mutex_lock(&sub_info->sp_qlock);
+ for (;;) {
+ while (sub_info->sp_evq_head == NULL && SH_BOUND(shp)) {
+ (void) cond_wait(&sub_info->sp_cv, &sub_info->sp_qlock);
+ }
+ evqp = sub_info->sp_evq_head;
+ while (evqp) {
+ (void) mutex_unlock(&sub_info->sp_qlock);
+ (void) sub_info->sp_func(evqp->sq_ev);
+ (void) mutex_lock(&sub_info->sp_qlock);
+ sub_info->sp_evq_head = sub_info->sp_evq_head->sq_next;
+ free(evqp->sq_ev);
+ free(evqp);
+ evqp = sub_info->sp_evq_head;
+ }
+ if (!SH_BOUND(shp)) {
+ (void) mutex_unlock(&sub_info->sp_qlock);
+ return;
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+/*
+ * Data structure used to communicate event subscription cache updates
+ * to publishers via a registration door
+ */
+struct reg_args {
+ uint32_t ra_sub_id;
+ uint32_t ra_op;
+ uint64_t ra_buf_ptr;
+};
+
+
+/*
+ * event_deliver_service - generic event delivery service routine. This routine
+ * is called in response to a door call to post an event.
+ *
+ */
+static void
+event_deliver_service(void *cookie, char *args, size_t alen,
+ door_desc_t *ddp, uint_t ndid)
+{
+ int ret = 0;
+ subscriber_priv_t *sub_info;
+ sysevent_handle_t *shp;
+ sysevent_queue_t *new_eq;
+
+ if (args == NULL || alen < sizeof (uint32_t)) {
+ ret = EINVAL;
+ goto return_from_door;
+ }
+
+ /* Publisher checking on subscriber */
+ if (alen == sizeof (uint32_t)) {
+ ret = 0;
+ goto return_from_door;
+ }
+
+ shp = (sysevent_handle_t *)cookie;
+ if (shp == NULL) {
+ ret = EBADF;
+ goto return_from_door;
+ }
+
+ /*
+ * Mustn't block if we are trying to update the registration with
+ * the publisher
+ */
+ if (mutex_trylock(SH_LOCK(shp)) != 0) {
+ ret = EAGAIN;
+ goto return_from_door;
+ }
+
+ if (!SH_BOUND(shp)) {
+ ret = EBADF;
+ (void) mutex_unlock(SH_LOCK(shp));
+ goto return_from_door;
+ }
+
+ sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
+ if (sub_info == NULL) {
+ ret = EBADF;
+ (void) mutex_unlock(SH_LOCK(shp));
+ goto return_from_door;
+ }
+
+ new_eq = (sysevent_queue_t *)calloc(1,
+ sizeof (sysevent_queue_t));
+ if (new_eq == NULL) {
+ ret = EAGAIN;
+ (void) mutex_unlock(SH_LOCK(shp));
+ goto return_from_door;
+ }
+
+ /*
+ * Allocate and copy the event buffer into the subscriber's
+ * address space
+ */
+ new_eq->sq_ev = calloc(1, alen);
+ if (new_eq->sq_ev == NULL) {
+ free(new_eq);
+ ret = EAGAIN;
+ (void) mutex_unlock(SH_LOCK(shp));
+ goto return_from_door;
+ }
+ (void) bcopy(args, new_eq->sq_ev, alen);
+
+ (void) mutex_lock(&sub_info->sp_qlock);
+ if (sub_info->sp_evq_head == NULL) {
+ sub_info->sp_evq_head = new_eq;
+ } else {
+ sub_info->sp_evq_tail->sq_next = new_eq;
+ }
+ sub_info->sp_evq_tail = new_eq;
+
+ (void) cond_signal(&sub_info->sp_cv);
+ (void) mutex_unlock(&sub_info->sp_qlock);
+ (void) mutex_unlock(SH_LOCK(shp));
+
+return_from_door:
+ (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
+ (void) door_return(NULL, 0, NULL, 0);
+}
+
+/*
+ * Sysevent subscription information is maintained in the kernel. Updates
+ * to the in-kernel registration database is expected to be infrequent and
+ * offers consistency for publishers and subscribers that may come and go
+ * for a given channel.
+ *
+ * To expedite registration lookups by publishers, a cached copy of the
+ * kernel registration database is kept per-channel. Caches are invalidated
+ * and refreshed upon state changes to the in-kernel registration database.
+ *
+ * To prevent stale subscriber data, publishers may remove subsriber
+ * registrations from the in-kernel registration database in the event
+ * that a particular subscribing process is unresponsive.
+ *
+ * The following routines provide a mechanism to update publisher and subscriber
+ * information for a specified channel.
+ */
+
+/*
+ * clnt_deliver_event - Deliver an event through the consumer's event
+ * delivery door
+ *
+ * Returns -1 if message not delivered. With errno set to cause of error.
+ * Returns 0 for success with the results returned in posting buffer.
+ */
+static int
+clnt_deliver_event(int service_door, void *data, size_t datalen,
+ void *result, size_t rlen)
+{
+ int error = 0;
+ door_arg_t door_arg;
+
+ door_arg.rbuf = result;
+ door_arg.rsize = rlen;
+ door_arg.data_ptr = data;
+ door_arg.data_size = datalen;
+ door_arg.desc_ptr = NULL;
+ door_arg.desc_num = 0;
+
+ /*
+ * Make door call
+ */
+ while ((error = door_call(service_door, &door_arg)) != 0) {
+ if (errno == EAGAIN || errno == EINTR) {
+ continue;
+ } else {
+ error = errno;
+ break;
+ }
+ }
+
+ return (error);
+}
+
+static int
+update_publisher_cache(subscriber_priv_t *sub_info, int update_op,
+ uint32_t sub_id, size_t datasz, uchar_t *data)
+{
+ int pub_fd;
+ uint32_t result = 0;
+ struct reg_args *rargs;
+
+ rargs = (struct reg_args *)calloc(1, sizeof (struct reg_args) +
+ datasz);
+ if (rargs == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ rargs->ra_sub_id = sub_id;
+ rargs->ra_op = update_op;
+ bcopy(data, (char *)&rargs->ra_buf_ptr, datasz);
+
+ pub_fd = open(sub_info->sp_door_name, O_RDONLY);
+ (void) clnt_deliver_event(pub_fd, (void *)rargs,
+ sizeof (struct reg_args) + datasz, &result, sizeof (result));
+ (void) close(pub_fd);
+
+ free(rargs);
+ if (result != 0) {
+ errno = result;
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * update_kernel_registration - update the in-kernel registration for the
+ * given channel.
+ */
+static int
+update_kernel_registration(sysevent_handle_t *shp, int update_type,
+ int update_op, uint32_t *sub_id, size_t datasz, uchar_t *data)
+{
+ int error;
+ char *channel_name = SH_CHANNEL_NAME(shp);
+ se_pubsub_t udata;
+
+ udata.ps_channel_name_len = strlen(channel_name) + 1;
+ udata.ps_op = update_op;
+ udata.ps_type = update_type;
+ udata.ps_buflen = datasz;
+ udata.ps_id = *sub_id;
+
+ if ((error = modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
+ (uintptr_t)channel_name, (uintptr_t)data, (uintptr_t)&udata, 0))
+ != 0) {
+ return (error);
+ }
+
+ *sub_id = udata.ps_id;
+
+ return (error);
+}
+
+/*
+ * get_kernel_registration - get the current subscriber registration for
+ * the given channel
+ */
+static nvlist_t *
+get_kernel_registration(char *channel_name, uint32_t class_id)
+{
+ char *nvlbuf;
+ nvlist_t *nvl;
+ se_pubsub_t udata;
+
+ nvlbuf = calloc(1, MAX_SUBSCRIPTION_SZ);
+ if (nvlbuf == NULL) {
+ return (NULL);
+ }
+
+ udata.ps_buflen = MAX_SUBSCRIPTION_SZ;
+ udata.ps_channel_name_len = strlen(channel_name) + 1;
+ udata.ps_id = class_id;
+ udata.ps_op = SE_GET_REGISTRATION;
+ udata.ps_type = PUBLISHER;
+
+ if (modctl(MODEVENTS, (uintptr_t)MODEVENTS_REGISTER_EVENT,
+ (uintptr_t)channel_name, (uintptr_t)nvlbuf, (uintptr_t)&udata, 0)
+ != 0) {
+
+ /* Need a bigger buffer to hold channel registration */
+ if (errno == EAGAIN) {
+ free(nvlbuf);
+ nvlbuf = calloc(1, udata.ps_buflen);
+ if (nvlbuf == NULL)
+ return (NULL);
+
+ /* Try again */
+ if (modctl(MODEVENTS,
+ (uintptr_t)MODEVENTS_REGISTER_EVENT,
+ (uintptr_t)channel_name, (uintptr_t)nvlbuf,
+ (uintptr_t)&udata, 0) != 0) {
+ free(nvlbuf);
+ return (NULL);
+ }
+ } else {
+ free(nvlbuf);
+ return (NULL);
+ }
+ }
+
+ if (nvlist_unpack(nvlbuf, udata.ps_buflen, &nvl, 0) != 0) {
+ free(nvlbuf);
+ return (NULL);
+ }
+ free(nvlbuf);
+
+ return (nvl);
+}
+
+/*
+ * The following routines provide a mechanism for publishers to maintain
+ * subscriber information.
+ */
+
+static void
+dealloc_subscribers(sysevent_handle_t *shp)
+{
+ int i;
+ subscriber_data_t *sub;
+
+ for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
+ sub = SH_SUBSCRIBER(shp, i);
+ if (sub != NULL) {
+ free(sub->sd_door_name);
+ free(sub);
+ }
+ SH_SUBSCRIBER(shp, i) = NULL;
+ }
+}
+
+static int
+alloc_subscriber(sysevent_handle_t *shp, uint32_t sub_id, int oflag)
+{
+ subscriber_data_t *sub;
+ char door_name[MAXPATHLEN];
+
+ if (SH_SUBSCRIBER(shp, sub_id) != NULL) {
+ return (0);
+ }
+
+ /* Allocate and initialize the subscriber data */
+ sub = (subscriber_data_t *)calloc(1,
+ sizeof (subscriber_data_t));
+ if (sub == NULL) {
+ return (-1);
+ }
+ if (snprintf(door_name, MAXPATHLEN, "%s/%d",
+ SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
+ free(sub);
+ return (-1);
+ }
+
+ sub->sd_flag = ACTIVE;
+ sub->sd_door_name = strdup(door_name);
+ if (sub->sd_door_name == NULL) {
+ free(sub);
+ return (-1);
+ }
+
+ SH_SUBSCRIBER(shp, sub_id) = sub;
+ return (0);
+
+}
+
+/*
+ * The following routines are used to update and maintain the registration cache
+ * for a particular sysevent channel.
+ */
+
+static uint32_t
+hash_func(const char *s)
+{
+ uint32_t result = 0;
+ uint_t g;
+
+ while (*s != '\0') {
+ result <<= 4;
+ result += (uint32_t)*s++;
+ g = result & 0xf0000000;
+ if (g != 0) {
+ result ^= g >> 24;
+ result ^= g;
+ }
+ }
+
+ return (result);
+}
+
+subclass_lst_t *
+cache_find_subclass(class_lst_t *c_list, char *subclass)
+{
+ subclass_lst_t *sc_list;
+
+ if (c_list == NULL)
+ return (NULL);
+
+ sc_list = c_list->cl_subclass_list;
+
+ while (sc_list != NULL) {
+ if (strcmp(sc_list->sl_name, subclass) == 0) {
+ return (sc_list);
+ }
+ sc_list = sc_list->sl_next;
+ }
+
+ return (NULL);
+}
+
+
+static class_lst_t *
+cache_find_class(sysevent_handle_t *shp, char *class)
+{
+ int index;
+ class_lst_t *c_list;
+ class_lst_t **class_hash = SH_CLASS_HASH(shp);
+
+ if (strcmp(class, EC_ALL) == 0) {
+ return (class_hash[0]);
+ }
+
+ index = CLASS_HASH(class);
+ c_list = class_hash[index];
+ while (c_list != NULL) {
+ if (strcmp(class, c_list->cl_name) == 0) {
+ break;
+ }
+ c_list = c_list->cl_next;
+ }
+
+ return (c_list);
+}
+
+static int
+cache_insert_subclass(class_lst_t *c_list, char **subclass_names,
+ int subclass_num, uint32_t sub_id)
+{
+ int i;
+ subclass_lst_t *sc_list;
+
+ for (i = 0; i < subclass_num; ++i) {
+ if ((sc_list = cache_find_subclass(c_list, subclass_names[i]))
+ != NULL) {
+ sc_list->sl_num[sub_id] = 1;
+ } else {
+ sc_list = (subclass_lst_t *)calloc(1,
+ sizeof (subclass_lst_t));
+ if (sc_list == NULL)
+ return (-1);
+
+ sc_list->sl_name = strdup(subclass_names[i]);
+ if (sc_list->sl_name == NULL) {
+ free(sc_list);
+ return (-1);
+ }
+
+ sc_list->sl_num[sub_id] = 1;
+ sc_list->sl_next = c_list->cl_subclass_list;
+ c_list->cl_subclass_list = sc_list;
+ }
+ }
+
+ return (0);
+}
+
+static int
+cache_insert_class(sysevent_handle_t *shp, char *class,
+ char **subclass_names, int subclass_num, uint32_t sub_id)
+{
+ class_lst_t *c_list;
+
+ if (strcmp(class, EC_ALL) == 0) {
+ char *subclass_all = EC_SUB_ALL;
+
+ (void) cache_insert_subclass(SH_CLASS_HASH(shp)[0],
+ (char **)&subclass_all, 1, sub_id);
+ return (0);
+ }
+
+ /* New class, add to the registration cache */
+ if ((c_list = cache_find_class(shp, class)) == NULL) {
+
+ c_list = (class_lst_t *)calloc(1, sizeof (class_lst_t));
+ if (c_list == NULL) {
+ return (1);
+ }
+ c_list->cl_name = strdup(class);
+ if (c_list->cl_name == NULL) {
+ free(c_list);
+ return (1);
+ }
+
+ c_list->cl_subclass_list = (subclass_lst_t *)
+ calloc(1, sizeof (subclass_lst_t));
+ if (c_list->cl_subclass_list == NULL) {
+ free(c_list->cl_name);
+ free(c_list);
+ return (1);
+ }
+ c_list->cl_subclass_list->sl_name = strdup(EC_SUB_ALL);
+ if (c_list->cl_subclass_list->sl_name == NULL) {
+ free(c_list->cl_subclass_list);
+ free(c_list->cl_name);
+ free(c_list);
+ return (1);
+ }
+ c_list->cl_next = SH_CLASS_HASH(shp)[CLASS_HASH(class)];
+ SH_CLASS_HASH(shp)[CLASS_HASH(class)] = c_list;
+
+ }
+
+ /* Update the subclass list */
+ if (cache_insert_subclass(c_list, subclass_names, subclass_num,
+ sub_id) != 0)
+ return (1);
+
+ return (0);
+}
+
+static void
+cache_remove_all_class(sysevent_handle_t *shp, uint32_t sub_id)
+{
+ int i;
+ class_lst_t *c_list;
+ subclass_lst_t *sc_list;
+
+ for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
+ c_list = SH_CLASS_HASH(shp)[i];
+ while (c_list != NULL) {
+ sc_list = c_list->cl_subclass_list;
+ while (sc_list != NULL) {
+ sc_list->sl_num[sub_id] = 0;
+ sc_list = sc_list->sl_next;
+ }
+ c_list = c_list->cl_next;
+ }
+ }
+}
+
+static void
+cache_remove_class(sysevent_handle_t *shp, char *class, uint32_t sub_id)
+{
+ class_lst_t *c_list;
+ subclass_lst_t *sc_list;
+
+ if (strcmp(class, EC_ALL) == 0) {
+ cache_remove_all_class(shp, sub_id);
+ return;
+ }
+
+ if ((c_list = cache_find_class(shp, class)) == NULL) {
+ return;
+ }
+
+ sc_list = c_list->cl_subclass_list;
+ while (sc_list != NULL) {
+ sc_list->sl_num[sub_id] = 0;
+ sc_list = sc_list->sl_next;
+ }
+}
+
+static void
+free_cached_registration(sysevent_handle_t *shp)
+{
+ int i;
+ class_lst_t *clist, *next_clist;
+ subclass_lst_t *sc_list, *next_sc;
+
+ for (i = 0; i < CLASS_HASH_SZ + 1; i++) {
+ clist = SH_CLASS_HASH(shp)[i];
+ while (clist != NULL) {
+ sc_list = clist->cl_subclass_list;
+ while (sc_list != NULL) {
+ free(sc_list->sl_name);
+ next_sc = sc_list->sl_next;
+ free(sc_list);
+ sc_list = next_sc;
+ }
+ free(clist->cl_name);
+ next_clist = clist->cl_next;
+ free(clist);
+ clist = next_clist;
+ }
+ SH_CLASS_HASH(shp)[i] = NULL;
+ }
+}
+
+static int
+create_cached_registration(sysevent_handle_t *shp,
+ class_lst_t **class_hash)
+{
+ int i, j, new_class;
+ char *class_name;
+ uint_t num_elem;
+ uchar_t *subscribers;
+ nvlist_t *nvl;
+ nvpair_t *nvpair;
+ class_lst_t *clist;
+ subclass_lst_t *sc_list;
+
+ for (i = 0; i < CLASS_HASH_SZ + 1; ++i) {
+
+ if ((nvl = get_kernel_registration(SH_CHANNEL_NAME(shp), i))
+ == NULL) {
+ if (errno == ENOENT) {
+ class_hash[i] = NULL;
+ continue;
+ } else {
+ goto create_failed;
+ }
+ }
+
+
+ nvpair = NULL;
+ if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
+ goto create_failed;
+ }
+
+ new_class = 1;
+ while (new_class) {
+ /* Extract the class name from the nvpair */
+ if (nvpair_value_string(nvpair, &class_name) != 0) {
+ goto create_failed;
+ }
+ clist = (class_lst_t *)
+ calloc(1, sizeof (class_lst_t));
+ if (clist == NULL) {
+ goto create_failed;
+ }
+
+ clist->cl_name = strdup(class_name);
+ if (clist->cl_name == NULL) {
+ free(clist);
+ goto create_failed;
+ }
+
+ /*
+ * Extract the subclass name and registration
+ * from the nvpair
+ */
+ if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
+ == NULL) {
+ free(clist->cl_name);
+ free(clist);
+ goto create_failed;
+ }
+
+ clist->cl_next = class_hash[i];
+ class_hash[i] = clist;
+
+ for (;;) {
+
+ sc_list = (subclass_lst_t *)calloc(1,
+ sizeof (subclass_lst_t));
+ if (sc_list == NULL) {
+ goto create_failed;
+ }
+
+ sc_list->sl_next = clist->cl_subclass_list;
+ clist->cl_subclass_list = sc_list;
+
+ sc_list->sl_name = strdup(nvpair_name(nvpair));
+ if (sc_list->sl_name == NULL) {
+ goto create_failed;
+ }
+
+ if (nvpair_value_byte_array(nvpair,
+ &subscribers, &num_elem) != 0) {
+ goto create_failed;
+ }
+ bcopy(subscribers, (uchar_t *)sc_list->sl_num,
+ MAX_SUBSCRIBERS + 1);
+
+ for (j = 1; j <= MAX_SUBSCRIBERS; ++j) {
+ if (sc_list->sl_num[j] == 0)
+ continue;
+
+ if (alloc_subscriber(shp, j, 1) != 0) {
+ goto create_failed;
+ }
+ }
+
+ /*
+ * Check next nvpair - either subclass or
+ * class
+ */
+ if ((nvpair = nvlist_next_nvpair(nvl, nvpair))
+ == NULL) {
+ new_class = 0;
+ break;
+ } else if (strcmp(nvpair_name(nvpair),
+ CLASS_NAME) == 0) {
+ break;
+ }
+ }
+ }
+ nvlist_free(nvl);
+ }
+ return (0);
+
+create_failed:
+ dealloc_subscribers(shp);
+ free_cached_registration(shp);
+ if (nvl)
+ nvlist_free(nvl);
+ return (-1);
+
+}
+
+/*
+ * cache_update_service - generic event publisher service routine. This routine
+ * is called in response to a registration cache update.
+ *
+ */
+static void
+cache_update_service(void *cookie, char *args, size_t alen,
+ door_desc_t *ddp, uint_t ndid)
+{
+ int ret = 0;
+ uint_t num_elem;
+ char *class, **event_list;
+ size_t datalen;
+ uint32_t sub_id;
+ nvlist_t *nvl;
+ nvpair_t *nvpair = NULL;
+ struct reg_args *rargs;
+ sysevent_handle_t *shp;
+ subscriber_data_t *sub;
+
+ if (alen < sizeof (struct reg_args) || cookie == NULL) {
+ ret = EINVAL;
+ goto return_from_door;
+ }
+
+ rargs = (struct reg_args *)args;
+ shp = (sysevent_handle_t *)cookie;
+
+ datalen = alen - sizeof (struct reg_args);
+ sub_id = rargs->ra_sub_id;
+
+ (void) mutex_lock(SH_LOCK(shp));
+
+ switch (rargs->ra_op) {
+ case SE_UNREGISTER:
+ class = (char *)&rargs->ra_buf_ptr;
+ cache_remove_class(shp, (char *)class,
+ sub_id);
+ break;
+ case SE_UNBIND_REGISTRATION:
+
+ sub = SH_SUBSCRIBER(shp, sub_id);
+ if (sub == NULL)
+ break;
+
+ free(sub->sd_door_name);
+ free(sub);
+ cache_remove_class(shp, EC_ALL, sub_id);
+ SH_SUBSCRIBER(shp, sub_id) = NULL;
+
+ break;
+ case SE_BIND_REGISTRATION:
+
+ /* New subscriber */
+ if (alloc_subscriber(shp, sub_id, 0) != 0) {
+ ret = ENOMEM;
+ break;
+ }
+ break;
+ case SE_REGISTER:
+
+ if (SH_SUBSCRIBER(shp, sub_id) == NULL) {
+ ret = EINVAL;
+ break;
+ }
+ /* Get new registration data */
+ if (nvlist_unpack((char *)&rargs->ra_buf_ptr, datalen,
+ &nvl, 0) != 0) {
+ ret = EFAULT;
+ break;
+ }
+ if ((nvpair = nvlist_next_nvpair(nvl, nvpair)) == NULL) {
+ nvlist_free(nvl);
+ ret = EFAULT;
+ break;
+ }
+ if (nvpair_value_string_array(nvpair, &event_list, &num_elem)
+ != 0) {
+ nvlist_free(nvl);
+ ret = EFAULT;
+ break;
+ }
+ class = nvpair_name(nvpair);
+
+ ret = cache_insert_class(shp, class,
+ event_list, num_elem, sub_id);
+ if (ret != 0) {
+ cache_remove_class(shp, class, sub_id);
+ nvlist_free(nvl);
+ ret = EFAULT;
+ break;
+ }
+
+ nvlist_free(nvl);
+
+ break;
+ case SE_CLEANUP:
+ /* Cleanup stale subscribers */
+ sysevent_cleanup_subscribers(shp);
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ (void) mutex_unlock(SH_LOCK(shp));
+
+return_from_door:
+ (void) door_return((void *)&ret, sizeof (ret), NULL, 0);
+ (void) door_return(NULL, 0, NULL, 0);
+}
+
+/*
+ * sysevent_send_event -
+ * Send an event via the communication channel associated with the sysevent
+ * handle. Event notifications are broadcast to all subscribers based upon
+ * the event class and subclass. The handle must have been previously
+ * allocated and bound by
+ * sysevent_open_channel() and sysevent_bind_publisher()
+ */
+int
+sysevent_send_event(sysevent_handle_t *shp, sysevent_t *ev)
+{
+ int i, error, sub_fd, result = 0;
+ int deliver_error = 0;
+ int subscribers_sent = 0;
+ int want_resend, resend_cnt = 0;
+ char *event_class, *event_subclass;
+ uchar_t *all_class_subscribers, *all_subclass_subscribers;
+ uchar_t *subclass_subscribers;
+ subscriber_data_t *sub;
+ subclass_lst_t *sc_lst;
+
+ /* Check for proper registration */
+ event_class = sysevent_get_class_name(ev);
+ event_subclass = sysevent_get_subclass_name(ev);
+
+ (void) mutex_lock(SH_LOCK(shp));
+
+send_event:
+
+ want_resend = 0;
+ if (!SH_BOUND(shp)) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Find all subscribers for this event class/subclass */
+ sc_lst = cache_find_subclass(
+ cache_find_class(shp, EC_ALL), EC_SUB_ALL);
+ all_class_subscribers = sc_lst->sl_num;
+
+ sc_lst = cache_find_subclass(
+ cache_find_class(shp, event_class), EC_SUB_ALL);
+ if (sc_lst)
+ all_subclass_subscribers = sc_lst->sl_num;
+ else
+ all_subclass_subscribers = NULL;
+
+ sc_lst = cache_find_subclass(
+ cache_find_class(shp, event_class), event_subclass);
+ if (sc_lst)
+ subclass_subscribers = sc_lst->sl_num;
+ else
+ subclass_subscribers = NULL;
+
+ /* Send event buffer to all valid subscribers */
+ for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
+ if ((all_class_subscribers[i] |
+ (all_subclass_subscribers && all_subclass_subscribers[i]) |
+ (subclass_subscribers && subclass_subscribers[i])) == 0)
+ continue;
+
+ sub = SH_SUBSCRIBER(shp, i);
+ assert(sub != NULL);
+
+ /* Check for active subscriber */
+ if (!(sub->sd_flag & ACTIVE)) {
+ dprint("sysevent_send_event: subscriber %d inactive\n",
+ i);
+ continue;
+ }
+
+ /* Process only resend requests */
+ if (resend_cnt > 0 && !(sub->sd_flag & SEND_AGAIN)) {
+ continue;
+ }
+
+ if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
+ dprint("sysevent_send_event: Failed to open "
+ "%s: %s\n", sub->sd_door_name, strerror(errno));
+ continue;
+ }
+ result = 0;
+ error = clnt_deliver_event(sub_fd, ev,
+ sysevent_get_size(ev), &result, sizeof (result));
+
+ (void) close(sub_fd);
+
+ /* Successful door call */
+ if (error == 0) {
+ switch (result) {
+ /* Subscriber requested EAGAIN */
+ case EAGAIN:
+ if (resend_cnt > SE_MAX_RETRY_LIMIT) {
+ deliver_error = 1;
+ } else {
+ want_resend = 1;
+ dprint("sysevent_send_event: resend "
+ "requested for %d\n", i);
+ sub->sd_flag |= SEND_AGAIN;
+ }
+ break;
+ /* Bad sysevent handle for subscriber */
+ case EBADF:
+ case EINVAL:
+ dprint("sysevent_send_event: Bad sysevent "
+ "handle for %s", sub->sd_door_name);
+ sub->sd_flag = 0;
+ deliver_error = 1;
+ break;
+ /* Successful delivery */
+ default:
+ sub->sd_flag &= ~SEND_AGAIN;
+ ++subscribers_sent;
+ }
+ } else {
+ dprint("sysevent_send_event: Failed door call "
+ "to %s: %s: %d\n", sub->sd_door_name,
+ strerror(errno), result);
+ sub->sd_flag = 0;
+ deliver_error = 1;
+ }
+ }
+
+ if (want_resend) {
+ resend_cnt++;
+ goto send_event;
+ }
+
+ if (deliver_error) {
+ sysevent_cleanup_subscribers(shp);
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = EFAULT;
+ return (-1);
+ }
+
+ (void) mutex_unlock(SH_LOCK(shp));
+
+ if (subscribers_sent == 0) {
+ dprint("sysevent_send_event: No subscribers for %s:%s\n",
+ event_class, event_subclass);
+ errno = ENOENT;
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Common routine to establish an event channel through which an event
+ * publisher or subscriber may post or receive events.
+ */
+static sysevent_handle_t *
+sysevent_open_channel_common(const char *channel_path)
+{
+ uint32_t sub_id = 0;
+ char *begin_path;
+ struct stat chan_stat;
+ sysevent_handle_t *shp;
+
+
+ if (channel_path == NULL || strlen(channel_path) + 1 > MAXPATHLEN) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if (mkdir(channel_path, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
+ if (errno != EEXIST) {
+ errno = EACCES;
+ return (NULL);
+ }
+ }
+
+ /* Check channel file permissions */
+ if (stat(channel_path, &chan_stat) != 0) {
+ dprint("sysevent_open_channel: Invalid permissions for channel "
+ "%s\n", channel_path);
+ errno = EACCES;
+ return (NULL);
+ } else if (chan_stat.st_uid != getuid() ||
+ !(chan_stat.st_mode & S_IFDIR)) {
+ dprint("sysevent_open_channel: Invalid "
+ "permissions for channel %s\n: %d:%d:%d", channel_path,
+ (int)chan_stat.st_uid, (int)chan_stat.st_gid,
+ (int)chan_stat.st_mode);
+
+ errno = EACCES;
+ return (NULL);
+ }
+
+ shp = calloc(1, sizeof (sysevent_impl_hdl_t));
+ if (shp == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ SH_CHANNEL_NAME(shp) = NULL;
+ SH_CHANNEL_PATH(shp) = strdup(channel_path);
+ if (SH_CHANNEL_PATH(shp) == NULL) {
+ free(shp);
+ errno = ENOMEM;
+ return (NULL);
+ }
+
+ /* Extract the channel name */
+ begin_path = SH_CHANNEL_PATH(shp);
+ while (*begin_path != '\0' &&
+ (begin_path = strpbrk(begin_path, "/")) != NULL) {
+ ++begin_path;
+ SH_CHANNEL_NAME(shp) = begin_path;
+ }
+
+ if (update_kernel_registration(shp, 0,
+ SE_OPEN_REGISTRATION, &sub_id, 0, NULL) != 0) {
+ dprint("sysevent_open_channel: Failed for channel %s\n",
+ SH_CHANNEL_NAME(shp));
+ free(SH_CHANNEL_PATH(shp));
+ free(shp);
+ errno = EFAULT;
+ return (NULL);
+ }
+
+ (void) mutex_init(SH_LOCK(shp), USYNC_THREAD, NULL);
+
+ return (shp);
+}
+
+/*
+ * Establish a sysevent channel for publication and subscription
+ */
+sysevent_handle_t *
+sysevent_open_channel(const char *channel)
+{
+ int var_run_mounted = 0;
+ char full_channel[MAXPATHLEN + 1];
+ FILE *fp;
+ struct stat chan_stat;
+ struct extmnttab m;
+
+ if (channel == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ /*
+ * Check that /var/run is mounted as tmpfs before allowing a channel
+ * to be opened.
+ */
+ if ((fp = fopen(MNTTAB, "r")) == NULL) {
+ errno = EACCES;
+ return (NULL);
+ }
+
+ resetmnttab(fp);
+
+ while (getextmntent(fp, &m, sizeof (struct extmnttab)) == 0) {
+ if (strcmp(m.mnt_mountp, "/var/run") == 0 &&
+ strcmp(m.mnt_fstype, "tmpfs") == 0) {
+ (void) fclose(fp);
+ var_run_mounted = 1;
+ break;
+ }
+ }
+ (void) fclose(fp);
+
+ if (!var_run_mounted) {
+ errno = EACCES;
+ return (NULL);
+ }
+
+ if (stat(CHAN_PATH, &chan_stat) < 0) {
+ if (mkdir(CHAN_PATH,
+ S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0) {
+ dprint("sysevent_open_channel: Unable "
+ "to create channel directory %s:%s\n", CHAN_PATH,
+ strerror(errno));
+ if (errno != EEXIST) {
+ errno = EACCES;
+ return (NULL);
+ }
+ }
+ }
+
+ if (snprintf(full_channel, MAXPATHLEN, "%s/%s", CHAN_PATH, channel) >=
+ MAXPATHLEN) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ return (sysevent_open_channel_common(full_channel));
+}
+
+/*
+ * Establish a sysevent channel for publication and subscription
+ * Full path to the channel determined by the caller
+ */
+sysevent_handle_t *
+sysevent_open_channel_alt(const char *channel_path)
+{
+ return (sysevent_open_channel_common(channel_path));
+}
+
+/*
+ * sysevent_close_channel - Clean up resources associated with a previously
+ * opened sysevent channel
+ */
+void
+sysevent_close_channel(sysevent_handle_t *shp)
+{
+ int error = errno;
+ uint32_t sub_id = 0;
+
+ if (shp == NULL) {
+ return;
+ }
+
+ (void) mutex_lock(SH_LOCK(shp));
+ if (SH_BOUND(shp)) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ if (SH_TYPE(shp) == PUBLISHER)
+ sysevent_unbind_publisher(shp);
+ else if (SH_TYPE(shp) == SUBSCRIBER)
+ sysevent_unbind_subscriber(shp);
+ (void) mutex_lock(SH_LOCK(shp));
+ }
+
+ (void) update_kernel_registration(shp, 0,
+ SE_CLOSE_REGISTRATION, &sub_id, 0, NULL);
+ (void) mutex_unlock(SH_LOCK(shp));
+
+ free(SH_CHANNEL_PATH(shp));
+ free(shp);
+ errno = error;
+}
+
+/*
+ * sysevent_bind_publisher - Bind an event publisher to an event channel
+ */
+int
+sysevent_bind_publisher(sysevent_handle_t *shp)
+{
+ int error = 0;
+ int fd = -1;
+ char door_name[MAXPATHLEN];
+ uint32_t pub_id;
+ struct stat reg_stat;
+ publisher_priv_t *pub;
+
+ if (shp == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ (void) mutex_lock(SH_LOCK(shp));
+ if (SH_BOUND(shp)) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if ((pub = (publisher_priv_t *)calloc(1, sizeof (publisher_priv_t))) ==
+ NULL) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = ENOMEM;
+ return (-1);
+ }
+ SH_PRIV_DATA(shp) = (void *)pub;
+
+ if (snprintf(door_name, MAXPATHLEN, "%s/%s",
+ SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
+ free(pub);
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = ENOMEM;
+ return (-1);
+ }
+ if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
+ free(pub);
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = ENOMEM;
+ return (-1);
+ }
+
+ /* Only one publisher allowed per channel */
+ if (stat(SH_DOOR_NAME(shp), &reg_stat) != 0) {
+ if (errno != ENOENT) {
+ error = EINVAL;
+ goto fail;
+ }
+ }
+
+ /*
+ * Remove door file for robustness.
+ */
+ if (unlink(SH_DOOR_NAME(shp)) != 0)
+ dprint("sysevent_bind_publisher: Unlink of %s failed.\n",
+ SH_DOOR_NAME(shp));
+
+ /* Open channel registration door */
+ fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR,
+ S_IREAD|S_IWRITE);
+ if (fd == -1) {
+ error = EINVAL;
+ goto fail;
+ }
+
+ /*
+ * Create the registration service for this publisher.
+ */
+ if ((SH_DOOR_DESC(shp) = door_create(cache_update_service,
+ (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
+ dprint("sysevent_bind_publisher: door create failed: "
+ "%s\n", strerror(errno));
+ error = EFAULT;
+ goto fail;
+ }
+
+ (void) fdetach(SH_DOOR_NAME(shp));
+ if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
+ dprint("sysevent_bind_publisher: unable to "
+ "bind event channel: fattach: %s\n",
+ SH_DOOR_NAME(shp));
+ error = EACCES;
+ goto fail;
+ }
+
+ /* Bind this publisher in the kernel registration database */
+ if (update_kernel_registration(shp, PUBLISHER,
+ SE_BIND_REGISTRATION, &pub_id, 0, NULL) != 0) {
+ error = errno;
+ goto fail;
+ }
+
+ SH_ID(shp) = pub_id;
+ SH_BOUND(shp) = 1;
+ SH_TYPE(shp) = PUBLISHER;
+
+
+ /* Create the subscription registration cache */
+ if (create_cached_registration(shp, SH_CLASS_HASH(shp)) != 0) {
+ (void) update_kernel_registration(shp,
+ PUBLISHER, SE_UNBIND_REGISTRATION, &pub_id, 0, NULL);
+ error = EFAULT;
+ goto fail;
+ }
+ (void) close(fd);
+
+ (void) mutex_unlock(SH_LOCK(shp));
+
+ return (0);
+
+fail:
+ SH_BOUND(shp) = 0;
+ (void) door_revoke(SH_DOOR_DESC(shp));
+ (void) fdetach(SH_DOOR_NAME(shp));
+ free(SH_DOOR_NAME(shp));
+ free(pub);
+ (void) close(fd);
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = error;
+ return (-1);
+}
+
+/*
+ * sysevent_bind_subscriber - Bind an event receiver to an event channel
+ */
+int
+sysevent_bind_subscriber(sysevent_handle_t *shp,
+ void (*event_handler)(sysevent_t *ev))
+{
+ int fd = -1;
+ int error = 0;
+ uint32_t sub_id = 0;
+ char door_name[MAXPATHLEN];
+ subscriber_priv_t *sub_info;
+
+ if (shp == NULL || event_handler == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ (void) mutex_lock(SH_LOCK(shp));
+ if (SH_BOUND(shp)) {
+ errno = EINVAL;
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+
+ if ((sub_info = (subscriber_priv_t *)calloc(1,
+ sizeof (subscriber_priv_t))) == NULL) {
+ errno = ENOMEM;
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+
+ if (snprintf(door_name, MAXPATHLEN, "%s/%s",
+ SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
+ free(sub_info);
+ errno = EINVAL;
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+
+ if ((sub_info->sp_door_name = strdup(door_name)) == NULL) {
+ free(sub_info);
+ errno = ENOMEM;
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+ (void) cond_init(&sub_info->sp_cv, USYNC_THREAD, NULL);
+ (void) mutex_init(&sub_info->sp_qlock, USYNC_THREAD, NULL);
+ sub_info->sp_func = event_handler;
+
+ /* Update the in-kernel registration */
+ if (update_kernel_registration(shp, SUBSCRIBER,
+ SE_BIND_REGISTRATION, &sub_id, 0, NULL) != 0) {
+ error = errno;
+ goto fail;
+ }
+ SH_ID(shp) = sub_id;
+
+ if (snprintf(door_name, MAXPATHLEN, "%s/%d",
+ SH_CHANNEL_PATH(shp), sub_id) >= MAXPATHLEN) {
+ error = EINVAL;
+ goto fail;
+ }
+ if ((SH_DOOR_NAME(shp) = strdup(door_name)) == NULL) {
+ error = ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * Remove door file for robustness.
+ */
+ if (unlink(SH_DOOR_NAME(shp)) != 0)
+ dprint("sysevent_bind_subscriber: Unlink of %s failed.\n",
+ SH_DOOR_NAME(shp));
+
+ fd = open(SH_DOOR_NAME(shp), O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
+ if (fd == -1) {
+ error = EFAULT;
+ goto fail;
+ }
+
+ /*
+ * Create the sysevent door service for this client.
+ * syseventd will use this door service to propagate
+ * events to the client.
+ */
+ if ((SH_DOOR_DESC(shp) = door_create(event_deliver_service,
+ (void *)shp, DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
+ dprint("sysevent_bind_subscriber: door create failed: "
+ "%s\n", strerror(errno));
+ error = EFAULT;
+ goto fail;
+ }
+
+ (void) fdetach(SH_DOOR_NAME(shp));
+ if (fattach(SH_DOOR_DESC(shp), SH_DOOR_NAME(shp)) != 0) {
+ error = EFAULT;
+ goto fail;
+ }
+ (void) close(fd);
+
+ if (update_publisher_cache(sub_info, SE_BIND_REGISTRATION,
+ sub_id, 0, NULL) != 0) {
+ error = errno;
+ (void) update_kernel_registration(shp, SUBSCRIBER,
+ SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
+ goto fail;
+ }
+
+ SH_BOUND(shp) = 1;
+ SH_TYPE(shp) = SUBSCRIBER;
+ SH_PRIV_DATA(shp) = (void *)sub_info;
+
+
+ /* Create an event handler thread */
+ if (thr_create(NULL, NULL, (void *(*)(void *))subscriber_event_handler,
+ shp, THR_BOUND, &sub_info->sp_handler_tid) != 0) {
+ error = EFAULT;
+ goto fail;
+ }
+
+ (void) mutex_unlock(SH_LOCK(shp));
+
+ return (0);
+
+fail:
+ (void) close(fd);
+ (void) door_revoke(SH_DOOR_DESC(shp));
+ (void) fdetach(SH_DOOR_NAME(shp));
+ (void) cond_destroy(&sub_info->sp_cv);
+ (void) mutex_destroy(&sub_info->sp_qlock);
+ free(sub_info->sp_door_name);
+ free(sub_info);
+ if (SH_ID(shp)) {
+ (void) update_kernel_registration(shp, SUBSCRIBER,
+ SE_UNBIND_REGISTRATION, &sub_id, 0, NULL);
+ SH_ID(shp) = 0;
+ }
+ if (SH_BOUND(shp)) {
+ (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
+ sub_id, 0, NULL);
+ free(SH_DOOR_NAME(shp));
+ SH_BOUND(shp) = 0;
+ }
+ (void) mutex_unlock(SH_LOCK(shp));
+
+ errno = error;
+
+ return (-1);
+}
+
+/*
+ * sysevent_register_event - register an event class and associated subclasses
+ * for an event subscriber
+ */
+int
+sysevent_register_event(sysevent_handle_t *shp,
+ const char *ev_class, const char **ev_subclass,
+ int subclass_num)
+{
+ int error;
+ char *event_class = (char *)ev_class;
+ char **event_subclass_list = (char **)ev_subclass;
+ char *nvlbuf = NULL;
+ size_t datalen;
+ nvlist_t *nvl;
+
+ (void) mutex_lock(SH_LOCK(shp));
+ if (event_class == NULL || event_subclass_list == NULL ||
+ event_subclass_list[0] == NULL || SH_BOUND(shp) != 1 ||
+ subclass_num <= 0) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0) != 0) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+ if (nvlist_add_string_array(nvl, event_class, event_subclass_list,
+ subclass_num) != 0) {
+ nvlist_free(nvl);
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+ if (nvlist_pack(nvl, &nvlbuf, &datalen, NV_ENCODE_NATIVE, 0) != 0) {
+ nvlist_free(nvl);
+ (void) mutex_unlock(SH_LOCK(shp));
+ return (-1);
+ }
+ nvlist_free(nvl);
+
+ /* Store new subscriber in in-kernel registration */
+ if (update_kernel_registration(shp, SUBSCRIBER,
+ SE_REGISTER, &SH_ID(shp), datalen, (uchar_t *)nvlbuf)
+ != 0) {
+ error = errno;
+ free(nvlbuf);
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = error;
+ return (-1);
+ }
+ /* Update the publisher's cached registration */
+ if (update_publisher_cache(
+ (subscriber_priv_t *)SH_PRIV_DATA(shp), SE_REGISTER,
+ SH_ID(shp), datalen, (uchar_t *)nvlbuf) != 0) {
+ error = errno;
+ free(nvlbuf);
+ (void) mutex_unlock(SH_LOCK(shp));
+ errno = error;
+ return (-1);
+ }
+
+ free(nvlbuf);
+
+ (void) mutex_unlock(SH_LOCK(shp));
+
+ return (0);
+}
+
+/*
+ * sysevent_unregister_event - Unregister an event class and associated
+ * subclasses for an event subscriber
+ */
+void
+sysevent_unregister_event(sysevent_handle_t *shp, const char *class)
+{
+ size_t class_sz;
+
+ (void) mutex_lock(SH_LOCK(shp));
+
+ if (!SH_BOUND(shp)) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ return;
+ }
+
+ /* Remove subscriber from in-kernel registration */
+ class_sz = strlen(class) + 1;
+ (void) update_kernel_registration(shp, SUBSCRIBER,
+ SE_UNREGISTER, &SH_ID(shp), class_sz, (uchar_t *)class);
+ /* Update the publisher's cached registration */
+ (void) update_publisher_cache(
+ (subscriber_priv_t *)SH_PRIV_DATA(shp), SE_UNREGISTER,
+ SH_ID(shp), class_sz, (uchar_t *)class);
+
+ (void) mutex_unlock(SH_LOCK(shp));
+}
+
+static int
+cleanup_id(sysevent_handle_t *shp, uint32_t id, int type)
+{
+ dprint("cleanup_id: Cleaning up %s/%d\n", SH_CHANNEL_NAME(shp), id);
+
+ /* Remove registration from the kernel */
+ if (update_kernel_registration(shp, type, SE_CLEANUP, &id,
+ 0, NULL) != 0) {
+ dprint("cleanup_id: Unable to clean "
+ "up %s/%d\n", SH_CHANNEL_NAME(shp), id);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * sysevent_cleanup_subscribers: Allows the caller to cleanup resources
+ * allocated to unresponsive subscribers.
+ */
+void
+sysevent_cleanup_subscribers(sysevent_handle_t *shp)
+{
+ uint32_t ping, result;
+ int i, error, sub_fd;
+ subscriber_data_t *sub;
+
+ if (!SH_BOUND(shp)) {
+ return;
+ }
+
+ for (i = 1; i <= MAX_SUBSCRIBERS; ++i) {
+
+ sub = SH_SUBSCRIBER(shp, i);
+ if (sub == NULL) {
+ continue;
+ }
+
+ if ((sub_fd = open(sub->sd_door_name, O_RDONLY)) == -1) {
+ continue;
+ }
+ /* Check for valid and responsive subscriber */
+ error = clnt_deliver_event(sub_fd, &ping,
+ sizeof (uint32_t), &result, sizeof (result));
+ (void) close(sub_fd);
+
+ /* Only cleanup on EBADF (Invalid door descriptor) */
+ if (error != EBADF)
+ continue;
+
+ if (cleanup_id(shp, i, SUBSCRIBER) != 0)
+ continue;
+
+ cache_remove_class(shp, EC_ALL, i);
+
+ free(sub->sd_door_name);
+ free(sub);
+ SH_SUBSCRIBER(shp, i) = NULL;
+ }
+
+}
+
+/*
+ * sysevent_cleanup_publishers: Allows stale publisher handles to be deallocated
+ * as needed.
+ */
+void
+sysevent_cleanup_publishers(sysevent_handle_t *shp)
+{
+ (void) cleanup_id(shp, 1, PUBLISHER);
+}
+
+/*
+ * sysevent_unbind_subscriber: Unbind the subscriber from the sysevent channel.
+ */
+void
+sysevent_unbind_subscriber(sysevent_handle_t *shp)
+{
+ subscriber_priv_t *sub_info;
+
+ if (shp == NULL)
+ return;
+
+ (void) mutex_lock(SH_LOCK(shp));
+ if (SH_BOUND(shp) == 0) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ return;
+ }
+
+ /* Update the in-kernel registration */
+ (void) update_kernel_registration(shp, SUBSCRIBER,
+ SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
+
+ /* Update the sysevent channel publisher */
+ sub_info = (subscriber_priv_t *)SH_PRIV_DATA(shp);
+ (void) update_publisher_cache(sub_info, SE_UNBIND_REGISTRATION,
+ SH_ID(shp), 0, NULL);
+
+ /* Close down event delivery facilities */
+ (void) door_revoke(SH_DOOR_DESC(shp));
+ (void) fdetach(SH_DOOR_NAME(shp));
+
+
+ /*
+ * Release resources and wait for pending event delivery to
+ * complete.
+ */
+ (void) mutex_lock(&sub_info->sp_qlock);
+ SH_BOUND(shp) = 0;
+ /* Signal event handler and drain the subscriber's event queue */
+ (void) cond_signal(&sub_info->sp_cv);
+ (void) mutex_unlock(&sub_info->sp_qlock);
+ (void) thr_join(sub_info->sp_handler_tid, NULL, NULL);
+
+ (void) cond_destroy(&sub_info->sp_cv);
+ (void) mutex_destroy(&sub_info->sp_qlock);
+ free(sub_info->sp_door_name);
+ free(sub_info);
+ free(SH_DOOR_NAME(shp));
+ (void) mutex_unlock(SH_LOCK(shp));
+}
+
+/*
+ * sysevent_unbind_publisher: Unbind publisher from the sysevent channel.
+ */
+void
+sysevent_unbind_publisher(sysevent_handle_t *shp)
+{
+ if (shp == NULL)
+ return;
+
+ (void) mutex_lock(SH_LOCK(shp));
+ if (SH_BOUND(shp) == 0) {
+ (void) mutex_unlock(SH_LOCK(shp));
+ return;
+ }
+
+ /* Close down the registration facilities */
+ (void) door_revoke(SH_DOOR_DESC(shp));
+ (void) fdetach(SH_DOOR_NAME(shp));
+
+ /* Update the in-kernel registration */
+ (void) update_kernel_registration(shp, PUBLISHER,
+ SE_UNBIND_REGISTRATION, &SH_ID(shp), 0, NULL);
+ SH_BOUND(shp) = 0;
+
+ /* Free resources associated with bind */
+ free_cached_registration(shp);
+ dealloc_subscribers(shp);
+
+ free(SH_PRIV_DATA(shp));
+ free(SH_DOOR_NAME(shp));
+ SH_ID(shp) = 0;
+ (void) mutex_unlock(SH_LOCK(shp));
+}
+
+/*
+ * Evolving APIs to subscribe to syseventd(1M) system events.
+ */
+
+/*
+ * sysevent_bind_handle - Bind application event handler for syseventd
+ * subscription.
+ */
+sysevent_handle_t *
+sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
+{
+ sysevent_handle_t *shp;
+
+ if (getuid() != 0) {
+ errno = EACCES;
+ return (NULL);
+ }
+
+ if (event_handler == NULL) {
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ if ((shp = sysevent_open_channel(SYSEVENTD_CHAN)) == NULL) {
+ return (NULL);
+ }
+
+ if (sysevent_bind_subscriber(shp, event_handler) != 0) {
+
+ /*
+ * Ask syseventd to clean-up any stale subcribers and try to
+ * to bind again
+ */
+ if (errno == EBUSY) {
+ int pub_fd;
+ char door_name[MAXPATHLEN];
+ uint32_t result;
+ struct reg_args rargs;
+
+ if (snprintf(door_name, MAXPATHLEN, "%s/%s",
+ SH_CHANNEL_PATH(shp), REG_DOOR) >= MAXPATHLEN) {
+ sysevent_close_channel(shp);
+ errno = EINVAL;
+ return (NULL);
+ }
+
+ rargs.ra_op = SE_CLEANUP;
+ pub_fd = open(door_name, O_RDONLY);
+ (void) clnt_deliver_event(pub_fd, (void *)&rargs,
+ sizeof (struct reg_args), &result, sizeof (result));
+ (void) close(pub_fd);
+
+ /* Try to bind again */
+ if (sysevent_bind_subscriber(shp, event_handler) != 0) {
+ sysevent_close_channel(shp);
+ return (NULL);
+ }
+ } else {
+ sysevent_close_channel(shp);
+ return (NULL);
+ }
+ }
+
+ return (shp);
+}
+
+/*
+ * sysevent_unbind_handle - Unbind caller from syseventd subscriptions
+ */
+void
+sysevent_unbind_handle(sysevent_handle_t *shp)
+{
+ sysevent_unbind_subscriber(shp);
+ sysevent_close_channel(shp);
+}
+
+/*
+ * sysevent_subscribe_event - Subscribe to system event notification from
+ * syseventd(1M) for the class and subclasses specified.
+ */
+int
+sysevent_subscribe_event(sysevent_handle_t *shp, const char *event_class,
+ const char **event_subclass_list, int num_subclasses)
+{
+ return (sysevent_register_event(shp, event_class,
+ event_subclass_list, num_subclasses));
+}
+
+void
+sysevent_unsubscribe_event(sysevent_handle_t *shp, const char *event_class)
+{
+ sysevent_unregister_event(shp, event_class);
+}
diff --git a/usr/src/lib/libsysevent/libsysevent.h b/usr/src/lib/libsysevent/libsysevent.h
new file mode 100644
index 0000000000..35e25d1ecf
--- /dev/null
+++ b/usr/src/lib/libsysevent/libsysevent.h
@@ -0,0 +1,119 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSYSEVENT_H
+#define _LIBSYSEVENT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdio.h>
+#include <thread.h>
+#include <stddef.h>
+#include <synch.h>
+#include <sys/types.h>
+#include <sys/sysevent.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SYSEVENTD_CHAN "syseventd_channel"
+
+/* sysevent loadable module ops structure and related defines */
+#define SE_MAX_RETRY_LIMIT 3
+#define SE_RETRY_TIME 1 /* seconds */
+#define SE_NO_RETRY 1
+#define SE_MAJOR_VERSION 0
+#define SE_MINOR_VERSION 0
+
+struct slm_mod_ops {
+ int major_version;
+ int minor_version;
+ int retry_limit;
+ int (*deliver_event)();
+};
+
+typedef void *sysevent_handle_t;
+typedef void *subscriber_t;
+
+int sysevent_post_event(char *event_class, char *event_subclass, char *vendor,
+ char *pub_name, nvlist_t *attr_list, sysevent_id_t *eid);
+sysevent_t *sysevent_dup(sysevent_t *ev);
+void sysevent_free(sysevent_t *ev);
+int sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist);
+int sysevent_lookup_attr(sysevent_t *ev, char *name, int datatype,
+ sysevent_value_t *se_value);
+sysevent_attr_t *sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr);
+char *sysevent_attr_name(sysevent_attr_t *attr);
+int sysevent_attr_value(sysevent_attr_t *attr, sysevent_value_t *se_value);
+int sysevent_get_class(sysevent_t *ev);
+char *sysevent_get_class_name(sysevent_t *ev);
+int sysevent_get_subclass(sysevent_t *ev);
+char *sysevent_get_subclass_name(sysevent_t *ev);
+char *sysevent_get_pub(sysevent_t *ev);
+char *sysevent_get_vendor_name(sysevent_t *ev);
+char *sysevent_get_pub_name(sysevent_t *ev);
+void sysevent_get_pid(sysevent_t *ev, pid_t *pid);
+uint64_t sysevent_get_seq(sysevent_t *ev);
+void sysevent_get_time(sysevent_t *ev, hrtime_t *etime);
+size_t sysevent_get_size(sysevent_t *ev);
+
+/* syseventd subscriber interfaces */
+sysevent_handle_t *sysevent_bind_handle(void (*event_handler)(sysevent_t *ev));
+void sysevent_unbind_handle(sysevent_handle_t *sysevent_hdl);
+int sysevent_subscribe_event(sysevent_handle_t *sysevent_hdl,
+ const char *event_class, const char **event_subclass_list,
+ int num_subclasses);
+void sysevent_unsubscribe_event(sysevent_handle_t *sysevent_hdl,
+ const char *event_class);
+
+/* Subscriber private interfaces */
+sysevent_t *sysevent_alloc_event(char *event_class, char *event_subclass,
+ char *vendor, char *pub_name, nvlist_t *attr_list);
+int sysevent_send_event(sysevent_handle_t *shp, sysevent_t *ev);
+sysevent_handle_t *sysevent_open_channel(const char *channel);
+sysevent_handle_t *sysevent_open_channel_alt(const char *channel_path);
+void sysevent_close_channel(sysevent_handle_t *shp);
+int sysevent_bind_subscriber(sysevent_handle_t *shp,
+ void (*event_handler)(sysevent_t *ev));
+void sysevent_unbind_subscriber(sysevent_handle_t *shp);
+int sysevent_bind_publisher(sysevent_handle_t *shp);
+void sysevent_unbind_publisher(sysevent_handle_t *shp);
+int sysevent_register_event(sysevent_handle_t *shp, const char *event_class,
+ const char **event_subclass_list, int num_subclasses);
+void sysevent_unregister_event(sysevent_handle_t *shp,
+ const char *event_class);
+void sysevent_cleanup_subscribers(sysevent_handle_t *shp);
+void sysevent_cleanup_publishers(sysevent_handle_t *shp);
+
+/* Debug interfaces */
+void se_print(FILE *fp, sysevent_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSYSEVENT_H */
diff --git a/usr/src/lib/libsysevent/libsysevent_impl.h b/usr/src/lib/libsysevent/libsysevent_impl.h
new file mode 100644
index 0000000000..7d02359212
--- /dev/null
+++ b/usr/src/lib/libsysevent/libsysevent_impl.h
@@ -0,0 +1,163 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIBSYSEVENT_IMPL_H
+#define _LIBSYSEVENT_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * libsysevent implementation-specific structures
+ */
+
+/* sysevent publisher/subscriber handle and related data structures */
+
+#define CHAN_PATH "/var/run/sysevent_channels"
+#define REG_DOOR "reg_door"
+
+/* Subscription size values */
+#define MAX_SUBSCRIPTION_SZ 1024
+
+/* Sysevent Channel Handle */
+typedef struct sysevent_impl_handle {
+ int sh_bound; /* Channel bind status */
+ int sh_type; /* pub/sub channel binding */
+ uint32_t sh_id; /* pub/sub within channel */
+ int sh_door_desc; /* Service door descrip */
+ char *sh_door_name; /* Service door */
+ char *sh_channel_name; /* Event Channel name */
+ char *sh_channel_path; /* Full path to Event Chan */
+ void *sh_priv_data; /* Pub/Sub private data */
+ mutex_t sh_lock; /* lock to protect access */
+} sysevent_impl_hdl_t;
+
+/* Sysevent queue for subscriber delivery */
+typedef struct sysevent_queue {
+ struct sysevent_queue *sq_next;
+ sysevent_t *sq_ev;
+} sysevent_queue_t;
+
+/*
+ * Subscriber private data stored in the sysevent channel handle
+ */
+typedef struct subscriber_priv {
+ cond_t sp_cv; /* cv for event synch */
+ mutex_t sp_qlock; /* event queue lock */
+ char *sp_door_name; /* Publisher reg door */
+ thread_t sp_handler_tid; /* delivery handler thread id */
+ struct sysevent_queue *sp_evq_head; /* event q head */
+ struct sysevent_queue *sp_evq_tail; /* event q tail */
+ void (*sp_func)(sysevent_t *ev); /* deliver func */
+} subscriber_priv_t;
+
+/* Subscriber information stored on the publisher side */
+typedef struct subscriber_data {
+ int sd_flag; /* flag */
+ char *sd_door_name; /* Client door name */
+} subscriber_data_t;
+
+/* Publisher private data stored in the sysevent channel handle */
+typedef struct publisher_priv {
+ struct class_lst *pp_class_hash[CLASS_HASH_SZ + 1];
+ subscriber_data_t *pp_subscriber_list[MAX_SUBSCRIBERS + 1];
+} publisher_priv_t;
+
+/* Subscriber flag values */
+#define ACTIVE 1 /* Active subscriber */
+#define SEND_AGAIN 2 /* Resend of event requested */
+
+/* Sysevent handle access */
+#define SYSEVENT_IMPL_HNDL(sehp) ((sysevent_impl_hdl_t *)(void *)(sehp))
+#define SH_BOUND(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_bound)
+#define SH_TYPE(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_type)
+#define SH_RESULT(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_result)
+#define SH_ID(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_id)
+#define SH_DOOR_DESC(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_door_desc)
+#define SH_DOOR_NAME(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_door_name)
+#define SH_CHANNEL_NAME(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_channel_name)
+#define SH_CHANNEL_PATH(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_channel_path)
+#define SH_LOCK(sehp) (&(SYSEVENT_IMPL_HNDL(sehp)->sh_lock))
+#define SH_PRIV_DATA(sehp) (SYSEVENT_IMPL_HNDL(sehp)->sh_priv_data)
+
+#define SH_CLASS_HASH(sehp) (((publisher_priv_t *) \
+ SH_PRIV_DATA(sehp))->pp_class_hash)
+#define SH_SUBSCRIBER(sehp, id) (((publisher_priv_t *) \
+ SH_PRIV_DATA(sehp))->pp_subscriber_list[id])
+
+/*
+ * GPEC Interface definitions
+ */
+
+typedef struct evchan_subscriber evchan_subscr_t;
+
+typedef struct evchan_sub_head {
+ evchan_subscr_t *evchan_sub_next;
+} evchan_sub_head_t;
+
+/* Event channel handle */
+typedef struct evchan_impl_handle {
+ pid_t ev_pid; /* verify descend via fork() */
+ int ev_fd; /* descriptor for sev driver */
+ mutex_t ev_lock; /* lock to protect this structure */
+ evchan_sub_head_t ev_sub; /* anchor of subscriber list */
+} evchan_impl_hdl_t;
+
+/* Evchan handle access */
+#define EVCHAN_IMPL_HNDL(evcp) ((evchan_impl_hdl_t *)(void *)(evcp))
+#define EV_PID(evcp) (EVCHAN_IMPL_HNDL(evcp)->ev_pid)
+#define EV_FD(evcp) (EVCHAN_IMPL_HNDL(evcp)->ev_fd)
+#define EV_LOCK(evcp) (&(EVCHAN_IMPL_HNDL(evcp)->ev_lock))
+#define EV_SUB(evcp) (&(EVCHAN_IMPL_HNDL(evcp)->ev_sub))
+#define EV_SUB_NEXT(evcp) (EVCHAN_IMPL_HNDL(evcp)->ev_sub.evchan_sub_next)
+
+/*
+ * Subscriber private data
+ */
+struct evchan_subscriber {
+ evchan_subscr_t *evsub_next; /* list of subscribers */
+ evchan_impl_hdl_t *ev_subhead; /* link back to channel data */
+ int evsub_door_desc; /* Service door descriptor */
+ char *evsub_sid; /* identifier of subscriber */
+ void *evsub_cookie; /* subscriber cookie */
+ int (*evsub_func)(sysevent_t *, void *); /* subscriber event handler */
+};
+
+/* Access to subscriber data */
+#define EVCHAN_SUBSCR(subp) ((evchan_subscr_t *)(subp))
+
+/* Characters for channel name syntax */
+#define EVCH_ISCHANCHAR(c) (isalnum(c) || (c) == '.' || (c) == ':' || \
+ (c) == '-' || (c) == '_')
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBSYSEVENT_IMPL_H */
diff --git a/usr/src/lib/libsysevent/llib-lsysevent b/usr/src/lib/libsysevent/llib-lsysevent
new file mode 100644
index 0000000000..848135ec67
--- /dev/null
+++ b/usr/src/lib/libsysevent/llib-lsysevent
@@ -0,0 +1,134 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*LINTLIBRARY*/
+/*PROTOLIB1*/
+/*
+ * Copyright 2000-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * usr/src/lib/libsysevent/llib-lsysevent
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <synch.h>
+#include <thread.h>
+#include "libsysevent.h"
+
+int sysevent_post_event(char *event_class, char *event_subclass, char *vendor,
+ char *pub_name, nvlist_t *attr_list, sysevent_id_t *eid);
+sysevent_t *sysevent_dup(sysevent_t *ev);
+
+void sysevent_free(sysevent_t *ev);
+
+int sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist);
+
+int sysevent_lookup_attr(sysevent_t *ev, char *name, int datatype,
+ sysevent_value_t *se_value);
+
+sysevent_attr_t *sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr);
+
+char *sysevent_attr_name(sysevent_attr_t *attr);
+
+int sysevent_attr_value(sysevent_attr_t *attr, sysevent_value_t *se_value);
+
+int sysevent_get_class(sysevent_t *ev);
+
+char *sysevent_get_class_name(sysevent_t *ev);
+
+int sysevent_get_subclass(sysevent_t *ev);
+
+char *sysevent_get_subclass_name(sysevent_t *ev);
+
+char *sysevent_get_pub(sysevent_t *ev);
+
+char *sysevent_get_vendor_name(sysevent_t *ev);
+
+char *sysevent_get_pub_name(sysevent_t *ev);
+
+void sysevent_get_pid(sysevent_t *ev, pid_t *pid);
+
+uint64_t sysevent_get_seq(sysevent_t *ev);
+
+void sysevent_get_time(sysevent_t *ev, hrtime_t *etime);
+
+size_t sysevent_get_size(sysevent_t *ev);
+
+int sysevent_send_event(sysevent_handle_t *sysevent_hdl, sysevent_t *ev);
+
+sysevent_t * sysevent_alloc_event(char *event_class, char *event_subclass,
+ char *vendor, char *pub_name, nvlist_t *attr_list);
+
+sysevent_handle_t * sysevent_open_channel(const char *channel);
+
+sysevent_handle_t * sysevent_open_channel_alt(const char *channel_path);
+
+void sysevent_close_channel(sysevent_handle_t *shp);
+
+int sysevent_bind_subscriber(sysevent_handle_t *shp,
+ void (*callback)(sysevent_t *data));
+
+int sysevent_bind_publisher(sysevent_handle_t *shp);
+
+void sysevent_unbind_subscriber(sysevent_handle_t *shp);
+
+void sysevent_unbind_publisher(sysevent_handle_t *shp);
+
+int sysevent_register_event(sysevent_handle_t *shp, const char *event_class,
+ const char **event_subclass_list, int num_subclasses);
+
+void sysevent_unregister_event(sysevent_handle_t *shp,
+ const char *event_class);
+
+void sysevent_cleanup_publishers(sysevent_handle_t *shp);
+
+void sysevent_cleanup_subscribers(sysevent_handle_t *shp);
+
+sysevent_handle_t *sysevent_bind_handle(void (*event_handler)(sysevent_t *ev));
+
+void sysevent_unbind_handle(sysevent_handle_t *shp);
+
+int sysevent_subscribe_event(sysevent_handle_t *shp, const char *event_class,
+ const char **event_subclass_list, int num_subclasses);
+
+void sysevent_unsubscribe_event(sysevent_handle_t *shp,
+ const char *event_class);
+
+void se_print(FILE *fp, sysevent_t *ev);
+
+int sysevent_evc_bind(const char *channel_path, evchan_t **scpp,
+ uint32_t flags);
+
+void sysevent_evc_unbind(evchan_t *scp);
+
+int sysevent_evc_publish(evchan_t *scp, const char *event_class,
+ const char *event_subclass, const char *vendor, const char *pub_name,
+ nvlist_t *attr_list, uint32_t flags);
+
+int sysevent_evc_subscribe(evchan_t *scp, const char *sid,
+ const char *event_class,
+ int (*event_handler)(sysevent_t *ev, void *cookie),
+ void *cookie, uint32_t flags);
+
+void sysevent_evc_unsubscribe(evchan_t *scp, const char *sid);
+
+int sysevent_evc_control(evchan_t *scp, int cmd, ...);
diff --git a/usr/src/lib/libsysevent/sparc/Makefile b/usr/src/lib/libsysevent/sparc/Makefile
new file mode 100644
index 0000000000..e3cb1a270b
--- /dev/null
+++ b/usr/src/lib/libsysevent/sparc/Makefile
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2000,2002-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/sparc/Makefile
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libsysevent/sparcv9/Makefile b/usr/src/lib/libsysevent/sparcv9/Makefile
new file mode 100644
index 0000000000..9b07094621
--- /dev/null
+++ b/usr/src/lib/libsysevent/sparcv9/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2000,2002-2003 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/sparcv9/Makefile
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libsysevent/spec/Makefile b/usr/src/lib/libsysevent/spec/Makefile
new file mode 100644
index 0000000000..1c63d1251e
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/spec/Makefile
+
+include $(SRC)/lib/Makefile.spec.arch
diff --git a/usr/src/lib/libsysevent/spec/Makefile.targ b/usr/src/lib/libsysevent/spec/Makefile.targ
new file mode 100644
index 0000000000..f939d9d889
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/Makefile.targ
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/spec/Makefile.targ
+
+LIBRARY = libsysevent.a
+VERS = .1
+
+OBJECTS = sysevent.o
+
+SPECCPP = -I../..
diff --git a/usr/src/lib/libsysevent/spec/amd64/Makefile b/usr/src/lib/libsysevent/spec/amd64/Makefile
new file mode 100644
index 0000000000..2a38c1cba9
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/amd64/Makefile
@@ -0,0 +1,42 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+
+# Uncomment the following if the linker complains
+#amd64_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libsysevent/spec/i386/Makefile b/usr/src/lib/libsysevent/spec/i386/Makefile
new file mode 100644
index 0000000000..315735abeb
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/i386/Makefile
@@ -0,0 +1,42 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/spec/i386/Makefile
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+
+# Uncomment the following if the linker complains
+#i386_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libsysevent/spec/sparc/Makefile b/usr/src/lib/libsysevent/spec/sparc/Makefile
new file mode 100644
index 0000000000..8af986c67d
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/sparc/Makefile
@@ -0,0 +1,44 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/spec/sparc/Makefile
+
+.KEEP_STATE:
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+
+# Uncomment the following if the linker complains
+#sparc_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB)
diff --git a/usr/src/lib/libsysevent/spec/sparcv9/Makefile b/usr/src/lib/libsysevent/spec/sparcv9/Makefile
new file mode 100644
index 0000000000..a4e7cd3f3f
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/sparcv9/Makefile
@@ -0,0 +1,43 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright (c) 2000 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/spec/sparcv9/Makefile
+
+include ../Makefile.targ
+
+# Add arch specific objects here
+OBJECTS +=
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/lib/Makefile.lib.64
+
+# Uncomment the following if the linker complains
+#sparcv9_C_PICFLAGS = -K PIC
+
+include $(SRC)/lib/Makefile.spec
+
+install: $(ROOTABILIB64)
diff --git a/usr/src/lib/libsysevent/spec/sysevent.spec b/usr/src/lib/libsysevent/spec/sysevent.spec
new file mode 100644
index 0000000000..5f5a2ed660
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/sysevent.spec
@@ -0,0 +1,286 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#pragma ident "%Z%%M% %I% %E% SMI"
+#
+# lib/libsysevent/spec/sysevent.spec
+
+function sysevent_open_channel
+include <libsysevent.h>
+declaration sysevent_handle_t *sysevent_open_channel(const char *channel)
+version SUNWprivate_1.1
+end
+
+function sysevent_open_channel_alt
+include <libsysevent.h>
+declaration sysevent_handle_t *sysevent_open_channel_alt(const char *channel_path)
+version SUNWprivate_1.1
+end
+
+function sysevent_close_channel
+include <libsysevent.h>
+declaration void sysevent_close_channel(sysevent_handle_t *shp)
+version SUNWprivate_1.1
+end
+
+function sysevent_bind_subscriber
+include <libsysevent.h>
+declaration int sysevent_bind_subscriber(sysevent_handle_t *shp, void (*event_handler)(sysevent_t *ev))
+version SUNWprivate_1.1
+end
+
+function sysevent_bind_publisher
+include <libsysevent.h>
+declaration int sysevent_bind_publisher(sysevent_handle_t *shp)
+version SUNWprivate_1.1
+end
+
+function sysevent_unbind_subscriber
+include <libsysevent.h>
+declaration void sysevent_unbind_subscriber(sysevent_handle_t *shp)
+version SUNWprivate_1.1
+end
+
+function sysevent_unbind_publisher
+include <libsysevent.h>
+declaration void sysevent_unbind_publisher(sysevent_handle_t *shp)
+version SUNWprivate_1.1
+end
+
+function sysevent_send_event
+include <libsysevent.h>
+declaration int sysevent_send_event(sysevent_handle_t *shp, sysevent_t *ev)
+version SUNWprivate_1.1
+end
+
+function sysevent_register_event
+include <libsysevent.h>
+declaration int sysevent_register_event(sysevent_handle_t *shp, const char *event_class, const char **event_subclass_list, int subclass_num)
+version SUNWprivate_1.1
+end
+
+
+function sysevent_unregister_event
+include <libsysevent.h>
+declaration void sysevent_unregister_event(sysevent_handle_t *shp, const char *event_class)
+version SUNWprivate_1.1
+end
+
+function sysevent_cleanup_publishers
+include <libsysevent.h>
+declaration void sysevent_cleanup_publishers(sysevent_handle_t *shp)
+version SUNWprivate_1.1
+end
+
+function sysevent_cleanup_subscribers
+include <libsysevent.h>
+declaration void sysevent_cleanup_subscribers(sysevent_handle_t *shp)
+version SUNWprivate_1.1
+end
+
+function sysevent_bind_handle
+include <libsysevent.h>
+declaration sysevent_handle_t *sysevent_bind_handle(void (*event_handler)(sysevent_t *ev))
+version SUNW_1.1
+end
+
+function sysevent_unbind_handle
+include <libsysevent.h>
+declaration void sysevent_unbind_handle(sysevent_handle_t *shp)
+version SUNW_1.1
+end
+
+function sysevent_subscribe_event
+include <libsysevent.h>
+declaration int sysevent_subscribe_event(sysevent_handle_t *shp, const char *event_class, const char **event_subclass_list, int num_subclasses)
+version SUNW_1.1
+end
+
+function sysevent_unsubscribe_event
+include <libsysevent.h>
+declaration void sysevent_unsubscribe_event(sysevent_handle_t *shp, const char *event_class)
+version SUNW_1.1
+end
+
+function sysevent_alloc_event
+include <libsysevent.h>
+declaration sysevent_t *sysevent_alloc_event(char *event_class, char *event_subclass, char *vendor, char *pub_name, nvlist_t *attr_list)
+version SUNWprivate_1.1
+end
+
+function sysevent_post_event
+include <libsysevent.h>
+declaration int sysevent_post_event(char *event_class, char *event_subclass, char *vendor, char *pub_name, nvlist_t *attr_list, sysevent_id_t *eid)
+version SUNW_1.1
+end
+
+function sysevent_dup
+include <libsysevent.h>
+declaration sysevent_t *sysevent_dup(sysevent_t *ev)
+version SUNWprivate_1.1
+end
+
+function sysevent_free
+include <libsysevent.h>
+declaration void sysevent_free(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function sysevent_get_attr_list
+include <libsysevent.h>
+declaration int sysevent_get_attr_list(sysevent_t *ev, nvlist_t **nvlist)
+version SUNW_1.1
+end
+
+function sysevent_lookup_attr
+include <libsysevent.h>
+declaration int sysevent_lookup_attr(sysevent_t *ev, char *name, int datatype, sysevent_value_t *se_value)
+version SUNWprivate_1.1
+end
+
+function sysevent_attr_next
+include <libsysevent.h>
+declaration sysevent_attr_t *sysevent_attr_next(sysevent_t *ev, sysevent_attr_t *attr)
+version SUNWprivate_1.1
+end
+
+function sysevent_attr_name
+include <libsysevent.h>
+declaration char *sysevent_attr_name(sysevent_attr_t *attr)
+version SUNWprivate_1.1
+end
+
+function sysevent_attr_value
+include <libsysevent.h>
+declaration int sysevent_attr_value(sysevent_attr_t *attr, sysevent_value_t *se_value)
+version SUNWprivate_1.1
+end
+
+function sysevent_get_class
+include <libsysevent.h>
+declaration int sysevent_get_class(sysevent_t *ev)
+version SUNWprivate_1.1
+end
+
+function sysevent_get_class_name
+include <libsysevent.h>
+declaration char *sysevent_get_class_name(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function sysevent_get_subclass
+include <libsysevent.h>
+declaration int sysevent_get_subclass(sysevent_t *ev)
+version SUNWprivate_1.1
+end
+
+function sysevent_get_subclass_name
+include <libsysevent.h>
+declaration char *sysevent_get_subclass_name(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function sysevent_get_pub
+include <libsysevent.h>
+declaration char *sysevent_get_pub(sysevent_t *ev)
+version SUNWprivate_1.1
+end
+
+function sysevent_get_vendor_name
+include <libsysevent.h>
+declaration char *sysevent_get_vendor_name(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function sysevent_get_pub_name
+include <libsysevent.h>
+declaration char *sysevent_get_pub_name(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function sysevent_get_pid
+include <libsysevent.h>
+declaration void sysevent_get_pid(sysevent_t *ev, pid_t *pid)
+version SUNW_1.1
+end
+
+function sysevent_get_seq
+include <libsysevent.h>
+declaration uint64_t sysevent_get_seq(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function sysevent_get_time
+include <libsysevent.h>
+declaration void sysevent_get_time(sysevent_t *ev, hrtime_t *etime)
+version SUNW_1.1
+end
+
+function sysevent_get_size
+include <libsysevent.h>
+declaration size_t sysevent_get_size(sysevent_t *ev)
+version SUNW_1.1
+end
+
+function se_print
+include <libsysevent.h>
+declaration void se_print(FILE *fp, sysevent_t *ev)
+version SUNWprivate_1.1
+end
+
+function sysevent_evc_bind
+include <libsysevent.h>
+declaration int sysevent_evc_bind(const char *channel, evchan_t **scpp, uint32_t flags)
+version SUNWprivate_1.1
+end
+
+function sysevent_evc_unbind
+include <libsysevent.h>
+declaration void sysevent_evc_unbind(evchan_t *scp)
+version SUNWprivate_1.1
+end
+
+function sysevent_evc_publish
+include <libsysevent.h>
+declaration int sysevent_evc_publish(evchan_t *scp, const char *event_class,const char *event_subclass, const char *vendor, const char *pub_name, nvlist_t *attr_list, uint32_t flags)
+version SUNWprivate_1.1
+end
+
+function sysevent_evc_subscribe
+include <libsysevent.h>
+declaration int sysevent_evc_subscribe(evchan_t *scp, const char *sid, const char *event_class, int (*event_handler)(sysevent_t *ev, void *cookie), void *cookie, uint32_t flags)
+version SUNWprivate_1.1
+end
+
+function sysevent_evc_unsubscribe
+include <libsysevent.h>
+declaration void sysevent_evc_unsubscribe(evchan_t *scp, const char *sid)
+version SUNWprivate_1.1
+end
+
+function sysevent_evc_control
+include <libsysevent.h>
+declaration int sysevent_evc_control(evchan_t *scp, int cmd, ...)
+version SUNWprivate_1.1
+end
diff --git a/usr/src/lib/libsysevent/spec/versions b/usr/src/lib/libsysevent/spec/versions
new file mode 100644
index 0000000000..f91c9031df
--- /dev/null
+++ b/usr/src/lib/libsysevent/spec/versions
@@ -0,0 +1,57 @@
+#
+# Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (the "License"). You may not use this file except in compliance
+# with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# SUNW_1.2: Placeholder (was PSARC 2002/321 prior to 2004/398); kept
+# to preserve upward binary compatibility.
+# SUNW_1.1: Public (evolving) PSARC 2000/309 (Sysevent public APIs),
+# PSARC 2001/076 (User-level Sysevent Interfaces)
+# SUNWprivate_1.1: Project private PSARC 1999/322, PSARC 2002/321,
+# PSARC 2004/398.
+#
+
+sparc {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}
+
+sparcv9 {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}
+
+i386 {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}
+
+amd64 {
+ SUNW_1.2: {SUNW_1.1};
+ SUNW_1.1;
+ SUNWprivate_1.1;
+}