diff options
Diffstat (limited to 'usr/src/cmd/syseventd/modules')
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; +} |
