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