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 * 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 * 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 * 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 * 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 255 sc_free(void *p, size_t n) 256 { 257 free(p); 258 } 259 260 261 static char * 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 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 * 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 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 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 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 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 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 * 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 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 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 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 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 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 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 * 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 * 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 * 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 * 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 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 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 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 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 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 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 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 1957 queue_flush_thr() 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 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 * 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, NULL, (void *(*)(void*))queue_flush_thr, 2211 (void *)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 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 2277 sysevent_conf_dummy_event(sysevent_t *ev, int flag) 2278 { 2279 return (0); 2280 } 2281