diff options
Diffstat (limited to 'usr/src/lib/libsysevent')
| -rw-r--r-- | usr/src/lib/libsysevent/Makefile | 68 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/Makefile.com | 69 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/amd64/Makefile | 36 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/i386/Makefile | 36 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/libevchannel.c | 529 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/libsysevent.c | 2538 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/libsysevent.h | 119 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/libsysevent_impl.h | 163 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/llib-lsysevent | 134 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/sparc/Makefile | 36 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/sparcv9/Makefile | 37 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/Makefile | 30 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/Makefile.targ | 35 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/amd64/Makefile | 42 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/i386/Makefile | 42 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/sparc/Makefile | 44 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/sparcv9/Makefile | 43 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/sysevent.spec | 286 | ||||
| -rw-r--r-- | usr/src/lib/libsysevent/spec/versions | 57 |
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), ®_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; +} |
