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