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