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