summaryrefslogtreecommitdiff
path: root/usr/src/cmd/syseventd/modules
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/syseventd/modules')
-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
12 files changed, 3585 insertions, 0 deletions
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;
+}