1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * sysevent_conf_mod - syseventd daemon sysevent.conf module
28 *
29 *	This module provides a configuration file registration
30 *	mechanism whereby event producers can define an event
31 *	specification to be matched against events, with an
32 *	associated command line to be invoked for each matching event.
33 *	It includes a simple macro capability for flexibility in
34 *	generating arbitrary command line formats from event-associated
35 *	data, and a user specification so that commands can be invoked
36 *	with reduced privileges to eliminate a security risk.
37 *
38 *	sysevent.conf files contain event specifications and associated
39 *	command path and optional arguments.  System events received
40 *	from the kernel by the sysevent daemon, syseventd, are
41 *	compared against the event specifications in the sysevent.conf
42 *	files.  The command as specified by pathname and arguments
43 *	is invoked for each matching event.
44 *
45 *	All sysevent.conf files reside in /etc/sysevent/config.
46 *
47 */
48
49
50#include <stdio.h>
51
52#include <unistd.h>
53#include <stdarg.h>
54#include <stdlib.h>
55#include <string.h>
56#include <strings.h>
57#include <limits.h>
58#include <thread.h>
59#include <synch.h>
60#include <errno.h>
61#include <fcntl.h>
62#include <ctype.h>
63#include <pwd.h>
64#include <syslog.h>
65#include <sys/types.h>
66#include <sys/stat.h>
67#include <sys/sunddi.h>
68#include <sys/sysevent.h>
69#include <libsysevent.h>
70#include <libnvpair.h>
71#include <dirent.h>
72#include <locale.h>
73#include <signal.h>
74#include <wait.h>
75
76#include "syseventd.h"
77#include "syseventconfd_door.h"
78#include "sysevent_conf_mod.h"
79#include "message_conf_mod.h"
80
81
82static char	*whoami = "sysevent_conf_mod";
83
84/*
85 * Event sequencing, time stamp and retry count
86 */
87static int	ev_nretries;		/* retry count per event */
88static uint64_t	ev_seq;			/* current event sequencing number */
89static hrtime_t ev_ts;			/* current event timestamp */
90static int	first_event;		/* first event since init */
91
92/*
93 * State of the sysevent conf table, derived from
94 * the /etc/sysevent/config files
95 */
96static conftab_t		*conftab		= NULL;
97static syseventtab_t		*syseventtab		= NULL;
98static syseventtab_t		*syseventtab_tail	= NULL;
99static sysevent_handle_t	*confd_handle		= NULL;
100
101/*
102 * The cmd queue is a queue of commands ready to be sent
103 * to syseventconfd.  Each command consists of the path
104 * and arguments to be fork/exec'ed.  The daemon is unable
105 * to handle events during an active fork/exec and returns
106 * EAGAIN as a result.  It is grossly inefficient to bounce
107 * these events back to syseventd, so we queue them here for delivery.
108 */
109static cmdqueue_t		*cmdq		= NULL;
110static cmdqueue_t		*cmdq_tail	= NULL;
111static mutex_t			cmdq_lock;
112static cond_t			cmdq_cv;
113static int			cmdq_cnt;
114static thread_t			cmdq_thr_id;
115static cond_t			cmdq_thr_cv;
116static int			want_fini;
117
118/*
119 * State of the door channel to syseventconfd
120 */
121static int	confd_state	= CONFD_STATE_NOT_RUNNING;
122
123/*
124 * Number of times to retry event after restarting syeventconfd
125 */
126static int	confd_retries;
127
128/*
129 * Number of times to retry a failed transport
130 */
131static int	transport_retries;
132
133/*
134 * Normal sleep time when syseventconfd returns EAGAIN
135 * is one second but to avoid thrashing, sleep for
136 * something larger when syseventconfd not responding.
137 * This should never happen of course but it seems better
138 * to attempt to handle possible errors gracefully.
139 */
140static int	confd_err_msg_emitted;
141
142
143static int sysevent_conf_dummy_event(sysevent_t *, int);
144
145/*
146 * External references
147 */
148extern int	debug_level;
149extern char	*root_dir;
150extern void	syseventd_print(int level, char *format, ...);
151extern void	syseventd_err_print(char *format, ...);
152
153
154
155static struct slm_mod_ops sysevent_conf_mod_ops = {
156	SE_MAJOR_VERSION,		/* syseventd module major version */
157	SE_MINOR_VERSION,		/* syseventd module minor version */
158	SE_MAX_RETRY_LIMIT,		/* max retry if EAGAIN */
159	&sysevent_conf_event		/* event handler */
160};
161
162static struct slm_mod_ops sysevent_conf_dummy_mod_ops = {
163	SE_MAJOR_VERSION,		/* syseventd module major version */
164	SE_MINOR_VERSION,		/* syseventd module minor version */
165	0,				/* no retries, always succeeds */
166	&sysevent_conf_dummy_event	/* dummy event handler */
167};
168
169
170
171/*
172 * skip_spaces() - skip to next non-space character
173 */
174static char *
175skip_spaces(char **cpp)
176{
177	char *cp = *cpp;
178
179	while (*cp == ' ' || *cp == '\t')
180		cp++;
181	if (*cp == 0) {
182		*cpp = 0;
183		return (NULL);
184	}
185	return (cp);
186}
187
188
189/*
190 * Get next white-space separated field.
191 * next_field() will not check any characters on next line.
192 * Each entry is composed of a single line.
193 */
194static char *
195next_field(char **cpp)
196{
197	char *cp = *cpp;
198	char *start;
199
200	while (*cp == ' ' || *cp == '\t')
201		cp++;
202	if (*cp == 0) {
203		*cpp = 0;
204		return (NULL);
205	}
206	start = cp;
207	while (*cp && *cp != ' ' && *cp != '\t')
208		cp++;
209	if (*cp != 0)
210		*cp++ = 0;
211	*cpp = cp;
212	return (start);
213}
214
215
216
217/*
218 * The following functions are simple wrappers/equivalents
219 * for malloc, realloc, free, strdup and a special free
220 * for strdup.
221 *
222 * These functions ensure that any failed mallocs are
223 * reported via syslog() so if a command is not evoked
224 * in response to an event, the reason should be logged.
225 * These functions also provide a convenient place for
226 * hooks for checking for memory leaks.
227 */
228
229static void *
230sc_malloc(size_t n)
231{
232	void *p;
233
234	p = malloc(n);
235	if (p == NULL) {
236		syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
237	}
238	return (p);
239}
240
241/*ARGSUSED*/
242static void *
243sc_realloc(void *p, size_t current, size_t n)
244{
245	p = realloc(p, n);
246	if (p == NULL) {
247		syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
248	}
249	return (p);
250}
251
252
253/*ARGSUSED*/
254static void
255sc_free(void *p, size_t n)
256{
257	free(p);
258}
259
260
261static char *
262sc_strdup(char *cp)
263{
264	char *new;
265
266	new = malloc((unsigned)(strlen(cp) + 1));
267	if (new == NULL) {
268		syslog(LOG_ERR, OUT_OF_MEMORY_ERR);
269		return (NULL);
270	}
271	(void) strcpy(new, cp);
272	return (new);
273}
274
275
276static void
277sc_strfree(char *s)
278{
279	if (s)
280		free(s);
281}
282
283
284/*
285 * The following functions provide some simple dynamic string
286 * capability.  This module has no hard-coded maximum string
287 * lengths and should be able to parse and generate arbitrarily
288 * long strings, macro expansion and command lines.
289 *
290 * Each string must be explicitly allocated and freed.
291 */
292
293/*
294 * Allocate a dynamic string, with a hint to indicate how
295 * much memory to dynamically add to the string as it grows
296 * beyond its existing bounds, so as to avoid excessive
297 * reallocs as a string grows.
298 */
299static str_t *
300initstr(int hint)
301{
302	str_t	*str;
303
304	if ((str = sc_malloc(sizeof (str_t))) == NULL)
305		return (NULL);
306	str->s_str = NULL;
307	str->s_len = 0;
308	str->s_alloc = 0;
309	str->s_hint = hint;
310	return (str);
311}
312
313
314/*
315 * Free a dynamically-allocated string
316 */
317static void
318freestr(str_t *str)
319{
320	if (str->s_str) {
321		sc_free(str->s_str, str->s_alloc);
322	}
323	sc_free(str, sizeof (str_t));
324}
325
326
327/*
328 * Reset a dynamically-allocated string, allows reuse
329 * rather than freeing the old and allocating a new one.
330 */
331static void
332resetstr(str_t *str)
333{
334	str->s_len = 0;
335}
336
337
338/*
339 * Copy a (simple) string onto a dynamically-allocated string
340 */
341static int
342strcopys(str_t *str, char *s)
343{
344	char	*new_str;
345	int	len = strlen(s) + 1;
346
347	if (str->s_alloc < len) {
348		new_str = (str->s_str == NULL) ?
349			sc_malloc(len+str->s_hint) :
350			sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
351		if (new_str == NULL) {
352			return (1);
353		}
354		str->s_str = new_str;
355		str->s_alloc = len + str->s_hint;
356	}
357	(void) strcpy(str->s_str, s);
358	str->s_len = len - 1;
359	return (0);
360}
361
362
363/*
364 * Concatenate a (simple) string onto a dynamically-allocated string
365 */
366static int
367strcats(str_t *str, char *s)
368{
369	char	*new_str;
370	int	len = str->s_len + strlen(s) + 1;
371
372	if (str->s_alloc < len) {
373		new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) :
374			sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
375		if (new_str == NULL) {
376			return (1);
377		}
378		str->s_str = new_str;
379		str->s_alloc = len + str->s_hint;
380	}
381	(void) strcpy(str->s_str + str->s_len, s);
382	str->s_len = len - 1;
383	return (0);
384}
385
386
387/*
388 * Concatenate a character onto a dynamically-allocated string
389 */
390static int
391strcatc(str_t *str, int c)
392{
393	char	*new_str;
394	int	len = str->s_len + 2;
395
396	if (str->s_alloc < len) {
397		new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) :
398			sc_realloc(str->s_str, str->s_alloc, len+str->s_hint);
399		if (new_str == NULL) {
400			return (1);
401		}
402		str->s_str = new_str;
403		str->s_alloc = len + str->s_hint;
404	}
405	*(str->s_str + str->s_len) = (char)c;
406	*(str->s_str + str->s_len + 1) = 0;
407	str->s_len++;
408	return (0);
409}
410
411/*
412 * fgets() equivalent using a dynamically-allocated string
413 */
414static char *
415fstrgets(str_t *line, FILE *fp)
416{
417	int	c;
418
419	resetstr(line);
420	while ((c = fgetc(fp)) != EOF) {
421		if (strcatc(line, c))
422			return (NULL);
423		if (c == '\n')
424			break;
425	}
426	if (line->s_len == 0)
427		return (NULL);
428	return (line->s_str);
429}
430
431/*
432 * Truncate a dynamically-allocated string at index position 'pos'
433 */
434static void
435strtrunc(str_t *str, int pos)
436{
437	if (str->s_len > pos) {
438		str->s_len = pos;
439		*(str->s_str + pos) = 0;
440	}
441}
442
443
444
445/*
446 * Parse a sysevent.conf file, adding each entry spec to the event table.
447 *
448 * The format of an entry in a sysevent.conf file is:
449 *
450 *    class subclass vendor publisher user reserved1 reserved path arguments
451 *
452 * Fields are separated by either SPACE or TAB characters.  A
453 * '#' (number sign) at the beginning of a line indicates a
454 * comment.  Comment lines and blank lines are ignored.
455 *
456 * class
457 *    The class of the event.
458 *
459 * subclass
460 *    The subclass of the event.
461 *
462 * vendor
463 *    The name of the vendor defining the event, usually the
464 *    stock symbol.  Events generated by system components
465 *    provided by Sun Microsystems, Inc.  always define vendor
466 *    as 'SUNW'.
467 *
468 * publisher
469 *    The name of the application, driver or system module
470 *    producing the event.
471 *
472 * user
473 *    The name of the user under which the command should be
474 *    run.  This allows commands to run with access privileges
475 *    other than those for root.  The user field should be '-'
476 *    for commands to be run as root.
477 *
478 * reserved1
479 *    Must be '-'.
480 *
481 * reserved2
482 *    Must be '-'.
483 *
484 * path
485 *    Pathname of the command to be invoked for matching events.
486 *
487 * arguments
488 *    Optional argument with possible macro substitution to permit
489 *    arbitrary command line construction with event-specific data.
490 */
491static void
492parse_conf_file(char *conf_file)
493{
494	char	conf_path[PATH_MAX];
495	FILE	*fp;
496	char	*lp;
497	str_t	*line;
498	int	lineno = 0;
499	char	*vendor, *publisher;
500	char	*class, *subclass;
501	char	*user;
502	char	*reserved1, *reserved2;
503	char	*path, *args;
504	syseventtab_t *sep;
505	struct passwd pwd;
506	struct passwd *pwdp;
507	char	pwdbuf[1024];
508	int	do_setuid;
509	pid_t	saved_uid;
510	gid_t	saved_gid;
511	int	i, err;
512
513	(void) snprintf(conf_path, PATH_MAX, "%s/%s",
514		SYSEVENT_CONFIG_DIR, conf_file);
515
516	syseventd_print(DBG_CONF_FILE, "%s: reading %s\n", whoami, conf_path);
517
518	if ((fp = fopen(conf_path, "r")) == NULL) {
519		syslog(LOG_ERR, CANNOT_OPEN_ERR, conf_file, strerror(errno));
520		return;
521	}
522
523	if ((line = initstr(128)) == NULL)
524		return;
525
526	while ((lp = fstrgets(line, fp)) != NULL) {
527		lineno++;
528		if (*lp == '\n' || *lp == '#')
529			continue;
530		*(lp + strlen(lp)-1) = 0;
531
532		syseventd_print(DBG_CONF_FILE, "[%d]: %s\n",
533			lineno, lp);
534
535		if ((class = next_field(&lp)) == NULL)
536			goto mal_formed;
537		if ((subclass = next_field(&lp)) == NULL)
538			goto mal_formed;
539		if ((vendor = next_field(&lp)) == NULL)
540			goto mal_formed;
541		if ((publisher = next_field(&lp)) == NULL)
542			goto mal_formed;
543		if ((user = next_field(&lp)) == NULL)
544			goto mal_formed;
545		if ((reserved1 = next_field(&lp)) == NULL)
546			goto mal_formed;
547		if ((reserved2 = next_field(&lp)) == NULL)
548			goto mal_formed;
549		if ((path = next_field(&lp)) == NULL)
550			goto mal_formed;
551		args = skip_spaces(&lp);
552
553		/*
554		 * validate user
555		 */
556		do_setuid = 0;
557		if ((strcmp(user, "-") != 0) && (strcmp(user, "root") != 0)) {
558			i = getpwnam_r(user, &pwd, pwdbuf,
559					sizeof (pwdbuf), &pwdp);
560			if (i != 0 || pwdp == NULL) {
561				syslog(LOG_ERR, NO_USER_ERR,
562					conf_file, lineno, user);
563				continue;
564			}
565			do_setuid = 1;
566		}
567
568		/*
569		 * validate reserved fields
570		 */
571		if (strcmp(reserved1, "-") != 0) {
572			syslog(LOG_ERR, RESERVED_FIELD_ERR,
573				conf_file, lineno, reserved1);
574			continue;
575		}
576		if (strcmp(reserved2, "-") != 0) {
577			syslog(LOG_ERR, RESERVED_FIELD_ERR,
578				conf_file, lineno, reserved2);
579			continue;
580		}
581
582		/*
583		 * ensure path is executable by user
584		 */
585		err = 0;
586		if (do_setuid) {
587			saved_uid = getuid();
588			saved_gid = getgid();
589			if (setregid(pwdp->pw_gid, -1) == -1) {
590				syslog(LOG_ERR, SETREGID_ERR,
591					whoami, pwdp->pw_gid, strerror(errno));
592				err = -1;
593			}
594			if (setreuid(pwdp->pw_uid, -1) == -1) {
595				syslog(LOG_ERR, SETREUID_ERR,
596					whoami, pwdp->pw_uid, strerror(errno));
597				err = -1;
598			}
599		}
600		if ((i = access(path, X_OK)) == -1) {
601			syslog(LOG_ERR, CANNOT_EXECUTE_ERR,
602				conf_file, lineno, path, strerror(errno));
603		}
604		if (do_setuid) {
605			if (setreuid(saved_uid, -1) == -1) {
606				syslog(LOG_ERR, SETREUID_ERR,
607					whoami, saved_uid, strerror(errno));
608				err = -1;
609			}
610			if (setregid(saved_gid, -1) == -1) {
611				syslog(LOG_ERR, SETREGID_ERR,
612					whoami, saved_gid, strerror(errno));
613				err = -1;
614			}
615		}
616		if (i == -1 || err == -1)
617			continue;
618
619		/*
620		 * all sanity tests successful - perform allocations
621		 * to add entry to table
622		 */
623		if ((sep = sc_malloc(sizeof (syseventtab_t))) == NULL)
624			break;
625
626		sep->se_conf_file = conf_file;
627		sep->se_lineno = lineno;
628		sep->se_vendor = sc_strdup(vendor);
629		sep->se_publisher = sc_strdup(publisher);
630		sep->se_class = sc_strdup(class);
631		sep->se_subclass = sc_strdup(subclass);
632		sep->se_user = sc_strdup(user);
633		if (do_setuid) {
634			sep->se_uid = pwdp->pw_uid;
635			sep->se_gid = pwdp->pw_gid;
636		} else {
637			sep->se_uid = 0;
638			sep->se_gid = 0;
639		}
640		sep->se_reserved1 = sc_strdup(reserved1);
641		sep->se_reserved2 = sc_strdup(reserved2);
642		sep->se_path = sc_strdup(path);
643		sep->se_args = (args == NULL) ? NULL : sc_strdup(args);
644		sep->se_next = NULL;
645
646		if (sep->se_vendor == NULL || sep->se_publisher == NULL ||
647		    sep->se_class == NULL || sep->se_subclass == NULL ||
648		    sep->se_user == NULL || sep->se_reserved1 == NULL ||
649		    sep->se_reserved2 == NULL || sep->se_path == NULL ||
650		    (args && sep->se_args == NULL)) {
651			sc_strfree(sep->se_vendor);
652			sc_strfree(sep->se_publisher);
653			sc_strfree(sep->se_class);
654			sc_strfree(sep->se_subclass);
655			sc_strfree(sep->se_user);
656			sc_strfree(sep->se_reserved1);
657			sc_strfree(sep->se_reserved2);
658			sc_strfree(sep->se_path);
659			sc_strfree(sep->se_args);
660			sc_free(sep, sizeof (syseventtab_t));
661			break;
662		}
663
664		/*
665		 * link new entry into the table
666		 */
667		if (syseventtab == NULL) {
668			syseventtab = sep;
669			syseventtab_tail = sep;
670		} else {
671			syseventtab_tail->se_next = sep;
672			syseventtab_tail = sep;
673		}
674
675		if (debug_level >= DBG_DETAILED) {
676			syseventtab_t *sp;
677			for (sp = syseventtab; sp; sp = sp->se_next) {
678				syseventd_print(DBG_DETAILED,
679					"    vendor=%s\n", sp->se_vendor);
680				syseventd_print(DBG_DETAILED,
681					"    publisher=%s\n", sp->se_publisher);
682				syseventd_print(DBG_DETAILED,
683					"    class=%s\n", sp->se_class);
684				syseventd_print(DBG_DETAILED,
685					"    subclass=%s\n", sp->se_subclass);
686				syseventd_print(DBG_DETAILED,
687					"    user=%s uid=%d gid=%d\n",
688					sp->se_user, sp->se_uid, sp->se_gid);
689				syseventd_print(DBG_DETAILED,
690					"    reserved1=%s\n", sp->se_reserved1);
691				syseventd_print(DBG_DETAILED,
692					"    reserved2=%s\n", sp->se_reserved2);
693				syseventd_print(DBG_DETAILED,
694					"    path=%s\n", sp->se_path);
695				if (sp->se_args != NULL) {
696					syseventd_print(DBG_DETAILED,
697						"    args=%s\n", sp->se_args);
698				}
699			}
700		}
701
702		continue;
703
704mal_formed:
705		syslog(LOG_ERR, SYNTAX_ERR, conf_file, lineno);
706	}
707
708	freestr(line);
709	(void) fclose(fp);
710}
711
712
713/*
714 * Build the events specification table, a summation of all
715 * event specification found in the installed sysevent.conf
716 * configuration files.
717 *
718 * All sysevent.conf files reside in the /etc/sysevent/config
719 * and may contain zero or more event/command specifications.
720 * A sysevent.conf file should be named as follows:
721 *
722 *        <vendor>,[<publisher>,][<class>,]sysevent.conf
723 *
724 * Event/command specifications delivered by the base Solaris
725 * system are provided in /etc/sysevent/config/SUNW,sysevent.conf.
726 * Event/command specifications delivered by optional
727 * Sun-supplied packages may install additional sysevent.conf
728 * files in /etc/sysevent/config using vendor SUNW, and additional
729 * publisher and/or event class naming to distinguish the
730 * events required for those products.  Products provided
731 * by third-party hardware or software companies may
732 * distinguish their sysevent.conf files by vendor, and
733 * by publisher and/or event class within vendor.
734 *
735 * Files residing in /etc/sysevent/config with a '.' (period)
736 * as the first character of the name and files with a suffix
737 * of other than "sysevent.conf" are ignored.
738 */
739static void
740build_event_table()
741{
742	conftab_t	*cfp = NULL;
743	DIR		*dir;
744	struct dirent	*result;
745	conftab_t	*new_cfp;
746	char		*str;
747
748	if ((dir = opendir(SYSEVENT_CONFIG_DIR)) == NULL) {
749		syslog(LOG_ERR, CANNOT_OPEN_ERR,
750			SYSEVENT_CONFIG_DIR, strerror(errno));
751		return;
752	}
753
754	while ((result = readdir(dir)) != NULL) {
755		if (result->d_name[0] == '.')
756			continue;
757
758		/*
759		 * file must have extension "sysevent.conf"
760		 */
761		if ((str = strrchr(result->d_name, ',')) != NULL) {
762			str++;
763		} else {
764			str = result->d_name;
765		}
766		if (strcmp(str, "sysevent.conf") != 0) {
767			syseventd_print(DBG_CONF_FILE,
768				"%s: ignoring %s\n", whoami, str);
769			continue;
770		}
771
772		/*
773		 * Add to file table and parse this conf file
774		 */
775		if ((str = sc_strdup(result->d_name)) == NULL)
776			goto err;
777		if ((new_cfp = sc_malloc(sizeof (conftab_t))) == NULL) {
778			sc_strfree(str);
779			goto err;
780		}
781		if (conftab == NULL) {
782			conftab = new_cfp;
783		} else {
784			for (cfp = conftab; cfp->cf_next; cfp = cfp->cf_next)
785				;
786			cfp->cf_next = new_cfp;
787		}
788		cfp = new_cfp;
789		cfp->cf_conf_file = str;
790		cfp->cf_next = NULL;
791
792		parse_conf_file(cfp->cf_conf_file);
793	}
794
795err:
796	if (closedir(dir) == -1) {
797		if (errno == EAGAIN)
798			goto err;
799		syslog(LOG_ERR, CLOSEDIR_ERR,
800			SYSEVENT_CONFIG_DIR, strerror(errno));
801	}
802}
803
804
805static int
806enter_lock(char *lock_file)
807{
808	struct flock	lock;
809	int		lock_fd;
810
811	(void) strlcpy(lock_file, LOCK_FILENAME, PATH_MAX);
812	lock_fd = open(lock_file, O_CREAT|O_RDWR, 0644);
813	if (lock_fd < 0) {
814		syslog(LOG_ERR, MSG_LOCK_CREATE_ERR,
815			whoami, lock_file, strerror(errno));
816		return (-1);
817	}
818
819	lock.l_type = F_WRLCK;
820	lock.l_whence = SEEK_SET;
821	lock.l_start = 0;
822	lock.l_len = 0;
823
824retry:
825	if (fcntl(lock_fd, F_SETLKW, &lock) == -1) {
826		if (errno == EAGAIN || errno == EINTR)
827			goto retry;
828		(void) close(lock_fd);
829		syslog(LOG_ERR, MSG_LOCK_SET_ERR,
830			whoami, lock_file, strerror(errno));
831		return (-1);
832	}
833
834	return (lock_fd);
835}
836
837
838static void
839exit_lock(int lock_fd, char *lock_file)
840{
841	struct flock	lock;
842
843	lock.l_type = F_UNLCK;
844	lock.l_whence = SEEK_SET;
845	lock.l_start = 0;
846	lock.l_len = 0;
847
848	if (fcntl(lock_fd, F_SETLK, &lock) == -1) {
849		syslog(LOG_ERR, MSG_LOCK_CLR_ERR,
850			whoami, lock_file, strerror(errno));
851	}
852
853	if (close(lock_fd) == -1) {
854		syslog(LOG_ERR, MSG_LOCK_CLOSE_ERR,
855			whoami, lock_file, strerror(errno));
856	}
857}
858
859
860/*
861 * Free the events specification table, constructed by
862 * parsing all the sysevent.conf files found.
863 *
864 * The free of this table is in response to a HUP
865 * given to the syseventd daemon, permitting the
866 * table to be rebuilt after adding a new sysevent.conf
867 * file or changing an existing one without shutting
868 * down the daemon.
869 */
870static void
871free_event_table()
872{
873	syseventtab_t *sep;
874	syseventtab_t *sep_next;
875	conftab_t *cfp;
876	conftab_t *cfp_next;
877
878	sep = syseventtab;
879	while (sep) {
880		sc_strfree(sep->se_vendor);
881		sc_strfree(sep->se_publisher);
882		sc_strfree(sep->se_class);
883		sc_strfree(sep->se_subclass);
884		sc_strfree(sep->se_user);
885		sc_strfree(sep->se_reserved1);
886		sc_strfree(sep->se_reserved2);
887		sc_strfree(sep->se_path);
888		if (sep->se_args)
889			sc_strfree(sep->se_args);
890		sep_next = sep->se_next;
891		sc_free(sep, sizeof (syseventtab_t));
892		sep = sep_next;
893	}
894	syseventtab = NULL;
895
896	cfp = conftab;
897	while (cfp) {
898		sc_strfree(cfp->cf_conf_file);
899		cfp_next = cfp->cf_next;
900		sc_free(cfp, sizeof (conftab_t));
901		cfp = cfp_next;
902	}
903	conftab = NULL;
904}
905
906
907
908static char ident_chars[] = "_";
909
910/*
911 * Return a dynamically-allocated string containing the
912 * the next identifier in the string being parsed, pointed
913 * at by 'id'.  'end' returns a pointer to the character
914 * after the identifier.
915 *
916 * Identifiers are all alphanumeric ascii characters and
917 * those contained in ident_chars.
918 *
919 * The returned string must be explicitly freed via
920 * freestr().
921 */
922static str_t *
923snip_identifier(char *id, char **end)
924{
925	str_t	*token;
926
927	if ((token = initstr(32)) == NULL)
928		return (NULL);
929
930	while (*id != 0) {
931		if (isascii(*id) &&
932		    (isalnum(*id) || strchr(ident_chars, *id) != NULL)) {
933			if (strcatc(token, *id++)) {
934				freestr(token);
935				return (NULL);
936			}
937		} else {
938			*end = id;
939			return (token);
940		}
941	}
942
943	*end = id;
944	return (token);
945}
946
947
948/*
949 * Identical to snip_identifier(), but the identifier
950 * is delimited by the characters { and }.
951 */
952static str_t *
953snip_delimited_identifier(char *id, char **end)
954{
955	str_t	*token;
956
957	if ((token = initstr(32)) == NULL)
958		return (NULL);
959
960	while (*id != 0) {
961		if (*id == '}') {
962			*end = id+1;
963			return (token);
964		}
965		if (strcatc(token, *id++)) {
966			freestr(token);
967			return (NULL);
968		}
969	}
970
971	if (*id == 0) {
972		freestr(token);
973		return (NULL);
974	}
975
976	*end = id;
977	return (token);
978}
979
980
981/*
982 * Return a string with the name of the attribute type
983 */
984static char *nv_attr_type_strings[] = {
985	"unknown",
986	"boolean",
987	"byte",
988	"int16",
989	"uint16",
990	"int32",
991	"uint32",
992	"int64",
993	"uint64",
994	"string",
995	"byte-array",
996	"int16-array",
997	"uint16-array",
998	"int32-array",
999	"uint32-array",
1000	"int64-array",
1001	"uint64-array",
1002	"string-array",
1003	"hrtime"
1004};
1005
1006static char *
1007se_attr_type_to_str(int se_attr_type)
1008{
1009	if (se_attr_type >= 0 &&
1010	    se_attr_type < sizeof (nv_attr_type_strings) / sizeof (char *)) {
1011		return (nv_attr_type_strings[se_attr_type]);
1012	}
1013	return (nv_attr_type_strings[DATA_TYPE_UNKNOWN]);
1014}
1015
1016
1017/*
1018 * Find and return the data matching the macro name 'token'
1019 *
1020 * Predefined macros are simply substituted with the
1021 * data from the event header:
1022 *
1023 *	$vendor - the vendor string defining the event.
1024 *
1025 *	$publisher - the publisher string defining the event.
1026 *
1027 *	$class - the class string defining the event.
1028 *
1029 *	$subclass - the subclass string defining the event.
1030 *
1031 *	$sequence - the sequence number of the event.
1032 *
1033 *	$timestamp - the timestamp of the event.
1034 *
1035 * Attributes with signed data types (DATA_TYPE_INT16,
1036 * DATA_TYPE_INT32 and DATA_TYPE_INT64) are expanded
1037 * as decimal digits.
1038 *
1039 * Attributes with unsigned data types (DATA_TYPE_BYTE,
1040 * DATA_TYPE_UINT16, DATA_TYPE_UINT32, DATA_TYPE_UINT64 and
1041 * DATA_TYPE_HTTIME) are expanded as hexadecimal digits
1042 * with a "0x" prefix.
1043 *
1044 * Attributes with string data type (DATA_TYPE_STRING)
1045 * are expanded with the string data.  The data is
1046 * not quoted.  If if it desired that the quoted strings
1047 * be generated on the command line, put quotes around
1048 * the macro call in the arguments.
1049 *
1050 * Array types are expanded with each element expanded
1051 * as defined for that scalar type, with a space separating
1052 * each element substitution.
1053 */
1054
1055static str_t *
1056find_macro_definition(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep,
1057	char *token, sysevent_hdr_info_t *hdr)
1058{
1059	nvpair_t		*nvp;
1060	int			nmatches;
1061	char			num[64];
1062	str_t			*replacement;
1063	int			i;
1064	uint_t			nelems;
1065	union {
1066		uchar_t		x_byte;
1067		int16_t		x_int16;
1068		uint16_t	x_uint16;
1069		int32_t		x_int32;
1070		uint32_t	x_uint32;
1071		int64_t		x_int64;
1072		uint64_t	x_uint64;
1073		hrtime_t	x_time;
1074		char		*x_string;
1075		uchar_t		*x_byte_array;
1076		int16_t		*x_int16_array;
1077		int32_t		*x_int32_array;
1078		int64_t		*x_int64_array;
1079		uint16_t	*x_uint16_array;
1080		uint32_t	*x_uint32_array;
1081		uint64_t	*x_uint64_array;
1082		char		**x_string_array;
1083	} x;
1084
1085
1086	if ((replacement = initstr(128)) == NULL) {
1087		return (NULL);
1088	}
1089
1090	if (strcmp(token, "vendor") == 0) {
1091		if (strcopys(replacement, hdr->vendor)) {
1092			freestr(replacement);
1093			return (NULL);
1094		}
1095		return (replacement);
1096	}
1097
1098	if (strcmp(token, "publisher") == 0) {
1099		if (strcopys(replacement, hdr->publisher)) {
1100			freestr(replacement);
1101			return (NULL);
1102		}
1103		return (replacement);
1104	}
1105
1106	if (strcmp(token, "class") == 0) {
1107		if (strcopys(replacement, hdr->class)) {
1108			freestr(replacement);
1109			return (NULL);
1110		}
1111		return (replacement);
1112	}
1113
1114	if (strcmp(token, "subclass") == 0) {
1115		if (strcopys(replacement, hdr->subclass)) {
1116			freestr(replacement);
1117			return (NULL);
1118		}
1119		return (replacement);
1120	}
1121
1122	if ((strcmp(token, "sequence") == 0) ||
1123	    (strcmp(token, "timestamp") == 0)) {
1124		if (strcmp(token, "sequence") == 0) {
1125			(void) snprintf(num, sizeof (num),
1126				"0x%llx", sysevent_get_seq(ev));
1127		} else {
1128			hrtime_t ts;
1129			sysevent_get_time(ev, &ts);
1130			(void) snprintf(num, sizeof (num), "0x%llx", ts);
1131		}
1132		if (strcopys(replacement, num)) {
1133			freestr(replacement);
1134			return (NULL);
1135		}
1136		return (replacement);
1137	}
1138
1139	nmatches = 0;
1140
1141	if (nvlist) {
1142		nvpair_t *nvp_match;
1143		nvp = NULL;
1144		while ((nvp = nvlist_next_nvpair(nvlist, nvp)) != NULL) {
1145			if (debug_level >= DBG_DETAILED) {
1146				syseventd_print(DBG_DETAILED,
1147				"    attribute: %s %s\n", nvpair_name(nvp),
1148				se_attr_type_to_str(nvpair_type(nvp)));
1149			}
1150			if (strcmp(token, nvpair_name(nvp)) == 0) {
1151				nmatches++;
1152				nvp_match = nvp;
1153			}
1154		}
1155		nvp = nvp_match;
1156	}
1157
1158	if (nmatches == 0) {
1159		syslog(LOG_ERR, MACRO_UNDEF_ERR,
1160			sep->se_conf_file, sep->se_lineno, token);
1161		freestr(replacement);
1162		return (NULL);
1163	} else if (nmatches > 1) {
1164		syslog(LOG_ERR, MACRO_MULT_DEF_ERR,
1165			sep->se_conf_file, sep->se_lineno, token);
1166		freestr(replacement);
1167		return (NULL);
1168	}
1169
1170	switch (nvpair_type(nvp)) {
1171	case DATA_TYPE_BYTE:
1172		(void) nvpair_value_byte(nvp, &x.x_byte);
1173		(void) snprintf(num, sizeof (num), "0x%x", x.x_byte);
1174		if (strcats(replacement, num)) {
1175			freestr(replacement);
1176			return (NULL);
1177		}
1178		break;
1179	case DATA_TYPE_INT16:
1180		(void) nvpair_value_int16(nvp, &x.x_int16);
1181		(void) snprintf(num, sizeof (num), "%d", x.x_int16);
1182		if (strcats(replacement, num)) {
1183			freestr(replacement);
1184			return (NULL);
1185		}
1186		break;
1187	case DATA_TYPE_UINT16:
1188		(void) nvpair_value_uint16(nvp, &x.x_uint16);
1189		(void) snprintf(num, sizeof (num), "0x%x", x.x_uint16);
1190		if (strcats(replacement, num)) {
1191			freestr(replacement);
1192			return (NULL);
1193		}
1194		break;
1195	case DATA_TYPE_INT32:
1196		(void) nvpair_value_int32(nvp, &x.x_int32);
1197		(void) snprintf(num, sizeof (num), "%d", x.x_int32);
1198		if (strcats(replacement, num)) {
1199			freestr(replacement);
1200			return (NULL);
1201		}
1202		break;
1203	case DATA_TYPE_UINT32:
1204		(void) nvpair_value_uint32(nvp, &x.x_uint32);
1205		(void) snprintf(num, sizeof (num), "0x%x", x.x_uint32);
1206		if (strcats(replacement, num)) {
1207			freestr(replacement);
1208			return (NULL);
1209		}
1210		break;
1211	case DATA_TYPE_INT64:
1212		(void) nvpair_value_int64(nvp, &x.x_int64);
1213		(void) snprintf(num, sizeof (num), "%lld", x.x_int64);
1214		if (strcats(replacement, num)) {
1215			freestr(replacement);
1216			return (NULL);
1217		}
1218		break;
1219	case DATA_TYPE_UINT64:
1220		(void) nvpair_value_uint64(nvp, &x.x_uint64);
1221		(void) snprintf(num, sizeof (num), "0x%llx", x.x_uint64);
1222		if (strcats(replacement, num)) {
1223			freestr(replacement);
1224			return (NULL);
1225		}
1226		break;
1227	case DATA_TYPE_STRING:
1228		(void) nvpair_value_string(nvp, &x.x_string);
1229		if (strcats(replacement, x.x_string)) {
1230			freestr(replacement);
1231			return (NULL);
1232		}
1233		break;
1234	case DATA_TYPE_BYTE_ARRAY: {
1235			uchar_t	*p;
1236			(void) nvpair_value_byte_array(nvp,
1237				&x.x_byte_array, &nelems);
1238			p = x.x_byte_array;
1239			for (i = 0; i < nelems; i++) {
1240				(void) snprintf(num, sizeof (num),
1241					"0x%x ", *p++ & 0xff);
1242				if (strcats(replacement, num)) {
1243					freestr(replacement);
1244					return (NULL);
1245				}
1246			}
1247		}
1248		break;
1249	case DATA_TYPE_INT16_ARRAY: {
1250			int16_t *p;
1251			(void) nvpair_value_int16_array(nvp,
1252				&x.x_int16_array, &nelems);
1253			p = x.x_int16_array;
1254			for (i = 0; i < nelems; i++) {
1255				(void) snprintf(num, sizeof (num), "%d ", *p++);
1256				if (strcats(replacement, num)) {
1257					freestr(replacement);
1258					return (NULL);
1259				}
1260			}
1261		}
1262		break;
1263
1264	case DATA_TYPE_UINT16_ARRAY: {
1265			uint16_t *p;
1266			(void) nvpair_value_uint16_array(nvp,
1267				&x.x_uint16_array, &nelems);
1268			p = x.x_uint16_array;
1269			for (i = 0; i < nelems; i++) {
1270				(void) snprintf(num, sizeof (num),
1271					"0x%x ", *p++);
1272				if (strcats(replacement, num)) {
1273					freestr(replacement);
1274					return (NULL);
1275				}
1276			}
1277		}
1278		break;
1279
1280	case DATA_TYPE_INT32_ARRAY: {
1281			int32_t *p;
1282			(void) nvpair_value_int32_array(nvp,
1283				&x.x_int32_array, &nelems);
1284			p = x.x_int32_array;
1285			for (i = 0; i < nelems; i++) {
1286				(void) snprintf(num, sizeof (num), "%d ", *p++);
1287				if (strcats(replacement, num)) {
1288					freestr(replacement);
1289					return (NULL);
1290				}
1291			}
1292		}
1293		break;
1294
1295	case DATA_TYPE_UINT32_ARRAY: {
1296			uint32_t *p;
1297			(void) nvpair_value_uint32_array(nvp,
1298				&x.x_uint32_array, &nelems);
1299			p = x.x_uint32_array;
1300			for (i = 0; i < nelems; i++) {
1301				(void) snprintf(num, sizeof (num),
1302					"0x%x ", *p++);
1303				if (strcats(replacement, num)) {
1304					freestr(replacement);
1305					return (NULL);
1306				}
1307			}
1308		}
1309		break;
1310
1311	case DATA_TYPE_INT64_ARRAY: {
1312			int64_t *p;
1313			(void) nvpair_value_int64_array(nvp,
1314				&x.x_int64_array, &nelems);
1315			p = x.x_int64_array;
1316			for (i = 0; i < nelems; i++) {
1317				(void) snprintf(num, sizeof (num),
1318					"%lld ", *p++);
1319				if (strcats(replacement, num)) {
1320					freestr(replacement);
1321					return (NULL);
1322				}
1323			}
1324		}
1325		break;
1326
1327	case DATA_TYPE_UINT64_ARRAY: {
1328			uint64_t *p;
1329			(void) nvpair_value_uint64_array(nvp,
1330				&x.x_uint64_array, &nelems);
1331			p = x.x_uint64_array;
1332			for (i = 0; i < nelems; i++) {
1333				(void) snprintf(num, sizeof (num),
1334					"0x%llx ", *p++);
1335				if (strcats(replacement, num)) {
1336					freestr(replacement);
1337					return (NULL);
1338				}
1339			}
1340		}
1341		break;
1342
1343	case DATA_TYPE_STRING_ARRAY: {
1344			char **p;
1345			(void) nvpair_value_string_array(nvp,
1346				&x.x_string_array, &nelems);
1347			p = x.x_string_array;
1348			for (i = 0; i < nelems; i++) {
1349				if (strcats(replacement, *p++) ||
1350				    strcats(replacement, " ")) {
1351					freestr(replacement);
1352					return (NULL);
1353				}
1354			}
1355		}
1356		break;
1357
1358	case DATA_TYPE_HRTIME:
1359		(void) nvpair_value_hrtime(nvp, &x.x_time);
1360		(void) snprintf(num, sizeof (num), "0x%llx", x.x_time);
1361		if (strcats(replacement, num)) {
1362			freestr(replacement);
1363			return (NULL);
1364		}
1365		break;
1366	default:
1367		syslog(LOG_ERR, ATTR_UNSUPPORTED_ERR,
1368			sep->se_conf_file, sep->se_lineno,
1369			nvpair_type(nvp), token);
1370		freestr(replacement);
1371		return (NULL);
1372	}
1373
1374	return (replacement);
1375}
1376
1377/*
1378 * Expand macros in the command template provided in an event
1379 * specification with the data from the event or event attributes.
1380 *
1381 * Macros are introduced by the '$' character, with the macro
1382 * name being the following token separated by a SPACE or
1383 * TAB character.  If the macro name is embedded in text,
1384 * it may be delineated by '${' and "}'.  A backslash before
1385 * the '$' causes macro expansion not to occur.
1386 *
1387 * The following predefined macros are defined for each event:
1388 *
1389 *	$vendor - the vendor string defining the event.
1390 *
1391 *	$publisher - the publisher string defining the event.
1392 *
1393 *	$class - the class string defining the event.
1394 *
1395 *	$subclass - the subclass string defining the event.
1396 *
1397 *	$sequence - the sequence number of the event.
1398 *
1399 *	$timestamp - the timestamp of the event.
1400 *
1401 *
1402 * Macro names other than those predefined are compared against
1403 * the attribute list provided with the event.  An attribute
1404 * with name matching the macro name causes the value of
1405 * of the attribute to be substituted as ASCII text on the
1406 * generated command line.
1407 *
1408 * Use of a macro for which no attribute with that name
1409 * is defined, or for which multiple attributes with that
1410 * name are provided, cause an error and the command is
1411 * not invoked.
1412 */
1413static int
1414expand_macros(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep,
1415	str_t *line, sysevent_hdr_info_t *hdr)
1416{
1417	char	*p;
1418	int	state;
1419	char	*end;
1420	str_t	*token;
1421	str_t	*remainder;
1422	str_t	*replacement;
1423	int	count;
1424	int	dollar_position;
1425
1426	syseventd_print(DBG_MACRO, "    expanding macros: '%s'\n", line->s_str);
1427
1428reset:
1429	state = 0;
1430	count = 0;
1431	for (p = line->s_str; *p != 0; p++, count++) {
1432		switch (state) {
1433		case 0:				/* initial state */
1434			if (*p == '\\') {
1435				state = 1;
1436			} else if (*p == '$') {
1437				dollar_position = count;
1438				state = 2;
1439			}
1440			break;
1441		case 1:				/* skip characters */
1442			state = 0;		/* after backslash */
1443			break;
1444		case 2:				/* character after $ */
1445			if (*p == '{') {
1446				token = snip_delimited_identifier(p+1, &end);
1447			} else {
1448				token = snip_identifier(p, &end);
1449			}
1450			if (token == NULL)
1451				goto failed;
1452
1453			if ((remainder = initstr(128)) == NULL) {
1454				freestr(token);
1455				return (1);
1456			}
1457			if (strcopys(remainder, end)) {
1458				freestr(token);
1459				freestr(remainder);
1460				return (1);
1461			}
1462			replacement = find_macro_definition(ev, nvlist,
1463				sep, token->s_str, hdr);
1464			if (replacement == NULL) {
1465				freestr(token);
1466				freestr(remainder);
1467				return (1);
1468			}
1469			syseventd_print(DBG_MACRO,
1470				"    '%s' expands to '%s'\n",
1471				token->s_str, replacement->s_str);
1472
1473			strtrunc(line, dollar_position);
1474			if (strcats(line, replacement->s_str)) {
1475				freestr(token);
1476				freestr(replacement);
1477				freestr(remainder);
1478				return (1);
1479			}
1480			if (strcats(line, remainder->s_str)) {
1481				freestr(token);
1482				freestr(replacement);
1483				freestr(remainder);
1484				return (1);
1485			}
1486
1487			syseventd_print(DBG_MACRO,
1488				"    with macro expanded: '%s'\n", line->s_str);
1489
1490			freestr(token);
1491			freestr(replacement);
1492			freestr(remainder);
1493			goto reset;
1494		}
1495	}
1496
1497failed:
1498	if (state != 0) {
1499		syslog(LOG_ERR, SYNTAX_ERR, sep->se_conf_file, sep->se_lineno);
1500		return (1);
1501	}
1502
1503	return (0);
1504}
1505
1506
1507static void
1508start_syseventconfd()
1509{
1510	int	err;
1511
1512	err = system1("/usr/lib/sysevent/syseventconfd",
1513		"/usr/lib/sysevent/syseventconfd");
1514
1515	if (err != 0 && confd_err_msg_emitted == 0) {
1516		if (confd_state == CONFD_STATE_NOT_RUNNING) {
1517			syslog(LOG_ERR, SYSEVENTCONFD_START_ERR,
1518				strerror(errno));
1519		} else {
1520			syslog(LOG_ERR, SYSEVENTCONFD_RESTART_ERR,
1521				strerror(errno));
1522		}
1523	}
1524}
1525
1526
1527static int
1528system1(const char *s_path, const char *s)
1529{
1530	struct sigaction cbuf, ibuf, qbuf, ignore, dfl;
1531	sigset_t mask;
1532	sigset_t savemask;
1533	struct stat st;
1534	pid_t pid;
1535	int status, w;
1536
1537	/* Check the requested command */
1538	if (s == NULL) {
1539		errno = EINVAL;
1540		return (-1);
1541	}
1542
1543	/* Check the ability to execute devfsadmd from this process */
1544	if (stat(s_path, &st) < 0) {
1545		return (-1);
1546	}
1547	if (((geteuid() == st.st_uid) && ((st.st_mode & S_IXUSR) == 0)) ||
1548		((getegid() == st.st_gid) && ((st.st_mode & S_IXGRP) == 0)) ||
1549		((st.st_mode & S_IXOTH) == 0)) {
1550		errno = EPERM;
1551		return (-1);
1552	}
1553
1554	/*
1555	 * Block SIGCHLD and set up a default handler for the duration of the
1556	 * system1 call.
1557	 */
1558	(void) sigemptyset(&mask);
1559	(void) sigaddset(&mask, SIGCHLD);
1560	(void) sigprocmask(SIG_BLOCK, &mask, &savemask);
1561	(void) memset(&dfl, 0, sizeof (dfl));
1562	dfl.sa_handler = SIG_DFL;
1563	(void) sigaction(SIGCHLD, &dfl, &cbuf);
1564
1565	/* Fork off the child process (using fork1(), because it's MT-safe) */
1566	switch (pid = fork1()) {
1567		case -1:
1568			/* Error */
1569			(void) sigaction(SIGCHLD, &cbuf, NULL);
1570			(void) sigprocmask(SIG_SETMASK, &savemask, NULL);
1571			return (-1);
1572		case 0:
1573			/* Set-up an initial signal mask for the child */
1574			(void) sigemptyset(&mask);
1575			(void) sigprocmask(SIG_SETMASK, &mask, NULL);
1576			closefrom(3);
1577			(void) execl(s_path, s, (char *)0);
1578			_exit(-1);
1579			break;
1580		default:
1581			/* Parent */
1582			break;
1583	}
1584
1585	(void) memset(&ignore, 0, sizeof (ignore));
1586	ignore.sa_handler = SIG_IGN;
1587	(void) sigaction(SIGINT, &ignore, &ibuf);
1588	(void) sigaction(SIGQUIT, &ignore, &qbuf);
1589
1590	do {
1591		w = waitpid(pid, &status, 0);
1592	} while (w == -1 && errno == EINTR);
1593
1594	(void) sigaction(SIGINT, &ibuf, NULL);
1595	(void) sigaction(SIGQUIT, &qbuf, NULL);
1596
1597	(void) sigaction(SIGCHLD, &cbuf, NULL);
1598	(void) sigprocmask(SIG_SETMASK, &savemask, NULL);
1599
1600	return ((w == -1)? w: status);
1601}
1602
1603/*
1604 * Free all commands on the cmd queue
1605 */
1606static void
1607abort_cmd_queue()
1608{
1609	cmdqueue_t	*cmd;
1610	cmdqueue_t	*next;
1611	int		nevents = 0;
1612
1613	while ((cmd = cmdq) != NULL) {
1614		next = cmd->next;
1615		cmdq_cnt--;
1616		sysevent_free(cmd->event);
1617		sc_free(cmd, sizeof (cmdqueue_t));
1618		cmdq = next;
1619		nevents++;
1620	}
1621	cmdq_tail = NULL;
1622
1623	/*
1624	 * Generate error msgs if events were discarded or
1625	 * we are entering the disabled state.
1626	 */
1627	if (nevents > 0) {
1628		syslog(LOG_ERR, N_EVENTS_DISCARDED_ERR, nevents);
1629	}
1630	if (want_fini == 0) {
1631		confd_state = CONFD_STATE_DISABLED;
1632		syslog(LOG_ERR, SERVICE_DISABLED_MSG);
1633	}
1634}
1635
1636/*
1637 * For a matching event specification, build the command to be
1638 * invoked in response to the event.  Building the command involves
1639 * expanding macros supplied in the event specification command
1640 * with values from the actual event.  These macros can be
1641 * the class/subclass/vendor/publisher strings, or arbitrary
1642 * attribute data attached to the event.
1643 *
1644 * This module does not invoke (fork/exec) the command itself,
1645 * since this module is running in the context of the syseventd
1646 * daemon, and fork/exec's done here interfere with the door
1647 * upcall delivering events from the kernel to the daemon.
1648 * Instead, we build a separate event and nvlist with the
1649 * attributes of the command to be invoked, and pass that on
1650 * to the syseventconfd daemon, which is basically a fork/exec
1651 * server on our behalf.
1652 *
1653 * Errors queuing the event are returned to syseventd with
1654 * EAGAIN, allowing syseventd to manage a limited number of
1655 * retries after a short delay.
1656 */
1657static int
1658queue_event(sysevent_t *ev, syseventtab_t *sep, sysevent_hdr_info_t *hdr)
1659{
1660	str_t		*line;
1661	nvlist_t	*nvlist;
1662	char		*argv0;
1663	sysevent_t	*cmd_event;
1664	nvlist_t	*cmd_nvlist;
1665	cmdqueue_t	*new_cmd;
1666
1667	if ((line = initstr(128)) == NULL)
1668		return (1);
1669
1670	if ((argv0 = strrchr(sep->se_path, '/')) == NULL) {
1671		argv0 = sep->se_path;
1672	} else {
1673		argv0++;
1674	}
1675	if (strcopys(line, argv0)) {
1676		freestr(line);
1677		return (1);
1678	}
1679
1680	if (sep->se_args) {
1681		if (strcats(line, " ")) {
1682			freestr(line);
1683			return (1);
1684		}
1685		if (strcats(line, sep->se_args)) {
1686			freestr(line);
1687			return (1);
1688		}
1689
1690		if (sysevent_get_attr_list(ev, &nvlist) != 0) {
1691			syslog(LOG_ERR, GET_ATTR_LIST_ERR,
1692				sep->se_conf_file, sep->se_lineno,
1693				strerror(errno));
1694			freestr(line);
1695			return (1);
1696		}
1697		if (expand_macros(ev, nvlist, sep, line, hdr)) {
1698			freestr(line);
1699			nvlist_free(nvlist);
1700			return (1);
1701		}
1702		nvlist_free(nvlist);
1703	}
1704
1705	if (debug_level >= DBG_EXEC) {
1706		syseventd_print(DBG_EXEC, "%s, line %d: path = %s\n",
1707			sep->se_conf_file, sep->se_lineno, sep->se_path);
1708		syseventd_print(DBG_EXEC, "    cmd = %s\n", line->s_str);
1709	}
1710
1711	cmd_nvlist = NULL;
1712	if ((errno = nvlist_alloc(&cmd_nvlist, NV_UNIQUE_NAME, 0)) != 0) {
1713		freestr(line);
1714		syslog(LOG_ERR, NVLIST_ALLOC_ERR,
1715			sep->se_conf_file, sep->se_lineno,
1716			strerror(errno));
1717		return (1);
1718	}
1719
1720	if ((errno = nvlist_add_string(cmd_nvlist, "path", sep->se_path)) != 0)
1721		goto err;
1722	if ((errno = nvlist_add_string(cmd_nvlist, "cmd", line->s_str)) != 0)
1723		goto err;
1724	if ((errno = nvlist_add_string(cmd_nvlist, "file",
1725	    sep->se_conf_file)) != 0)
1726		goto err;
1727	if ((errno = nvlist_add_int32(cmd_nvlist, "line", sep->se_lineno)) != 0)
1728		goto err;
1729	if ((errno = nvlist_add_string(cmd_nvlist, "user", sep->se_user)) != 0)
1730		goto err;
1731
1732	if (sep->se_uid != (uid_t)0) {
1733		if ((errno = nvlist_add_int32(cmd_nvlist, "uid",
1734		    sep->se_uid)) != 0)
1735			goto err;
1736		if ((errno = nvlist_add_int32(cmd_nvlist, "gid",
1737		    sep->se_gid)) != 0)
1738			goto err;
1739	}
1740
1741	cmd_event = sysevent_alloc_event(hdr->class, hdr->subclass, hdr->vendor,
1742		hdr->publisher, cmd_nvlist);
1743	if (cmd_event == NULL) {
1744		syslog(LOG_ERR, SYSEVENT_ALLOC_ERR,
1745			sep->se_conf_file, sep->se_lineno,
1746			strerror(errno));
1747		nvlist_free(cmd_nvlist);
1748		freestr(line);
1749		return (1);
1750	}
1751
1752	nvlist_free(cmd_nvlist);
1753	freestr(line);
1754
1755	/*
1756	 * Place cmd_event on queue to be transported to syseventconfd
1757	 */
1758	if ((new_cmd = sc_malloc(sizeof (cmdqueue_t))) == NULL) {
1759		sysevent_free(cmd_event);
1760		return (1);
1761	}
1762	new_cmd->event = cmd_event;
1763	new_cmd->next = NULL;
1764	(void) mutex_lock(&cmdq_lock);
1765	if (cmdq == NULL) {
1766		cmdq = new_cmd;
1767	} else {
1768		cmdq_tail->next = new_cmd;
1769	}
1770	cmdq_cnt++;
1771	cmdq_tail = new_cmd;
1772
1773	/*
1774	 * signal queue flush thread
1775	 */
1776	(void) cond_signal(&cmdq_cv);
1777
1778	(void) mutex_unlock(&cmdq_lock);
1779
1780	return (0);
1781
1782err:
1783	syslog(LOG_ERR, NVLIST_BUILD_ERR,
1784		sep->se_conf_file, sep->se_lineno, strerror(errno));
1785	nvlist_free(cmd_nvlist);
1786	freestr(line);
1787	return (1);
1788}
1789
1790
1791static int
1792transport_event(sysevent_t *event)
1793{
1794	int	rval;
1795
1796	rval = sysevent_send_event(confd_handle, event);
1797	if (rval != 0) {
1798		switch (errno) {
1799		case EAGAIN:
1800		case EINTR:
1801			/*
1802			 * syseventconfd daemon may be forking, stop
1803			 * attempting to empty the queue momentarily.
1804			 */
1805			rval = errno;
1806			break;
1807		case ENOENT:
1808		case EBADF:
1809			/*
1810			 * start/restart the syseventconfd daemon,
1811			 * allowing for some delay when starting
1812			 * up before it begins to reply.
1813			 */
1814			if (confd_state == CONFD_STATE_NOT_RUNNING ||
1815			    confd_state == CONFD_STATE_OK) {
1816				confd_state = CONFD_STATE_STARTED;
1817				start_syseventconfd();
1818				confd_retries = 0;
1819				rval = EAGAIN;
1820			} else if (confd_state == CONFD_STATE_STARTED &&
1821			    confd_retries < 16) {
1822				if (++confd_retries == 16) {
1823					confd_state = CONFD_STATE_ERR;
1824					if (confd_err_msg_emitted == 0) {
1825						syslog(LOG_ERR,
1826						    SYSEVENTCONFD_ERR);
1827						confd_err_msg_emitted = 1;
1828					}
1829				}
1830				rval = EAGAIN;
1831			} else {
1832				rval = errno;
1833			}
1834			break;
1835		default:
1836			syslog(LOG_ERR, SYSEVENTCONFD_TRAN_ERR,
1837				strerror(errno));
1838			rval = errno;
1839			break;
1840		}
1841	} else if (confd_state != CONFD_STATE_OK) {
1842		if (confd_state == CONFD_STATE_ERR) {
1843			syslog(LOG_ERR, SYSEVENTCONFD_OK);
1844			confd_err_msg_emitted = 0;
1845		}
1846		confd_state = CONFD_STATE_OK;
1847		confd_retries = 0;
1848		confd_err_msg_emitted = 0;
1849	}
1850	return (rval);
1851}
1852
1853
1854/*
1855 * Send events on queue to syseventconfd daemon.  We queue events
1856 * here since the daemon is unable to handle events during an
1857 * active fork/exec, returning EAGAIN as a result.  It is grossly
1858 * inefficient to bounce these events back to syseventd, so
1859 * we queue them here for delivery.
1860 *
1861 * EAGAIN/EINTR don't indicate errors with the transport to
1862 * syseventconfd itself, just the daemon is busy or some
1863 * other transient difficulty.  We retry EBADF and other errors
1864 * for some time, then eventually give up - something's broken.
1865 *
1866 * Error handling strategy:
1867 * If we're trying to shut down and the syseventconfd daemon isn't
1868 * responding, abort the queue so we don't cause the fini to hang
1869 * forever.  Otherwise, EAGAIN/EINTR are retried forever, as
1870 * we presume the daemon is active but either busy or some transient
1871 * state is preventing the transport.  We make considerable effort
1872 * to retry EBADF since the daemon may take some time to come up when
1873 * restarted so don't want to give up too easily.  Once we enter
1874 * the DISABLED state, we stop handling events altogther to
1875 * avoid thrashing the system if the syseventconfd binary is
1876 * corrupted or missing.  This state can be cleared by issuing
1877 * a HUP signal to the syseventd daemon.  For errors other than
1878 * EAGAIN/EINTR/EBADF, we just drop the event and if we get
1879 * a certain number of these in a row, we enter the DISABLED
1880 * state.
1881 */
1882
1883static void
1884transport_queued_events()
1885{
1886	int		rval;
1887	cmdqueue_t	*cmd;
1888
1889	(void) mutex_lock(&cmdq_lock);
1890	while (cmdq != NULL) {
1891		cmd = cmdq;
1892		(void) mutex_unlock(&cmdq_lock);
1893		rval = transport_event(cmd->event);
1894		(void) mutex_lock(&cmdq_lock);
1895		if (rval != 0) {
1896			switch (rval) {
1897			case EAGAIN:
1898			case EINTR:
1899				/*
1900				 * Limit retries in the case of fini
1901				 */
1902				if (want_fini) {
1903					if (++transport_retries == 16) {
1904						abort_cmd_queue();
1905					}
1906				}
1907				(void) mutex_unlock(&cmdq_lock);
1908				return;
1909			case EBADF:
1910				/*
1911				 * retry up to 16 times
1912				 */
1913				if (want_fini || ++transport_retries == 16) {
1914					abort_cmd_queue();
1915				}
1916				(void) mutex_unlock(&cmdq_lock);
1917				return;
1918			default:
1919				/*
1920				 * After 16 sequential errors, give up
1921				 */
1922				if (++transport_retries == 16) {
1923					abort_cmd_queue();
1924					(void) mutex_unlock(&cmdq_lock);
1925					return;
1926				}
1927				/*
1928				 * We don't retry these errors, we
1929				 * fall through to remove this event
1930				 * from the queue.
1931				 */
1932				break;
1933			}
1934		} else {
1935			transport_retries = 0;
1936		}
1937
1938		/*
1939		 * Take completed event off queue
1940		 */
1941		cmdq_cnt--;
1942		cmdq = cmdq->next;
1943		if (cmdq == NULL) {
1944			cmdq_tail = NULL;
1945		}
1946		(void) mutex_unlock(&cmdq_lock);
1947		sysevent_free(cmd->event);
1948		sc_free(cmd, sizeof (cmdqueue_t));
1949		(void) mutex_lock(&cmdq_lock);
1950	}
1951
1952	(void) mutex_unlock(&cmdq_lock);
1953}
1954
1955
1956static void
1957queue_flush_thr()
1958{
1959	int	n;
1960
1961	(void) mutex_lock(&cmdq_lock);
1962	for (;;) {
1963		while (cmdq_cnt == 0 && want_fini == 0) {
1964			(void) cond_wait(&cmdq_cv, &cmdq_lock);
1965		}
1966		if (cmdq_cnt == 0 && want_fini) {
1967			(void) cond_signal(&cmdq_thr_cv);
1968			(void) mutex_unlock(&cmdq_lock);
1969			thr_exit(NULL);
1970			/*NOTREACHED*/
1971		}
1972		(void) mutex_unlock(&cmdq_lock);
1973		transport_queued_events();
1974		(void) mutex_lock(&cmdq_lock);
1975		if (cmdq_cnt != 0) {
1976			(void) mutex_unlock(&cmdq_lock);
1977			if (want_fini == 0 && confd_err_msg_emitted) {
1978				for (n = 0; n < 60; n++) {
1979					(void) sleep(1);
1980					if (want_fini)
1981						break;
1982				}
1983			} else {
1984				(void) sleep(1);
1985			}
1986			(void) mutex_lock(&cmdq_lock);
1987		}
1988	}
1989}
1990
1991
1992/*
1993 * syseventd daemon module event handler
1994 *
1995 * The syseventd daemon calls this handler with each event
1996 * for this module to handle the event as appropriate.
1997 * The task of this module is to compare the event's
1998 * class/subclass/publisher/vendor against the list of
1999 * event specifications provided in the installed
2000 * sysevent.conf files.  Build and execute the
2001 * defined command for that event specification
2002 * for each match.
2003 *
2004 * Events are matched against the class, subclass, vendor
2005 * and publisher specifications.  Any field not to be matched
2006 * against an event should be set to '-'.  A specification
2007 * of '- - - -' generates a match against every event.
2008 */
2009/*ARGSUSED*/
2010static int
2011sysevent_conf_event(sysevent_t *ev, int flag)
2012{
2013	int	ret = 0;
2014	char	*vendor;
2015	char	*publisher;
2016	char	*class;
2017	char	*subclass;
2018	syseventtab_t *sep;
2019	sysevent_hdr_info_t hdr;
2020	uint64_t seq;
2021	hrtime_t ts;
2022
2023	/*
2024	 * If we've been completely unable to communicate with
2025	 * syseventconfd, there's not much we can do.
2026	 */
2027	if (confd_state == CONFD_STATE_DISABLED) {
2028		return (0);
2029	}
2030
2031	/*
2032	 * sysevent_get_seq(ev) < ev_seq):
2033	 *	an event we have played before, ignore it
2034	 * sysevent_get_seq(ev) == ev_seq):
2035	 *	ev_nretries > 0, an event being retried
2036	 * sysevent_get_seq(ev) > ev_seq):
2037	 *	a new event
2038	 */
2039	if (debug_level >= DBG_EVENTS) {
2040		if (sysevent_get_seq(ev) == ev_seq && ev_nretries > 0) {
2041			syseventd_print(DBG_EVENTS,
2042			    "sequence: %lld/%lld, retry %d\n",
2043			    sysevent_get_seq(ev), ev_seq, ev_nretries);
2044		} else if (sysevent_get_seq(ev) > ev_seq) {
2045			syseventd_print(DBG_EVENTS,
2046			    "sequence: %lld/%lld\n",
2047			    sysevent_get_seq(ev), ev_seq);
2048		}
2049	}
2050
2051	seq = sysevent_get_seq(ev);
2052	sysevent_get_time(ev, &ts);
2053
2054	if (seq > ev_seq || ts > ev_ts) {
2055		ev_nretries = 0;
2056	} else if (first_event == 0 &&
2057	    (((seq < ev_seq) || (seq == 0 && ts > ev_ts)) ||
2058	    (seq == ev_seq && ev_nretries == 0))) {
2059		syseventd_print(DBG_TEST,
2060		    "out-of-order sequence: received %lld/0x%llx, "
2061		    "expected %lld/0x%llx\n", seq, ts, ev_seq+1, ev_ts);
2062		return (ret);
2063	}
2064
2065	ev_ts = ts;
2066	ev_seq = seq;
2067	first_event = 0;
2068
2069	/*
2070	 * sysevent_get_vendor_name() and sysevent_get_pub_name()
2071	 * allocate strings which must be freed.
2072	 */
2073	vendor = sysevent_get_vendor_name(ev);
2074	publisher = sysevent_get_pub_name(ev);
2075	class = sysevent_get_class_name(ev);
2076	subclass = sysevent_get_subclass_name(ev);
2077
2078	if (vendor == NULL || publisher == NULL) {
2079		syseventd_print(DBG_EVENTS, "Short on memory with vendor "
2080		    "and/or publisher string generation\n");
2081		/* Temporary short on memory */
2082		ev_nretries++;
2083		free(publisher);
2084		free(vendor);
2085		return (EAGAIN);
2086	}
2087
2088	syseventd_print(DBG_EVENTS,
2089		"%s event %lld: vendor='%s' publisher='%s' class='%s' "
2090		"subclass='%s'\n", whoami, sysevent_get_seq(ev), vendor,
2091		publisher, class, subclass);
2092
2093	for (sep = syseventtab; sep; sep = sep->se_next) {
2094		if (strcmp(sep->se_vendor, "-") != 0) {
2095			if (strcmp(sep->se_vendor, vendor) != 0)
2096				continue;
2097		}
2098		if (strcmp(sep->se_publisher, "-") != 0) {
2099			if (strcmp(sep->se_publisher, publisher) != 0)
2100				continue;
2101		}
2102		if (strcmp(sep->se_class, "-") != 0) {
2103			if (strcmp(sep->se_class, class) != 0)
2104				continue;
2105		}
2106		if (strcmp(sep->se_subclass, "-") != 0) {
2107			if (strcmp(sep->se_subclass, subclass) != 0)
2108				continue;
2109		}
2110		syseventd_print(DBG_MATCHES, "    event match: %s, line %d\n",
2111			sep->se_conf_file, sep->se_lineno);
2112		hdr.class = class;
2113		hdr.subclass = subclass;
2114		hdr.vendor = vendor;
2115		hdr.publisher = publisher;
2116		if ((ret = queue_event(ev, sep, &hdr)) != 0)
2117			break;
2118	}
2119
2120	if (ret == 0) {
2121		ev_nretries = 0;
2122	} else {
2123		/*
2124		 * Ask syseventd to retry any failed event.  If we have
2125		 * reached the limit on retries, emit a msg that we're
2126		 * not going to be able to service it.
2127		 */
2128		if (ev_nretries == SE_MAX_RETRY_LIMIT) {
2129			syslog(LOG_ERR, SYSEVENT_SEND_ERR,
2130				sep->se_conf_file, sep->se_lineno, errno);
2131		} else {
2132			syseventd_print(DBG_TEST, "%s event %lld: "
2133			    "'%s' '%s' '%s' '%s - errno %d, retry %d\n",
2134			    whoami, sysevent_get_seq(ev), vendor,
2135			    publisher, class, subclass, errno, ev_nretries);
2136		}
2137		ret = EAGAIN;
2138		ev_nretries++;
2139	}
2140
2141	free(publisher);
2142	free(vendor);
2143
2144	return (ret);
2145}
2146
2147/*
2148 * syseventd daemon module initialization
2149 */
2150struct slm_mod_ops *
2151slm_init()
2152{
2153	char	lock_file[PATH_MAX+1];
2154	int	lock_fd;
2155	int	err;
2156
2157	/*
2158	 * This functionality is not supported in the mini-root
2159	 * environment, ie install.  If root_dir is set, implying
2160	 * install, we quietly fail.  Return dummy ops rather
2161	 * than NULL to avoid error msgs out of syseventd.
2162	 */
2163	if (strcmp(root_dir, "") != 0) {
2164		return (&sysevent_conf_dummy_mod_ops);
2165	}
2166
2167	ev_nretries = 0;
2168	first_event = 1;
2169
2170	/*
2171	 * Initialize the channel to syseventconfd
2172	 */
2173	confd_handle = sysevent_open_channel_alt(SYSEVENTCONFD_SERVICE_DOOR);
2174	if (confd_handle == NULL) {
2175		syslog(LOG_ERR, CHANNEL_OPEN_ERR);
2176		return (NULL);
2177	}
2178
2179	if (sysevent_bind_publisher(confd_handle) != 0) {
2180		if (errno == EBUSY) {
2181			sysevent_cleanup_publishers(confd_handle);
2182			if (sysevent_bind_publisher(confd_handle) != 0) {
2183				sysevent_close_channel(confd_handle);
2184				return (NULL);
2185			}
2186		}
2187	}
2188
2189	sysevent_cleanup_subscribers(confd_handle);
2190
2191	cmdq = NULL;
2192	cmdq_tail = NULL;
2193	cmdq_cnt = 0;
2194	want_fini = 0;
2195	confd_err_msg_emitted = 0;
2196	if (confd_state != CONFD_STATE_OK) {
2197		confd_state = CONFD_STATE_NOT_RUNNING;
2198	}
2199
2200	confd_retries = 0;
2201	transport_retries = 0;
2202
2203	(void) mutex_init(&cmdq_lock, USYNC_THREAD, NULL);
2204	(void) cond_init(&cmdq_cv, USYNC_THREAD, NULL);
2205	(void) cond_init(&cmdq_thr_cv, USYNC_THREAD, NULL);
2206
2207	/*
2208	 * Create thread to flush cmd queue
2209	 */
2210	if ((err = thr_create(NULL, 0, (void *(*)(void*))queue_flush_thr,
2211	    NULL, 0, &cmdq_thr_id)) != 0) {
2212		syslog(LOG_ERR, THR_CREATE_ERR, strerror(err));
2213		sysevent_close_channel(confd_handle);
2214		confd_handle = NULL;
2215		(void) mutex_destroy(&cmdq_lock);
2216		(void) cond_destroy(&cmdq_cv);
2217		(void) cond_destroy(&cmdq_thr_cv);
2218		return (NULL);
2219	}
2220
2221	if ((lock_fd = enter_lock(lock_file)) == -1) {
2222		(void) thr_join(cmdq_thr_id, NULL, NULL);
2223		sysevent_close_channel(confd_handle);
2224		confd_handle = NULL;
2225		(void) mutex_destroy(&cmdq_lock);
2226		(void) cond_destroy(&cmdq_cv);
2227		(void) cond_destroy(&cmdq_thr_cv);
2228		return (NULL);
2229	}
2230
2231	build_event_table();
2232	exit_lock(lock_fd, lock_file);
2233	return (&sysevent_conf_mod_ops);
2234}
2235
2236/*
2237 * syseventd daemon module tear-down
2238 */
2239void
2240slm_fini()
2241{
2242	int	err;
2243
2244	/*
2245	 * Nothing to clean up if we're in the install environment
2246	 */
2247	if (strcmp(root_dir, "") != 0) {
2248		return;
2249	}
2250
2251	/*
2252	 * Wait for the queue to drain
2253	 */
2254	(void) mutex_lock(&cmdq_lock);
2255	want_fini = 1;
2256	(void) cond_signal(&cmdq_cv);
2257	(void) cond_wait(&cmdq_thr_cv, &cmdq_lock);
2258	(void) mutex_unlock(&cmdq_lock);
2259
2260	/*
2261	 * Shut down the the queue flush thread
2262	 */
2263	if ((err = thr_join(cmdq_thr_id, NULL, NULL)) != 0) {
2264		syslog(LOG_ERR, THR_JOIN_ERR, strerror(err));
2265	}
2266
2267	sysevent_close_channel(confd_handle);
2268	confd_handle = NULL;
2269	(void) mutex_destroy(&cmdq_lock);
2270	(void) cond_destroy(&cmdq_cv);
2271	(void) cond_destroy(&cmdq_thr_cv);
2272	free_event_table();
2273}
2274
2275/*ARGSUSED*/
2276static int
2277sysevent_conf_dummy_event(sysevent_t *ev, int flag)
2278{
2279	return (0);
2280}
2281