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