summaryrefslogtreecommitdiff
path: root/usr/src/cmd/syseventd
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/syseventd
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/syseventd')
-rw-r--r--usr/src/cmd/syseventd/Makefile97
-rw-r--r--usr/src/cmd/syseventd/Makefile.com98
-rw-r--r--usr/src/cmd/syseventd/Makefile.targ44
-rw-r--r--usr/src/cmd/syseventd/daemons/Makefile51
-rw-r--r--usr/src/cmd/syseventd/daemons/Makefile.com40
-rw-r--r--usr/src/cmd/syseventd/daemons/Makefile.targ47
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventconfd/Makefile44
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventconfd/message_confd.h86
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.c755
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.h102
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd_door.h48
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/Makefile42
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/message.h139
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/sysevent_client.c153
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.c96
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.h57
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/syseventd.c1626
-rw-r--r--usr/src/cmd/syseventd/daemons/syseventd/syseventd.h139
-rw-r--r--usr/src/cmd/syseventd/etc/Makefile45
-rw-r--r--usr/src/cmd/syseventd/etc/README64
-rw-r--r--usr/src/cmd/syseventd/modules/Makefile52
-rw-r--r--usr/src/cmd/syseventd/modules/Makefile.com55
-rw-r--r--usr/src/cmd/syseventd/modules/Makefile.targ40
-rw-r--r--usr/src/cmd/syseventd/modules/devfsadmd_mod/Makefile46
-rw-r--r--usr/src/cmd/syseventd/modules/devfsadmd_mod/devfsadmd_mod.c351
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile46
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_conf_mod/message_conf_mod.h148
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.c2317
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.h171
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_reg_mod/Makefile44
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_reg_mod/message_reg_mod.h55
-rw-r--r--usr/src/cmd/syseventd/modules/sysevent_reg_mod/sysevent_reg_mod.c260
-rw-r--r--usr/src/cmd/syseventd/svc-syseventd78
-rw-r--r--usr/src/cmd/syseventd/sysevent.xml93
34 files changed, 7529 insertions, 0 deletions
diff --git a/usr/src/cmd/syseventd/Makefile b/usr/src/cmd/syseventd/Makefile
new file mode 100644
index 0000000000..7b6445db3e
--- /dev/null
+++ b/usr/src/cmd/syseventd/Makefile
@@ -0,0 +1,97 @@
+#
+# 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 2004 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# cmd/syseventd/Makefile
+#
+
+#
+# The syseventd source is organized in a two-level hierarchy,
+# with the top level for daemons and modules. Daemons (cmds)
+# need to be built with the usr/src/cmd hierarchy of Makefiles
+# for the proper set of cc flags etc., whereas loadable modules
+# need usr/src/lib. Each bottom-level Makefile includes
+# the relevant set of common Makefiles (for daemons or modules),
+# and also inherit the upper level set of common Makefiles
+# for all syseventd components.
+#
+#
+# usr/src/cmd/syseventd
+# |
+# Makefile
+# Makefile.com
+# Makefile.targ
+# |
+# ------------------------------------------
+# | | |
+# daemons modules etc
+# | | |
+# Makefile Makefile Makefile
+# Makefile.com Makefile.com
+# Makefile.targ Makefile.targ
+# | |
+# ------------------ ------------------
+# | | | |
+# syseventd syseventconfd devfsadmd_mod sysevent_conf_mod
+#
+# Makefile Makefile Makefile Makefile
+#
+
+MANIFEST= sysevent.xml
+SVCMETHOD= svc-syseventd
+
+include ../Makefile.cmd
+
+.PARALLEL:
+
+SUBDIRS= \
+ etc \
+ daemons \
+ modules
+
+ROOTMANIFESTDIR= $(ROOTSVCSYSTEM)
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+$(ROOTMANIFEST):= FILEMODE= 444
+
+.KEEP_STATE:
+
+all clean clobber lint _msg: $(SUBDIRS)
+
+install: $(SUBDIRS) $(ROOTMANIFEST) $(ROOTSVCMETHOD)
+
+check: $(CHKMANIFEST)
+
+FRC:
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
diff --git a/usr/src/cmd/syseventd/Makefile.com b/usr/src/cmd/syseventd/Makefile.com
new file mode 100644
index 0000000000..c629fc847d
--- /dev/null
+++ b/usr/src/cmd/syseventd/Makefile.com
@@ -0,0 +1,98 @@
+#
+# 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"
+#
+
+#
+# Compile flags & libraries for all sysevent daemons and modules
+#
+# NOTE: any library added to the next line must be present in the CD miniroot
+# together with all of their dependencies.
+#
+LDLIBS += -lsysevent
+
+CPPFLAGS += -D_POSIX_PTHREAD_SEMANTICS
+CPPFLAGS += -D_REENTRANT
+CFLAGS += $(CCVERBOSE)
+LINTFLAGS += -m
+
+#
+# install specifics - directories
+#
+
+ROOTETC=$(ROOT)/etc
+ROOTLIB=$(ROOT)/usr/lib
+
+ROOTLIBSYSEVENTDIR = $(ROOTLIB)/sysevent
+ROOTLIBSYSEVENTSYSEVENTD = $(ROOTLIBSYSEVENTDIR)/syseventd
+ROOTLIBSYSEVENTSYSEVENTCONFD = $(ROOTLIBSYSEVENTDIR)/syseventconfd
+ROOTLIBSYSEVENTMODULEDIR= $(ROOTLIBSYSEVENTDIR)/modules
+ROOTETCSYSEVENTDIR = $(ROOTETC)/sysevent
+ROOTETCSYSEVENTCONFIGDIR= $(ROOTETCSYSEVENTDIR)/config
+
+#
+# To play well with what we inherit from Makefile.lib
+#
+LIBLINKS =
+DYNLIB = $(LIBRARY:%=%.so)
+LIBS = $(DYNLIB)
+ROOTLIBDIR = $(ROOTLIBSYSEVENTMODULEDIR)
+
+#
+# install macro for syseventd & syseventconfd
+#
+ROOTLIBSYSEVENTSYSEVENTD = $(PROG:%=$(ROOTLIBSYSEVENTDIR)/%)
+
+#
+# install macro for /etc/sysevent/config files
+#
+ROOTETCSYSEVENTCONFIGFILES= $(CONFIG_FILES:%=$(ROOTETCSYSEVENTCONFIGDIR)/%)
+
+#
+# explicit ownership and permissions
+#
+
+$(ROOTLIBSYSEVENTDIR) := OWNER= root
+$(ROOTLIBSYSEVENTDIR) := GROUP= bin
+
+$(ROOTLIBSYSEVENTSYSEVENTD) := OWNER= root
+$(ROOTLIBSYSEVENTSYSEVENTD) := GROUP= bin
+
+$(ROOTLIBSYSEVENTSYSEVENTCONFD) := OWNER= root
+$(ROOTLIBSYSEVENTSYSEVENTCONFD) := GROUP= bin
+
+$(ROOTLIBSYSEVENTMODULEDIR) := OWNER= root
+$(ROOTLIBSYSEVENTMODULEDIR) := GROUP= bin
+
+$(ROOTETCSYSEVENTDIR) := OWNER= root
+$(ROOTETCSYSEVENTDIR) := GROUP= sys
+
+$(ROOTETCSYSEVENTCONFIGDIR) := OWNER= root
+$(ROOTETCSYSEVENTCONFIGDIR) := GROUP= sys
+
+$(ROOTETCSYSEVENTCONFIGFILES) := OWNER= root
+$(ROOTETCSYSEVENTCONFIGFILES) := GROUP= sys
+$(ROOTETCSYSEVENTCONFIGFILES) := FILEMODE= 0444
diff --git a/usr/src/cmd/syseventd/Makefile.targ b/usr/src/cmd/syseventd/Makefile.targ
new file mode 100644
index 0000000000..dc08338e18
--- /dev/null
+++ b/usr/src/cmd/syseventd/Makefile.targ
@@ -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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/Makefile.targ
+
+#
+# Common install rules and targets for both sysevent cmds & modules
+#
+
+$(ROOTLIBSYSEVENTDIR) $(ROOTETCSYSEVENTDIR) $(ROOTETCSYSEVENTCONFIGDIR):
+ $(INS.dir)
+
+$(ROOTLIBSYSEVENTDIR)/%: %
+ $(INS.file)
+
+$(ROOTLIBSYSEVENTMODULEDIR):
+ $(INS.dir)
+
+$(ROOTETCSYSEVENTCONFIGDIR)/% : %
+ $(INS.file)
diff --git a/usr/src/cmd/syseventd/daemons/Makefile b/usr/src/cmd/syseventd/daemons/Makefile
new file mode 100644
index 0000000000..18aa3a8a81
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/Makefile
@@ -0,0 +1,51 @@
+#
+# 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 (c) 1998-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/daemons/Makefile
+#
+
+.PARALLEL:
+
+SUBDIRS= \
+ syseventd \
+ syseventconfd
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+FRC:
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
diff --git a/usr/src/cmd/syseventd/daemons/Makefile.com b/usr/src/cmd/syseventd/daemons/Makefile.com
new file mode 100644
index 0000000000..8c250296bf
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/Makefile.com
@@ -0,0 +1,40 @@
+#
+# 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 (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/daemons/Makefile.com
+
+#
+# Common prologue for sysevent daemons
+#
+
+include $(SRC)/cmd/Makefile.cmd
+include $(SRC)/cmd/syseventd/Makefile.com
+
+OBJS = $(SRCS:%.c=%.o)
+
+POFILES = $(SRCS:.c=.po)
+POFILE = $(PROG)_msg.po
diff --git a/usr/src/cmd/syseventd/daemons/Makefile.targ b/usr/src/cmd/syseventd/daemons/Makefile.targ
new file mode 100644
index 0000000000..f378cd457f
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/Makefile.targ
@@ -0,0 +1,47 @@
+#
+# 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 (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/daemons/Makefile.targ
+
+#
+# Common epilogue for sysevent daemons
+#
+
+lint: lint_SRCS
+
+clean:
+ $(RM) $(OBJS)
+
+include $(SRC)/cmd/Makefile.targ
+include $(SRC)/cmd/syseventd/Makefile.targ
+
+$(POFILE): $(POFILES)
+ $(CAT) $(POFILES) >$@
+
+$(PROG): $(OBJS)
+ $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS)
+ $(POST_PROCESS)
diff --git a/usr/src/cmd/syseventd/daemons/syseventconfd/Makefile b/usr/src/cmd/syseventd/daemons/syseventconfd/Makefile
new file mode 100644
index 0000000000..848a0ccec8
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventconfd/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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = syseventconfd
+SRCS = $(PROG:%=%.c)
+
+include ../Makefile.com
+
+LDLIBS += -lnvpair
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all \
+ $(ROOTLIBSYSEVENTDIR) \
+ $(ROOTLIBSYSEVENTSYSEVENTCONFD)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/syseventd/daemons/syseventconfd/message_confd.h b/usr/src/cmd/syseventd/daemons/syseventconfd/message_confd.h
new file mode 100644
index 0000000000..a35d53b07b
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventconfd/message_confd.h
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#ifndef _MESSAGE_CONFD_H
+#define _MESSAGE_CONFD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define OUT_OF_MEMORY_ERR \
+ gettext("Out of memory.")
+
+#define INIT_ROOT_DIR_ERR \
+ gettext("Initialization error: could not allocate space for the local \
+ root directory - %s\n")
+
+#define INIT_THR_CREATE_ERR \
+ gettext("Initialization error:could not create signal thread - '%s' \
+ \n")
+
+#define CANNOT_FORK_ERR \
+ gettext("cannot fork - %s\n")
+
+#define SETUID_ERR \
+ gettext("%s, line %d: " "cannot setuid to user '%s' - %s\n")
+
+#define CANNOT_EXEC_ERR \
+ gettext("cannot exec %s - %s\n")
+
+#define CHILD_EXIT_STATUS_ERR \
+ gettext("process %d exited with status %d\n")
+
+#define CHILD_EXIT_CORE_ERR \
+ gettext("process %d dumped core - %s\n")
+
+#define CHILD_EXIT_SIGNAL_ERR \
+ gettext("process %d - %s\n")
+
+#define CHANNEL_OPEN_ERR \
+ gettext("unable to open channel to syseventd\n")
+
+#define CHANNEL_BIND_ERR \
+ gettext("unable to bind channel to syseventd\n")
+
+#define NO_NVLIST_ERR \
+ gettext("missing nvlist\n")
+
+#define NVLIST_FORMAT_ERR \
+ gettext("nvlist missing '%s'\n")
+
+#define NVLIST_FILE_LINE_FORMAT_ERR \
+ gettext("%s, line %d: nvlist missing '%s'\n")
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESSAGE_CONFD_H */
diff --git a/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.c b/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.c
new file mode 100644
index 0000000000..e3aa946454
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.c
@@ -0,0 +1,755 @@
+/*
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * syseventconfd - The sysevent conf daemon
+ *
+ * This daemon is a companion to the sysevent_conf_mod module.
+ *
+ * The sysevent_conf_mod module receives events from syseventd,
+ * and compares those events against event specs in the
+ * sysevent.conf files. For each matching event spec, the
+ * specified command is invoked.
+ *
+ * This daemon manages the fork/exec's on behalf of sysevent_conf_mod.
+ * The events and associated nvlist are delivered via a door upcall
+ * from sysevent_conf_mod. Arriving events are queued, and the
+ * main thread of this daemon dequeues events one by one, and
+ * builds the necessary arguments to fork/exec the command.
+ *
+ * Since sysevent_conf_mod is running in the context of syseventd,
+ * invoking the fork/exec from that module blocks the door upcalls
+ * from the kernel delivering events to syseventd. We avoid a
+ * major performance bottleneck in this fashion.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <strings.h>
+#include <unistd.h>
+#include <synch.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <door.h>
+#include <libsysevent.h>
+#include <limits.h>
+#include <locale.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/wait.h>
+
+#include "syseventconfd.h"
+#include "syseventconfd_door.h"
+#include "message_confd.h"
+
+
+
+static int debug_level = 0;
+static char *root_dir = ""; /* Relative root for lock and door */
+static char *prog;
+
+static struct cmd *cmd_list;
+static struct cmd *cmd_tail;
+
+static mutex_t cmd_list_lock;
+static cond_t cmd_list_cv;
+
+extern char *optarg;
+
+/*
+ * Support for door server thread handling
+ */
+#define MAX_SERVER_THREADS 1
+
+static mutex_t create_cnt_lock;
+static int cnt_servers = 0;
+
+
+static void
+usage() {
+ (void) fprintf(stderr, "usage: syseventconfd [-d <debug_level>]\n");
+ exit(2);
+}
+
+
+static void
+set_root_dir(char *dir)
+{
+ root_dir = malloc(strlen(dir) + 1);
+ if (root_dir == NULL) {
+ syserrmsg(INIT_ROOT_DIR_ERR, strerror(errno));
+ exit(2);
+ }
+ (void) strcpy(root_dir, dir);
+}
+
+
+void
+main(int argc, char **argv)
+{
+ int c;
+ int fd;
+ sigset_t set;
+ struct cmd *cmd;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (getuid() != 0) {
+ (void) fprintf(stderr, "Must be root to run syseventconfd\n");
+ exit(1);
+ }
+
+ if ((prog = strrchr(argv[0], '/')) == NULL) {
+ prog = argv[0];
+ } else {
+ prog++;
+ }
+
+ if ((c = getopt(argc, argv, "d:r:")) != EOF) {
+ switch (c) {
+ case 'd':
+ debug_level = atoi(optarg);
+ break;
+ case 'r':
+ /*
+ * Private flag for suninstall to run
+ * daemon during install.
+ */
+ set_root_dir(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+
+ if (fork()) {
+ exit(0);
+ }
+
+ (void) chdir("/");
+
+ (void) setsid();
+ if (debug_level <= 1) {
+ closefrom(0);
+ fd = open("/dev/null", 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ }
+
+ openlog("syseventconfd", LOG_PID, LOG_DAEMON);
+
+ printmsg(1,
+ "syseventconfd started, debug level = %d\n", debug_level);
+
+ /*
+ * Block all signals to all threads include the main thread.
+ * The sigwait_thr thread will catch and process all signals.
+ */
+ (void) sigfillset(&set);
+ (void) thr_sigsetmask(SIG_BLOCK, &set, NULL);
+
+ /* Create signal catching thread */
+ if (thr_create(NULL, NULL, (void *(*)(void *))sigwait_thr,
+ (void *)NULL, 0, NULL) < 0) {
+ syserrmsg(INIT_THR_CREATE_ERR, strerror(errno));
+ exit(2);
+ }
+
+ /*
+ * Init mutex and list of cmds to be fork/exec'ed
+ * This is multi-threaded so the fork/exec can be
+ * done without blocking the door upcall.
+ */
+ cmd_list = NULL;
+ cmd_tail = NULL;
+
+ (void) mutex_init(&create_cnt_lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&cmd_list_lock, USYNC_THREAD, NULL);
+ (void) cond_init(&cmd_list_cv, USYNC_THREAD, NULL);
+
+ /*
+ * Open communication channel from sysevent_conf_mod
+ */
+ if (open_channel() == NULL) {
+ exit(1);
+ }
+
+ /*
+ * main thread to wait for events to arrive and be placed
+ * on the queue. As events are queued, dequeue them
+ * here and invoke the associated fork/exec.
+ */
+ (void) mutex_lock(&cmd_list_lock);
+ for (;;) {
+ while (cmd_list == NULL)
+ (void) cond_wait(&cmd_list_cv, &cmd_list_lock);
+
+ cmd = cmd_list;
+ cmd_list = cmd->cmd_next;
+ if (cmd_list == NULL)
+ cmd_tail = NULL;
+
+ (void) mutex_unlock(&cmd_list_lock);
+ exec_cmd(cmd);
+ free_cmd(cmd);
+ (void) mutex_lock(&cmd_list_lock);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * Events sent via the door call from sysevent_conf_mod arrive
+ * here. Queue each event for the main thread to invoke, and
+ * return. We want to avoid doing the fork/exec while in the
+ * context of the door call.
+ */
+/*ARGSUSED*/
+static void
+event_handler(sysevent_t *event)
+{
+ nvlist_t *nvlist;
+ struct cmd *cmd;
+
+ nvlist = NULL;
+ if (sysevent_get_attr_list(event, &nvlist) != 0) {
+ syslog(LOG_ERR, NO_NVLIST_ERR);
+ return;
+ }
+
+ if ((cmd = alloc_cmd(nvlist)) != NULL) {
+ (void) mutex_lock(&cmd_list_lock);
+ if (cmd_list == NULL) {
+ cmd_list = cmd;
+ cmd_tail = cmd;
+ } else {
+ cmd_tail->cmd_next = cmd;
+ cmd_tail = cmd;
+ }
+ cmd->cmd_next = NULL;
+ (void) cond_signal(&cmd_list_cv);
+ (void) mutex_unlock(&cmd_list_lock);
+ }
+
+ nvlist_free(nvlist);
+}
+
+
+/*
+ * Decode the command, build the exec args and fork/exec the command
+ * All command attributes are packed into the nvlist bundled with
+ * the delivered event.
+ */
+static void
+exec_cmd(struct cmd *cmd)
+{
+ char *path;
+ char *cmdline;
+ uid_t uid;
+ gid_t gid;
+ char *file;
+ int line;
+ char *user;
+ arg_t *args;
+ pid_t pid;
+ char *lp;
+ char *p;
+ int i;
+ sigset_t set, prior_set;
+
+ if (nvlist_lookup_string(cmd->cmd_nvlist, "user", &user) != 0) {
+ syslog(LOG_ERR, NVLIST_FORMAT_ERR, "user");
+ return;
+ }
+ if (nvlist_lookup_string(cmd->cmd_nvlist, "file", &file) != 0) {
+ syslog(LOG_ERR, NVLIST_FORMAT_ERR, "file");
+ return;
+ }
+
+ if (nvlist_lookup_string(cmd->cmd_nvlist, "path", &path) != 0) {
+ syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "path");
+ return;
+ }
+ if (nvlist_lookup_string(cmd->cmd_nvlist, "cmd", &cmdline) != 0) {
+ syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "cmd");
+ return;
+ }
+ if (nvlist_lookup_int32(cmd->cmd_nvlist, "line", &line) != 0) {
+ syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "line");
+ return;
+ }
+ if (nvlist_lookup_int32(cmd->cmd_nvlist, "uid", (int *)&uid) == 0) {
+ if (nvlist_lookup_int32(cmd->cmd_nvlist,
+ "gid", (int *)&gid) != 0) {
+ syslog(LOG_ERR, NVLIST_FILE_LINE_FORMAT_ERR, "gid");
+ return;
+ }
+ } else {
+ uid = 0;
+ gid = 0;
+ }
+
+ args = init_arglist(32);
+
+ lp = cmdline;
+ while ((p = next_arg(&lp)) != NULL) {
+ if (add_arg(args, p)) {
+ free_arglist(args);
+ return;
+ }
+ }
+
+ if (debug_level >= DBG_EXEC) {
+ printmsg(DBG_EXEC, "path=%s\n", path);
+ printmsg(DBG_EXEC, "cmd=%s\n", cmdline);
+ }
+
+ if (debug_level >= DBG_EXEC_ARGS) {
+ for (i = 0; i < args->arg_nargs; i++) {
+ printmsg(DBG_EXEC_ARGS,
+ "arg[%d]: '%s'\n", i, args->arg_args[i]);
+ }
+ }
+
+ (void) sigprocmask(SIG_SETMASK, NULL, &set);
+ (void) sigaddset(&set, SIGCHLD);
+ (void) sigprocmask(SIG_SETMASK, &set, &prior_set);
+
+again:
+ if ((pid = fork1()) == (pid_t)-1) {
+ if (errno == EINTR)
+ goto again;
+ syslog(LOG_ERR, CANNOT_FORK_ERR, strerror(errno));
+ free_arglist(args);
+ return;
+ }
+ if (pid != (pid_t)0) {
+ (void) sigprocmask(SIG_SETMASK, &prior_set, NULL);
+ free_arglist(args);
+ return;
+ }
+
+ /*
+ * The child
+ */
+ (void) close(0);
+ (void) close(1);
+ (void) close(2);
+ (void) open("/dev/null", O_RDONLY);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+ if (uid != (uid_t)0) {
+ i = setgid(gid);
+ if (i == 0)
+ i = setuid(uid);
+ if (i != 0) {
+ syslog(LOG_ERR, SETUID_ERR,
+ file, line, user, strerror(errno));
+ _exit(0);
+ }
+ }
+
+ /*
+ * Unblock all signals in the child
+ */
+ (void) sigprocmask(SIG_UNBLOCK, &prior_set, NULL);
+
+ if (execv(path, args->arg_args) == -1) {
+ syslog(LOG_ERR, CANNOT_EXEC_ERR,
+ path, strerror(errno));
+ _exit(0);
+ }
+}
+
+
+/*
+ * Thread to handle in-coming signals
+ */
+static void
+sigwait_thr()
+{
+ int sig;
+ sigset_t signal_set;
+
+ /*
+ * SIGCHLD is ignored by default, and we need to handle this
+ * signal to reap the status of all children spawned by
+ * this daemon.
+ */
+ (void) sigset(SIGCHLD, reapchild);
+
+ for (;;) {
+ (void) sigfillset(&signal_set);
+ if (sigwait(&signal_set, &sig) == 0) {
+ /*
+ * Block all signals until the signal handler completes
+ */
+ (void) sigfillset(&signal_set);
+ (void) thr_sigsetmask(SIG_BLOCK, &signal_set, NULL);
+
+ if (sig == SIGCHLD) {
+ reapchild(sig);
+ } else {
+ flt_handler(sig);
+ }
+ }
+ }
+ /* NOTREACHED */
+}
+
+
+
+/*
+ * reapchild - reap the status of each child as it exits
+ */
+/*ARGSUSED*/
+static void
+reapchild(int sig)
+{
+ siginfo_t info;
+ char *signam;
+ int err;
+
+ for (;;) {
+ (void) memset(&info, 0, sizeof (info));
+ err = waitid(P_ALL, 0, &info, WNOHANG|WEXITED);
+ if (err == -1) {
+ if (errno != EINTR && errno != EAGAIN)
+ return;
+ } else if (info.si_pid == 0) {
+ return;
+ }
+
+ if (debug_level >= DBG_CHILD) {
+ printmsg(DBG_CHILD, CHILD_EXIT_STATUS_ERR,
+ info.si_pid, info.si_status);
+ }
+
+ if (info.si_status) {
+ if (info.si_code == CLD_EXITED) {
+ syserrmsg(CHILD_EXIT_STATUS_ERR,
+ info.si_pid, info.si_status);
+ } else {
+ signam = strsignal(info.si_status);
+ if (signam == NULL)
+ signam = "";
+ if (info.si_code == CLD_DUMPED) {
+ syserrmsg(
+ CHILD_EXIT_CORE_ERR,
+ info.si_pid, signam);
+ } else {
+ syserrmsg(
+ CHILD_EXIT_SIGNAL_ERR,
+ info.si_pid, signam);
+ }
+ }
+ }
+ }
+}
+
+
+/*
+ * Fault handler for other signals caught
+ */
+/*ARGSUSED*/
+static void
+flt_handler(int sig)
+{
+ struct sigaction act;
+
+ (void) memset(&act, 0, sizeof (act));
+ act.sa_handler = SIG_DFL;
+ act.sa_flags = SA_RESTART;
+ (void) sigfillset(&act.sa_mask);
+ (void) sigaction(sig, &act, NULL);
+
+ switch (sig) {
+ case SIGINT:
+ case SIGSTOP:
+ case SIGTERM:
+ case SIGHUP:
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+
+
+static arg_t *
+init_arglist(int hint)
+{
+ arg_t *arglist;
+
+ if ((arglist = sc_malloc(sizeof (arg_t))) == NULL)
+ return (NULL);
+ arglist->arg_args = NULL;
+ arglist->arg_nargs = 0;
+ arglist->arg_alloc = 0;
+ arglist->arg_hint = hint;
+ return (arglist);
+}
+
+
+static void
+free_arglist(arg_t *arglist)
+{
+ if (arglist->arg_args) {
+ free(arglist->arg_args);
+ }
+ free(arglist);
+}
+
+
+static int
+add_arg(arg_t *arglist, char *arg)
+{
+ char **new_args;
+ int len;
+
+ len = arglist->arg_nargs + 2;
+ if (arglist->arg_alloc < len) {
+ arglist->arg_alloc = len + arglist->arg_hint;
+ new_args = (arglist->arg_nargs == 0) ?
+ sc_malloc(arglist->arg_alloc * sizeof (char **)) :
+ sc_realloc(arglist->arg_args,
+ arglist->arg_alloc * sizeof (char **));
+ if (new_args == NULL)
+ return (1);
+ arglist->arg_args = new_args;
+ }
+
+ arglist->arg_args[arglist->arg_nargs++] = arg;
+ arglist->arg_args[arglist->arg_nargs] = NULL;
+
+ return (0);
+}
+
+/*
+ * next_arg() is used to break up a command line
+ * into the arguments for execv(2). Break up
+ * arguments separated by spaces, but respecting
+ * single/double quotes.
+ */
+static char *
+next_arg(char **cpp)
+{
+ char *cp = *cpp;
+ char *start;
+ char quote;
+
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == 0) {
+ *cpp = 0;
+ return (NULL);
+ }
+ start = cp;
+ while (*cp && *cp != ' ' && *cp != '\t') {
+ if (*cp == '"' || *cp == '\'') {
+ quote = *cp++;
+ while (*cp && *cp != quote) {
+ cp++;
+ }
+ if (*cp == 0) {
+ *cpp = 0;
+ return (NULL);
+ } else {
+ cp++;
+ }
+ } else {
+ cp++;
+ }
+ }
+ if (*cp != 0)
+ *cp++ = 0;
+ *cpp = cp;
+ return (start);
+}
+
+
+static struct cmd *
+alloc_cmd(nvlist_t *nvlist)
+{
+ struct cmd *cmd;
+
+ cmd = sc_malloc(sizeof (struct cmd));
+ if (cmd) {
+ if (nvlist_dup(nvlist, &cmd->cmd_nvlist, 0) != 0) {
+ syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
+ free(cmd);
+ return (NULL);
+ }
+ }
+ return (cmd);
+}
+
+static void
+free_cmd(struct cmd *cmd)
+{
+ nvlist_free(cmd->cmd_nvlist);
+ free(cmd);
+}
+
+
+static void *
+sc_malloc(size_t n)
+{
+ void *p;
+
+ p = malloc(n);
+ if (p == NULL) {
+ syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
+ }
+ return (p);
+}
+
+static void *
+sc_realloc(void *p, size_t n)
+{
+ p = realloc(p, n);
+ if (p == NULL) {
+ syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
+ }
+ return (p);
+}
+
+
+
+/*
+ * syserrsg - print error messages to the terminal if not
+ * yet daemonized or to syslog.
+ */
+/*PRINTFLIKE1*/
+static void
+syserrmsg(char *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ (void) vsyslog(LOG_ERR, message, ap);
+ va_end(ap);
+}
+
+/*
+ * printmsg - print messages to the terminal or to syslog
+ * the following levels are implemented:
+ */
+/*PRINTFLIKE2*/
+static void
+printmsg(int level, char *message, ...)
+{
+ va_list ap;
+
+ if (level > debug_level) {
+ return;
+ }
+
+ va_start(ap, message);
+ (void) syslog(LOG_DEBUG, "%s[%ld]: ", prog, getpid());
+ (void) vsyslog(LOG_DEBUG, message, ap);
+ va_end(ap);
+}
+
+/* ARGSUSED */
+static void *
+create_door_thr(void *arg)
+{
+ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ (void) door_return(NULL, 0, NULL, 0);
+ return (NULL);
+}
+
+/*
+ * Control creation of door server threads
+ *
+ * If first creation of server thread fails there is nothing
+ * we can do about. Doors would not work.
+ */
+/* ARGSUSED */
+static void
+mk_thr_pool(door_info_t *dip)
+{
+ (void) mutex_lock(&create_cnt_lock);
+ if (++cnt_servers > MAX_SERVER_THREADS) {
+ cnt_servers--;
+ (void) mutex_unlock(&create_cnt_lock);
+ return;
+ }
+ (void) mutex_unlock(&create_cnt_lock);
+
+ (void) thr_create(NULL, 0, create_door_thr, NULL,
+ THR_BOUND|THR_DETACHED, NULL);
+}
+
+static sysevent_handle_t *
+open_channel()
+{
+ char door_path[MAXPATHLEN];
+ const char *subclass_list;
+ sysevent_handle_t *handle;
+
+ if (snprintf(door_path, sizeof (door_path), "%s/%s",
+ root_dir, SYSEVENTCONFD_SERVICE_DOOR) >= sizeof (door_path)) {
+ syserrmsg(CHANNEL_OPEN_ERR);
+ return (NULL);
+ }
+
+ /*
+ * Setup of door server create function to limit the
+ * amount of door servers
+ */
+ (void) door_server_create(mk_thr_pool);
+
+ handle = sysevent_open_channel_alt(door_path);
+ if (handle == NULL) {
+ syserrmsg(CHANNEL_OPEN_ERR);
+ return (NULL);
+ }
+ if (sysevent_bind_subscriber(handle, event_handler) != 0) {
+ syserrmsg(CHANNEL_BIND_ERR);
+ sysevent_close_channel(handle);
+ return (NULL);
+ }
+ subclass_list = EC_SUB_ALL;
+ if (sysevent_register_event(handle, EC_ALL, &subclass_list, 1)
+ != 0) {
+ syserrmsg(CHANNEL_BIND_ERR);
+ (void) sysevent_unbind_subscriber(handle);
+ (void) sysevent_close_channel(handle);
+ return (NULL);
+ }
+ return (handle);
+}
diff --git a/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.h b/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.h
new file mode 100644
index 0000000000..9eec33ee12
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd.h
@@ -0,0 +1,102 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SYSEVENTCONFD_H
+#define _SYSEVENTCONFD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * debug levels
+ */
+#define DBG_EXEC 1 /* path and args for each fork/exec */
+#define DBG_EVENTS 2 /* received events */
+#define DBG_CHILD 3 /* child exit status */
+#define DBG_EXEC_ARGS 4 /* more detail on exec args */
+
+
+/*
+ * list of cmds received from syseventd/sysevent_conf_mod
+ */
+struct cmd {
+ nvlist_t *cmd_nvlist;
+ struct cmd *cmd_next;
+ struct cmd *cmd_taiL;
+};
+
+
+/*
+ * Structures for building arbitarily long strings and argument lists
+ */
+typedef struct arg {
+ char **arg_args;
+ int arg_nargs;
+ int arg_alloc;
+ int arg_hint;
+} arg_t;
+
+typedef struct str {
+ char *s_str;
+ int s_len;
+ int s_alloc;
+ int s_hint;
+} str_t;
+
+
+/*
+ * Prototypes
+ */
+void main(int argc, char **argv);
+static void event_handler(sysevent_t *event);
+static void exec_cmd(struct cmd *cmd);
+static void sigwait_thr(void);
+static void reapchild(int sig);
+static void flt_handler(int sig);
+static void syserrmsg(char *message, ...);
+static void printmsg(int level, char *message, ...);
+static void set_root_dir(char *dir);
+static void usage(void);
+static arg_t *init_arglist(int hint);
+static void free_arglist(arg_t *arglist);
+static int add_arg(arg_t *arglist, char *arg);
+static char *next_arg(char **cpp);
+static struct cmd *alloc_cmd(nvlist_t *nvlist);
+static void free_cmd(struct cmd *cmd);
+static void *sc_malloc(size_t n);
+static void *sc_realloc(void *p, size_t n);
+static sysevent_handle_t *open_channel(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYSEVENTCONFD_H */
diff --git a/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd_door.h b/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd_door.h
new file mode 100644
index 0000000000..763b56223d
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventconfd/syseventconfd_door.h
@@ -0,0 +1,48 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SYSEVENTCONFD_DOOR_H
+#define _SYSEVENTCONFD_DOOR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Door for channel sysevent_conf_mod -> syseventconfd
+ */
+#define SYSEVENTCONFD_SERVICE_DOOR \
+ "/etc/sysevent/syseventconfd_event_channel"
+
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYSEVENTCONFD_DOOR_H */
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/Makefile b/usr/src/cmd/syseventd/daemons/syseventd/Makefile
new file mode 100644
index 0000000000..310736ba03
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/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 2005 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+PROG = syseventd
+SRCS = syseventd.c sysevent_signal.c sysevent_client.c
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all \
+ $(ROOTLIBSYSEVENTDIR) \
+ $(ROOTLIBSYSEVENTSYSEVENTD)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/message.h b/usr/src/cmd/syseventd/daemons/syseventd/message.h
new file mode 100644
index 0000000000..e20f3dec4d
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/message.h
@@ -0,0 +1,139 @@
+/*
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MESSAGE_H
+#define _MESSAGE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SIGHUP_CAUGHT \
+ gettext("SIGHUP caught - reloading modules\n")
+
+#define DAEMON_RESTARTED \
+ gettext("Daemon restarted\n")
+
+#define UNKNOWN_SIGNAL_CAUGHT \
+ gettext("Signal '%d' caught\n")
+
+#define FATAL_ERROR \
+ gettext("Fatal:attempting to dump core\n")
+
+#define INIT_ROOT_DIR_ERR \
+ gettext("Initialization error: could not allocate space for the local \
+ root directory - %s\n")
+
+#define INIT_EV_BUF_ERR \
+ gettext("Initialization error: could not allocate space for the event \
+ buffer - %s\n")
+
+#define INIT_SIG_BLOCK_ERR \
+ gettext("Initialization error: Unable to block signals before creating \
+ event dispatch thread\n")
+
+#define INIT_THR_CREATE_ERR \
+ gettext("Initialization error:could not create dispatch thread - '%s' \
+ \n")
+
+#define INIT_SIG_UNBLOCK_ERR \
+ gettext("Initialization error: Unable to unblock signals after \
+ creating event dispatch thread\n")
+
+#define KERNEL_REPLAY_ERR \
+ gettext("Kernel unable to post events: '%s'\n")
+
+#define LOAD_MOD_ALLOC_ERR \
+ gettext("Unable to allocate load module data structure %s: %s")
+
+#define LOAD_MOD_OPEN_ERR \
+ gettext("Unable to open module directory '%s': '%s'")
+
+#define LOAD_MOD_READ_ERR \
+ gettext("Unable to read module directory '%s': '%s'")
+
+#define LOAD_MOD_DLOPEN_ERR \
+ gettext("Unable to open module '%s': '%s'")
+
+#define LOAD_MOD_DLSYM_ERR \
+ gettext("Unable to read symbols for module '%s': '%s'")
+
+#define LOAD_MOD_NO_INIT \
+ gettext("Invalid module init routine for '%s': '%s'")
+
+#define LOAD_MOD_EINVAL \
+ gettext("Invalid ops vector for module '%s'")
+
+#define LOAD_MOD_VERSION_MISMATCH \
+ gettext("Invalid major number for module '%s': \
+ syseventd version '%d' module version '%d'")
+
+#define INIT_OPEN_DOOR_ERR \
+ gettext("Unable to open kernel event door: '%s'")
+
+#define INIT_CREATE_DOOR_ERR \
+ gettext("Unable to create kernel event door: '%s'")
+
+#define INIT_FATTACH_ERR \
+ gettext("Kernel door failed to attach: '%s'")
+
+#define INIT_DOOR_NAME_ERR \
+ gettext("Unable to establish door name with kernel: '%s'")
+
+#define INIT_LOCK_OPEN_ERR \
+ gettext("Unable to open daemon lock file '%s': '%s'")
+
+#define INIT_LOCK_ERR \
+ gettext("Unable to obtain daemon lock file '%s': '%s'")
+
+#define INIT_PATH_ERR \
+ gettext("Unable to open '%s': file path invalid")
+
+#define INIT_UNLOCK_ERR \
+ gettext("Unable to release daemon lock file '%s': '%s'")
+
+#define INIT_LOCK_CLOSE_ERR \
+ gettext("Unable to close daemon lock file '%s': '%s'")
+
+#define INIT_CLIENT_TBL_ERR \
+ gettext("Unable to initialize event client table\n")
+
+#define GET_DATA_FAILED \
+ gettext("Incomplete event buffer 0X%llx.%llx")
+
+#define WAIT_FAILED_ERR \
+ gettext("waitpid() failed: %s\n")
+
+#define SEMA_WAIT_FAILED_ERR \
+ gettext("sema_wait() failed: %s\n")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESSAGE_H */
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/sysevent_client.c b/usr/src/cmd/syseventd/daemons/syseventd/sysevent_client.c
new file mode 100644
index 0000000000..04a410991b
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/sysevent_client.c
@@ -0,0 +1,153 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * syseventd client interfaces
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <door.h>
+#include <errno.h>
+#include <strings.h>
+#include <thread.h>
+#include <pthread.h>
+#include <synch.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <stropts.h>
+#include <locale.h>
+#include <libsysevent.h>
+#include <sys/stat.h>
+#include <sys/sysevent.h>
+
+#include "syseventd.h"
+#include "message.h"
+
+/*
+ * sysevent_client.c - contains routines particular to syseventd client
+ * management (addition and deletion).
+ */
+
+/* Global client table and lock */
+struct sysevent_client *sysevent_client_tbl[MAX_SLM];
+mutex_t client_tbl_lock;
+
+/*
+ * initialize_client_tbl - Initialize each client entry in the syseventd
+ * client table. Each entry in the client table
+ * entry represents one shared-object (SLM) client.
+ */
+void
+initialize_client_tbl()
+{
+ struct sysevent_client *scp;
+ int i;
+
+ for (i = 0; i < MAX_SLM; ++i) {
+ if ((scp = (struct sysevent_client *)malloc(
+ sizeof (struct sysevent_client))) == NULL)
+ goto init_error;
+
+ if (mutex_init(&scp->client_lock, USYNC_THREAD, NULL) != 0)
+ goto init_error;
+
+ scp->client_data = NULL;
+ scp->client_num = i;
+ scp->eventq = NULL;
+
+ /* Clear all flags when setting UNLOADED */
+ scp->client_flags = SE_CLIENT_UNLOADED;
+
+ sysevent_client_tbl[i] = scp;
+ }
+
+ return;
+
+init_error:
+ syseventd_err_print(INIT_CLIENT_TBL_ERR);
+ syseventd_exit(1);
+}
+
+/*
+ * insert_client - called when a new SLM is loaded with syseventd. The
+ * client specific data is updated to reflect this addition
+ */
+int
+insert_client(void *client_data, int client_type, int retry_limit)
+{
+ int i;
+ struct sysevent_client *scp;
+
+ (void) mutex_lock(&client_tbl_lock);
+ for (i = 0; i < MAX_SLM; ++i) {
+ scp = sysevent_client_tbl[i];
+ if (scp->client_data == NULL) {
+ (void) mutex_lock(&scp->client_lock);
+ scp->client_data = client_data;
+ scp->client_type = client_type;
+ scp->retry_limit = retry_limit;
+ scp->client_flags |= SE_CLIENT_LOADED;
+ (void) cond_init(&scp->client_cv, USYNC_THREAD,
+ NULL);
+ (void) mutex_unlock(&scp->client_lock);
+ (void) mutex_unlock(&client_tbl_lock);
+ return (i);
+ }
+ }
+
+ (void) mutex_unlock(&client_tbl_lock);
+ syseventd_print(1, "Unable to insert into syseventd client table\n");
+ return (-1);
+}
+
+/*
+ * delete_client - called to remove an SLM from the client table. Client
+ * removal may occur when syseventd terminates, receives
+ * a SIGHUP or the client must be force unloaded due
+ * it's unresponsive nature.
+ */
+void
+delete_client(int id)
+{
+ struct sysevent_client *scp;
+
+ scp = sysevent_client_tbl[id];
+
+ free(scp->client_data);
+ scp->client_data = NULL;
+
+ /* Clear all flags when setting UNLOADED */
+ scp->client_flags = SE_CLIENT_UNLOADED;
+ (void) cond_destroy(&scp->client_cv);
+ bzero(&scp->client_cv, sizeof (cond_t));
+}
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.c b/usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.c
new file mode 100644
index 0000000000..a72cc83683
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.c
@@ -0,0 +1,96 @@
+/*
+ * 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"
+#include <signal.h>
+#include <thread.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "sysevent_signal.h"
+
+static se_signal_f *sig_handlers[NSIG];
+static void *sig_data[NSIG];
+
+static void
+sig_stub(int sig, siginfo_t *sip, void *ucp)
+{
+ sig_handlers[sig](sig, sip, (ucontext_t *)ucp, sig_data[sig]);
+}
+
+int
+se_signal_sethandler(int sig, se_signal_f *handler, void *data)
+{
+ struct sigaction act;
+ int status;
+
+ sig_handlers[sig] = handler;
+ sig_data[sig] = data;
+
+ if (handler == SIG_DFL || handler == SIG_IGN) {
+ act.sa_handler = handler;
+ act.sa_flags = SA_RESTART;
+ } else {
+ act.sa_sigaction = sig_stub;
+ act.sa_flags = SA_SIGINFO | SA_RESTART;
+ }
+
+ (void) sigfillset(&act.sa_mask);
+
+ if ((status = sigaction(sig, &act, NULL)) == 0)
+ (void) se_signal_unblock(sig);
+
+ return (status);
+}
+
+int
+se_signal_unblock(int sig)
+{
+ sigset_t set;
+
+ (void) sigemptyset(&set);
+ (void) sigaddset(&set, sig);
+
+ return (thr_sigsetmask(SIG_UNBLOCK, &set, NULL));
+}
+
+int
+se_signal_blockall(void)
+{
+ sigset_t set;
+
+ (void) sigfillset(&set);
+ return (thr_sigsetmask(SIG_BLOCK, &set, NULL));
+}
+
+int
+se_signal_unblockall(void)
+{
+ sigset_t set;
+
+ (void) sigfillset(&set);
+ return (thr_sigsetmask(SIG_UNBLOCK, &set, NULL));
+}
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.h b/usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.h
new file mode 100644
index 0000000000..a3062f6566
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/sysevent_signal.h
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#ifndef _DEVFSEVENT_SIGNAL_H
+#define _DEVFSEVENT_SIGNAL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <ucontext.h>
+#include <signal.h>
+
+typedef void se_signal_f(int, siginfo_t *, ucontext_t *, void *);
+
+extern int se_signal_sethandler(int, se_signal_f *, void *);
+extern se_signal_f *se_signal_gethandler(int, void **);
+
+extern int se_signal_raise(int);
+extern int se_signal_pgrp(int);
+
+extern int se_signal_block(int);
+extern int se_signal_unblock(int);
+
+extern int se_signal_blockall(void);
+extern int se_signal_unblockall(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEVFSEVENT_SIGNAL_H */
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/syseventd.c b/usr/src/cmd/syseventd/daemons/syseventd/syseventd.c
new file mode 100644
index 0000000000..38b88a25ec
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/syseventd.c
@@ -0,0 +1,1626 @@
+/*
+ * 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"
+
+/*
+ * syseventd - The system event daemon
+ *
+ * This daemon dispatches event buffers received from the
+ * kernel to all interested SLM clients. SLMs in turn
+ * deliver the buffers to their particular application
+ * clients.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <door.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <strings.h>
+#include <unistd.h>
+#include <synch.h>
+#include <syslog.h>
+#include <thread.h>
+#include <libsysevent.h>
+#include <limits.h>
+#include <locale.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent_impl.h>
+#include <sys/modctl.h>
+#include <sys/stat.h>
+#include <sys/systeminfo.h>
+#include <sys/wait.h>
+
+#include "sysevent_signal.h"
+#include "syseventd.h"
+#include "message.h"
+
+extern int insert_client(void *client, int client_type, int retry_limit);
+extern void delete_client(int id);
+extern void initialize_client_tbl(void);
+
+extern struct sysevent_client *sysevent_client_tbl[];
+extern mutex_t client_tbl_lock;
+
+#define DEBUG_LEVEL_FORK 9 /* will run in background at all */
+ /* levels less than DEBUG_LEVEL_FORK */
+
+int debug_level = 0;
+char *root_dir = ""; /* Relative root for lock and door */
+
+/* Maximum number of outstanding events dispatched */
+#define SE_EVENT_DISPATCH_CNT 100
+
+static int upcall_door; /* Kernel event door */
+static int door_upcall_retval; /* Kernel event posting return value */
+static int fini_pending = 0; /* fini pending flag */
+static int deliver_buf = 0; /* Current event buffer from kernel */
+static int dispatch_buf = 0; /* Current event buffer dispatched */
+static sysevent_t **eventbuf; /* Global array of event buffers */
+static struct ev_completion *event_compq; /* Event completion queue */
+static mutex_t ev_comp_lock; /* Event completion queue lock */
+static mutex_t err_mutex; /* error logging lock */
+static mutex_t door_lock; /* sync door return access */
+static rwlock_t mod_unload_lock; /* sync module unloading */
+
+/* declarations and definitions for avoiding multiple daemons running */
+#define DAEMON_LOCK_FILE "/etc/sysevent/syseventd_lock"
+char local_lock_file[PATH_MAX + 1];
+static int hold_daemon_lock;
+static int daemon_lock_fd;
+
+/*
+ * sema_eventbuf - guards against the global buffer eventbuf
+ * being written to before it has been dispatched to clients
+ *
+ * sema_dispatch - synchronizes between the kernel uploading thread
+ * (producer) and the userland dispatch_message thread (consumer).
+ *
+ * sema_resource - throttles outstanding event consumption.
+ *
+ * event_comp_cv - synchronizes threads waiting for the event completion queue
+ * to empty or become active.
+ */
+static sema_t sema_eventbuf, sema_dispatch, sema_resource;
+static cond_t event_comp_cv;
+
+/* Self-tuning concurrency level */
+#define MIN_CONCURRENCY_LEVEL 4
+static int concurrency_level = MIN_CONCURRENCY_LEVEL;
+
+
+/* SLM defines */
+#define MODULE_SUFFIX ".so"
+#define EVENT_FINI "slm_fini"
+#define EVENT_INIT "slm_init"
+
+#define SE_TIMEOUT 60 /* Client dispatch timeout (seconds) */
+
+/* syslog message related */
+static int logflag = 0;
+static char *prog;
+
+/* function prototypes */
+static void door_upcall(void *cookie, char *args, size_t alen, door_desc_t *ddp,
+ uint_t ndid);
+static void dispatch_message(void);
+static int dispatch(void);
+static void event_completion_thr(void);
+static void usage(void);
+
+static void syseventd_init(void);
+static void syseventd_fini(int sig);
+
+static pid_t enter_daemon_lock(void);
+static void exit_daemon_lock(void);
+
+static void
+usage() {
+ (void) fprintf(stderr, "usage: syseventd [-d <debug_level>] "
+ "[-r <root_dir>]\n");
+ (void) fprintf(stderr, "higher debug levels get progressively ");
+ (void) fprintf(stderr, "more detailed debug information.\n");
+ (void) fprintf(stderr, "syseventd will run in background if ");
+ (void) fprintf(stderr, "run with a debug_level less than %d.\n",
+ DEBUG_LEVEL_FORK);
+ exit(2);
+}
+
+
+/* common exit function which ensures releasing locks */
+void
+syseventd_exit(int status)
+{
+ syseventd_print(1, "exit status = %d\n", status);
+
+ if (hold_daemon_lock) {
+ exit_daemon_lock();
+ }
+
+ exit(status);
+}
+
+
+/*
+ * hup_handler - SIGHUP handler. SIGHUP is used to force a reload of
+ * all SLMs. During fini, events are drained from all
+ * client event queues. The events that have been consumed
+ * by all clients are freed from the kernel event queue.
+ *
+ * Events that have not yet been delivered to all clients
+ * are not freed and will be replayed after all SLMs have
+ * been (re)loaded.
+ *
+ * After all client event queues have been drained, each
+ * SLM client is unloaded. The init phase will (re)load
+ * each SLM and initiate event replay and delivery from
+ * the kernel.
+ *
+ */
+/*ARGSUSED*/
+static void
+hup_handler(int sig)
+{
+ syseventd_err_print(SIGHUP_CAUGHT);
+ (void) fflush(0);
+ syseventd_fini(sig);
+ syseventd_init();
+ syseventd_err_print(DAEMON_RESTARTED);
+ (void) fflush(0);
+}
+
+/*
+ * Fault handler for other signals caught
+ */
+/*ARGSUSED*/
+static void
+flt_handler(int sig)
+{
+ char signame[SIG2STR_MAX];
+
+ if (sig2str(sig, signame) == -1) {
+ syseventd_err_print(UNKNOWN_SIGNAL_CAUGHT, sig);
+ }
+
+ (void) se_signal_sethandler(sig, SIG_DFL, NULL);
+
+ switch (sig) {
+ case SIGINT:
+ case SIGSTOP:
+ case SIGTERM:
+ /* Close kernel door */
+ (void) door_revoke(upcall_door);
+
+ /* Gracefully exit current event delivery threads */
+ syseventd_fini(sig);
+
+ (void) fflush(0);
+ (void) se_signal_unblockall();
+ syseventd_exit(1);
+ /*NOTREACHED*/
+ default:
+ syseventd_err_print(FATAL_ERROR);
+ (void) fflush(0);
+
+ }
+}
+
+static void
+sigwait_thr()
+{
+ int sig;
+ int err;
+ sigset_t signal_set;
+
+ for (;;) {
+ syseventd_print(3, "sigwait thread waiting for signal\n");
+ (void) sigfillset(&signal_set);
+ err = sigwait(&signal_set, &sig);
+ if (err) {
+ syseventd_exit(2);
+ }
+
+ /*
+ * Block all signals until the signal handler completes
+ */
+ if (sig == SIGHUP) {
+ hup_handler(sig);
+ } else {
+ flt_handler(sig);
+ }
+ }
+ /* NOTREACHED */
+}
+
+static void
+set_root_dir(char *dir)
+{
+ root_dir = malloc(strlen(dir) + 1);
+ if (root_dir == NULL) {
+ syseventd_err_print(INIT_ROOT_DIR_ERR, strerror(errno));
+ syseventd_exit(2);
+ }
+ (void) strcpy(root_dir, dir);
+}
+
+void
+main(int argc, char **argv)
+{
+ int i, c;
+ int fd;
+ pid_t pid;
+ extern char *optarg;
+
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ if (getuid() != 0) {
+ (void) fprintf(stderr, "Must be root to run syseventd\n");
+ syseventd_exit(1);
+ }
+
+ if (argc > 5) {
+ usage();
+ }
+
+ if ((prog = strrchr(argv[0], '/')) == NULL) {
+ prog = argv[0];
+ } else {
+ prog++;
+ }
+
+ if ((c = getopt(argc, argv, "d:r:")) != EOF) {
+ switch (c) {
+ case 'd':
+ debug_level = atoi(optarg);
+ break;
+ case 'r':
+ /*
+ * Private flag for suninstall to run
+ * daemon during install.
+ */
+ set_root_dir(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ /* demonize ourselves */
+ if (debug_level < DEBUG_LEVEL_FORK) {
+
+ if (fork()) {
+ syseventd_exit(0);
+ }
+
+ /* child */
+
+ (void) chdir("/");
+ (void) setsid();
+ if (debug_level <= 1) {
+ closefrom(0);
+ fd = open("/dev/null", 0);
+ (void) dup2(fd, 1);
+ (void) dup2(fd, 2);
+ logflag = 1;
+ }
+ }
+
+ openlog("syseventd", LOG_PID, LOG_DAEMON);
+
+ (void) mutex_init(&err_mutex, USYNC_THREAD, NULL);
+
+ syseventd_print(8,
+ "syseventd started, debug level = %d\n", debug_level);
+
+ /* only one instance of syseventd can run at a time */
+ if ((pid = enter_daemon_lock()) != getpid()) {
+ syseventd_print(1,
+ "event daemon pid %ld already running\n", pid);
+ exit(3);
+ }
+
+ /* initialize semaphores and eventbuf */
+ (void) sema_init(&sema_eventbuf, SE_EVENT_DISPATCH_CNT,
+ USYNC_THREAD, NULL);
+ (void) sema_init(&sema_dispatch, 0, USYNC_THREAD, NULL);
+ (void) sema_init(&sema_resource, SE_EVENT_DISPATCH_CNT,
+ USYNC_THREAD, NULL);
+ (void) cond_init(&event_comp_cv, USYNC_THREAD, NULL);
+ eventbuf = (sysevent_t **)calloc(SE_EVENT_DISPATCH_CNT,
+ sizeof (sysevent_t *));
+ if (eventbuf == NULL) {
+ syseventd_print(1, "Unable to allocate event buffer array\n");
+ exit(2);
+ }
+ for (i = 0; i < SE_EVENT_DISPATCH_CNT; ++i) {
+ eventbuf[i] = malloc(LOGEVENT_BUFSIZE);
+ if (eventbuf[i] == NULL) {
+ syseventd_print(1, "Unable to allocate event "
+ "buffers\n");
+ exit(2);
+ }
+ }
+
+ (void) mutex_init(&client_tbl_lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&ev_comp_lock, USYNC_THREAD, NULL);
+ (void) mutex_init(&door_lock, USYNC_THREAD, NULL);
+ (void) rwlock_init(&mod_unload_lock, USYNC_THREAD, NULL);
+
+ event_compq = NULL;
+
+ syseventd_print(8, "start the message thread running\n");
+
+ /*
+ * Block all signals to all threads include the main thread.
+ * The sigwait_thr thread will process any signals and initiate
+ * a graceful recovery if possible.
+ */
+ if (se_signal_blockall() < 0) {
+ syseventd_err_print(INIT_SIG_BLOCK_ERR);
+ syseventd_exit(2);
+ }
+
+ if (thr_create(NULL, NULL, (void *(*)(void *))dispatch_message,
+ (void *)0, 0, NULL) < 0) {
+ syseventd_err_print(INIT_THR_CREATE_ERR, strerror(errno));
+ syseventd_exit(2);
+ }
+ if (thr_create(NULL, NULL,
+ (void *(*)(void *))event_completion_thr, NULL,
+ THR_BOUND, NULL) != 0) {
+ syseventd_err_print(INIT_THR_CREATE_ERR, strerror(errno));
+ syseventd_exit(2);
+ }
+ /* Create signal catching thread */
+ if (thr_create(NULL, NULL, (void *(*)(void *))sigwait_thr,
+ NULL, 0, NULL) < 0) {
+ syseventd_err_print(INIT_THR_CREATE_ERR, strerror(errno));
+ syseventd_exit(2);
+ }
+
+ setbuf(stdout, (char *)NULL);
+
+ /* Initialize and load SLM clients */
+ initialize_client_tbl();
+ syseventd_init();
+
+ syseventd_print(8, "Pausing\n");
+
+ for (;;) {
+ (void) pause();
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * door_upcall - called from the kernel via kernel sysevent door
+ * to upload event(s).
+ *
+ * This routine should never block. If resources are
+ * not available to immediately accept the event buffer
+ * EAGAIN is returned to the kernel.
+ *
+ * Once resources are available, the kernel is notified
+ * via a modctl interface to resume event delivery to
+ * syseventd.
+ *
+ */
+/*ARGSUSED*/
+static void
+door_upcall(void *cookie, char *args, size_t alen,
+ door_desc_t *ddp, uint_t ndid)
+{
+ sysevent_t *ev;
+ int rval;
+
+
+ (void) mutex_lock(&door_lock);
+ if (args == NULL) {
+ rval = EINVAL;
+ } else if (sema_trywait(&sema_eventbuf)) {
+ ev = (sysevent_t *)
+ &((log_event_upcall_arg_t *)(void *)args)->buf;
+ syseventd_print(2, "door_upcall: busy event %llx "
+ "retry\n", sysevent_get_seq(ev));
+ rval = door_upcall_retval = EAGAIN;
+ } else {
+ /*
+ * Copy received message to local buffer.
+ */
+ size_t size;
+ ev = (sysevent_t *)
+ &((log_event_upcall_arg_t *)(void *)args)->buf;
+
+ syseventd_print(2, "door_upcall: event %llx in eventbuf %d\n",
+ sysevent_get_seq(ev), deliver_buf);
+ size = sysevent_get_size(ev) > LOGEVENT_BUFSIZE ?
+ LOGEVENT_BUFSIZE : sysevent_get_size(ev);
+ (void) bcopy(ev, eventbuf[deliver_buf], size);
+ deliver_buf = (deliver_buf + 1) % SE_EVENT_DISPATCH_CNT;
+ rval = 0;
+ (void) sema_post(&sema_dispatch);
+ }
+
+ (void) mutex_unlock(&door_lock);
+
+ /*
+ * Filling in return values for door_return
+ */
+ (void) door_return((void *)&rval, sizeof (rval), NULL, 0);
+ (void) door_return(NULL, 0, NULL, 0);
+}
+
+/*
+ * dispatch_message - dispatch message thread
+ * This thread spins until an event buffer is delivered
+ * delivered from the kernel.
+ *
+ * It will wait to dispatch an event to any clients
+ * until adequate resources are available to process
+ * the event buffer.
+ */
+static void
+dispatch_message(void)
+{
+ int error;
+
+ for (;;) {
+ syseventd_print(3, "dispatch_message: thread started\n");
+ /*
+ * Spin till a message comes
+ */
+ while (sema_wait(&sema_dispatch) != 0) {
+ syseventd_print(1,
+ "dispatch_message: sema_wait failed\n");
+ (void) sleep(1);
+ }
+
+ syseventd_print(3, "dispatch_message: sema_dispatch\n");
+
+ /*
+ * Wait for available resources
+ */
+ while (sema_wait(&sema_resource) != 0) {
+ syseventd_print(1, "dispatch_message: sema_wait "
+ "failed\n");
+ (void) sleep(1);
+ }
+
+ syseventd_print(2, "dispatch_message: eventbuf %d\n",
+ dispatch_buf);
+
+ /*
+ * Client dispatch
+ */
+ do {
+ error = dispatch();
+ } while (error == EAGAIN);
+
+ syseventd_print(2, "eventbuf %d dispatched\n", dispatch_buf);
+ dispatch_buf = (dispatch_buf + 1) % SE_EVENT_DISPATCH_CNT;
+
+ /*
+ * kernel received a busy signal -
+ * kickstart the kernel delivery thread
+ * door_lock blocks the kernel so we hold it for the
+ * shortest time possible.
+ */
+ (void) mutex_lock(&door_lock);
+ if (door_upcall_retval == EAGAIN && !fini_pending) {
+ syseventd_print(3, "dispatch_message: retrigger "
+ "door_upcall_retval = %d\n",
+ door_upcall_retval);
+ (void) modctl(MODEVENTS, (uintptr_t)MODEVENTS_FLUSH,
+ NULL, NULL, NULL, 0);
+ door_upcall_retval = 0;
+ }
+ (void) mutex_unlock(&door_lock);
+ }
+ /* NOTREACHED */
+}
+
+/*
+ * drain_eventq - Called to drain all pending events from the client's
+ * event queue.
+ */
+static void
+drain_eventq(struct sysevent_client *scp, int status)
+{
+ struct event_dispatch_pkg *d_pkg;
+ struct event_dispatchq *eventq, *eventq_next;
+
+ syseventd_print(3, "Draining eventq for client %d\n",
+ scp->client_num);
+
+ eventq = scp->eventq;
+ while (eventq) {
+ /*
+ * Mark all dispatched events as completed, but indicate the
+ * error status
+ */
+ d_pkg = eventq->d_pkg;
+
+ syseventd_print(4, "drain event 0X%llx for client %d\n",
+ sysevent_get_seq(d_pkg->ev), scp->client_num);
+
+ if (d_pkg->completion_state == SE_NOT_DISPATCHED) {
+ d_pkg->completion_status = status;
+ d_pkg->completion_state = SE_COMPLETE;
+ (void) sema_post(d_pkg->completion_sema);
+ }
+
+ eventq_next = eventq->next;
+ free(eventq);
+ eventq = eventq_next;
+ scp->eventq = eventq;
+ }
+}
+
+/*
+ * client_deliver_event_thr - Client delivery thread
+ * This thread will process any events on this
+ * client's eventq.
+ */
+static void
+client_deliver_event_thr(void *arg)
+{
+ int flag, error, i;
+ sysevent_t *ev;
+ hrtime_t now;
+ module_t *mod;
+ struct event_dispatchq *eventq;
+ struct sysevent_client *scp;
+ struct event_dispatch_pkg *d_pkg;
+
+ scp = (struct sysevent_client *)arg;
+ mod = (module_t *)scp->client_data;
+
+ (void) mutex_lock(&scp->client_lock);
+ for (;;) {
+ while (scp->eventq == NULL) {
+
+ /*
+ * Client has been suspended or unloaded, go no further.
+ */
+ if (fini_pending) {
+ scp->client_flags &= ~SE_CLIENT_THR_RUNNING;
+ syseventd_print(3, "Client %d delivery thread "
+ "exiting flags: 0X%x\n",
+ scp->client_num, scp->client_flags);
+ (void) mutex_unlock(&scp->client_lock);
+ return;
+ }
+
+ (void) cond_wait(&scp->client_cv, &scp->client_lock);
+
+ }
+
+ /*
+ * Process events from the head of the eventq, eventq is locked
+ * going into the processing.
+ */
+ eventq = scp->eventq;
+ while (eventq != NULL) {
+ d_pkg = eventq->d_pkg;
+ d_pkg->completion_state = SE_OUTSTANDING;
+ (void) mutex_unlock(&scp->client_lock);
+
+
+ flag = error = 0;
+ ev = d_pkg->ev;
+
+ syseventd_print(3, "Start delivery for client %d "
+ "with retry count %d\n",
+ scp->client_num, d_pkg->retry_count);
+
+ /*
+ * Retry limit has been reached by this client, indicate
+ * that no further retries are allowed
+ */
+ for (i = 0; i <= scp->retry_limit; ++i) {
+ if (i == scp->retry_limit)
+ flag = SE_NO_RETRY;
+
+ /* Start the clock for the event delivery */
+ d_pkg->start_time = gethrtime();
+
+ syseventd_print(9, "Deliver to module client "
+ "%s\n", mod->name);
+
+ error = mod->deliver_event(ev, flag);
+
+ /* Can not allow another retry */
+ if (i == scp->retry_limit)
+ error = 0;
+
+ /* Stop the clock */
+ now = gethrtime();
+
+ /*
+ * Suspend event processing and drain the
+ * event q for latent clients
+ */
+ if (now - d_pkg->start_time >
+ ((hrtime_t)SE_TIMEOUT * NANOSEC)) {
+ syseventd_print(1, "Unresponsive "
+ "client %d: Draining eventq and "
+ "suspending event delivery\n",
+ scp->client_num);
+ (void) mutex_lock(&scp->client_lock);
+ scp->client_flags &=
+ ~SE_CLIENT_THR_RUNNING;
+ scp->client_flags |=
+ SE_CLIENT_SUSPENDED;
+
+ /* Cleanup current event */
+ d_pkg->completion_status = EFAULT;
+ d_pkg->completion_state = SE_COMPLETE;
+ (void) sema_post(
+ d_pkg->completion_sema);
+
+ /*
+ * Drain the remaining events from the
+ * queue.
+ */
+ drain_eventq(scp, EINVAL);
+ (void) mutex_unlock(&scp->client_lock);
+ return;
+ }
+
+ /* Event delivery retry requested */
+ if (fini_pending || error != EAGAIN) {
+ break;
+ } else {
+ (void) sleep(SE_RETRY_TIME);
+ }
+ }
+
+ (void) mutex_lock(&scp->client_lock);
+ d_pkg->completion_status = error;
+ d_pkg->completion_state = SE_COMPLETE;
+ (void) sema_post(d_pkg->completion_sema);
+
+ /* Update eventq pointer */
+ if (scp->eventq != NULL) {
+ scp->eventq = eventq->next;
+ free(eventq);
+ eventq = scp->eventq;
+ } else {
+ free(eventq);
+ break;
+ }
+
+ syseventd_print(3, "Completed delivery with "
+ "error %d\n", error);
+ }
+
+ syseventd_print(3, "No more events to process for client %d\n",
+ scp->client_num);
+
+ /* Return if this was a synchronous delivery */
+ if (!SE_CLIENT_IS_THR_RUNNING(scp)) {
+ (void) mutex_unlock(&scp->client_lock);
+ return;
+ }
+
+ }
+}
+
+/*
+ * client_deliver_event - Client specific event delivery
+ * This routine will allocate and initialize the
+ * neccessary per-client dispatch data.
+ *
+ * If the eventq is not empty, it may be assumed that
+ * a delivery thread exists for this client and the
+ * dispatch data is appended to the eventq.
+ *
+ * The dispatch package is freed by the event completion
+ * thread (event_completion_thr) and the eventq entry
+ * is freed by the event delivery thread.
+ */
+static struct event_dispatch_pkg *
+client_deliver_event(struct sysevent_client *scp, sysevent_t *ev,
+ sema_t *completion_sema)
+{
+ size_t ev_sz = sysevent_get_size(ev);
+ struct event_dispatchq *newq, *tmp;
+ struct event_dispatch_pkg *d_pkg;
+
+ syseventd_print(3, "client_deliver_event: id 0x%llx size %d\n",
+ (longlong_t)sysevent_get_seq(ev), ev_sz);
+ if (debug_level == 9) {
+ se_print(stdout, ev);
+ }
+
+ /*
+ * Check for suspended client
+ */
+ (void) mutex_lock(&scp->client_lock);
+ if (SE_CLIENT_IS_SUSPENDED(scp) || !SE_CLIENT_IS_THR_RUNNING(scp)) {
+ (void) mutex_unlock(&scp->client_lock);
+ return (NULL);
+ }
+
+ /*
+ * Allocate a new dispatch package and eventq entry
+ */
+ newq = (struct event_dispatchq *)malloc(
+ sizeof (struct event_dispatchq));
+ if (newq == NULL) {
+ (void) mutex_unlock(&scp->client_lock);
+ return (NULL);
+ }
+
+ d_pkg = (struct event_dispatch_pkg *)malloc(
+ sizeof (struct event_dispatch_pkg));
+ if (d_pkg == NULL) {
+ free(newq);
+ (void) mutex_unlock(&scp->client_lock);
+ return (NULL);
+ }
+
+ /* Initialize the dispatch package */
+ d_pkg->scp = scp;
+ d_pkg->retry_count = 0;
+ d_pkg->completion_status = 0;
+ d_pkg->completion_state = SE_NOT_DISPATCHED;
+ d_pkg->completion_sema = completion_sema;
+ d_pkg->ev = ev;
+ newq->d_pkg = d_pkg;
+ newq->next = NULL;
+
+ if (scp->eventq != NULL) {
+
+ /* Add entry to the end of the eventq */
+ tmp = scp->eventq;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = newq;
+ } else {
+ /* event queue empty, wakeup delivery thread */
+ scp->eventq = newq;
+ (void) cond_signal(&scp->client_cv);
+ }
+ (void) mutex_unlock(&scp->client_lock);
+
+ return (d_pkg);
+}
+
+/*
+ * event_completion_thr - Event completion thread. This thread routine
+ * waits for all client delivery thread to complete
+ * delivery of a particular event.
+ */
+static void
+event_completion_thr()
+{
+ int ret, i, client_count, ok_to_free;
+ sysevent_id_t eid;
+ struct sysevent_client *scp;
+ struct ev_completion *ev_comp;
+ struct event_dispatchq *dispatchq;
+ struct event_dispatch_pkg *d_pkg;
+
+ (void) mutex_lock(&ev_comp_lock);
+ for (;;) {
+ while (event_compq == NULL) {
+ (void) cond_wait(&event_comp_cv, &ev_comp_lock);
+ }
+
+ /*
+ * Process event completions from the head of the
+ * completion queue
+ */
+ ev_comp = event_compq;
+ while (ev_comp) {
+ (void) mutex_unlock(&ev_comp_lock);
+ eid.eid_seq = sysevent_get_seq(ev_comp->ev);
+ sysevent_get_time(ev_comp->ev, &eid.eid_ts);
+ client_count = ev_comp->client_count;
+ ok_to_free = 1;
+
+ syseventd_print(3, "Wait for event completion of "
+ "event 0X%llx on %d clients\n",
+ eid.eid_seq, client_count);
+
+ while (client_count) {
+ syseventd_print(9, "Waiting for %d clients on "
+ "event id 0X%llx\n", client_count,
+ eid.eid_seq);
+
+ (void) sema_wait(&ev_comp->client_sema);
+ --client_count;
+ }
+
+ syseventd_print(3, "Cleaning up clients for event "
+ "0X%llx\n", eid.eid_seq);
+ dispatchq = ev_comp->dispatch_list;
+ while (dispatchq != NULL) {
+ d_pkg = dispatchq->d_pkg;
+ scp = d_pkg->scp;
+
+ if (d_pkg->completion_status == EAGAIN)
+ ok_to_free = 0;
+
+ syseventd_print(4, "Delivery of 0X%llx "
+ "complete for client %d retry count %d "
+ "status %d\n", eid.eid_seq,
+ scp->client_num,
+ d_pkg->retry_count,
+ d_pkg->completion_status);
+
+ free(d_pkg);
+ ev_comp->dispatch_list = dispatchq->next;
+ free(dispatchq);
+ dispatchq = ev_comp->dispatch_list;
+ }
+
+ if (ok_to_free) {
+ for (i = 0; i < MAX_MODCTL_RETRY; ++i) {
+ if ((ret = modctl(MODEVENTS,
+ (uintptr_t)MODEVENTS_FREEDATA,
+ (uintptr_t)&eid, NULL,
+ NULL, 0)) != 0) {
+ syseventd_print(1, "attempting "
+ "to free event 0X%llx\n",
+ eid.eid_seq);
+
+ /*
+ * Kernel may need time to
+ * move this event buffer to
+ * the sysevent sent queue
+ */
+ (void) sleep(1);
+ } else {
+ break;
+ }
+ }
+ if (ret) {
+ syseventd_print(1, "Unable to free "
+ "event 0X%llx from the "
+ "kernel\n", eid.eid_seq);
+ }
+ } else {
+ syseventd_print(1, "Not freeing event 0X%llx\n",
+ eid.eid_seq);
+ }
+
+ syseventd_print(2, "Event delivery complete for id "
+ "0X%llx\n", eid.eid_seq);
+
+ (void) mutex_lock(&ev_comp_lock);
+ event_compq = ev_comp->next;
+ free(ev_comp->ev);
+ free(ev_comp);
+ ev_comp = event_compq;
+ (void) sema_post(&sema_resource);
+ }
+
+ /*
+ * Event completion queue is empty, signal possible unload
+ * operation
+ */
+ (void) cond_signal(&event_comp_cv);
+
+ syseventd_print(3, "No more events\n");
+ }
+}
+
+/*
+ * dispatch - Dispatch the current event buffer to all valid SLM clients.
+ */
+static int
+dispatch(void)
+{
+ int ev_sz, i, client_count = 0;
+ sysevent_t *new_ev;
+ sysevent_id_t eid;
+ struct ev_completion *ev_comp, *tmp;
+ struct event_dispatchq *dispatchq, *client_list;
+ struct event_dispatch_pkg *d_pkg;
+
+ /* Check for module unload operation */
+ if (rw_tryrdlock(&mod_unload_lock) != 0) {
+ syseventd_print(2, "unload in progress abort delivery\n");
+ (void) sema_post(&sema_eventbuf);
+ (void) sema_post(&sema_resource);
+ return (0);
+ }
+
+ syseventd_print(3, "deliver dispatch buffer %d", dispatch_buf);
+ eid.eid_seq = sysevent_get_seq(eventbuf[dispatch_buf]);
+ sysevent_get_time(eventbuf[dispatch_buf], &eid.eid_ts);
+ syseventd_print(3, "deliver msg id: 0x%llx\n", eid.eid_seq);
+
+ /*
+ * ev_comp is used to hold event completion data. It is freed
+ * by the event completion thread (event_completion_thr).
+ */
+ ev_comp = (struct ev_completion *)
+ malloc(sizeof (struct ev_completion));
+ if (ev_comp == NULL) {
+ (void) rw_unlock(&mod_unload_lock);
+ syseventd_print(1, "Can not allocate event completion buffer "
+ "for event id 0X%llx\n", eid.eid_seq);
+ return (EAGAIN);
+ }
+ ev_comp->dispatch_list = NULL;
+ ev_comp->next = NULL;
+ (void) sema_init(&ev_comp->client_sema, 0, USYNC_THREAD, NULL);
+
+ ev_sz = sysevent_get_size(eventbuf[dispatch_buf]);
+ new_ev = calloc(1, ev_sz);
+ if (new_ev == NULL) {
+ free(ev_comp);
+ (void) rw_unlock(&mod_unload_lock);
+ syseventd_print(1, "Can not allocate new event buffer "
+ "for event id 0X%llx\n", eid.eid_seq);
+ return (EAGAIN);
+ }
+
+
+ /*
+ * For long messages, copy additional data from kernel
+ */
+ if (ev_sz > LOGEVENT_BUFSIZE) {
+ int ret = 0;
+
+ /* Ok to release eventbuf for next event buffer from kernel */
+ (void) sema_post(&sema_eventbuf);
+
+ for (i = 0; i < MAX_MODCTL_RETRY; ++i) {
+ if ((ret = modctl(MODEVENTS,
+ (uintptr_t)MODEVENTS_GETDATA,
+ (uintptr_t)&eid,
+ (uintptr_t)ev_sz,
+ (uintptr_t)new_ev, 0))
+ == 0)
+ break;
+ else
+ (void) sleep(1);
+ }
+ if (ret) {
+ syseventd_print(1, "GET_DATA failed for 0X%llx:%llx\n",
+ eid.eid_ts, eid.eid_seq);
+ free(new_ev);
+ free(ev_comp);
+ (void) rw_unlock(&mod_unload_lock);
+ return (EAGAIN);
+ }
+ } else {
+ (void) bcopy(eventbuf[dispatch_buf], new_ev, ev_sz);
+ /* Ok to release eventbuf for next event buffer from kernel */
+ (void) sema_post(&sema_eventbuf);
+ }
+
+
+ /*
+ * Deliver a copy of eventbuf to clients so
+ * eventbuf can be used for the next message
+ */
+ for (i = 0; i < MAX_SLM; ++i) {
+
+ /* Don't bother for suspended or unloaded clients */
+ if (!SE_CLIENT_IS_LOADED(sysevent_client_tbl[i]) ||
+ SE_CLIENT_IS_SUSPENDED(sysevent_client_tbl[i]))
+ continue;
+
+ /*
+ * Allocate event dispatch queue entry. All queue entries
+ * are freed by the event completion thread as client
+ * delivery completes.
+ */
+ dispatchq = (struct event_dispatchq *)malloc(
+ sizeof (struct event_dispatchq));
+ if (dispatchq == NULL) {
+ syseventd_print(1, "Can not allocate dispatch q "
+ "for event id 0X%llx client %d\n", eid.eid_seq, i);
+ continue;
+ }
+ dispatchq->next = NULL;
+
+ /* Initiate client delivery */
+ d_pkg = client_deliver_event(sysevent_client_tbl[i],
+ new_ev, &ev_comp->client_sema);
+ if (d_pkg == NULL) {
+ syseventd_print(1, "Can not allocate dispatch "
+ "package for event id 0X%llx client %d\n",
+ eid.eid_seq, i);
+ free(dispatchq);
+ continue;
+ }
+ dispatchq->d_pkg = d_pkg;
+ ++client_count;
+
+ if (ev_comp->dispatch_list == NULL) {
+ ev_comp->dispatch_list = dispatchq;
+ client_list = dispatchq;
+ } else {
+ client_list->next = dispatchq;
+ client_list = client_list->next;
+ }
+ }
+
+ ev_comp->client_count = client_count;
+ ev_comp->ev = new_ev;
+
+ (void) mutex_lock(&ev_comp_lock);
+
+ if (event_compq == NULL) {
+ syseventd_print(3, "Wakeup event completion thread for "
+ "id 0X%llx\n", eid.eid_seq);
+ event_compq = ev_comp;
+ (void) cond_signal(&event_comp_cv);
+ } else {
+
+ /* Add entry to the end of the event completion queue */
+ tmp = event_compq;
+ while (tmp->next != NULL)
+ tmp = tmp->next;
+ tmp->next = ev_comp;
+ syseventd_print(3, "event added to completion queue for "
+ "id 0X%llx\n", eid.eid_seq);
+ }
+ (void) mutex_unlock(&ev_comp_lock);
+ (void) rw_unlock(&mod_unload_lock);
+
+ return (0);
+}
+
+#define MODULE_DIR_HW "/usr/platform/%s/lib/sysevent/modules/"
+#define MODULE_DIR_GEN "/usr/lib/sysevent/modules/"
+#define MOD_DIR_NUM 3
+static char dirname[MOD_DIR_NUM][MAXPATHLEN];
+
+static char *
+dir_num2name(int dirnum)
+{
+ char infobuf[MAXPATHLEN];
+
+ if (dirnum >= MOD_DIR_NUM)
+ return (NULL);
+
+ if (dirname[0][0] == '\0') {
+ if (sysinfo(SI_PLATFORM, infobuf, MAXPATHLEN) == -1) {
+ syseventd_print(1, "dir_num2name: "
+ "sysinfo error %s\n", strerror(errno));
+ return (NULL);
+ } else if (snprintf(dirname[0], sizeof (dirname[0]),
+ MODULE_DIR_HW, infobuf) >= sizeof (dirname[0])) {
+ syseventd_print(1, "dir_num2name: "
+ "platform name too long: %s\n",
+ infobuf);
+ return (NULL);
+ }
+ if (sysinfo(SI_MACHINE, infobuf, MAXPATHLEN) == -1) {
+ syseventd_print(1, "dir_num2name: "
+ "sysinfo error %s\n", strerror(errno));
+ return (NULL);
+ } else if (snprintf(dirname[1], sizeof (dirname[1]),
+ MODULE_DIR_HW, infobuf) >= sizeof (dirname[1])) {
+ syseventd_print(1, "dir_num2name: "
+ "machine name too long: %s\n",
+ infobuf);
+ return (NULL);
+ }
+ (void) strcpy(dirname[2], MODULE_DIR_GEN);
+ }
+
+ return (dirname[dirnum]);
+}
+
+
+/*
+ * load_modules - Load modules found in the common syseventd module directories
+ * Modules that do not provide valid interfaces are rejected.
+ */
+static void
+load_modules(char *dirname)
+{
+ int client_id;
+ DIR *mod_dir;
+ module_t *mod;
+ struct dirent *retp, *entp;
+ struct slm_mod_ops *mod_ops;
+ struct sysevent_client *scp;
+
+ if (dirname == NULL)
+ return;
+
+ /* Return silently if module directory does not exist */
+ if ((mod_dir = opendir(dirname)) == NULL) {
+ syseventd_print(1, "Unable to open module directory %s: %s\n",
+ dirname, strerror(errno));
+ return;
+ }
+
+ syseventd_print(3, "loading modules from %s\n", dirname);
+
+ entp = malloc(PATH_MAX + 1 + sizeof (struct dirent));
+ if (entp == NULL) {
+ syseventd_err_print(LOAD_MOD_ALLOC_ERR, "entp",
+ strerror(errno));
+ (void) closedir(mod_dir);
+ return;
+ }
+
+ /*
+ * Go through directory, looking for files ending with .so
+ */
+ while (readdir_r(mod_dir, entp, &retp) == 0) {
+ void *dlh, *f;
+ char *tmp, modpath[MAXPATHLEN];
+
+ if (retp == NULL) {
+ break;
+ }
+
+ if (((tmp = strstr(entp->d_name, MODULE_SUFFIX)) == NULL) ||
+ (tmp[strlen(MODULE_SUFFIX)] != '\0')) {
+ continue;
+ }
+
+ if (snprintf(modpath, sizeof (modpath), "%s%s",
+ dirname, entp->d_name) >= sizeof (modpath)) {
+ syseventd_err_print(INIT_PATH_ERR, modpath);
+ continue;
+ }
+ if ((dlh = dlopen(modpath, RTLD_LAZY)) == NULL) {
+ syseventd_err_print(LOAD_MOD_DLOPEN_ERR,
+ modpath, dlerror());
+ continue;
+ } else if ((f = dlsym(dlh, EVENT_INIT)) == NULL) {
+ syseventd_err_print(LOAD_MOD_NO_INIT,
+ modpath, dlerror());
+ (void) dlclose(dlh);
+ continue;
+ }
+
+ mod = malloc(sizeof (*mod));
+ if (mod == NULL) {
+ syseventd_err_print(LOAD_MOD_ALLOC_ERR, "mod",
+ strerror(errno));
+ (void) dlclose(dlh);
+ continue;
+ }
+
+ mod->name = strdup(entp->d_name);
+ if (mod->name == NULL) {
+ syseventd_err_print(LOAD_MOD_ALLOC_ERR, "mod->name",
+ strerror(errno));
+ (void) dlclose(dlh);
+ free(mod);
+ continue;
+ }
+
+ mod->dlhandle = dlh;
+ mod->event_mod_init = (struct slm_mod_ops *(*)())f;
+
+ /* load in other module functions */
+ mod->event_mod_fini = (void (*)())dlsym(dlh, EVENT_FINI);
+ if (mod->event_mod_fini == NULL) {
+ syseventd_err_print(LOAD_MOD_DLSYM_ERR, mod->name,
+ dlerror());
+ free(mod->name);
+ free(mod);
+ (void) dlclose(dlh);
+ continue;
+ }
+
+ /* Call module init routine */
+ if ((mod_ops = mod->event_mod_init()) == NULL) {
+ syseventd_err_print(LOAD_MOD_EINVAL, mod->name);
+ free(mod->name);
+ free(mod);
+ (void) dlclose(dlh);
+ continue;
+ }
+ if (mod_ops->major_version != SE_MAJOR_VERSION) {
+ syseventd_err_print(LOAD_MOD_VERSION_MISMATCH,
+ mod->name, SE_MAJOR_VERSION,
+ mod_ops->major_version);
+ mod->event_mod_fini();
+ free(mod->name);
+ free(mod);
+ (void) dlclose(dlh);
+ continue;
+ }
+
+ mod->deliver_event = mod_ops->deliver_event;
+ /* Add module entry to client list */
+ if ((client_id = insert_client((void *)mod, SLM_CLIENT,
+ (mod_ops->retry_limit <= SE_MAX_RETRY_LIMIT ?
+ mod_ops->retry_limit : SE_MAX_RETRY_LIMIT)))
+ < 0) {;
+ syseventd_err_print(LOAD_MOD_ALLOC_ERR, "insert_client",
+ strerror(errno));
+ mod->event_mod_fini();
+ free(mod->name);
+ free(mod);
+ (void) dlclose(dlh);
+ continue;
+ }
+
+ scp = sysevent_client_tbl[client_id];
+ ++concurrency_level;
+ (void) thr_setconcurrency(concurrency_level);
+ if (thr_create(NULL, 0,
+ (void *(*)(void *))client_deliver_event_thr,
+ (void *)scp, THR_BOUND, &scp->tid) != 0) {
+
+ syseventd_err_print(LOAD_MOD_ALLOC_ERR, "insert_client",
+ strerror(errno));
+ mod->event_mod_fini();
+ free(mod->name);
+ free(mod);
+ (void) dlclose(dlh);
+ continue;
+ }
+ scp->client_flags |= SE_CLIENT_THR_RUNNING;
+
+ syseventd_print(3, "loaded module %s\n", entp->d_name);
+ }
+
+ free(entp);
+ (void) closedir(mod_dir);
+ syseventd_print(3, "modules loaded\n");
+}
+
+/*
+ * unload_modules - modules are unloaded prior to graceful shutdown or
+ * before restarting the daemon upon receipt of
+ * SIGHUP.
+ */
+static void
+unload_modules(int sig)
+{
+ int i, count, done;
+ module_t *mod;
+ struct sysevent_client *scp;
+
+ /*
+ * unload modules that are ready, skip those that have not
+ * drained their event queues.
+ */
+ count = done = 0;
+ while (done < MAX_SLM) {
+ /* Don't wait indefinitely for unresponsive clients */
+ if (sig != SIGHUP && count > SE_TIMEOUT) {
+ break;
+ }
+
+ done = 0;
+
+ /* Shutdown clients */
+ for (i = 0; i < MAX_SLM; ++i) {
+ scp = sysevent_client_tbl[i];
+ if (mutex_trylock(&scp->client_lock) == 0) {
+ if (scp->client_type != SLM_CLIENT ||
+ scp->client_data == NULL) {
+ (void) mutex_unlock(&scp->client_lock);
+ done++;
+ continue;
+ }
+ } else {
+ syseventd_print(3, "Skipping unload of "
+ "client %d: client locked\n",
+ scp->client_num);
+ continue;
+ }
+
+ /*
+ * Drain the eventq and wait for delivery thread to
+ * cleanly exit
+ */
+ drain_eventq(scp, EAGAIN);
+ (void) cond_signal(&scp->client_cv);
+ (void) mutex_unlock(&scp->client_lock);
+ (void) thr_join(scp->tid, NULL, NULL);
+
+ /*
+ * It is now safe to unload the module
+ */
+ mod = (module_t *)scp->client_data;
+ syseventd_print(2, "Unload %s\n", mod->name);
+ mod->event_mod_fini();
+ (void) dlclose(mod->dlhandle);
+ free(mod->name);
+ (void) mutex_lock(&client_tbl_lock);
+ delete_client(i);
+ (void) mutex_unlock(&client_tbl_lock);
+ ++done;
+
+ }
+ ++count;
+ (void) sleep(1);
+ }
+
+ /*
+ * Wait for event completions
+ */
+ syseventd_print(2, "waiting for event completions\n");
+ (void) mutex_lock(&ev_comp_lock);
+ while (event_compq != NULL) {
+ (void) cond_wait(&event_comp_cv, &ev_comp_lock);
+ }
+ (void) mutex_unlock(&ev_comp_lock);
+}
+
+/*
+ * syseventd_init - Called at daemon (re)start-up time to load modules
+ * and kickstart the kernel delivery engine.
+ */
+static void
+syseventd_init()
+{
+ int i, fd;
+ char local_door_file[PATH_MAX + 1];
+
+ fini_pending = 0;
+
+ concurrency_level = MIN_CONCURRENCY_LEVEL;
+ (void) thr_setconcurrency(concurrency_level);
+
+ /*
+ * Load client modules for event delivering
+ */
+ for (i = 0; i < MOD_DIR_NUM; ++i) {
+ load_modules(dir_num2name(i));
+ }
+
+ /*
+ * Create kernel delivery door service
+ */
+ syseventd_print(8, "Create a door for kernel upcalls\n");
+ if (snprintf(local_door_file, sizeof (local_door_file), "%s%s",
+ root_dir, LOGEVENT_DOOR_UPCALL) >= sizeof (local_door_file)) {
+ syseventd_err_print(INIT_PATH_ERR, local_door_file);
+ syseventd_exit(5);
+ }
+
+ /*
+ * Remove door file for robustness.
+ */
+ if (unlink(local_door_file) != 0)
+ syseventd_print(8, "Unlink of %s failed.\n", local_door_file);
+
+ fd = open(local_door_file, O_CREAT|O_RDWR, S_IREAD|S_IWRITE);
+ if ((fd == -1) && (errno != EEXIST)) {
+ syseventd_err_print(INIT_OPEN_DOOR_ERR, strerror(errno));
+ syseventd_exit(5);
+ }
+ (void) close(fd);
+
+ upcall_door = door_create(door_upcall, NULL,
+ DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
+ if (upcall_door == -1) {
+ syseventd_err_print(INIT_CREATE_DOOR_ERR, strerror(errno));
+ syseventd_exit(5);
+ }
+
+ (void) fdetach(local_door_file);
+retry:
+ if (fattach(upcall_door, local_door_file) != 0) {
+ if (errno == EBUSY)
+ goto retry;
+ syseventd_err_print(INIT_FATTACH_ERR, strerror(errno));
+ (void) door_revoke(upcall_door);
+ syseventd_exit(5);
+ }
+
+ /*
+ * Tell kernel the door name and start delivery
+ */
+ syseventd_print(2,
+ "local_door_file = %s\n", local_door_file);
+ if (modctl(MODEVENTS,
+ (uintptr_t)MODEVENTS_SET_DOOR_UPCALL_FILENAME,
+ (uintptr_t)local_door_file, NULL, NULL, 0) < 0) {
+ syseventd_err_print(INIT_DOOR_NAME_ERR, strerror(errno));
+ syseventd_exit(6);
+ }
+
+ door_upcall_retval = 0;
+
+ if (modctl(MODEVENTS, (uintptr_t)MODEVENTS_FLUSH, NULL, NULL, NULL, 0)
+ < 0) {
+ syseventd_err_print(KERNEL_REPLAY_ERR, strerror(errno));
+ syseventd_exit(7);
+ }
+}
+
+/*
+ * syseventd_fini - shut down daemon, but do not exit
+ */
+static void
+syseventd_fini(int sig)
+{
+ /*
+ * Indicate that event queues should be drained and no
+ * additional events be accepted
+ */
+ fini_pending = 1;
+
+ /* Close the kernel event door to halt delivery */
+ (void) door_revoke(upcall_door);
+
+ syseventd_print(1, "Unloading modules\n");
+ (void) rw_wrlock(&mod_unload_lock);
+ unload_modules(sig);
+ (void) rw_unlock(&mod_unload_lock);
+
+}
+
+/*
+ * enter_daemon_lock - lock the daemon file lock
+ *
+ * Use an advisory lock to ensure that only one daemon process is active
+ * in the system at any point in time. If the lock is held by another
+ * process, do not block but return the pid owner of the lock to the
+ * caller immediately. The lock is cleared if the holding daemon process
+ * exits for any reason even if the lock file remains, so the daemon can
+ * be restarted if necessary. The lock file is DAEMON_LOCK_FILE.
+ */
+static pid_t
+enter_daemon_lock(void)
+{
+ struct flock lock;
+
+ syseventd_print(8, "enter_daemon_lock: lock file = %s\n",
+ DAEMON_LOCK_FILE);
+
+ if (snprintf(local_lock_file, sizeof (local_lock_file), "%s%s",
+ root_dir, DAEMON_LOCK_FILE) >= sizeof (local_lock_file)) {
+ syseventd_err_print(INIT_PATH_ERR, local_lock_file);
+ syseventd_exit(8);
+ }
+ daemon_lock_fd = open(local_lock_file, O_CREAT|O_RDWR, 0644);
+ if (daemon_lock_fd < 0) {
+ syseventd_err_print(INIT_LOCK_OPEN_ERR,
+ local_lock_file, strerror(errno));
+ syseventd_exit(8);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+
+ if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
+ if (fcntl(daemon_lock_fd, F_GETLK, &lock) == -1) {
+ syseventd_err_print(INIT_LOCK_ERR,
+ local_lock_file, strerror(errno));
+ exit(2);
+ }
+ return (lock.l_pid);
+ }
+ hold_daemon_lock = 1;
+
+ return (getpid());
+}
+
+/*
+ * exit_daemon_lock - release the daemon file lock
+ */
+static void
+exit_daemon_lock(void)
+{
+ struct flock lock;
+
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+
+ if (fcntl(daemon_lock_fd, F_SETLK, &lock) == -1) {
+ syseventd_err_print(INIT_UNLOCK_ERR,
+ local_lock_file, strerror(errno));
+ }
+
+ if (close(daemon_lock_fd) == -1) {
+ syseventd_err_print(INIT_LOCK_CLOSE_ERR,
+ local_lock_file, strerror(errno));
+ exit(-1);
+ }
+}
+
+/*
+ * syseventd_err_print - print error messages to the terminal if not
+ * yet daemonized or to syslog.
+ */
+/*PRINTFLIKE1*/
+void
+syseventd_err_print(char *message, ...)
+{
+ va_list ap;
+
+ (void) mutex_lock(&err_mutex);
+ va_start(ap, message);
+
+ if (logflag) {
+ (void) vsyslog(LOG_ERR, message, ap);
+ } else {
+ (void) fprintf(stderr, "%s: ", prog);
+ (void) vfprintf(stderr, message, ap);
+ }
+ va_end(ap);
+ (void) mutex_unlock(&err_mutex);
+}
+
+/*
+ * syseventd_print - print messages to the terminal or to syslog
+ * the following levels are implemented:
+ *
+ * 1 - transient errors that does not affect normal program flow
+ * 2 - upcall/dispatch interaction
+ * 3 - program flow trace as each message goes through the daemon
+ * 8 - all the nit-gritty details of startup and shutdown
+ * 9 - very verbose event flow tracing (no daemonization of syseventd)
+ *
+ */
+/*PRINTFLIKE2*/
+void
+syseventd_print(int level, char *message, ...)
+{
+ va_list ap;
+ static int newline = 1;
+
+ if (level > debug_level) {
+ return;
+ }
+
+ (void) mutex_lock(&err_mutex);
+ va_start(ap, message);
+ if (logflag) {
+ (void) syslog(LOG_DEBUG, "%s[%ld]: ",
+ prog, getpid());
+ (void) vsyslog(LOG_DEBUG, message, ap);
+ } else {
+ if (newline) {
+ (void) fprintf(stdout, "%s[%ld]: ",
+ prog, getpid());
+ (void) vfprintf(stdout, message, ap);
+ } else {
+ (void) vfprintf(stdout, message, ap);
+ }
+ }
+ if (message[strlen(message)-1] == '\n') {
+ newline = 1;
+ } else {
+ newline = 0;
+ }
+ va_end(ap);
+ (void) mutex_unlock(&err_mutex);
+}
diff --git a/usr/src/cmd/syseventd/daemons/syseventd/syseventd.h b/usr/src/cmd/syseventd/daemons/syseventd/syseventd.h
new file mode 100644
index 0000000000..43abb572ca
--- /dev/null
+++ b/usr/src/cmd/syseventd/daemons/syseventd/syseventd.h
@@ -0,0 +1,139 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SYSEVENTD_H
+#define _SYSEVENTD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAX_SLM 20 /* Max sysevent loadable modules */
+#define MAX_MODCTL_RETRY 3 /* Maximum number of modctl retries */
+ /* for free and get data */
+#define LOGEVENT_BUFSIZE 1024 /* message size greater than this */
+ /* requires another kernel trip */
+
+/* Debug and errlogging stuff */
+extern void syseventd_print(int level, char *message, ...);
+extern void syseventd_err_print(char *message, ...);
+extern void syseventd_exit(int status);
+extern int debug_level;
+
+/*
+ * struct event_dispatch_pkg - per-client event dispatch package
+ */
+struct event_dispatch_pkg {
+ hrtime_t start_time; /* start time ev delivery */
+ struct sysevent_client *scp; /* Client data */
+ sysevent_t *ev; /* event buf to deliver */
+ sema_t *completion_sema;
+ int completion_state;
+ int completion_status;
+ int retry_count; /* running retry counter */
+};
+
+/* completion states */
+#define SE_NOT_DISPATCHED 0 /* Not yet dispatched to client */
+#define SE_OUTSTANDING 1 /* Dispatched and outstanding */
+#define SE_COMPLETE 2 /* Delivery complete */
+
+/*
+ * struct event_dispatchq - queue of dispatched event dispatch pacakges
+ */
+struct event_dispatchq {
+ struct event_dispatchq *next;
+ struct event_dispatch_pkg *d_pkg;
+};
+
+/*
+ * struct ev_completion - event completion data
+ * This structure contains information regarding the
+ * event delivery status for each client dispatched.
+ */
+struct ev_completion {
+ sysevent_t *ev; /* event */
+ struct ev_completion *next;
+ struct event_dispatchq *dispatch_list; /* per_client dspatch packages */
+ int client_count; /* Number of clients */
+ /* event was dispatched to. */
+ sema_t client_sema; /* Client completion */
+ /* synchronization */
+};
+
+/*
+ * module_t - SLM client module data
+ */
+typedef struct module {
+struct module *next;
+ char *name;
+ void *dlhandle;
+ int (*deliver_event)();
+ struct slm_mod_ops *(*event_mod_init)();
+ void (*event_mod_fini)();
+} module_t;
+
+/*
+ * struct sysevent_client - per-client data
+ */
+struct sysevent_client {
+ mutex_t client_lock; /* Lock for struct data */
+ cond_t client_cv; /* Deliver cond variable */
+ thread_t tid; /* Client deliver thread id */
+ int client_type; /* SLM only */
+ int client_num; /* Assigned at load time */
+ int client_flags; /* Client flags */
+ int retry_limit; /* Defined by slm_mod_ops */
+ void *client_data; /* Client-type specific data */
+ struct event_dispatchq *eventq; /* Client event queue */
+};
+
+/* Client types */
+#define SLM_CLIENT 0
+
+/* Client flags */
+#define SE_CLIENT_UNLOADED 0
+#define SE_CLIENT_LOADED 0X00000001
+#define SE_CLIENT_SUSPENDED 0X00000002
+#define SE_CLIENT_THR_RUNNING 0X00000004
+
+#define SE_CLIENT_IS_UNLOADED(scp) \
+ ((scp)->client_flags == SE_CLIENT_UNLOADED)
+#define SE_CLIENT_IS_LOADED(scp) \
+ ((scp)->client_flags & SE_CLIENT_LOADED)
+#define SE_CLIENT_IS_SUSPENDED(scp) \
+ ((scp)->client_flags & SE_CLIENT_SUSPENDED)
+#define SE_CLIENT_IS_THR_RUNNING(scp) \
+ ((scp)->client_flags & SE_CLIENT_THR_RUNNING)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYSEVENTD_H */
diff --git a/usr/src/cmd/syseventd/etc/Makefile b/usr/src/cmd/syseventd/etc/Makefile
new file mode 100644
index 0000000000..7c8941efbe
--- /dev/null
+++ b/usr/src/cmd/syseventd/etc/Makefile
@@ -0,0 +1,45 @@
+#
+# 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 (c) 2000-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/etc/Makefile
+#
+
+CONFIG_FILES= README
+
+include $(SRC)/cmd/Makefile.cmd
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all clean clobber lint _msg:
+
+install: all \
+ $(ROOTETCSYSEVENTDIR) \
+ $(ROOTETCSYSEVENTCONFIGDIR) \
+ $(ROOTETCSYSEVENTCONFIGFILES)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/syseventd/etc/README b/usr/src/cmd/syseventd/etc/README
new file mode 100644
index 0000000000..27287c08c0
--- /dev/null
+++ b/usr/src/cmd/syseventd/etc/README
@@ -0,0 +1,64 @@
+#
+# 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 (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+
+Configuration files for syseventd(1M).
+
+These files are not intended to be edited and any changes made
+will not be preserved across upgrades.
+
+The format for file names in this directory is:
+
+ <vendor>,[<publisher>,][<class>,[<subclass>,]]sysevent.conf
+
+Files with a suffix other than ',sysevent.conf' are ignored.
+
+After making a change to a file in this directory, restart the
+syseventd daemon by issuing:
+
+ pkill -HUP syseventd
+
+
+The format for each line in a sysevent.conf file is:
+
+ <class> <subclass> <vendor> <publisher> <user> <reserved1>
+ <reserved2> <path> <arguments>
+
+<class>, <subclass>, <vendor> or <publisher> may be "-" to
+match any class, subclass, vendor or publisher, respectively.
+
+<user> may be "-" for commands to be invoked as root, or a
+valid user name.
+
+<reserved1> and <reserved2> must be "-".
+
+<path> must be the path of the command to be invoked in response
+to the event, and must be executable by <user>.
+
+<arguments> are optional, and may be any arbitrary arguments
+for the command to be invoked. Macros may be used to substitute
+actual parameters from the matching event on the command line.
diff --git a/usr/src/cmd/syseventd/modules/Makefile b/usr/src/cmd/syseventd/modules/Makefile
new file mode 100644
index 0000000000..ac87308c03
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/Makefile
@@ -0,0 +1,52 @@
+#
+# 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 (c) 1998-2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/modules/Makefile
+#
+
+.PARALLEL:
+
+SUBDIRS= \
+ devfsadmd_mod \
+ sysevent_conf_mod \
+ sysevent_reg_mod
+
+all:= TARGET= all
+install:= TARGET= install
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+lint:= TARGET= lint
+_msg:= TARGET= _msg
+
+.KEEP_STATE:
+
+all install clean clobber lint _msg: $(SUBDIRS)
+
+FRC:
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
diff --git a/usr/src/cmd/syseventd/modules/Makefile.com b/usr/src/cmd/syseventd/modules/Makefile.com
new file mode 100644
index 0000000000..8720a40b2c
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/Makefile.com
@@ -0,0 +1,55 @@
+#
+# 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 (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/modules/Makefile.com
+
+#
+# Common prologue for Makefiles for all sysevent loadable .so modules
+#
+
+SRCS = $(LIBRARY:%=%.c)
+OBJECTS = $(LIBRARY:%=%.o)
+
+include $(SRC)/lib/Makefile.lib
+include $(SRC)/cmd/syseventd/Makefile.com
+
+#
+# Build modules with -Kpic, -z combreloc and -z text but not -z defs
+# Note: -K pic is inherited by including Makefile.lib
+#
+HSONAME =
+ZDEFS =
+
+#
+# sysevent loadable modules require sysevent header files
+#
+CPPFLAGS += -I ../../daemons/syseventd
+
+POFILES = $(SRCS:.c=.po)
+POFILE = $(LIBRARY).po
+
+CLOBBERFILES += $(LIBRARY)
diff --git a/usr/src/cmd/syseventd/modules/Makefile.targ b/usr/src/cmd/syseventd/modules/Makefile.targ
new file mode 100644
index 0000000000..2596b9e5d8
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/Makefile.targ
@@ -0,0 +1,40 @@
+#
+# 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"
+#
+# cmd/syseventd/modules/Makefile.targ
+#
+# Common sysevent module Makefile epilogue
+#
+
+lint: lintcheck
+
+_msg: $(MSGDOMAINPOFILE)
+
+include $(SRC)/lib/Makefile.targ
+include $(SRC)/cmd/syseventd/Makefile.targ
+
+include $(SRC)/Makefile.msg.targ
diff --git a/usr/src/cmd/syseventd/modules/devfsadmd_mod/Makefile b/usr/src/cmd/syseventd/modules/devfsadmd_mod/Makefile
new file mode 100644
index 0000000000..0385e82cc5
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/devfsadmd_mod/Makefile
@@ -0,0 +1,46 @@
+#
+# 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 (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/modules/devfsadmd_mod/Makefile
+#
+
+LIBRARY = devfsadmd_mod
+
+include ../Makefile.com
+
+CPPFLAGS += -I $(SRC)/cmd/devfsadm
+
+.KEEP_STATE:
+
+all: $(DYNLIB)
+
+install: all \
+ $(ROOTLIBSYSEVENTDIR) \
+ $(ROOTLIBDIR) \
+ $(ROOTLIBS)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/syseventd/modules/devfsadmd_mod/devfsadmd_mod.c b/usr/src/cmd/syseventd/modules/devfsadmd_mod/devfsadmd_mod.c
new file mode 100644
index 0000000000..aea2021bde
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/devfsadmd_mod/devfsadmd_mod.c
@@ -0,0 +1,351 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <thread.h>
+#include <wait.h>
+#include <synch.h>
+#include <syslog.h>
+#include <libintl.h>
+#include <sys/stat.h>
+#include <sys/sunddi.h>
+
+#include <libsysevent.h>
+
+#include "sysevent_signal.h"
+#include "../devfsadm/devfsadm.h"
+
+/*
+ * SLM for devfsadmd device configuration daemon
+ */
+
+extern char *root_dir;
+extern void syseventd_print();
+
+sysevent_handle_t *sysevent_hp;
+
+/* Alternate root declarations during install */
+static int use_alt_root = 0;
+
+static int devfsadmdeliver_event(sysevent_t *ev, int flag);
+
+static struct slm_mod_ops devfsadm_mod_ops = {
+ SE_MAJOR_VERSION, SE_MINOR_VERSION, 10, devfsadmdeliver_event};
+
+typedef struct ev_queue {
+ struct ev_queue *evq_next;
+ sysevent_t *evq_ev;
+} ev_queue_t;
+
+static mutex_t evq_lock;
+static cond_t evq_cv;
+static ev_queue_t *eventq_head;
+static ev_queue_t *eventq_tail;
+
+#define DELIVER_FAILED gettext("/devices or /dev may not be current \
+ (devfsadmd not responding) Run devfsadm")
+#define RETRY_MAX 3
+
+static int
+system1(const char *s_path, const char *s)
+{
+ struct sigaction cbuf, ibuf, qbuf, ignore, dfl;
+ sigset_t mask, savemask;
+ struct stat st;
+ pid_t pid;
+ int status, w;
+
+ /* Check the requested command */
+ if (s == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Check the ability to execute devfsadmd from this process */
+ if (stat(s_path, &st) < 0) {
+ return (-1);
+ }
+ if (((geteuid() == st.st_uid) && ((st.st_mode & S_IXUSR) == 0)) ||
+ ((getegid() == st.st_gid) && ((st.st_mode & S_IXGRP) == 0)) ||
+ ((st.st_mode & S_IXOTH) == 0)) {
+ errno = EPERM;
+ return (-1);
+ }
+
+ /*
+ * Block SIGCHLD and set up a default handler for the duration of the
+ * system1 call.
+ */
+ (void) sigemptyset(&mask);
+ (void) sigaddset(&mask, SIGCHLD);
+ (void) sigprocmask(SIG_BLOCK, &mask, &savemask);
+ (void) memset(&dfl, 0, sizeof (dfl));
+ dfl.sa_handler = SIG_DFL;
+ (void) sigaction(SIGCHLD, &dfl, &cbuf);
+
+ /* Fork off the child process (using fork1(), because it's MT-safe) */
+ switch (pid = fork1()) {
+ case -1:
+ /* Error */
+ (void) sigaction(SIGCHLD, &cbuf, NULL);
+ (void) sigprocmask(SIG_SETMASK, &savemask, NULL);
+ return (-1);
+ case 0:
+ /* Set-up an initial signal mask for the child */
+ (void) sigemptyset(&mask);
+ (void) sigprocmask(SIG_SETMASK, &mask, NULL);
+ (void) execl(s_path, s, (char *)0);
+ _exit(-1);
+ break;
+ default:
+ /* Parent */
+ break;
+ }
+
+ (void) memset(&ignore, 0, sizeof (ignore));
+ ignore.sa_handler = SIG_IGN;
+ (void) sigaction(SIGINT, &ignore, &ibuf);
+ (void) sigaction(SIGQUIT, &ignore, &qbuf);
+
+ do {
+ w = waitpid(pid, &status, 0);
+ } while (w == -1 && errno == EINTR);
+
+ (void) sigaction(SIGINT, &ibuf, NULL);
+ (void) sigaction(SIGQUIT, &qbuf, NULL);
+ (void) sigaction(SIGCHLD, &cbuf, NULL);
+ (void) sigprocmask(SIG_SETMASK, &savemask, NULL);
+
+ return ((w == -1)? w: status);
+}
+
+/*
+ * devfsadmdeliver_event - called by syseventd to deliver an event buffer.
+ * The event buffer is subsequently delivered to
+ * devfsadmd. If devfsadmd, is not responding to the
+ * delivery attempt, we will try to startup the
+ * daemon. MT protection is provided by syseventd
+ * and the client lock. This insures sequential
+ * event delivery and protection from re-entrance.
+ */
+/*ARGSUSED*/
+static int
+devfsadmdeliver_event(sysevent_t *ev, int flag)
+{
+ int ev_size;
+ ev_queue_t *new_evq;
+
+ /* Not initialized */
+ if (sysevent_hp == NULL) {
+ return (0);
+ }
+
+ /* Quick return for uninteresting events */
+ if (strcmp(sysevent_get_class_name(ev), EC_DEVFS) != 0) {
+ return (0);
+ }
+
+ /* Queue event for delivery to devfsadmd */
+ new_evq = (ev_queue_t *)calloc(1, sizeof (ev_queue_t));
+ if (new_evq == NULL) {
+ return (EAGAIN);
+ }
+
+ ev_size = sysevent_get_size(ev);
+ new_evq->evq_ev = (sysevent_t *)malloc(ev_size);
+ if (new_evq->evq_ev == NULL) {
+ free(new_evq);
+ return (EAGAIN);
+ }
+ bcopy(ev, new_evq->evq_ev, ev_size);
+
+ (void) mutex_lock(&evq_lock);
+ if (eventq_head == NULL) {
+ eventq_head = new_evq;
+ } else {
+ eventq_tail->evq_next = new_evq;
+ }
+ eventq_tail = new_evq;
+
+ (void) cond_signal(&evq_cv);
+ (void) mutex_unlock(&evq_lock);
+
+ return (0);
+}
+
+static int cleanup;
+thread_t deliver_thr_id;
+
+void
+devfsadmd_deliver_thr()
+{
+ int retry;
+ ev_queue_t *evqp;
+
+ (void) mutex_lock(&evq_lock);
+ for (;;) {
+ while (eventq_head == NULL) {
+ (void) cond_wait(&evq_cv, &evq_lock);
+ if (cleanup && eventq_head == NULL) {
+ (void) cond_signal(&evq_cv);
+ (void) mutex_unlock(&evq_lock);
+ return;
+ }
+ }
+
+ /* Send events on to devfsadmd */
+ evqp = eventq_head;
+ while (evqp) {
+ (void) mutex_unlock(&evq_lock);
+ retry = 0;
+ while (sysevent_send_event(sysevent_hp,
+ evqp->evq_ev) != 0) {
+ /*
+ * If we are using an alternate root, devfsadm
+ * is run to handle node creation.
+ */
+ if (use_alt_root == 0) {
+ /*
+ * daemon unresponsive -
+ * restart daemon and retry once more
+ * if not installing
+ */
+ if (errno == EBADF || errno == ENOENT) {
+ if (system1(
+ DEVFSADMD_START_PATH,
+ DEVFSADMD_START) != -1) {
+ (void) sleep(1);
+ }
+ }
+ ++retry;
+ if (retry < RETRY_MAX) {
+ continue;
+ } else {
+ syslog(LOG_ERR, DELIVER_FAILED);
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ (void) mutex_lock(&evq_lock);
+ if (eventq_head != NULL) {
+ eventq_head = eventq_head->evq_next;
+ if (eventq_head == NULL)
+ eventq_tail = NULL;
+ }
+ free(evqp->evq_ev);
+ free(evqp);
+ evqp = eventq_head;
+ }
+ if (cleanup) {
+ (void) cond_signal(&evq_cv);
+ (void) mutex_unlock(&evq_lock);
+ return;
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+struct slm_mod_ops *
+slm_init()
+{
+ char alt_door[MAXPATHLEN];
+
+ if (strcmp(root_dir, "") == 0) {
+ /* Initialize the private sysevent handle */
+ sysevent_hp = sysevent_open_channel_alt(DEVFSADM_SERVICE_DOOR);
+ } else {
+
+ /* Try alternate door during install time */
+ if (snprintf(alt_door, MAXPATHLEN, "%s%s", "/tmp",
+ DEVFSADM_SERVICE_DOOR) >= MAXPATHLEN)
+ return (NULL);
+
+ sysevent_hp = sysevent_open_channel_alt(alt_door);
+ use_alt_root = 1;
+ }
+ if (sysevent_hp == NULL) {
+ syseventd_print(0, "Unable to allocate sysevent handle"
+ " for devfsadm module\n");
+ return (NULL);
+ }
+
+ if (sysevent_bind_publisher(sysevent_hp) != 0) {
+ if (errno == EBUSY) {
+ sysevent_cleanup_publishers(sysevent_hp);
+ if (sysevent_bind_publisher(sysevent_hp) != 0) {
+ (void) sysevent_close_channel(sysevent_hp);
+ return (NULL);
+ }
+ }
+ }
+
+ sysevent_cleanup_subscribers(sysevent_hp);
+ cleanup = 0;
+ eventq_head = NULL;
+ eventq_tail = NULL;
+
+ (void) mutex_init(&evq_lock, USYNC_THREAD, NULL);
+ (void) cond_init(&evq_cv, USYNC_THREAD, NULL);
+
+ if (thr_create(NULL, NULL, (void *(*)(void *))devfsadmd_deliver_thr,
+ NULL, THR_BOUND, &deliver_thr_id) != 0) {
+ (void) mutex_destroy(&evq_lock);
+ (void) cond_destroy(&evq_cv);
+ sysevent_close_channel(sysevent_hp);
+ return (NULL);
+ }
+
+ return (&devfsadm_mod_ops);
+}
+
+void
+slm_fini()
+{
+ /* Wait for all events to be flushed out to devfsadmd */
+ (void) mutex_lock(&evq_lock);
+ cleanup = 1;
+ (void) cond_signal(&evq_cv);
+ (void) cond_wait(&evq_cv, &evq_lock);
+ (void) mutex_unlock(&evq_lock);
+
+ /* Wait for delivery thread to exit */
+ (void) thr_join(deliver_thr_id, NULL, NULL);
+
+ (void) mutex_destroy(&evq_lock);
+ (void) cond_destroy(&evq_cv);
+
+ sysevent_close_channel(sysevent_hp);
+ sysevent_hp = NULL;
+}
diff --git a/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile
new file mode 100644
index 0000000000..ee2b012ff7
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/Makefile
@@ -0,0 +1,46 @@
+#
+# 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 (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/modules/sysevent_conf_mod/Makefile
+#
+
+LIBRARY = sysevent_conf_mod
+
+include ../Makefile.com
+
+CPPFLAGS += -I ../../daemons/syseventconfd
+
+.KEEP_STATE:
+
+all: $(DYNLIB)
+
+install: all \
+ $(ROOTLIBSYSEVENTDIR) \
+ $(ROOTLIBDIR) \
+ $(ROOTLIBS)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/syseventd/modules/sysevent_conf_mod/message_conf_mod.h b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/message_conf_mod.h
new file mode 100644
index 0000000000..c3111ee146
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/message_conf_mod.h
@@ -0,0 +1,148 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MESSAGE_CONF_MOD_H
+#define _MESSAGE_CONF_MOD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define OUT_OF_MEMORY_ERR \
+ gettext("Out of memory.")
+
+#define CANNOT_OPEN_ERR \
+ gettext("cannot open %s - %s\n")
+
+#define NO_USER_ERR \
+ gettext("%s, line %d: " "user '%s' not recognized\n")
+
+#define RESERVED_FIELD_ERR \
+ gettext("%s, line %d: " "reserved field '%s' must be '-'\n")
+
+#define SETREGID_ERR \
+ gettext("%s: setregid(%d) - %s\n")
+
+#define SETREUID_ERR \
+ gettext("%s: setreuid(%d)- %s\n")
+
+#define CANNOT_EXECUTE_ERR \
+ gettext("%s, line %d: no execute access to %s - %s\n")
+
+#define SYNTAX_ERR \
+ gettext("%s, line %d: syntax error\n")
+
+#define PATHCONF_ERR \
+ gettext("pathconf(%s, NAME_MAX) failed - %s\n")
+
+#define READDIR_ERR \
+ gettext("readdir(%s) failed - %s\n")
+
+#define CLOSEDIR_ERR \
+ gettext("closedir(%s) failed - %s\n")
+
+#define MACRO_UNDEF_ERR \
+ gettext("%s, line %d: macro '%s' undefined\n")
+
+#define MACRO_MULT_DEF_ERR \
+ gettext("%s, line %d: multiple definitions of macro '%s'\n")
+
+#define ATTR_VALUE_ERR \
+ gettext("%s, line %d: attribute type error for macro '%s'\n")
+
+#define ATTR_UNSUPPORTED_ERR \
+ gettext("%s, line %d: unsupported attribute type (0x%x) " \
+ "for macro '%s'\n")
+
+#define GET_ATTR_LIST_ERR \
+ gettext("%s, line %d: unable to get nvlist - %s\n")
+
+#define NVLIST_ALLOC_ERR \
+ gettext("%s, line %d: error allocating nvlist - %s\n")
+
+#define NVLIST_BUILD_ERR \
+ gettext("%s, line %d: error building nvlist - %s\n")
+
+#define SYSEVENT_ALLOC_ERR \
+ gettext("%s, line %d: error allocating event - %s\n")
+
+#define SYSEVENT_SEND_ERR \
+ gettext("%s, line %d: error sending event (%d) - " \
+ "syseventconfd not responding?\n")
+
+#define CHANNEL_OPEN_ERR \
+ gettext("unable to open channel to syseventconfd\n")
+
+#define SYSEVENTCONFD_ERR \
+ gettext("syseventconfd not responding?\n")
+
+#define SYSEVENTCONFD_OK \
+ gettext("syseventconfd ok\n")
+
+#define SYSEVENTCONFD_TRAN_ERR \
+ gettext("syseventconfd transport error - %s\n")
+
+#define SYSEVENTCONFD_START_ERR \
+ gettext("error starting syseventconfd - %s\n")
+
+#define SYSEVENTCONFD_RESTART_ERR \
+ gettext("error restarting syseventconfd - %s\n")
+
+#define THR_CREATE_ERR \
+ gettext("thread create error at init - %s\n")
+
+#define THR_JOIN_ERR \
+ gettext("thread join error at fini - %s\n")
+
+#define N_EVENTS_DISCARDED_ERR \
+ gettext("discarding %d queued events\n")
+
+#define SERVICE_DISABLED_MSG \
+ gettext("sysevent_conf_mod service disabled - " \
+ "restart with 'pkill -HUP syseventd'\n")
+
+#define MSG_LOCK_CREATE_ERR \
+ gettext("%s: error creating lock %s - %s\n")
+
+#define MSG_LOCK_SET_ERR \
+ gettext("%s: error setting lock in %s - %s\n")
+
+#define MSG_LOCK_CLR_ERR \
+ gettext("%s: error clearing lock in %s - %s\n")
+
+#define MSG_LOCK_CLOSE_ERR \
+ gettext("%s: error closing lock %s - %s\n")
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESSAGE_CONF_MOD_H */
diff --git a/usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.c b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.c
new file mode 100644
index 0000000000..7a1bdb2413
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.c
@@ -0,0 +1,2317 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * sysevent_conf_mod - syseventd daemon sysevent.conf module
+ *
+ * This module provides a configuration file registration
+ * mechanism whereby event producers can define an event
+ * specification to be matched against events, with an
+ * associated command line to be invoked for each matching event.
+ * It includes a simple macro capability for flexibility in
+ * generating arbitrary command line formats from event-associated
+ * data, and a user specification so that commands can be invoked
+ * with reduced privileges to eliminate a security risk.
+ *
+ * sysevent.conf files contain event specifications and associated
+ * command path and optional arguments. System events received
+ * from the kernel by the sysevent daemon, syseventd, are
+ * compared against the event specifications in the sysevent.conf
+ * files. The command as specified by pathname and arguments
+ * is invoked for each matching event.
+ *
+ * All sysevent.conf files reside in /etc/sysevent/config.
+ *
+ */
+
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <limits.h>
+#include <thread.h>
+#include <synch.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <pwd.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sunddi.h>
+#include <sys/sysevent.h>
+#include <libsysevent.h>
+#include <libnvpair.h>
+#include <dirent.h>
+#include <locale.h>
+#include <signal.h>
+#include <wait.h>
+
+#include "syseventd.h"
+#include "syseventconfd_door.h"
+#include "sysevent_conf_mod.h"
+#include "message_conf_mod.h"
+
+
+static char *whoami = "sysevent_conf_mod";
+
+/*
+ * Event sequencing, time stamp and retry count
+ */
+static int ev_nretries; /* retry count per event */
+static uint64_t ev_seq; /* current event sequencing number */
+static hrtime_t ev_ts; /* current event timestamp */
+static int first_event; /* first event since init */
+
+/*
+ * State of the sysevent conf table, derived from
+ * the /etc/sysevent/config files
+ */
+static conftab_t *conftab = NULL;
+static syseventtab_t *syseventtab = NULL;
+static syseventtab_t *syseventtab_tail = NULL;
+static sysevent_handle_t *confd_handle = NULL;
+
+/*
+ * The cmd queue is a queue of commands ready to be sent
+ * to syseventconfd. Each command consists of the path
+ * and arguments to be fork/exec'ed. The daemon is unable
+ * to handle events during an active fork/exec and returns
+ * EAGAIN as a result. It is grossly inefficient to bounce
+ * these events back to syseventd, so we queue them here for delivery.
+ */
+static cmdqueue_t *cmdq = NULL;
+static cmdqueue_t *cmdq_tail = NULL;
+static mutex_t cmdq_lock;
+static cond_t cmdq_cv;
+static int cmdq_cnt;
+static thread_t cmdq_thr_id;
+static cond_t cmdq_thr_cv;
+static int want_fini;
+
+/*
+ * State of the door channel to syseventconfd
+ */
+static int confd_state = CONFD_STATE_NOT_RUNNING;
+
+/*
+ * Number of times to retry event after restarting syeventconfd
+ */
+static int confd_retries;
+
+/*
+ * Number of times to retry a failed transport
+ */
+static int transport_retries;
+
+/*
+ * Normal sleep time when syseventconfd returns EAGAIN
+ * is one second but to avoid thrashing, sleep for
+ * something larger when syseventconfd not responding.
+ * This should never happen of course but it seems better
+ * to attempt to handle possible errors gracefully.
+ */
+static int confd_err_msg_emitted;
+
+
+static int sysevent_conf_dummy_event(sysevent_t *, int);
+
+/*
+ * External references
+ */
+extern int debug_level;
+extern char *root_dir;
+extern void syseventd_print(int level, char *format, ...);
+extern void syseventd_err_print(char *format, ...);
+
+
+
+static struct slm_mod_ops sysevent_conf_mod_ops = {
+ SE_MAJOR_VERSION, /* syseventd module major version */
+ SE_MINOR_VERSION, /* syseventd module minor version */
+ SE_MAX_RETRY_LIMIT, /* max retry if EAGAIN */
+ &sysevent_conf_event /* event handler */
+};
+
+static struct slm_mod_ops sysevent_conf_dummy_mod_ops = {
+ SE_MAJOR_VERSION, /* syseventd module major version */
+ SE_MINOR_VERSION, /* syseventd module minor version */
+ 0, /* no retries, always succeeds */
+ &sysevent_conf_dummy_event /* dummy event handler */
+};
+
+
+
+/*
+ * skip_spaces() - skip to next non-space character
+ */
+static char *
+skip_spaces(char **cpp)
+{
+ char *cp = *cpp;
+
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == 0) {
+ *cpp = 0;
+ return (NULL);
+ }
+ return (cp);
+}
+
+
+/*
+ * Get next white-space separated field.
+ * next_field() will not check any characters on next line.
+ * Each entry is composed of a single line.
+ */
+static char *
+next_field(char **cpp)
+{
+ char *cp = *cpp;
+ char *start;
+
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == 0) {
+ *cpp = 0;
+ return (NULL);
+ }
+ start = cp;
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != 0)
+ *cp++ = 0;
+ *cpp = cp;
+ return (start);
+}
+
+
+
+/*
+ * The following functions are simple wrappers/equivalents
+ * for malloc, realloc, free, strdup and a special free
+ * for strdup.
+ *
+ * These functions ensure that any failed mallocs are
+ * reported via syslog() so if a command is not evoked
+ * in response to an event, the reason should be logged.
+ * These functions also provide a convenient place for
+ * hooks for checking for memory leaks.
+ */
+
+static void *
+sc_malloc(size_t n)
+{
+ void *p;
+
+ p = malloc(n);
+ if (p == NULL) {
+ syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
+ }
+ return (p);
+}
+
+/*ARGSUSED*/
+static void *
+sc_realloc(void *p, size_t current, size_t n)
+{
+ p = realloc(p, n);
+ if (p == NULL) {
+ syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
+ }
+ return (p);
+}
+
+
+/*ARGSUSED*/
+static void
+sc_free(void *p, size_t n)
+{
+ free(p);
+}
+
+
+static char *
+sc_strdup(char *cp)
+{
+ char *new;
+
+ new = malloc((unsigned)(strlen(cp) + 1));
+ if (new == NULL) {
+ syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
+ return (NULL);
+ }
+ (void) strcpy(new, cp);
+ return (new);
+}
+
+
+static void
+sc_strfree(char *s)
+{
+ if (s)
+ free(s);
+}
+
+
+/*
+ * The following functions provide some simple dynamic string
+ * capability. This module has no hard-coded maximum string
+ * lengths and should be able to parse and generate arbitrarily
+ * long strings, macro expansion and command lines.
+ *
+ * Each string must be explicitly allocated and freed.
+ */
+
+/*
+ * Allocate a dynamic string, with a hint to indicate how
+ * much memory to dynamically add to the string as it grows
+ * beyond its existing bounds, so as to avoid excessive
+ * reallocs as a string grows.
+ */
+static str_t *
+initstr(int hint)
+{
+ str_t *str;
+
+ if ((str = sc_malloc(sizeof (str_t))) == NULL)
+ return (NULL);
+ str->s_str = NULL;
+ str->s_len = 0;
+ str->s_alloc = 0;
+ str->s_hint = hint;
+ return (str);
+}
+
+
+/*
+ * Free a dynamically-allocated string
+ */
+static void
+freestr(str_t *str)
+{
+ if (str->s_str) {
+ sc_free(str->s_str, str->s_alloc);
+ }
+ sc_free(str, sizeof (str_t));
+}
+
+
+/*
+ * Reset a dynamically-allocated string, allows reuse
+ * rather than freeing the old and allocating a new one.
+ */
+static void
+resetstr(str_t *str)
+{
+ str->s_len = 0;
+}
+
+
+/*
+ * Copy a (simple) string onto a dynamically-allocated string
+ */
+static int
+strcopys(str_t *str, char *s)
+{
+ char *new_str;
+ int len = strlen(s) + 1;
+
+ if (str->s_alloc < len) {
+ new_str = (str->s_str == NULL) ?
+ sc_malloc(len+str->s_hint) :
+ sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
+ if (new_str == NULL) {
+ return (1);
+ }
+ str->s_str = new_str;
+ str->s_alloc = len + str->s_hint;
+ }
+ (void) strcpy(str->s_str, s);
+ str->s_len = len - 1;
+ return (0);
+}
+
+
+/*
+ * Concatenate a (simple) string onto a dynamically-allocated string
+ */
+static int
+strcats(str_t *str, char *s)
+{
+ char *new_str;
+ int len = str->s_len + strlen(s) + 1;
+
+ if (str->s_alloc < len) {
+ new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) :
+ sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
+ if (new_str == NULL) {
+ return (1);
+ }
+ str->s_str = new_str;
+ str->s_alloc = len + str->s_hint;
+ }
+ (void) strcpy(str->s_str + str->s_len, s);
+ str->s_len = len - 1;
+ return (0);
+}
+
+
+/*
+ * Concatenate a character onto a dynamically-allocated string
+ */
+static int
+strcatc(str_t *str, int c)
+{
+ char *new_str;
+ int len = str->s_len + 2;
+
+ if (str->s_alloc < len) {
+ new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) :
+ sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
+ if (new_str == NULL) {
+ return (1);
+ }
+ str->s_str = new_str;
+ str->s_alloc = len + str->s_hint;
+ }
+ *(str->s_str + str->s_len) = (char)c;
+ *(str->s_str + str->s_len + 1) = 0;
+ str->s_len++;
+ return (0);
+}
+
+/*
+ * fgets() equivalent using a dynamically-allocated string
+ */
+static char *
+fstrgets(str_t *line, FILE *fp)
+{
+ int c;
+
+ resetstr(line);
+ while ((c = fgetc(fp)) != EOF) {
+ if (strcatc(line, c))
+ return (NULL);
+ if (c == '\n')
+ break;
+ }
+ if (line->s_len == 0)
+ return (NULL);
+ return (line->s_str);
+}
+
+/*
+ * Truncate a dynamically-allocated string at index position 'pos'
+ */
+static void
+strtrunc(str_t *str, int pos)
+{
+ if (str->s_len > pos) {
+ str->s_len = pos;
+ *(str->s_str + pos) = 0;
+ }
+}
+
+
+
+/*
+ * Parse a sysevent.conf file, adding each entry spec to the event table.
+ *
+ * The format of an entry in a sysevent.conf file is:
+ *
+ * class subclass vendor publisher user reserved1 reserved path arguments
+ *
+ * Fields are separated by either SPACE or TAB characters. A
+ * '#' (number sign) at the beginning of a line indicates a
+ * comment. Comment lines and blank lines are ignored.
+ *
+ * class
+ * The class of the event.
+ *
+ * subclass
+ * The subclass of the event.
+ *
+ * vendor
+ * The name of the vendor defining the event, usually the
+ * stock symbol. Events generated by system components
+ * provided by Sun Microsystems, Inc. always define vendor
+ * as 'SUNW'.
+ *
+ * publisher
+ * The name of the application, driver or system module
+ * producing the event.
+ *
+ * user
+ * The name of the user under which the command should be
+ * run. This allows commands to run with access privileges
+ * other than those for root. The user field should be '-'
+ * for commands to be run as root.
+ *
+ * reserved1
+ * Must be '-'.
+ *
+ * reserved2
+ * Must be '-'.
+ *
+ * path
+ * Pathname of the command to be invoked for matching events.
+ *
+ * arguments
+ * Optional argument with possible macro substitution to permit
+ * arbitrary command line construction with event-specific data.
+ */
+static void
+parse_conf_file(char *conf_file)
+{
+ char conf_path[PATH_MAX];
+ FILE *fp;
+ char *lp;
+ str_t *line;
+ int lineno = 0;
+ char *vendor, *publisher;
+ char *class, *subclass;
+ char *user;
+ char *reserved1, *reserved2;
+ char *path, *args;
+ syseventtab_t *sep;
+ struct passwd pwd;
+ struct passwd *pwdp;
+ char pwdbuf[1024];
+ int do_setuid;
+ pid_t saved_uid;
+ gid_t saved_gid;
+ int i, err;
+
+ (void) snprintf(conf_path, PATH_MAX, "%s/%s",
+ SYSEVENT_CONFIG_DIR, conf_file);
+
+ syseventd_print(DBG_CONF_FILE, "%s: reading %s\n", whoami, conf_path);
+
+ if ((fp = fopen(conf_path, "r")) == NULL) {
+ syslog(LOG_ERR, CANNOT_OPEN_ERR, conf_file, strerror(errno));
+ return;
+ }
+
+ if ((line = initstr(128)) == NULL)
+ return;
+
+ while ((lp = fstrgets(line, fp)) != NULL) {
+ lineno++;
+ if (*lp == '\n' || *lp == '#')
+ continue;
+ *(lp + strlen(lp)-1) = 0;
+
+ syseventd_print(DBG_CONF_FILE, "[%d]: %s\n",
+ lineno, lp);
+
+ if ((class = next_field(&lp)) != NULL) {
+ subclass = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ vendor = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ publisher = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ user = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ reserved1 = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ reserved2 = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ path = next_field(&lp);
+ if (lp == NULL)
+ goto mal_formed;
+ args = skip_spaces(&lp);
+ }
+
+ /*
+ * validate user
+ */
+ do_setuid = 0;
+ if ((strcmp(user, "-") != 0) && (strcmp(user, "root") != 0)) {
+ i = getpwnam_r(user, &pwd, pwdbuf,
+ sizeof (pwdbuf), &pwdp);
+ if (i != 0 || pwdp == NULL) {
+ syslog(LOG_ERR, NO_USER_ERR,
+ conf_file, lineno, user);
+ continue;
+ }
+ do_setuid = 1;
+ }
+
+ /*
+ * validate reserved fields
+ */
+ if (strcmp(reserved1, "-") != 0) {
+ syslog(LOG_ERR, RESERVED_FIELD_ERR,
+ conf_file, lineno, reserved1);
+ continue;
+ }
+ if (strcmp(reserved2, "-") != 0) {
+ syslog(LOG_ERR, RESERVED_FIELD_ERR,
+ conf_file, lineno, reserved2);
+ continue;
+ }
+
+ /*
+ * ensure path is executable by user
+ */
+ err = 0;
+ if (do_setuid) {
+ saved_uid = getuid();
+ saved_gid = getgid();
+ if (setregid(pwdp->pw_gid, -1) == -1) {
+ syslog(LOG_ERR, SETREGID_ERR,
+ whoami, pwdp->pw_gid, strerror(errno));
+ err = -1;
+ }
+ if (setreuid(pwdp->pw_uid, -1) == -1) {
+ syslog(LOG_ERR, SETREUID_ERR,
+ whoami, pwdp->pw_uid, strerror(errno));
+ err = -1;
+ }
+ }
+ if ((i = access(path, X_OK)) == -1) {
+ syslog(LOG_ERR, CANNOT_EXECUTE_ERR,
+ conf_file, lineno, path, strerror(errno));
+ }
+ if (do_setuid) {
+ if (setreuid(saved_uid, -1) == -1) {
+ syslog(LOG_ERR, SETREUID_ERR,
+ whoami, saved_uid, strerror(errno));
+ err = -1;
+ }
+ if (setregid(saved_gid, -1) == -1) {
+ syslog(LOG_ERR, SETREGID_ERR,
+ whoami, saved_gid, strerror(errno));
+ err = -1;
+ }
+ }
+ if (i == -1 || err == -1)
+ continue;
+
+ /*
+ * all sanity tests successful - perform allocations
+ * to add entry to table
+ */
+ if ((sep = sc_malloc(sizeof (syseventtab_t))) == NULL)
+ break;
+
+ sep->se_conf_file = conf_file;
+ sep->se_lineno = lineno;
+ sep->se_vendor = sc_strdup(vendor);
+ sep->se_publisher = sc_strdup(publisher);
+ sep->se_class = sc_strdup(class);
+ sep->se_subclass = sc_strdup(subclass);
+ sep->se_user = sc_strdup(user);
+ if (do_setuid) {
+ sep->se_uid = pwdp->pw_uid;
+ sep->se_gid = pwdp->pw_gid;
+ } else {
+ sep->se_uid = 0;
+ sep->se_gid = 0;
+ }
+ sep->se_reserved1 = sc_strdup(reserved1);
+ sep->se_reserved2 = sc_strdup(reserved2);
+ sep->se_path = sc_strdup(path);
+ sep->se_args = (args == NULL) ? NULL : sc_strdup(args);
+ sep->se_next = NULL;
+
+ if (sep->se_vendor == NULL || sep->se_publisher == NULL ||
+ sep->se_class == NULL || sep->se_subclass == NULL ||
+ sep->se_user == NULL || sep->se_reserved1 == NULL ||
+ sep->se_reserved2 == NULL || sep->se_path == NULL ||
+ (args && sep->se_args == NULL)) {
+ sc_strfree(sep->se_vendor);
+ sc_strfree(sep->se_publisher);
+ sc_strfree(sep->se_class);
+ sc_strfree(sep->se_subclass);
+ sc_strfree(sep->se_user);
+ sc_strfree(sep->se_reserved1);
+ sc_strfree(sep->se_reserved2);
+ sc_strfree(sep->se_path);
+ sc_strfree(sep->se_args);
+ sc_free(sep, sizeof (syseventtab_t));
+ break;
+ }
+
+ /*
+ * link new entry into the table
+ */
+ if (syseventtab == NULL) {
+ syseventtab = sep;
+ syseventtab_tail = sep;
+ } else {
+ syseventtab_tail->se_next = sep;
+ syseventtab_tail = sep;
+ }
+
+ if (debug_level >= DBG_DETAILED) {
+ syseventtab_t *sp;
+ for (sp = syseventtab; sp; sp = sp->se_next) {
+ syseventd_print(DBG_DETAILED,
+ " vendor=%s\n", sp->se_vendor);
+ syseventd_print(DBG_DETAILED,
+ " publisher=%s\n", sp->se_publisher);
+ syseventd_print(DBG_DETAILED,
+ " class=%s\n", sp->se_class);
+ syseventd_print(DBG_DETAILED,
+ " subclass=%s\n", sp->se_subclass);
+ syseventd_print(DBG_DETAILED,
+ " user=%s uid=%d gid=%d\n",
+ sp->se_user, sp->se_uid, sp->se_gid);
+ syseventd_print(DBG_DETAILED,
+ " reserved1=%s\n", sp->se_reserved1);
+ syseventd_print(DBG_DETAILED,
+ " reserved2=%s\n", sp->se_reserved2);
+ syseventd_print(DBG_DETAILED,
+ " path=%s\n", sp->se_path);
+ if (sp->se_args != NULL) {
+ syseventd_print(DBG_DETAILED,
+ " args=%s\n", sp->se_args);
+ }
+ }
+ }
+
+ continue;
+
+mal_formed:
+ syslog(LOG_ERR, SYNTAX_ERR, conf_file, lineno);
+ }
+
+ freestr(line);
+ (void) fclose(fp);
+}
+
+
+/*
+ * Build the events specification table, a summation of all
+ * event specification found in the installed sysevent.conf
+ * configuration files.
+ *
+ * All sysevent.conf files reside in the /etc/sysevent/config
+ * and may contain zero or more event/command specifications.
+ * A sysevent.conf file should be named as follows:
+ *
+ * <vendor>,[<publisher>,][<class>,]sysevent.conf
+ *
+ * Event/command specifications delivered by the base Solaris
+ * system are provided in /etc/sysevent/config/SUNW,sysevent.conf.
+ * Event/command specifications delivered by optional
+ * Sun-supplied packages may install additional sysevent.conf
+ * files in /etc/sysevent/config using vendor SUNW, and additional
+ * publisher and/or event class naming to distinguish the
+ * events required for those products. Products provided
+ * by third-party hardware or software companies may
+ * distinguish their sysevent.conf files by vendor, and
+ * by publisher and/or event class within vendor.
+ *
+ * Files residing in /etc/sysevent/config with a '.' (period)
+ * as the first character of the name and files with a suffix
+ * of other than "sysevent.conf" are ignored.
+ */
+static void
+build_event_table()
+{
+ conftab_t *cfp = NULL;
+ DIR *dir;
+ struct dirent *result;
+ struct dirent *entry;
+ int err;
+ conftab_t *new_cfp;
+ char *str;
+ long max_name;
+ size_t dirent_size;
+
+ max_name = pathconf(SYSEVENT_CONFIG_DIR, _PC_NAME_MAX);
+ if ((int)max_name == -1) {
+ syslog(LOG_ERR, PATHCONF_ERR,
+ SYSEVENT_CONFIG_DIR, strerror(errno));
+ return;
+ }
+
+ dirent_size = sizeof (struct dirent) + (int)max_name + 1;
+ entry = (struct dirent *)sc_malloc(dirent_size);
+ if (entry == NULL)
+ return;
+
+ if ((dir = opendir(SYSEVENT_CONFIG_DIR)) == NULL) {
+ syslog(LOG_ERR, CANNOT_OPEN_ERR,
+ SYSEVENT_CONFIG_DIR, strerror(errno));
+ sc_free(entry, dirent_size);
+ return;
+ }
+
+ for (;;) {
+ err = readdir_r(dir, entry, &result);
+ if (err != 0) {
+ syslog(LOG_ERR, READDIR_ERR, SYSEVENT_CONFIG_DIR, err);
+ goto err;
+ }
+ if (result == NULL)
+ break;
+ if (result->d_name[0] == '.')
+ continue;
+
+ /*
+ * file must have extension "sysevent.conf"
+ */
+ if ((str = strrchr(result->d_name, ',')) != NULL) {
+ str++;
+ } else {
+ str = result->d_name;
+ }
+ if (strcmp(str, "sysevent.conf") != 0) {
+ syseventd_print(DBG_CONF_FILE,
+ "%s: ignoring %s\n", whoami, str);
+ continue;
+ }
+
+ /*
+ * Add to file table and parse this conf file
+ */
+ if ((str = sc_strdup(result->d_name)) == NULL)
+ goto err;
+ if ((new_cfp = sc_malloc(sizeof (conftab_t))) == NULL) {
+ sc_strfree(str);
+ goto err;
+ }
+ if (conftab == NULL) {
+ conftab = new_cfp;
+ } else {
+ for (cfp = conftab; cfp->cf_next; cfp = cfp->cf_next)
+ ;
+ cfp->cf_next = new_cfp;
+ }
+ cfp = new_cfp;
+ cfp->cf_conf_file = str;
+ cfp->cf_next = NULL;
+
+ parse_conf_file(cfp->cf_conf_file);
+ }
+
+err:
+ if (closedir(dir) == -1) {
+ if (errno == EAGAIN)
+ goto err;
+ syslog(LOG_ERR, CLOSEDIR_ERR,
+ SYSEVENT_CONFIG_DIR, strerror(errno));
+ }
+ sc_free(entry, dirent_size);
+}
+
+
+static int
+enter_lock(char *lock_file)
+{
+ struct flock lock;
+ int lock_fd;
+
+ (void) snprintf(lock_file, PATH_MAX, "%s/%s",
+ SYSEVENT_CONFIG_DIR, LOCK_FILENAME);
+ lock_fd = open(lock_file, O_CREAT|O_RDWR, 0644);
+ if (lock_fd < 0) {
+ syslog(LOG_ERR, MSG_LOCK_CREATE_ERR,
+ whoami, lock_file, strerror(errno));
+ return (-1);
+ }
+
+ lock.l_type = F_WRLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+
+retry:
+ if (fcntl(lock_fd, F_SETLKW, &lock) == -1) {
+ if (errno == EAGAIN || errno == EINTR)
+ goto retry;
+ (void) close(lock_fd);
+ syslog(LOG_ERR, MSG_LOCK_SET_ERR,
+ whoami, lock_file, strerror(errno));
+ return (-1);
+ }
+
+ return (lock_fd);
+}
+
+
+static void
+exit_lock(int lock_fd, char *lock_file)
+{
+ struct flock lock;
+
+ lock.l_type = F_UNLCK;
+ lock.l_whence = SEEK_SET;
+ lock.l_start = 0;
+ lock.l_len = 0;
+
+ if (fcntl(lock_fd, F_SETLK, &lock) == -1) {
+ syslog(LOG_ERR, MSG_LOCK_CLR_ERR,
+ whoami, lock_file, strerror(errno));
+ }
+
+ if (close(lock_fd) == -1) {
+ syslog(LOG_ERR, MSG_LOCK_CLOSE_ERR,
+ whoami, lock_file, strerror(errno));
+ }
+}
+
+
+/*
+ * Free the events specification table, constructed by
+ * parsing all the sysevent.conf files found.
+ *
+ * The free of this table is in response to a HUP
+ * given to the syseventd daemon, permitting the
+ * table to be rebuilt after adding a new sysevent.conf
+ * file or changing an existing one without shutting
+ * down the daemon.
+ */
+static void
+free_event_table()
+{
+ syseventtab_t *sep;
+ syseventtab_t *sep_next;
+ conftab_t *cfp;
+ conftab_t *cfp_next;
+
+ sep = syseventtab;
+ while (sep) {
+ sc_strfree(sep->se_vendor);
+ sc_strfree(sep->se_publisher);
+ sc_strfree(sep->se_class);
+ sc_strfree(sep->se_subclass);
+ sc_strfree(sep->se_user);
+ sc_strfree(sep->se_reserved1);
+ sc_strfree(sep->se_reserved2);
+ sc_strfree(sep->se_path);
+ if (sep->se_args)
+ sc_strfree(sep->se_args);
+ sep_next = sep->se_next;
+ sc_free(sep, sizeof (syseventtab_t));
+ sep = sep_next;
+ }
+ syseventtab = NULL;
+
+ cfp = conftab;
+ while (cfp) {
+ sc_strfree(cfp->cf_conf_file);
+ cfp_next = cfp->cf_next;
+ sc_free(cfp, sizeof (conftab_t));
+ cfp = cfp_next;
+ }
+ conftab = NULL;
+}
+
+
+
+static char ident_chars[] = "_";
+
+/*
+ * Return a dynamically-allocated string containing the
+ * the next identifier in the string being parsed, pointed
+ * at by 'id'. 'end' returns a pointer to the character
+ * after the identifier.
+ *
+ * Identifiers are all alphanumeric ascii characters and
+ * those contained in ident_chars.
+ *
+ * The returned string must be explicitly freed via
+ * freestr().
+ */
+static str_t *
+snip_identifier(char *id, char **end)
+{
+ str_t *token;
+
+ if ((token = initstr(32)) == NULL)
+ return (NULL);
+
+ while (*id != 0) {
+ if (isascii(*id) &&
+ (isalnum(*id) || strchr(ident_chars, *id) != NULL)) {
+ if (strcatc(token, *id++)) {
+ freestr(token);
+ return (NULL);
+ }
+ } else {
+ *end = id;
+ return (token);
+ }
+ }
+
+ *end = id;
+ return (token);
+}
+
+
+/*
+ * Identical to snip_identifier(), but the identifier
+ * is delimited by the characters { and }.
+ */
+static str_t *
+snip_delimited_identifier(char *id, char **end)
+{
+ str_t *token;
+
+ if ((token = initstr(32)) == NULL)
+ return (NULL);
+
+ while (*id != 0) {
+ if (*id == '}') {
+ *end = id+1;
+ return (token);
+ }
+ if (strcatc(token, *id++)) {
+ freestr(token);
+ return (NULL);
+ }
+ }
+
+ if (*id == 0) {
+ freestr(token);
+ return (NULL);
+ }
+
+ *end = id;
+ return (token);
+}
+
+
+/*
+ * Return a string with the name of the attribute type
+ */
+static char *nv_attr_type_strings[] = {
+ "unknown",
+ "boolean",
+ "byte",
+ "int16",
+ "uint16",
+ "int32",
+ "uint32",
+ "int64",
+ "uint64",
+ "string",
+ "byte-array",
+ "int16-array",
+ "uint16-array",
+ "int32-array",
+ "uint32-array",
+ "int64-array",
+ "uint64-array",
+ "string-array",
+ "hrtime"
+};
+
+static char *
+se_attr_type_to_str(int se_attr_type)
+{
+ if (se_attr_type >= 0 &&
+ se_attr_type < sizeof (nv_attr_type_strings) / sizeof (char *)) {
+ return (nv_attr_type_strings[se_attr_type]);
+ }
+ return (nv_attr_type_strings[DATA_TYPE_UNKNOWN]);
+}
+
+
+/*
+ * Find and return the data matching the macro name 'token'
+ *
+ * Predefined macros are simply substituted with the
+ * data from the event header:
+ *
+ * $vendor - the vendor string defining the event.
+ *
+ * $publisher - the publisher string defining the event.
+ *
+ * $class - the class string defining the event.
+ *
+ * $subclass - the subclass string defining the event.
+ *
+ * $sequence - the sequence number of the event.
+ *
+ * $timestamp - the timestamp of the event.
+ *
+ * Attributes with signed data types (DATA_TYPE_INT16,
+ * DATA_TYPE_INT32 and DATA_TYPE_INT64) are expanded
+ * as decimal digits.
+ *
+ * Attributes with unsigned data types (DATA_TYPE_BYTE,
+ * DATA_TYPE_UINT16, DATA_TYPE_UINT32, DATA_TYPE_UINT64 and
+ * DATA_TYPE_HTTIME) are expanded as hexadecimal digits
+ * with a "0x" prefix.
+ *
+ * Attributes with string data type (DATA_TYPE_STRING)
+ * are expanded with the string data. The data is
+ * not quoted. If if it desired that the quoted strings
+ * be generated on the command line, put quotes around
+ * the macro call in the arguments.
+ *
+ * Array types are expanded with each element expanded
+ * as defined for that scalar type, with a space separating
+ * each element substitution.
+ */
+
+static str_t *
+find_macro_definition(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep,
+ char *token, sysevent_hdr_info_t *hdr)
+{
+ nvpair_t *nvp;
+ int nmatches;
+ char num[64];
+ str_t *replacement;
+ int i;
+ uint_t nelems;
+ union {
+ uchar_t x_byte;
+ int16_t x_int16;
+ uint16_t x_uint16;
+ int32_t x_int32;
+ uint32_t x_uint32;
+ int64_t x_int64;
+ uint64_t x_uint64;
+ hrtime_t x_time;
+ char *x_string;
+ uchar_t *x_byte_array;
+ int16_t *x_int16_array;
+ int32_t *x_int32_array;
+ int64_t *x_int64_array;
+ uint16_t *x_uint16_array;
+ uint32_t *x_uint32_array;
+ uint64_t *x_uint64_array;
+ char **x_string_array;
+ } x;
+
+
+ if ((replacement = initstr(128)) == NULL) {
+ return (NULL);
+ }
+
+ if (strcmp(token, "vendor") == 0) {
+ if (strcopys(replacement, hdr->vendor)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ return (replacement);
+ }
+
+ if (strcmp(token, "publisher") == 0) {
+ if (strcopys(replacement, hdr->publisher)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ return (replacement);
+ }
+
+ if (strcmp(token, "class") == 0) {
+ if (strcopys(replacement, hdr->class)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ return (replacement);
+ }
+
+ if (strcmp(token, "subclass") == 0) {
+ if (strcopys(replacement, hdr->subclass)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ return (replacement);
+ }
+
+ if ((strcmp(token, "sequence") == 0) ||
+ (strcmp(token, "timestamp") == 0)) {
+ if (strcmp(token, "sequence") == 0) {
+ (void) snprintf(num, sizeof (num),
+ "0x%llx", sysevent_get_seq(ev));
+ } else {
+ hrtime_t ts;
+ sysevent_get_time(ev, &ts);
+ (void) snprintf(num, sizeof (num), "0x%llx", ts);
+ }
+ if (strcopys(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ return (replacement);
+ }
+
+ nmatches = 0;
+
+ if (nvlist) {
+ nvpair_t *nvp_match;
+ nvp = NULL;
+ while ((nvp = nvlist_next_nvpair(nvlist, nvp)) != NULL) {
+ if (debug_level >= DBG_DETAILED) {
+ syseventd_print(DBG_DETAILED,
+ " attribute: %s %s\n", nvpair_name(nvp),
+ se_attr_type_to_str(nvpair_type(nvp)));
+ }
+ if (strcmp(token, nvpair_name(nvp)) == 0) {
+ nmatches++;
+ nvp_match = nvp;
+ }
+ }
+ nvp = nvp_match;
+ }
+
+ if (nmatches == 0) {
+ syslog(LOG_ERR, MACRO_UNDEF_ERR,
+ sep->se_conf_file, sep->se_lineno, token);
+ freestr(replacement);
+ return (NULL);
+ } else if (nmatches > 1) {
+ syslog(LOG_ERR, MACRO_MULT_DEF_ERR,
+ sep->se_conf_file, sep->se_lineno, token);
+ freestr(replacement);
+ return (NULL);
+ }
+
+ switch (nvpair_type(nvp)) {
+ case DATA_TYPE_BYTE:
+ (void) nvpair_value_byte(nvp, &x.x_byte);
+ (void) snprintf(num, sizeof (num), "0x%x", x.x_byte);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_INT16:
+ (void) nvpair_value_int16(nvp, &x.x_int16);
+ (void) snprintf(num, sizeof (num), "%d", x.x_int16);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_UINT16:
+ (void) nvpair_value_uint16(nvp, &x.x_uint16);
+ (void) snprintf(num, sizeof (num), "0x%x", x.x_uint16);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_INT32:
+ (void) nvpair_value_int32(nvp, &x.x_int32);
+ (void) snprintf(num, sizeof (num), "%d", x.x_int32);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_UINT32:
+ (void) nvpair_value_uint32(nvp, &x.x_uint32);
+ (void) snprintf(num, sizeof (num), "0x%x", x.x_uint32);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_INT64:
+ (void) nvpair_value_int64(nvp, &x.x_int64);
+ (void) snprintf(num, sizeof (num), "%lld", x.x_int64);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_UINT64:
+ (void) nvpair_value_uint64(nvp, &x.x_uint64);
+ (void) snprintf(num, sizeof (num), "0x%llx", x.x_uint64);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_STRING:
+ (void) nvpair_value_string(nvp, &x.x_string);
+ if (strcats(replacement, x.x_string)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ case DATA_TYPE_BYTE_ARRAY: {
+ uchar_t *p;
+ (void) nvpair_value_byte_array(nvp,
+ &x.x_byte_array, &nelems);
+ p = x.x_byte_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num),
+ "0x%x ", *p++ & 0xff);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+ case DATA_TYPE_INT16_ARRAY: {
+ int16_t *p;
+ (void) nvpair_value_int16_array(nvp,
+ &x.x_int16_array, &nelems);
+ p = x.x_int16_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num), "%d ", *p++);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_UINT16_ARRAY: {
+ uint16_t *p;
+ (void) nvpair_value_uint16_array(nvp,
+ &x.x_uint16_array, &nelems);
+ p = x.x_uint16_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num),
+ "0x%x ", *p++);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_INT32_ARRAY: {
+ int32_t *p;
+ (void) nvpair_value_int32_array(nvp,
+ &x.x_int32_array, &nelems);
+ p = x.x_int32_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num), "%d ", *p++);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_UINT32_ARRAY: {
+ uint32_t *p;
+ (void) nvpair_value_uint32_array(nvp,
+ &x.x_uint32_array, &nelems);
+ p = x.x_uint32_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num),
+ "0x%x ", *p++);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_INT64_ARRAY: {
+ int64_t *p;
+ (void) nvpair_value_int64_array(nvp,
+ &x.x_int64_array, &nelems);
+ p = x.x_int64_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num),
+ "%lld ", *p++);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_UINT64_ARRAY: {
+ uint64_t *p;
+ (void) nvpair_value_uint64_array(nvp,
+ &x.x_uint64_array, &nelems);
+ p = x.x_uint64_array;
+ for (i = 0; i < nelems; i++) {
+ (void) snprintf(num, sizeof (num),
+ "0x%llx ", *p++);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_STRING_ARRAY: {
+ char **p;
+ (void) nvpair_value_string_array(nvp,
+ &x.x_string_array, &nelems);
+ p = x.x_string_array;
+ for (i = 0; i < nelems; i++) {
+ if (strcats(replacement, *p++) ||
+ strcats(replacement, " ")) {
+ freestr(replacement);
+ return (NULL);
+ }
+ }
+ }
+ break;
+
+ case DATA_TYPE_HRTIME:
+ (void) nvpair_value_hrtime(nvp, &x.x_time);
+ (void) snprintf(num, sizeof (num), "0x%llx", x.x_time);
+ if (strcats(replacement, num)) {
+ freestr(replacement);
+ return (NULL);
+ }
+ break;
+ default:
+ syslog(LOG_ERR, ATTR_UNSUPPORTED_ERR,
+ sep->se_conf_file, sep->se_lineno,
+ nvpair_type(nvp), token);
+ freestr(replacement);
+ return (NULL);
+ }
+
+ return (replacement);
+}
+
+/*
+ * Expand macros in the command template provided in an event
+ * specification with the data from the event or event attributes.
+ *
+ * Macros are introduced by the '$' character, with the macro
+ * name being the following token separated by a SPACE or
+ * TAB character. If the macro name is embedded in text,
+ * it may be delineated by '${' and "}'. A backslash before
+ * the '$' causes macro expansion not to occur.
+ *
+ * The following predefined macros are defined for each event:
+ *
+ * $vendor - the vendor string defining the event.
+ *
+ * $publisher - the publisher string defining the event.
+ *
+ * $class - the class string defining the event.
+ *
+ * $subclass - the subclass string defining the event.
+ *
+ * $sequence - the sequence number of the event.
+ *
+ * $timestamp - the timestamp of the event.
+ *
+ *
+ * Macro names other than those predefined are compared against
+ * the attribute list provided with the event. An attribute
+ * with name matching the macro name causes the value of
+ * of the attribute to be substituted as ASCII text on the
+ * generated command line.
+ *
+ * Use of a macro for which no attribute with that name
+ * is defined, or for which multiple attributes with that
+ * name are provided, cause an error and the command is
+ * not invoked.
+ */
+static int
+expand_macros(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep,
+ str_t *line, sysevent_hdr_info_t *hdr)
+{
+ char *p;
+ int state;
+ char *end;
+ str_t *token;
+ str_t *remainder;
+ str_t *replacement;
+ int count;
+ int dollar_position;
+
+ syseventd_print(DBG_MACRO, " expanding macros: '%s'\n", line->s_str);
+
+reset:
+ state = 0;
+ count = 0;
+ for (p = line->s_str; *p != 0; p++, count++) {
+ switch (state) {
+ case 0: /* initial state */
+ if (*p == '\\') {
+ state = 1;
+ } else if (*p == '$') {
+ dollar_position = count;
+ state = 2;
+ }
+ break;
+ case 1: /* skip characters */
+ state = 0; /* after backslash */
+ break;
+ case 2: /* character after $ */
+ if (*p == '{') {
+ token = snip_delimited_identifier(p+1, &end);
+ } else {
+ token = snip_identifier(p, &end);
+ }
+ if (token == NULL)
+ goto failed;
+
+ if ((remainder = initstr(128)) == NULL) {
+ freestr(token);
+ return (1);
+ }
+ if (strcopys(remainder, end)) {
+ freestr(token);
+ freestr(remainder);
+ return (1);
+ }
+ replacement = find_macro_definition(ev, nvlist,
+ sep, token->s_str, hdr);
+ if (replacement == NULL) {
+ freestr(token);
+ freestr(remainder);
+ return (1);
+ }
+ syseventd_print(DBG_MACRO,
+ " '%s' expands to '%s'\n",
+ token->s_str, replacement->s_str);
+
+ strtrunc(line, dollar_position);
+ if (strcats(line, replacement->s_str)) {
+ freestr(token);
+ freestr(replacement);
+ freestr(remainder);
+ return (1);
+ }
+ if (strcats(line, remainder->s_str)) {
+ freestr(token);
+ freestr(replacement);
+ freestr(remainder);
+ return (1);
+ }
+
+ syseventd_print(DBG_MACRO,
+ " with macro expanded: '%s'\n", line->s_str);
+
+ freestr(token);
+ freestr(replacement);
+ freestr(remainder);
+ goto reset;
+ }
+ }
+
+failed:
+ if (state != 0) {
+ syslog(LOG_ERR, SYNTAX_ERR, sep->se_conf_file, sep->se_lineno);
+ return (1);
+ }
+
+ return (0);
+}
+
+
+static void
+start_syseventconfd()
+{
+ int err;
+
+ err = system1("/usr/lib/sysevent/syseventconfd",
+ "/usr/lib/sysevent/syseventconfd");
+
+ if (err != 0 && confd_err_msg_emitted == 0) {
+ if (confd_state == CONFD_STATE_NOT_RUNNING) {
+ syslog(LOG_ERR, SYSEVENTCONFD_START_ERR,
+ strerror(errno));
+ } else {
+ syslog(LOG_ERR, SYSEVENTCONFD_RESTART_ERR,
+ strerror(errno));
+ }
+ }
+}
+
+
+static int
+system1(const char *s_path, const char *s)
+{
+ struct sigaction cbuf, ibuf, qbuf, ignore, dfl;
+ sigset_t mask;
+ sigset_t savemask;
+ struct stat st;
+ pid_t pid;
+ int status, w;
+
+ /* Check the requested command */
+ if (s == NULL) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ /* Check the ability to execute devfsadmd from this process */
+ if (stat(s_path, &st) < 0) {
+ return (-1);
+ }
+ if (((geteuid() == st.st_uid) && ((st.st_mode & S_IXUSR) == 0)) ||
+ ((getegid() == st.st_gid) && ((st.st_mode & S_IXGRP) == 0)) ||
+ ((st.st_mode & S_IXOTH) == 0)) {
+ errno = EPERM;
+ return (-1);
+ }
+
+ /*
+ * Block SIGCHLD and set up a default handler for the duration of the
+ * system1 call.
+ */
+ (void) sigemptyset(&mask);
+ (void) sigaddset(&mask, SIGCHLD);
+ (void) sigprocmask(SIG_BLOCK, &mask, &savemask);
+ (void) memset(&dfl, 0, sizeof (dfl));
+ dfl.sa_handler = SIG_DFL;
+ (void) sigaction(SIGCHLD, &dfl, &cbuf);
+
+ /* Fork off the child process (using fork1(), because it's MT-safe) */
+ switch (pid = fork1()) {
+ case -1:
+ /* Error */
+ (void) sigaction(SIGCHLD, &cbuf, NULL);
+ (void) sigprocmask(SIG_SETMASK, &savemask, NULL);
+ return (-1);
+ case 0:
+ /* Set-up an initial signal mask for the child */
+ (void) sigemptyset(&mask);
+ (void) sigprocmask(SIG_SETMASK, &mask, NULL);
+ (void) execl(s_path, s, (char *)0);
+ _exit(-1);
+ break;
+ default:
+ /* Parent */
+ break;
+ }
+
+ (void) memset(&ignore, 0, sizeof (ignore));
+ ignore.sa_handler = SIG_IGN;
+ (void) sigaction(SIGINT, &ignore, &ibuf);
+ (void) sigaction(SIGQUIT, &ignore, &qbuf);
+
+ do {
+ w = waitpid(pid, &status, 0);
+ } while (w == -1 && errno == EINTR);
+
+ (void) sigaction(SIGINT, &ibuf, NULL);
+ (void) sigaction(SIGQUIT, &qbuf, NULL);
+
+ (void) sigaction(SIGCHLD, &cbuf, NULL);
+ (void) sigprocmask(SIG_SETMASK, &savemask, NULL);
+
+ return ((w == -1)? w: status);
+}
+
+/*
+ * Free all commands on the cmd queue
+ */
+static void
+abort_cmd_queue()
+{
+ cmdqueue_t *cmd;
+ cmdqueue_t *next;
+ int nevents = 0;
+
+ while ((cmd = cmdq) != NULL) {
+ next = cmd->next;
+ cmdq_cnt--;
+ sysevent_free(cmd->event);
+ sc_free(cmd, sizeof (cmdqueue_t));
+ cmdq = next;
+ nevents++;
+ }
+ cmdq_tail = NULL;
+
+ /*
+ * Generate error msgs if events were discarded or
+ * we are entering the disabled state.
+ */
+ if (nevents > 0) {
+ syslog(LOG_ERR, N_EVENTS_DISCARDED_ERR, nevents);
+ }
+ if (want_fini == 0) {
+ confd_state = CONFD_STATE_DISABLED;
+ syslog(LOG_ERR, SERVICE_DISABLED_MSG);
+ }
+}
+
+/*
+ * For a matching event specification, build the command to be
+ * invoked in response to the event. Building the command involves
+ * expanding macros supplied in the event specification command
+ * with values from the actual event. These macros can be
+ * the class/subclass/vendor/publisher strings, or arbitrary
+ * attribute data attached to the event.
+ *
+ * This module does not invoke (fork/exec) the command itself,
+ * since this module is running in the context of the syseventd
+ * daemon, and fork/exec's done here interfere with the door
+ * upcall delivering events from the kernel to the daemon.
+ * Instead, we build a separate event and nvlist with the
+ * attributes of the command to be invoked, and pass that on
+ * to the syseventconfd daemon, which is basically a fork/exec
+ * server on our behalf.
+ *
+ * Errors queuing the event are returned to syseventd with
+ * EAGAIN, allowing syseventd to manage a limited number of
+ * retries after a short delay.
+ */
+static int
+queue_event(sysevent_t *ev, syseventtab_t *sep, sysevent_hdr_info_t *hdr)
+{
+ str_t *line;
+ nvlist_t *nvlist;
+ char *argv0;
+ sysevent_t *cmd_event;
+ nvlist_t *cmd_nvlist;
+ cmdqueue_t *new_cmd;
+
+ if ((line = initstr(128)) == NULL)
+ return (1);
+
+ if ((argv0 = strrchr(sep->se_path, '/')) == NULL) {
+ argv0 = sep->se_path;
+ } else {
+ argv0++;
+ }
+ if (strcopys(line, argv0)) {
+ freestr(line);
+ return (1);
+ }
+
+ if (sep->se_args) {
+ if (strcats(line, " ")) {
+ freestr(line);
+ return (1);
+ }
+ if (strcats(line, sep->se_args)) {
+ freestr(line);
+ return (1);
+ }
+
+ if (sysevent_get_attr_list(ev, &nvlist) != 0) {
+ syslog(LOG_ERR, GET_ATTR_LIST_ERR,
+ sep->se_conf_file, sep->se_lineno,
+ strerror(errno));
+ freestr(line);
+ return (1);
+ }
+ if (expand_macros(ev, nvlist, sep, line, hdr)) {
+ freestr(line);
+ if (nvlist)
+ nvlist_free(nvlist);
+ return (1);
+ }
+ if (nvlist)
+ nvlist_free(nvlist);
+ }
+
+ if (debug_level >= DBG_EXEC) {
+ syseventd_print(DBG_EXEC, "%s, line %d: path = %s\n",
+ sep->se_conf_file, sep->se_lineno, sep->se_path);
+ syseventd_print(DBG_EXEC, " cmd = %s\n", line->s_str);
+ }
+
+ cmd_nvlist = NULL;
+ if ((errno = nvlist_alloc(&cmd_nvlist, NV_UNIQUE_NAME, 0)) != 0) {
+ freestr(line);
+ syslog(LOG_ERR, NVLIST_ALLOC_ERR,
+ sep->se_conf_file, sep->se_lineno,
+ strerror(errno));
+ return (1);
+ }
+
+ if ((errno = nvlist_add_string(cmd_nvlist, "path", sep->se_path)) != 0)
+ goto err;
+ if ((errno = nvlist_add_string(cmd_nvlist, "cmd", line->s_str)) != 0)
+ goto err;
+ if ((errno = nvlist_add_string(cmd_nvlist, "file",
+ sep->se_conf_file)) != 0)
+ goto err;
+ if ((errno = nvlist_add_int32(cmd_nvlist, "line", sep->se_lineno)) != 0)
+ goto err;
+ if ((errno = nvlist_add_string(cmd_nvlist, "user", sep->se_user)) != 0)
+ goto err;
+
+ if (sep->se_uid != (uid_t)0) {
+ if ((errno = nvlist_add_int32(cmd_nvlist, "uid",
+ sep->se_uid)) != 0)
+ goto err;
+ if ((errno = nvlist_add_int32(cmd_nvlist, "gid",
+ sep->se_gid)) != 0)
+ goto err;
+ }
+
+ cmd_event = sysevent_alloc_event(hdr->class, hdr->subclass, hdr->vendor,
+ hdr->publisher, cmd_nvlist);
+ if (cmd_event == NULL) {
+ syslog(LOG_ERR, SYSEVENT_ALLOC_ERR,
+ sep->se_conf_file, sep->se_lineno,
+ strerror(errno));
+ nvlist_free(cmd_nvlist);
+ freestr(line);
+ return (1);
+ }
+
+ nvlist_free(cmd_nvlist);
+ freestr(line);
+
+ /*
+ * Place cmd_event on queue to be transported to syseventconfd
+ */
+ if ((new_cmd = sc_malloc(sizeof (cmdqueue_t))) == NULL) {
+ sysevent_free(cmd_event);
+ return (1);
+ }
+ new_cmd->event = cmd_event;
+ new_cmd->next = NULL;
+ (void) mutex_lock(&cmdq_lock);
+ if (cmdq == NULL) {
+ cmdq = new_cmd;
+ } else {
+ cmdq_tail->next = new_cmd;
+ }
+ cmdq_cnt++;
+ cmdq_tail = new_cmd;
+
+ /*
+ * signal queue flush thread
+ */
+ (void) cond_signal(&cmdq_cv);
+
+ (void) mutex_unlock(&cmdq_lock);
+
+ return (0);
+
+err:
+ syslog(LOG_ERR, NVLIST_BUILD_ERR,
+ sep->se_conf_file, sep->se_lineno, strerror(errno));
+ nvlist_free(cmd_nvlist);
+ freestr(line);
+ return (1);
+}
+
+
+static int
+transport_event(sysevent_t *event)
+{
+ int rval;
+
+ rval = sysevent_send_event(confd_handle, event);
+ if (rval != 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EINTR:
+ /*
+ * syseventconfd daemon may be forking, stop
+ * attempting to empty the queue momentarily.
+ */
+ rval = errno;
+ break;
+ case ENOENT:
+ case EBADF:
+ /*
+ * start/restart the syseventconfd daemon,
+ * allowing for some delay when starting
+ * up before it begins to reply.
+ */
+ if (confd_state == CONFD_STATE_NOT_RUNNING ||
+ confd_state == CONFD_STATE_OK) {
+ confd_state = CONFD_STATE_STARTED;
+ start_syseventconfd();
+ confd_retries = 0;
+ rval = EAGAIN;
+ } else if (confd_state == CONFD_STATE_STARTED &&
+ confd_retries < 16) {
+ if (++confd_retries == 16) {
+ confd_state = CONFD_STATE_ERR;
+ if (confd_err_msg_emitted == 0) {
+ syslog(LOG_ERR,
+ SYSEVENTCONFD_ERR);
+ confd_err_msg_emitted = 1;
+ }
+ }
+ rval = EAGAIN;
+ } else {
+ rval = errno;
+ }
+ break;
+ default:
+ syslog(LOG_ERR, SYSEVENTCONFD_TRAN_ERR,
+ strerror(errno));
+ rval = errno;
+ break;
+ }
+ } else if (confd_state != CONFD_STATE_OK) {
+ if (confd_state == CONFD_STATE_ERR) {
+ syslog(LOG_ERR, SYSEVENTCONFD_OK);
+ confd_err_msg_emitted = 0;
+ }
+ confd_state = CONFD_STATE_OK;
+ confd_retries = 0;
+ confd_err_msg_emitted = 0;
+ }
+ return (rval);
+}
+
+
+/*
+ * Send events on queue to syseventconfd daemon. We queue events
+ * here since the daemon is unable to handle events during an
+ * active fork/exec, returning EAGAIN as a result. It is grossly
+ * inefficient to bounce these events back to syseventd, so
+ * we queue them here for delivery.
+ *
+ * EAGAIN/EINTR don't indicate errors with the transport to
+ * syseventconfd itself, just the daemon is busy or some
+ * other transient difficulty. We retry EBADF and other errors
+ * for some time, then eventually give up - something's broken.
+ *
+ * Error handling strategy:
+ * If we're trying to shut down and the syseventconfd daemon isn't
+ * responding, abort the queue so we don't cause the fini to hang
+ * forever. Otherwise, EAGAIN/EINTR are retried forever, as
+ * we presume the daemon is active but either busy or some transient
+ * state is preventing the transport. We make considerable effort
+ * to retry EBADF since the daemon may take some time to come up when
+ * restarted so don't want to give up too easily. Once we enter
+ * the DISABLED state, we stop handling events altogther to
+ * avoid thrashing the system if the syseventconfd binary is
+ * corrupted or missing. This state can be cleared by issuing
+ * a HUP signal to the syseventd daemon. For errors other than
+ * EAGAIN/EINTR/EBADF, we just drop the event and if we get
+ * a certain number of these in a row, we enter the DISABLED
+ * state.
+ */
+
+static void
+transport_queued_events()
+{
+ int rval;
+ cmdqueue_t *cmd;
+
+ (void) mutex_lock(&cmdq_lock);
+ while (cmdq != NULL) {
+ cmd = cmdq;
+ (void) mutex_unlock(&cmdq_lock);
+ rval = transport_event(cmd->event);
+ (void) mutex_lock(&cmdq_lock);
+ if (rval != 0) {
+ switch (rval) {
+ case EAGAIN:
+ case EINTR:
+ /*
+ * Limit retries in the case of fini
+ */
+ if (want_fini) {
+ if (++transport_retries == 16) {
+ abort_cmd_queue();
+ }
+ }
+ (void) mutex_unlock(&cmdq_lock);
+ return;
+ case EBADF:
+ /*
+ * retry up to 16 times
+ */
+ if (want_fini || ++transport_retries == 16) {
+ abort_cmd_queue();
+ }
+ (void) mutex_unlock(&cmdq_lock);
+ return;
+ default:
+ /*
+ * After 16 sequential errors, give up
+ */
+ if (++transport_retries == 16) {
+ abort_cmd_queue();
+ (void) mutex_unlock(&cmdq_lock);
+ return;
+ }
+ /*
+ * We don't retry these errors, we
+ * fall through to remove this event
+ * from the queue.
+ */
+ break;
+ }
+ } else {
+ transport_retries = 0;
+ }
+
+ /*
+ * Take completed event off queue
+ */
+ cmdq_cnt--;
+ cmdq = cmdq->next;
+ if (cmdq == NULL) {
+ cmdq_tail = NULL;
+ }
+ (void) mutex_unlock(&cmdq_lock);
+ sysevent_free(cmd->event);
+ sc_free(cmd, sizeof (cmdqueue_t));
+ (void) mutex_lock(&cmdq_lock);
+ }
+
+ (void) mutex_unlock(&cmdq_lock);
+}
+
+
+static void
+queue_flush_thr()
+{
+ int n;
+
+ (void) mutex_lock(&cmdq_lock);
+ for (;;) {
+ while (cmdq_cnt == 0 && want_fini == 0) {
+ (void) cond_wait(&cmdq_cv, &cmdq_lock);
+ }
+ if (cmdq_cnt == 0 && want_fini) {
+ (void) cond_signal(&cmdq_thr_cv);
+ (void) mutex_unlock(&cmdq_lock);
+ thr_exit(NULL);
+ /*NOTREACHED*/
+ }
+ (void) mutex_unlock(&cmdq_lock);
+ transport_queued_events();
+ (void) mutex_lock(&cmdq_lock);
+ if (cmdq_cnt != 0) {
+ (void) mutex_unlock(&cmdq_lock);
+ if (want_fini == 0 && confd_err_msg_emitted) {
+ for (n = 0; n < 60; n++) {
+ (void) sleep(1);
+ if (want_fini)
+ break;
+ }
+ } else {
+ (void) sleep(1);
+ }
+ (void) mutex_lock(&cmdq_lock);
+ }
+ }
+}
+
+
+/*
+ * syseventd daemon module event handler
+ *
+ * The syseventd daemon calls this handler with each event
+ * for this module to handle the event as appropriate.
+ * The task of this module is to compare the event's
+ * class/subclass/publisher/vendor against the list of
+ * event specifications provided in the installed
+ * sysevent.conf files. Build and execute the
+ * defined command for that event specification
+ * for each match.
+ *
+ * Events are matched against the class, subclass, vendor
+ * and publisher specifications. Any field not to be matched
+ * against an event should be set to '-'. A specification
+ * of '- - - -' generates a match against every event.
+ */
+/*ARGSUSED*/
+static int
+sysevent_conf_event(sysevent_t *ev, int flag)
+{
+ int ret = 0;
+ char *vendor;
+ char *publisher;
+ char *class;
+ char *subclass;
+ syseventtab_t *sep;
+ sysevent_hdr_info_t hdr;
+ uint64_t seq;
+ hrtime_t ts;
+
+ /*
+ * If we've been completely unable to communicate with
+ * syseventconfd, there's not much we can do.
+ */
+ if (confd_state == CONFD_STATE_DISABLED) {
+ return (0);
+ }
+
+ /*
+ * sysevent_get_seq(ev) < ev_seq):
+ * an event we have played before, ignore it
+ * sysevent_get_seq(ev) == ev_seq):
+ * ev_nretries > 0, an event being retried
+ * sysevent_get_seq(ev) > ev_seq):
+ * a new event
+ */
+ if (debug_level >= DBG_EVENTS) {
+ if (sysevent_get_seq(ev) == ev_seq && ev_nretries > 0) {
+ syseventd_print(DBG_EVENTS,
+ "sequence: %lld/%lld, retry %d\n",
+ sysevent_get_seq(ev), ev_seq, ev_nretries);
+ } else if (sysevent_get_seq(ev) > ev_seq) {
+ syseventd_print(DBG_EVENTS,
+ "sequence: %lld/%lld\n",
+ sysevent_get_seq(ev), ev_seq);
+ }
+ }
+
+ seq = sysevent_get_seq(ev);
+ sysevent_get_time(ev, &ts);
+
+ if (seq > ev_seq || ts > ev_ts) {
+ ev_nretries = 0;
+ } else if (first_event == 0 &&
+ (((seq < ev_seq) || (seq == 0 && ts > ev_ts)) ||
+ (seq == ev_seq && ev_nretries == 0))) {
+ syseventd_print(DBG_TEST,
+ "out-of-order sequence: received %lld/0x%llx, "
+ "expected %lld/0x%llx\n", seq, ts, ev_seq+1, ev_ts);
+ return (ret);
+ }
+
+ ev_ts = ts;
+ ev_seq = seq;
+ first_event = 0;
+
+ /*
+ * sysevent_get_vendor_name() and sysevent_get_pub_name()
+ * allocate strings which must be freed.
+ */
+ vendor = sysevent_get_vendor_name(ev);
+ publisher = sysevent_get_pub_name(ev);
+ class = sysevent_get_class_name(ev);
+ subclass = sysevent_get_subclass_name(ev);
+
+ if (vendor == NULL || publisher == NULL) {
+ syseventd_print(DBG_EVENTS, "Short on memory with vendor "
+ "and/or publisher string generation\n");
+ /* Temporary short on memory */
+ ev_nretries++;
+ free(publisher);
+ free(vendor);
+ return (EAGAIN);
+ }
+
+ syseventd_print(DBG_EVENTS,
+ "%s event %lld: vendor='%s' publisher='%s' class='%s' "
+ "subclass='%s'\n", whoami, sysevent_get_seq(ev), vendor,
+ publisher, class, subclass);
+
+ for (sep = syseventtab; sep; sep = sep->se_next) {
+ if (strcmp(sep->se_vendor, "-") != 0) {
+ if (strcmp(sep->se_vendor, vendor) != 0)
+ continue;
+ }
+ if (strcmp(sep->se_publisher, "-") != 0) {
+ if (strcmp(sep->se_publisher, publisher) != 0)
+ continue;
+ }
+ if (strcmp(sep->se_class, "-") != 0) {
+ if (strcmp(sep->se_class, class) != 0)
+ continue;
+ }
+ if (strcmp(sep->se_subclass, "-") != 0) {
+ if (strcmp(sep->se_subclass, subclass) != 0)
+ continue;
+ }
+ syseventd_print(DBG_MATCHES, " event match: %s, line %d\n",
+ sep->se_conf_file, sep->se_lineno);
+ hdr.class = class;
+ hdr.subclass = subclass;
+ hdr.vendor = vendor;
+ hdr.publisher = publisher;
+ if ((ret = queue_event(ev, sep, &hdr)) != 0)
+ break;
+ }
+
+ if (ret == 0) {
+ ev_nretries = 0;
+ } else {
+ /*
+ * Ask syseventd to retry any failed event. If we have
+ * reached the limit on retries, emit a msg that we're
+ * not going to be able to service it.
+ */
+ if (ev_nretries == SE_MAX_RETRY_LIMIT) {
+ syslog(LOG_ERR, SYSEVENT_SEND_ERR,
+ sep->se_conf_file, sep->se_lineno, errno);
+ } else {
+ syseventd_print(DBG_TEST, "%s event %lld: "
+ "'%s' '%s' '%s' '%s - errno %d, retry %d\n",
+ whoami, sysevent_get_seq(ev), vendor,
+ publisher, class, subclass, errno, ev_nretries);
+ }
+ ret = EAGAIN;
+ ev_nretries++;
+ }
+
+ free(publisher);
+ free(vendor);
+
+ return (ret);
+}
+
+/*
+ * syseventd daemon module initialization
+ */
+struct slm_mod_ops *
+slm_init()
+{
+ char lock_file[PATH_MAX+1];
+ int lock_fd;
+ int err;
+
+ /*
+ * This functionality is not supported in the mini-root
+ * environment, ie install. If root_dir is set, implying
+ * install, we quietly fail. Return dummy ops rather
+ * than NULL to avoid error msgs out of syseventd.
+ */
+ if (strcmp(root_dir, "") != 0) {
+ return (&sysevent_conf_dummy_mod_ops);
+ }
+
+ ev_nretries = 0;
+ first_event = 1;
+
+ /*
+ * Initialize the channel to syseventconfd
+ */
+ confd_handle = sysevent_open_channel_alt(SYSEVENTCONFD_SERVICE_DOOR);
+ if (confd_handle == NULL) {
+ syslog(LOG_ERR, CHANNEL_OPEN_ERR);
+ return (NULL);
+ }
+
+ if (sysevent_bind_publisher(confd_handle) != 0) {
+ if (errno == EBUSY) {
+ sysevent_cleanup_publishers(confd_handle);
+ if (sysevent_bind_publisher(confd_handle) != 0) {
+ sysevent_close_channel(confd_handle);
+ return (NULL);
+ }
+ }
+ }
+
+ sysevent_cleanup_subscribers(confd_handle);
+
+ cmdq = NULL;
+ cmdq_tail = NULL;
+ cmdq_cnt = 0;
+ want_fini = 0;
+ confd_err_msg_emitted = 0;
+ if (confd_state != CONFD_STATE_OK) {
+ confd_state = CONFD_STATE_NOT_RUNNING;
+ }
+
+ confd_retries = 0;
+ transport_retries = 0;
+
+ (void) mutex_init(&cmdq_lock, USYNC_THREAD, NULL);
+ (void) cond_init(&cmdq_cv, USYNC_THREAD, NULL);
+ (void) cond_init(&cmdq_thr_cv, USYNC_THREAD, NULL);
+
+ /*
+ * Create thread to flush cmd queue
+ */
+ if ((err = thr_create(NULL, NULL, (void *(*)(void*))queue_flush_thr,
+ (void *)NULL, 0, &cmdq_thr_id)) != 0) {
+ syslog(LOG_ERR, THR_CREATE_ERR, strerror(err));
+ sysevent_close_channel(confd_handle);
+ confd_handle = NULL;
+ (void) mutex_destroy(&cmdq_lock);
+ (void) cond_destroy(&cmdq_cv);
+ (void) cond_destroy(&cmdq_thr_cv);
+ return (NULL);
+ }
+
+ if ((lock_fd = enter_lock(lock_file)) == -1) {
+ (void) thr_join(cmdq_thr_id, NULL, NULL);
+ sysevent_close_channel(confd_handle);
+ confd_handle = NULL;
+ (void) mutex_destroy(&cmdq_lock);
+ (void) cond_destroy(&cmdq_cv);
+ (void) cond_destroy(&cmdq_thr_cv);
+ return (NULL);
+ }
+
+ build_event_table();
+ exit_lock(lock_fd, lock_file);
+ return (&sysevent_conf_mod_ops);
+}
+
+/*
+ * syseventd daemon module tear-down
+ */
+void
+slm_fini()
+{
+ int err;
+
+ /*
+ * Nothing to clean up if we're in the install environment
+ */
+ if (strcmp(root_dir, "") != 0) {
+ return;
+ }
+
+ /*
+ * Wait for the queue to drain
+ */
+ (void) mutex_lock(&cmdq_lock);
+ want_fini = 1;
+ (void) cond_signal(&cmdq_cv);
+ (void) cond_wait(&cmdq_thr_cv, &cmdq_lock);
+ (void) mutex_unlock(&cmdq_lock);
+
+ /*
+ * Shut down the the queue flush thread
+ */
+ if ((err = thr_join(cmdq_thr_id, NULL, NULL)) != 0) {
+ syslog(LOG_ERR, THR_JOIN_ERR, strerror(err));
+ }
+
+ sysevent_close_channel(confd_handle);
+ confd_handle = NULL;
+ (void) mutex_destroy(&cmdq_lock);
+ (void) cond_destroy(&cmdq_cv);
+ (void) cond_destroy(&cmdq_thr_cv);
+ free_event_table();
+}
+
+/*ARGSUSED*/
+static int
+sysevent_conf_dummy_event(sysevent_t *ev, int flag)
+{
+ return (0);
+}
diff --git a/usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.h b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.h
new file mode 100644
index 0000000000..f4a1465a40
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_conf_mod/sysevent_conf_mod.h
@@ -0,0 +1,171 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _SYSEVENT_CONF_MOD_H
+#define _SYSEVENT_CONF_MOD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+ * syseventd_print debug levels for sysevent_conf_mod
+ */
+#define DBG_TEST 1 /* info of interest when testing */
+#define DBG_EXEC 2 /* path and args to exec */
+#define DBG_EVENTS 3 /* received events */
+#define DBG_MATCHES 4 /* dump specs for matching events */
+#define DBG_MACRO 5 /* macro expansion */
+#define DBG_CONF_FILE 6 /* sysevent.conf parsing */
+#define DBG_DETAILED 7 /* all the above and more */
+
+
+/*
+ * Directory where sysevent.conf files reside
+ */
+#define SYSEVENT_CONFIG_DIR "/etc/sysevent/config"
+
+/*
+ * Lock file name to serialize registry updates
+ */
+#define LOCK_FILENAME "sysevent.lock"
+
+/*
+ * sysevent.conf files list
+ */
+typedef struct conftab {
+ char *cf_conf_file; /* source conf file */
+ struct conftab *cf_next;
+} conftab_t;
+
+/*
+ * sysevent.conf table
+ */
+typedef struct syseventtab {
+ char *se_conf_file; /* source conf file */
+ int se_lineno; /* line number */
+ char *se_vendor; /* vendor */
+ char *se_publisher; /* publisher */
+ char *se_class; /* event class */
+ char *se_subclass; /* event subclass */
+ char *se_user; /* user */
+ char *se_reserved1; /* reserved1 */
+ char *se_reserved2; /* reserved2 */
+ char *se_path; /* event path */
+ char *se_args; /* optional args */
+ uid_t se_uid; /* user id */
+ gid_t se_gid; /* group id */
+ struct syseventtab *se_next;
+} syseventtab_t;
+
+typedef struct sysevent_hdr_info {
+ char *class;
+ char *subclass;
+ char *vendor;
+ char *publisher;
+} sysevent_hdr_info_t;
+
+
+/*
+ * Structures for building arbitarily long strings and argument lists
+ */
+typedef struct str {
+ char *s_str;
+ int s_len;
+ int s_alloc;
+ int s_hint;
+} str_t;
+
+/*
+ * Queue of commands ready to be transported to syseventconfd
+ */
+typedef struct cmdqueue {
+ sysevent_t *event;
+ struct cmdqueue *next;
+} cmdqueue_t;
+
+/*
+ * syseventconfd state
+ */
+enum {
+ CONFD_STATE_OK,
+ CONFD_STATE_NOT_RUNNING,
+ CONFD_STATE_STARTED,
+ CONFD_STATE_ERR,
+ CONFD_STATE_DISABLED
+};
+
+
+/*
+ * Prototypes
+ */
+static char *skip_spaces(char **cpp);
+static char *next_field(char **cpp);
+static void *sc_malloc(size_t n);
+static void *sc_realloc(void *p, size_t current, size_t n);
+static void sc_free(void *p, size_t n);
+static char *sc_strdup(char *cp);
+static void sc_strfree(char *s);
+
+static str_t *initstr(int hint);
+static void freestr(str_t *str);
+static void resetstr(str_t *str);
+static int strcopys(str_t *str, char *s);
+static int strcats(str_t *str, char *s);
+static int strcatc(str_t *str, int c);
+static char *fstrgets(str_t *str, FILE *fp);
+static void strtrunc(str_t *str, int pos);
+
+static void build_event_table(void);
+static void free_event_table(void);
+static int enter_lock(char *lock_file);
+static void exit_lock(int lock_fd, char *lock_file);
+static str_t *snip_identifier(char *id, char **end);
+static str_t *snip_delimited_identifier(char *id, char **end);
+static char *se_attr_type_to_str(int se_attr_type);
+static str_t *find_macro_definition(sysevent_t *ev, nvlist_t *nvlist,
+ syseventtab_t *sep, char *token, sysevent_hdr_info_t *hdr);
+static int expand_macros(sysevent_t *ev, nvlist_t *nvlist,
+ syseventtab_t *sep, str_t *line, sysevent_hdr_info_t *hdr);
+static void start_syseventconfd(void);
+static int system1(const char *s_path, const char *s);
+static void abort_cmd_queue(void);
+static int queue_event(sysevent_t *ev, syseventtab_t *sep,
+ sysevent_hdr_info_t *hdr);
+static int transport_event(sysevent_t *cmd);
+static void transport_queued_events(void);
+static int sysevent_conf_event(sysevent_t *ev, int flag);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYSEVENT_CONF_MOD_H */
diff --git a/usr/src/cmd/syseventd/modules/sysevent_reg_mod/Makefile b/usr/src/cmd/syseventd/modules/sysevent_reg_mod/Makefile
new file mode 100644
index 0000000000..d99ca2c4ae
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_reg_mod/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
+#
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Copyright (c) 2001 by Sun Microsystems, Inc.
+# All rights reserved.
+#
+# cmd/syseventd/modules/sysevent_reg_mod/Makefile
+#
+
+LIBRARY = sysevent_reg_mod
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+all: $(DYNLIB)
+
+install: all \
+ $(ROOTLIBSYSEVENTDIR) \
+ $(ROOTLIBDIR) \
+ $(ROOTLIBS)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/syseventd/modules/sysevent_reg_mod/message_reg_mod.h b/usr/src/cmd/syseventd/modules/sysevent_reg_mod/message_reg_mod.h
new file mode 100644
index 0000000000..dfc38a2a1c
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_reg_mod/message_reg_mod.h
@@ -0,0 +1,55 @@
+/*
+ * 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-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _MESSAGE_REG_MOD_H
+#define _MESSAGE_REG_MOD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#define INIT_SUB_OPEN_CHAN_ERR \
+ gettext("sysevent_reg_mod: Can not open subscriber channel: %s\n")
+
+#define INIT_SUB_BIND_PUB_ERR \
+ gettext("sysevent_reg_mod: Can not bind publisher: %s\n")
+
+#define INIT_SUB_THR_CREATE_ERR \
+ gettext("sysevent_reg_mod: Can not create subscriber " \
+ "deliver thread: %s\n")
+
+#define INIT_ACCESS_ERR \
+ gettext("sysevent_reg_mod: syseventd channel permissions invalid\n")
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MESSAGE_REG_MOD_H */
diff --git a/usr/src/cmd/syseventd/modules/sysevent_reg_mod/sysevent_reg_mod.c b/usr/src/cmd/syseventd/modules/sysevent_reg_mod/sysevent_reg_mod.c
new file mode 100644
index 0000000000..146badfd43
--- /dev/null
+++ b/usr/src/cmd/syseventd/modules/sysevent_reg_mod/sysevent_reg_mod.c
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <stdio.h>
+#include <limits.h>
+#include <thread.h>
+#include <wait.h>
+#include <synch.h>
+#include <errno.h>
+#include <locale.h>
+#include <sys/stat.h>
+#include <sys/mnttab.h>
+#include <sys/sunddi.h>
+#include <sys/modctl.h>
+#include <sys/sysevent.h>
+#include <sys/sysevent_impl.h>
+
+#include <libsysevent.h>
+
+#include "message_reg_mod.h"
+
+/*
+ * SLM for sysevent event subscribers
+ */
+
+extern char *root_dir;
+extern void syseventd_print(int level, char *format, ...);
+extern void syseventd_err_print(char *format, ...);
+
+sysevent_handle_t *sysevent_hp;
+
+typedef struct ev_queue {
+ struct ev_queue *evq_next;
+ sysevent_t *evq_ev;
+} ev_queue_t;
+
+static mutex_t evq_lock;
+static cond_t evq_cv;
+static ev_queue_t *event_q = NULL;
+static int cleanup;
+static thread_t deliver_thr_id;
+
+static int
+init_channel()
+{
+ /*
+ * This functionality is not supported in the mini-root
+ * environment, ie install. If root_dir is set, implying
+ * install, we quietly fail.
+ */
+ if (strcmp(root_dir, "") != 0) {
+ return (EACCES);
+ }
+
+ /*
+ * Initialize the private sysevent handle
+ */
+ sysevent_hp = sysevent_open_channel(SYSEVENTD_CHAN);
+ if (sysevent_hp == NULL) {
+ if (errno == EACCES) {
+ syseventd_print(3, "sysevent_reg_mod: "
+ "sysevent_open_channel failed with %s init "
+ "deferred\n", strerror(errno));
+ return (errno);
+ } else {
+ syseventd_err_print(INIT_SUB_OPEN_CHAN_ERR,
+ strerror(errno));
+ return (errno);
+ }
+ }
+
+ if (sysevent_bind_publisher(sysevent_hp) != 0) {
+ /*
+ * Only one publisher allowed on the syseventd channel,
+ * cleanup previously allocated syseventd channel publishers
+ */
+ if (errno == EBUSY) {
+ sysevent_cleanup_publishers(sysevent_hp);
+ if (sysevent_bind_publisher(sysevent_hp) == 0)
+ return (0);
+ }
+
+ syseventd_err_print(INIT_SUB_BIND_PUB_ERR,
+ strerror(errno));
+ sysevent_close_channel(sysevent_hp);
+ sysevent_hp = NULL;
+ return (errno);
+ }
+
+ return (0);
+}
+
+static int
+deliver_event(sysevent_t *ev, int flag)
+{
+ int ret, ev_size;
+ ev_queue_t *new_evq, *tmp_evq;
+
+ /* Not initialized */
+ if (sysevent_hp == NULL) {
+
+ ret = init_channel();
+ if (ret != 0) {
+ if (ret == EBUSY && flag != SE_NO_RETRY) {
+ return (EAGAIN);
+ } else if (ret == EACCES) {
+ return (0);
+ } else {
+ syseventd_err_print(INIT_SUB_OPEN_CHAN_ERR,
+ strerror(ret));
+ return (0);
+ }
+ }
+ /* Check for stale syseventd subscribers */
+ sysevent_cleanup_subscribers(sysevent_hp);
+ syseventd_print(3, "sysevent_reg_mod: init successful");
+ }
+
+ /* Queue event for delivery to all subscribers */
+ new_evq = (ev_queue_t *)calloc(1, sizeof (ev_queue_t));
+ if (new_evq == NULL) {
+ return (EAGAIN);
+ }
+ ev_size = sysevent_get_size(ev);
+ new_evq->evq_ev = (sysevent_t *)malloc(ev_size);
+ if (new_evq->evq_ev == NULL) {
+ free(new_evq);
+ return (EAGAIN);
+ }
+ bcopy(ev, new_evq->evq_ev, ev_size);
+
+ (void) mutex_lock(&evq_lock);
+ if (event_q == NULL) {
+ event_q = new_evq;
+ } else {
+ tmp_evq = event_q;
+ while (tmp_evq->evq_next != NULL)
+ tmp_evq = tmp_evq->evq_next;
+ tmp_evq->evq_next = new_evq;
+ }
+ syseventd_print(3, "sysevent_reg_mod: queue event 0X%llx\n",
+ sysevent_get_seq(ev));
+
+ (void) cond_signal(&evq_cv);
+ (void) mutex_unlock(&evq_lock);
+
+ return (0);
+}
+
+void
+subscriber_deliver_thr()
+{
+ ev_queue_t *evqp;
+
+ (void) mutex_lock(&evq_lock);
+ for (;;) {
+ while (event_q == NULL && cleanup == 0) {
+ (void) cond_wait(&evq_cv, &evq_lock);
+ }
+
+ /* Send events on to all current subscribers */
+ evqp = event_q;
+ while (evqp) {
+ (void) mutex_unlock(&evq_lock);
+ syseventd_print(3, "sysevent_reg_mod: sending event "
+ "0X%llx\n", sysevent_get_seq(evqp->evq_ev));
+ if (sysevent_send_event(sysevent_hp,
+ evqp->evq_ev) != 0) {
+ syseventd_print(3, "sysevent_reg_mod: "
+ "failed to send event\n");
+ }
+ syseventd_print(3, "sysevent_reg_mod: event sent "
+ "0X%llx\n", sysevent_get_seq(evqp->evq_ev));
+ (void) mutex_lock(&evq_lock);
+ event_q = evqp->evq_next;
+ free(evqp->evq_ev);
+ free(evqp);
+ evqp = event_q;
+ }
+ if (cleanup) {
+ syseventd_print(3, "sysevent_reg_mod: deliver "
+ "thread exiting\n");
+ (void) mutex_unlock(&evq_lock);
+ (void) thr_exit(NULL);
+ /* NOTREACHED */
+ }
+ }
+
+ /* NOTREACHED */
+}
+
+static struct slm_mod_ops sysevent_reg_mod_ops = {
+ SE_MAJOR_VERSION, SE_MINOR_VERSION, SE_MAX_RETRY_LIMIT, deliver_event};
+
+struct slm_mod_ops *
+slm_init()
+{
+ cleanup = 0;
+ sysevent_hp = NULL;
+
+ (void) init_channel();
+
+ (void) mutex_init(&evq_lock, USYNC_THREAD, NULL);
+ (void) cond_init(&evq_cv, USYNC_THREAD, NULL);
+
+ if (thr_create(NULL, NULL, (void *(*)(void *))subscriber_deliver_thr,
+ NULL, 0, &deliver_thr_id) != 0) {
+ syseventd_err_print(INIT_SUB_THR_CREATE_ERR, strerror(errno));
+ return (NULL);
+ }
+
+ return (&sysevent_reg_mod_ops);
+}
+
+void
+slm_fini()
+{
+ (void) mutex_lock(&evq_lock);
+ cleanup = 1;
+ (void) cond_signal(&evq_cv);
+ (void) mutex_unlock(&evq_lock);
+
+ /* Wait for delivery threads to exit */
+ (void) thr_join(deliver_thr_id, NULL, NULL);
+
+ (void) mutex_destroy(&evq_lock);
+ (void) cond_destroy(&evq_cv);
+
+ sysevent_close_channel(sysevent_hp);
+ sysevent_hp = NULL;
+}
diff --git a/usr/src/cmd/syseventd/svc-syseventd b/usr/src/cmd/syseventd/svc-syseventd
new file mode 100644
index 0000000000..8c54a045fe
--- /dev/null
+++ b/usr/src/cmd/syseventd/svc-syseventd
@@ -0,0 +1,78 @@
+#!/sbin/sh
+#
+# 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"
+
+. /lib/svc/share/smf_include.sh
+
+case "$1" in
+'start')
+ if /usr/lib/sysevent/syseventd >/dev/msglog 2>&1; then :; else
+ exit
+ fi
+
+ # Establish the default framebuffer name.
+
+ fbdev=`/usr/sbin/prtconf -F 2>/dev/null`
+
+ if [ $? -eq 0 ]; then
+ set -- /devices$fbdev*
+ if [ -c $1 ]; then
+ rm -f /dev/fb
+ ln -s $1 /dev/fb
+ fi
+ fi
+ ;;
+
+#
+# devfsadmd and syseventconfd are started on-demand
+# by syseventd. syseventd should be stopped before devfsadm and syseventconfd.
+#
+'stop')
+ dpid=`/usr/bin/pgrep devfsadm`
+ scpid=`/usr/bin/pgrep syseventconfd`
+ spid=`/usr/bin/pgrep syseventd`
+ zone=`/sbin/zonename`
+ /usr/bin/pkill -x -u 0 -P 1 -z $zone rcm_daemon
+ /usr/bin/pkill -x -u 0 -P 1 -z $zone syseventd
+ /usr/bin/pkill -x -u 0 -P 1 -z $zone devfsadm
+ /usr/bin/pkill -x -u 0 -P 1 -z $zone syseventconfd
+
+ #
+ # Since pkill is not atomic (may miss forking processes), also
+ # kill entire service contract.
+ #
+ smf_kill_contract $2 TERM 1
+ [ $? -ne 0 ] && exit 1
+ ;;
+
+*)
+ echo "Usage: $0 { start | stop }"
+ exit 1
+ ;;
+
+esac
+exit 0
diff --git a/usr/src/cmd/syseventd/sysevent.xml b/usr/src/cmd/syseventd/sysevent.xml
new file mode 100644
index 0000000000..36a4674e84
--- /dev/null
+++ b/usr/src/cmd/syseventd/sysevent.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ 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"
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+ Service manifest for syseventd.
+-->
+
+<service_bundle type='manifest' name='SUNWcsd:syseventd'>
+
+<service
+ name='system/sysevent'
+ type='service'
+ version='1'>
+
+ <create_default_instance enabled='true' />
+
+ <single_instance />
+
+ <dependency
+ name='usr'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/filesystem/minimal' />
+ </dependency>
+
+ <dependent
+ name='syseventd_single-user'
+ grouping='require_all'
+ restart_on='none'>
+ <service_fmri value='svc:/milestone/single-user' />
+ </dependent>
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/lib/svc/method/svc-syseventd %m'
+ timeout_seconds='60' />
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec='/lib/svc/method/svc-syseventd %m %{restarter/contract}'
+ timeout_seconds='60' />
+
+ <stability value='Unstable' />
+
+ <template>
+ <common_name>
+ <loctext xml:lang='C'>
+ system event notification
+ </loctext>
+ </common_name>
+
+ <documentation>
+ <manpage
+ title='syseventd'
+ section='1M'
+ manpath='/usr/share/man' />
+ </documentation>
+ </template>
+</service>
+
+</service_bundle>