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 if (nvlist) 1700 nvlist_free(nvlist); 1701 return (1); 1702 } 1703 if (nvlist) 1704 nvlist_free(nvlist); 1705 } 1706 1707 if (debug_level >= DBG_EXEC) { 1708 syseventd_print(DBG_EXEC, "%s, line %d: path = %s\n", 1709 sep->se_conf_file, sep->se_lineno, sep->se_path); 1710 syseventd_print(DBG_EXEC, " cmd = %s\n", line->s_str); 1711 } 1712 1713 cmd_nvlist = NULL; 1714 if ((errno = nvlist_alloc(&cmd_nvlist, NV_UNIQUE_NAME, 0)) != 0) { 1715 freestr(line); 1716 syslog(LOG_ERR, NVLIST_ALLOC_ERR, 1717 sep->se_conf_file, sep->se_lineno, 1718 strerror(errno)); 1719 return (1); 1720 } 1721 1722 if ((errno = nvlist_add_string(cmd_nvlist, "path", sep->se_path)) != 0) 1723 goto err; 1724 if ((errno = nvlist_add_string(cmd_nvlist, "cmd", line->s_str)) != 0) 1725 goto err; 1726 if ((errno = nvlist_add_string(cmd_nvlist, "file", 1727 sep->se_conf_file)) != 0) 1728 goto err; 1729 if ((errno = nvlist_add_int32(cmd_nvlist, "line", sep->se_lineno)) != 0) 1730 goto err; 1731 if ((errno = nvlist_add_string(cmd_nvlist, "user", sep->se_user)) != 0) 1732 goto err; 1733 1734 if (sep->se_uid != (uid_t)0) { 1735 if ((errno = nvlist_add_int32(cmd_nvlist, "uid", 1736 sep->se_uid)) != 0) 1737 goto err; 1738 if ((errno = nvlist_add_int32(cmd_nvlist, "gid", 1739 sep->se_gid)) != 0) 1740 goto err; 1741 } 1742 1743 cmd_event = sysevent_alloc_event(hdr->class, hdr->subclass, hdr->vendor, 1744 hdr->publisher, cmd_nvlist); 1745 if (cmd_event == NULL) { 1746 syslog(LOG_ERR, SYSEVENT_ALLOC_ERR, 1747 sep->se_conf_file, sep->se_lineno, 1748 strerror(errno)); 1749 nvlist_free(cmd_nvlist); 1750 freestr(line); 1751 return (1); 1752 } 1753 1754 nvlist_free(cmd_nvlist); 1755 freestr(line); 1756 1757 /* 1758 * Place cmd_event on queue to be transported to syseventconfd 1759 */ 1760 if ((new_cmd = sc_malloc(sizeof (cmdqueue_t))) == NULL) { 1761 sysevent_free(cmd_event); 1762 return (1); 1763 } 1764 new_cmd->event = cmd_event; 1765 new_cmd->next = NULL; 1766 (void) mutex_lock(&cmdq_lock); 1767 if (cmdq == NULL) { 1768 cmdq = new_cmd; 1769 } else { 1770 cmdq_tail->next = new_cmd; 1771 } 1772 cmdq_cnt++; 1773 cmdq_tail = new_cmd; 1774 1775 /* 1776 * signal queue flush thread 1777 */ 1778 (void) cond_signal(&cmdq_cv); 1779 1780 (void) mutex_unlock(&cmdq_lock); 1781 1782 return (0); 1783 1784 err: 1785 syslog(LOG_ERR, NVLIST_BUILD_ERR, 1786 sep->se_conf_file, sep->se_lineno, strerror(errno)); 1787 nvlist_free(cmd_nvlist); 1788 freestr(line); 1789 return (1); 1790 } 1791 1792 1793 static int 1794 transport_event(sysevent_t *event) 1795 { 1796 int rval; 1797 1798 rval = sysevent_send_event(confd_handle, event); 1799 if (rval != 0) { 1800 switch (errno) { 1801 case EAGAIN: 1802 case EINTR: 1803 /* 1804 * syseventconfd daemon may be forking, stop 1805 * attempting to empty the queue momentarily. 1806 */ 1807 rval = errno; 1808 break; 1809 case ENOENT: 1810 case EBADF: 1811 /* 1812 * start/restart the syseventconfd daemon, 1813 * allowing for some delay when starting 1814 * up before it begins to reply. 1815 */ 1816 if (confd_state == CONFD_STATE_NOT_RUNNING || 1817 confd_state == CONFD_STATE_OK) { 1818 confd_state = CONFD_STATE_STARTED; 1819 start_syseventconfd(); 1820 confd_retries = 0; 1821 rval = EAGAIN; 1822 } else if (confd_state == CONFD_STATE_STARTED && 1823 confd_retries < 16) { 1824 if (++confd_retries == 16) { 1825 confd_state = CONFD_STATE_ERR; 1826 if (confd_err_msg_emitted == 0) { 1827 syslog(LOG_ERR, 1828 SYSEVENTCONFD_ERR); 1829 confd_err_msg_emitted = 1; 1830 } 1831 } 1832 rval = EAGAIN; 1833 } else { 1834 rval = errno; 1835 } 1836 break; 1837 default: 1838 syslog(LOG_ERR, SYSEVENTCONFD_TRAN_ERR, 1839 strerror(errno)); 1840 rval = errno; 1841 break; 1842 } 1843 } else if (confd_state != CONFD_STATE_OK) { 1844 if (confd_state == CONFD_STATE_ERR) { 1845 syslog(LOG_ERR, SYSEVENTCONFD_OK); 1846 confd_err_msg_emitted = 0; 1847 } 1848 confd_state = CONFD_STATE_OK; 1849 confd_retries = 0; 1850 confd_err_msg_emitted = 0; 1851 } 1852 return (rval); 1853 } 1854 1855 1856 /* 1857 * Send events on queue to syseventconfd daemon. We queue events 1858 * here since the daemon is unable to handle events during an 1859 * active fork/exec, returning EAGAIN as a result. It is grossly 1860 * inefficient to bounce these events back to syseventd, so 1861 * we queue them here for delivery. 1862 * 1863 * EAGAIN/EINTR don't indicate errors with the transport to 1864 * syseventconfd itself, just the daemon is busy or some 1865 * other transient difficulty. We retry EBADF and other errors 1866 * for some time, then eventually give up - something's broken. 1867 * 1868 * Error handling strategy: 1869 * If we're trying to shut down and the syseventconfd daemon isn't 1870 * responding, abort the queue so we don't cause the fini to hang 1871 * forever. Otherwise, EAGAIN/EINTR are retried forever, as 1872 * we presume the daemon is active but either busy or some transient 1873 * state is preventing the transport. We make considerable effort 1874 * to retry EBADF since the daemon may take some time to come up when 1875 * restarted so don't want to give up too easily. Once we enter 1876 * the DISABLED state, we stop handling events altogther to 1877 * avoid thrashing the system if the syseventconfd binary is 1878 * corrupted or missing. This state can be cleared by issuing 1879 * a HUP signal to the syseventd daemon. For errors other than 1880 * EAGAIN/EINTR/EBADF, we just drop the event and if we get 1881 * a certain number of these in a row, we enter the DISABLED 1882 * state. 1883 */ 1884 1885 static void 1886 transport_queued_events() 1887 { 1888 int rval; 1889 cmdqueue_t *cmd; 1890 1891 (void) mutex_lock(&cmdq_lock); 1892 while (cmdq != NULL) { 1893 cmd = cmdq; 1894 (void) mutex_unlock(&cmdq_lock); 1895 rval = transport_event(cmd->event); 1896 (void) mutex_lock(&cmdq_lock); 1897 if (rval != 0) { 1898 switch (rval) { 1899 case EAGAIN: 1900 case EINTR: 1901 /* 1902 * Limit retries in the case of fini 1903 */ 1904 if (want_fini) { 1905 if (++transport_retries == 16) { 1906 abort_cmd_queue(); 1907 } 1908 } 1909 (void) mutex_unlock(&cmdq_lock); 1910 return; 1911 case EBADF: 1912 /* 1913 * retry up to 16 times 1914 */ 1915 if (want_fini || ++transport_retries == 16) { 1916 abort_cmd_queue(); 1917 } 1918 (void) mutex_unlock(&cmdq_lock); 1919 return; 1920 default: 1921 /* 1922 * After 16 sequential errors, give up 1923 */ 1924 if (++transport_retries == 16) { 1925 abort_cmd_queue(); 1926 (void) mutex_unlock(&cmdq_lock); 1927 return; 1928 } 1929 /* 1930 * We don't retry these errors, we 1931 * fall through to remove this event 1932 * from the queue. 1933 */ 1934 break; 1935 } 1936 } else { 1937 transport_retries = 0; 1938 } 1939 1940 /* 1941 * Take completed event off queue 1942 */ 1943 cmdq_cnt--; 1944 cmdq = cmdq->next; 1945 if (cmdq == NULL) { 1946 cmdq_tail = NULL; 1947 } 1948 (void) mutex_unlock(&cmdq_lock); 1949 sysevent_free(cmd->event); 1950 sc_free(cmd, sizeof (cmdqueue_t)); 1951 (void) mutex_lock(&cmdq_lock); 1952 } 1953 1954 (void) mutex_unlock(&cmdq_lock); 1955 } 1956 1957 1958 static void 1959 queue_flush_thr() 1960 { 1961 int n; 1962 1963 (void) mutex_lock(&cmdq_lock); 1964 for (;;) { 1965 while (cmdq_cnt == 0 && want_fini == 0) { 1966 (void) cond_wait(&cmdq_cv, &cmdq_lock); 1967 } 1968 if (cmdq_cnt == 0 && want_fini) { 1969 (void) cond_signal(&cmdq_thr_cv); 1970 (void) mutex_unlock(&cmdq_lock); 1971 thr_exit(NULL); 1972 /*NOTREACHED*/ 1973 } 1974 (void) mutex_unlock(&cmdq_lock); 1975 transport_queued_events(); 1976 (void) mutex_lock(&cmdq_lock); 1977 if (cmdq_cnt != 0) { 1978 (void) mutex_unlock(&cmdq_lock); 1979 if (want_fini == 0 && confd_err_msg_emitted) { 1980 for (n = 0; n < 60; n++) { 1981 (void) sleep(1); 1982 if (want_fini) 1983 break; 1984 } 1985 } else { 1986 (void) sleep(1); 1987 } 1988 (void) mutex_lock(&cmdq_lock); 1989 } 1990 } 1991 } 1992 1993 1994 /* 1995 * syseventd daemon module event handler 1996 * 1997 * The syseventd daemon calls this handler with each event 1998 * for this module to handle the event as appropriate. 1999 * The task of this module is to compare the event's 2000 * class/subclass/publisher/vendor against the list of 2001 * event specifications provided in the installed 2002 * sysevent.conf files. Build and execute the 2003 * defined command for that event specification 2004 * for each match. 2005 * 2006 * Events are matched against the class, subclass, vendor 2007 * and publisher specifications. Any field not to be matched 2008 * against an event should be set to '-'. A specification 2009 * of '- - - -' generates a match against every event. 2010 */ 2011 /*ARGSUSED*/ 2012 static int 2013 sysevent_conf_event(sysevent_t *ev, int flag) 2014 { 2015 int ret = 0; 2016 char *vendor; 2017 char *publisher; 2018 char *class; 2019 char *subclass; 2020 syseventtab_t *sep; 2021 sysevent_hdr_info_t hdr; 2022 uint64_t seq; 2023 hrtime_t ts; 2024 2025 /* 2026 * If we've been completely unable to communicate with 2027 * syseventconfd, there's not much we can do. 2028 */ 2029 if (confd_state == CONFD_STATE_DISABLED) { 2030 return (0); 2031 } 2032 2033 /* 2034 * sysevent_get_seq(ev) < ev_seq): 2035 * an event we have played before, ignore it 2036 * sysevent_get_seq(ev) == ev_seq): 2037 * ev_nretries > 0, an event being retried 2038 * sysevent_get_seq(ev) > ev_seq): 2039 * a new event 2040 */ 2041 if (debug_level >= DBG_EVENTS) { 2042 if (sysevent_get_seq(ev) == ev_seq && ev_nretries > 0) { 2043 syseventd_print(DBG_EVENTS, 2044 "sequence: %lld/%lld, retry %d\n", 2045 sysevent_get_seq(ev), ev_seq, ev_nretries); 2046 } else if (sysevent_get_seq(ev) > ev_seq) { 2047 syseventd_print(DBG_EVENTS, 2048 "sequence: %lld/%lld\n", 2049 sysevent_get_seq(ev), ev_seq); 2050 } 2051 } 2052 2053 seq = sysevent_get_seq(ev); 2054 sysevent_get_time(ev, &ts); 2055 2056 if (seq > ev_seq || ts > ev_ts) { 2057 ev_nretries = 0; 2058 } else if (first_event == 0 && 2059 (((seq < ev_seq) || (seq == 0 && ts > ev_ts)) || 2060 (seq == ev_seq && ev_nretries == 0))) { 2061 syseventd_print(DBG_TEST, 2062 "out-of-order sequence: received %lld/0x%llx, " 2063 "expected %lld/0x%llx\n", seq, ts, ev_seq+1, ev_ts); 2064 return (ret); 2065 } 2066 2067 ev_ts = ts; 2068 ev_seq = seq; 2069 first_event = 0; 2070 2071 /* 2072 * sysevent_get_vendor_name() and sysevent_get_pub_name() 2073 * allocate strings which must be freed. 2074 */ 2075 vendor = sysevent_get_vendor_name(ev); 2076 publisher = sysevent_get_pub_name(ev); 2077 class = sysevent_get_class_name(ev); 2078 subclass = sysevent_get_subclass_name(ev); 2079 2080 if (vendor == NULL || publisher == NULL) { 2081 syseventd_print(DBG_EVENTS, "Short on memory with vendor " 2082 "and/or publisher string generation\n"); 2083 /* Temporary short on memory */ 2084 ev_nretries++; 2085 free(publisher); 2086 free(vendor); 2087 return (EAGAIN); 2088 } 2089 2090 syseventd_print(DBG_EVENTS, 2091 "%s event %lld: vendor='%s' publisher='%s' class='%s' " 2092 "subclass='%s'\n", whoami, sysevent_get_seq(ev), vendor, 2093 publisher, class, subclass); 2094 2095 for (sep = syseventtab; sep; sep = sep->se_next) { 2096 if (strcmp(sep->se_vendor, "-") != 0) { 2097 if (strcmp(sep->se_vendor, vendor) != 0) 2098 continue; 2099 } 2100 if (strcmp(sep->se_publisher, "-") != 0) { 2101 if (strcmp(sep->se_publisher, publisher) != 0) 2102 continue; 2103 } 2104 if (strcmp(sep->se_class, "-") != 0) { 2105 if (strcmp(sep->se_class, class) != 0) 2106 continue; 2107 } 2108 if (strcmp(sep->se_subclass, "-") != 0) { 2109 if (strcmp(sep->se_subclass, subclass) != 0) 2110 continue; 2111 } 2112 syseventd_print(DBG_MATCHES, " event match: %s, line %d\n", 2113 sep->se_conf_file, sep->se_lineno); 2114 hdr.class = class; 2115 hdr.subclass = subclass; 2116 hdr.vendor = vendor; 2117 hdr.publisher = publisher; 2118 if ((ret = queue_event(ev, sep, &hdr)) != 0) 2119 break; 2120 } 2121 2122 if (ret == 0) { 2123 ev_nretries = 0; 2124 } else { 2125 /* 2126 * Ask syseventd to retry any failed event. If we have 2127 * reached the limit on retries, emit a msg that we're 2128 * not going to be able to service it. 2129 */ 2130 if (ev_nretries == SE_MAX_RETRY_LIMIT) { 2131 syslog(LOG_ERR, SYSEVENT_SEND_ERR, 2132 sep->se_conf_file, sep->se_lineno, errno); 2133 } else { 2134 syseventd_print(DBG_TEST, "%s event %lld: " 2135 "'%s' '%s' '%s' '%s - errno %d, retry %d\n", 2136 whoami, sysevent_get_seq(ev), vendor, 2137 publisher, class, subclass, errno, ev_nretries); 2138 } 2139 ret = EAGAIN; 2140 ev_nretries++; 2141 } 2142 2143 free(publisher); 2144 free(vendor); 2145 2146 return (ret); 2147 } 2148 2149 /* 2150 * syseventd daemon module initialization 2151 */ 2152 struct slm_mod_ops * 2153 slm_init() 2154 { 2155 char lock_file[PATH_MAX+1]; 2156 int lock_fd; 2157 int err; 2158 2159 /* 2160 * This functionality is not supported in the mini-root 2161 * environment, ie install. If root_dir is set, implying 2162 * install, we quietly fail. Return dummy ops rather 2163 * than NULL to avoid error msgs out of syseventd. 2164 */ 2165 if (strcmp(root_dir, "") != 0) { 2166 return (&sysevent_conf_dummy_mod_ops); 2167 } 2168 2169 ev_nretries = 0; 2170 first_event = 1; 2171 2172 /* 2173 * Initialize the channel to syseventconfd 2174 */ 2175 confd_handle = sysevent_open_channel_alt(SYSEVENTCONFD_SERVICE_DOOR); 2176 if (confd_handle == NULL) { 2177 syslog(LOG_ERR, CHANNEL_OPEN_ERR); 2178 return (NULL); 2179 } 2180 2181 if (sysevent_bind_publisher(confd_handle) != 0) { 2182 if (errno == EBUSY) { 2183 sysevent_cleanup_publishers(confd_handle); 2184 if (sysevent_bind_publisher(confd_handle) != 0) { 2185 sysevent_close_channel(confd_handle); 2186 return (NULL); 2187 } 2188 } 2189 } 2190 2191 sysevent_cleanup_subscribers(confd_handle); 2192 2193 cmdq = NULL; 2194 cmdq_tail = NULL; 2195 cmdq_cnt = 0; 2196 want_fini = 0; 2197 confd_err_msg_emitted = 0; 2198 if (confd_state != CONFD_STATE_OK) { 2199 confd_state = CONFD_STATE_NOT_RUNNING; 2200 } 2201 2202 confd_retries = 0; 2203 transport_retries = 0; 2204 2205 (void) mutex_init(&cmdq_lock, USYNC_THREAD, NULL); 2206 (void) cond_init(&cmdq_cv, USYNC_THREAD, NULL); 2207 (void) cond_init(&cmdq_thr_cv, USYNC_THREAD, NULL); 2208 2209 /* 2210 * Create thread to flush cmd queue 2211 */ 2212 if ((err = thr_create(NULL, NULL, (void *(*)(void*))queue_flush_thr, 2213 (void *)NULL, 0, &cmdq_thr_id)) != 0) { 2214 syslog(LOG_ERR, THR_CREATE_ERR, strerror(err)); 2215 sysevent_close_channel(confd_handle); 2216 confd_handle = NULL; 2217 (void) mutex_destroy(&cmdq_lock); 2218 (void) cond_destroy(&cmdq_cv); 2219 (void) cond_destroy(&cmdq_thr_cv); 2220 return (NULL); 2221 } 2222 2223 if ((lock_fd = enter_lock(lock_file)) == -1) { 2224 (void) thr_join(cmdq_thr_id, NULL, NULL); 2225 sysevent_close_channel(confd_handle); 2226 confd_handle = NULL; 2227 (void) mutex_destroy(&cmdq_lock); 2228 (void) cond_destroy(&cmdq_cv); 2229 (void) cond_destroy(&cmdq_thr_cv); 2230 return (NULL); 2231 } 2232 2233 build_event_table(); 2234 exit_lock(lock_fd, lock_file); 2235 return (&sysevent_conf_mod_ops); 2236 } 2237 2238 /* 2239 * syseventd daemon module tear-down 2240 */ 2241 void 2242 slm_fini() 2243 { 2244 int err; 2245 2246 /* 2247 * Nothing to clean up if we're in the install environment 2248 */ 2249 if (strcmp(root_dir, "") != 0) { 2250 return; 2251 } 2252 2253 /* 2254 * Wait for the queue to drain 2255 */ 2256 (void) mutex_lock(&cmdq_lock); 2257 want_fini = 1; 2258 (void) cond_signal(&cmdq_cv); 2259 (void) cond_wait(&cmdq_thr_cv, &cmdq_lock); 2260 (void) mutex_unlock(&cmdq_lock); 2261 2262 /* 2263 * Shut down the the queue flush thread 2264 */ 2265 if ((err = thr_join(cmdq_thr_id, NULL, NULL)) != 0) { 2266 syslog(LOG_ERR, THR_JOIN_ERR, strerror(err)); 2267 } 2268 2269 sysevent_close_channel(confd_handle); 2270 confd_handle = NULL; 2271 (void) mutex_destroy(&cmdq_lock); 2272 (void) cond_destroy(&cmdq_cv); 2273 (void) cond_destroy(&cmdq_thr_cv); 2274 free_event_table(); 2275 } 2276 2277 /*ARGSUSED*/ 2278 static int 2279 sysevent_conf_dummy_event(sysevent_t *ev, int flag) 2280 { 2281 return (0); 2282 } 2283