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