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