1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * sysevent_conf_mod - syseventd daemon sysevent.conf module 31 * 32 * This module provides a configuration file registration 33 * mechanism whereby event producers can define an event 34 * specification to be matched against events, with an 35 * associated command line to be invoked for each matching event. 36 * It includes a simple macro capability for flexibility in 37 * generating arbitrary command line formats from event-associated 38 * data, and a user specification so that commands can be invoked 39 * with reduced privileges to eliminate a security risk. 40 * 41 * sysevent.conf files contain event specifications and associated 42 * command path and optional arguments. System events received 43 * from the kernel by the sysevent daemon, syseventd, are 44 * compared against the event specifications in the sysevent.conf 45 * files. The command as specified by pathname and arguments 46 * is invoked for each matching event. 47 * 48 * All sysevent.conf files reside in /etc/sysevent/config. 49 * 50 */ 51 52 53 #include <stdio.h> 54 55 #include <unistd.h> 56 #include <stdarg.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include <strings.h> 60 #include <limits.h> 61 #include <thread.h> 62 #include <synch.h> 63 #include <errno.h> 64 #include <fcntl.h> 65 #include <ctype.h> 66 #include <pwd.h> 67 #include <syslog.h> 68 #include <sys/types.h> 69 #include <sys/stat.h> 70 #include <sys/sunddi.h> 71 #include <sys/sysevent.h> 72 #include <libsysevent.h> 73 #include <libnvpair.h> 74 #include <dirent.h> 75 #include <locale.h> 76 #include <signal.h> 77 #include <wait.h> 78 79 #include "syseventd.h" 80 #include "syseventconfd_door.h" 81 #include "sysevent_conf_mod.h" 82 #include "message_conf_mod.h" 83 84 85 static char *whoami = "sysevent_conf_mod"; 86 87 /* 88 * Event sequencing, time stamp and retry count 89 */ 90 static int ev_nretries; /* retry count per event */ 91 static uint64_t ev_seq; /* current event sequencing number */ 92 static hrtime_t ev_ts; /* current event timestamp */ 93 static int first_event; /* first event since init */ 94 95 /* 96 * State of the sysevent conf table, derived from 97 * the /etc/sysevent/config files 98 */ 99 static conftab_t *conftab = NULL; 100 static syseventtab_t *syseventtab = NULL; 101 static syseventtab_t *syseventtab_tail = NULL; 102 static sysevent_handle_t *confd_handle = NULL; 103 104 /* 105 * The cmd queue is a queue of commands ready to be sent 106 * to syseventconfd. Each command consists of the path 107 * and arguments to be fork/exec'ed. The daemon is unable 108 * to handle events during an active fork/exec and returns 109 * EAGAIN as a result. It is grossly inefficient to bounce 110 * these events back to syseventd, so we queue them here for delivery. 111 */ 112 static cmdqueue_t *cmdq = NULL; 113 static cmdqueue_t *cmdq_tail = NULL; 114 static mutex_t cmdq_lock; 115 static cond_t cmdq_cv; 116 static int cmdq_cnt; 117 static thread_t cmdq_thr_id; 118 static cond_t cmdq_thr_cv; 119 static int want_fini; 120 121 /* 122 * State of the door channel to syseventconfd 123 */ 124 static int confd_state = CONFD_STATE_NOT_RUNNING; 125 126 /* 127 * Number of times to retry event after restarting syeventconfd 128 */ 129 static int confd_retries; 130 131 /* 132 * Number of times to retry a failed transport 133 */ 134 static int transport_retries; 135 136 /* 137 * Normal sleep time when syseventconfd returns EAGAIN 138 * is one second but to avoid thrashing, sleep for 139 * something larger when syseventconfd not responding. 140 * This should never happen of course but it seems better 141 * to attempt to handle possible errors gracefully. 142 */ 143 static int confd_err_msg_emitted; 144 145 146 static int sysevent_conf_dummy_event(sysevent_t *, int); 147 148 /* 149 * External references 150 */ 151 extern int debug_level; 152 extern char *root_dir; 153 extern void syseventd_print(int level, char *format, ...); 154 extern void syseventd_err_print(char *format, ...); 155 156 157 158 static struct slm_mod_ops sysevent_conf_mod_ops = { 159 SE_MAJOR_VERSION, /* syseventd module major version */ 160 SE_MINOR_VERSION, /* syseventd module minor version */ 161 SE_MAX_RETRY_LIMIT, /* max retry if EAGAIN */ 162 &sysevent_conf_event /* event handler */ 163 }; 164 165 static struct slm_mod_ops sysevent_conf_dummy_mod_ops = { 166 SE_MAJOR_VERSION, /* syseventd module major version */ 167 SE_MINOR_VERSION, /* syseventd module minor version */ 168 0, /* no retries, always succeeds */ 169 &sysevent_conf_dummy_event /* dummy event handler */ 170 }; 171 172 173 174 /* 175 * skip_spaces() - skip to next non-space character 176 */ 177 static char * 178 skip_spaces(char **cpp) 179 { 180 char *cp = *cpp; 181 182 while (*cp == ' ' || *cp == '\t') 183 cp++; 184 if (*cp == 0) { 185 *cpp = 0; 186 return (NULL); 187 } 188 return (cp); 189 } 190 191 192 /* 193 * Get next white-space separated field. 194 * next_field() will not check any characters on next line. 195 * Each entry is composed of a single line. 196 */ 197 static char * 198 next_field(char **cpp) 199 { 200 char *cp = *cpp; 201 char *start; 202 203 while (*cp == ' ' || *cp == '\t') 204 cp++; 205 if (*cp == 0) { 206 *cpp = 0; 207 return (NULL); 208 } 209 start = cp; 210 while (*cp && *cp != ' ' && *cp != '\t') 211 cp++; 212 if (*cp != 0) 213 *cp++ = 0; 214 *cpp = cp; 215 return (start); 216 } 217 218 219 220 /* 221 * The following functions are simple wrappers/equivalents 222 * for malloc, realloc, free, strdup and a special free 223 * for strdup. 224 * 225 * These functions ensure that any failed mallocs are 226 * reported via syslog() so if a command is not evoked 227 * in response to an event, the reason should be logged. 228 * These functions also provide a convenient place for 229 * hooks for checking for memory leaks. 230 */ 231 232 static void * 233 sc_malloc(size_t n) 234 { 235 void *p; 236 237 p = malloc(n); 238 if (p == NULL) { 239 syslog(LOG_ERR, OUT_OF_MEMORY_ERR); 240 } 241 return (p); 242 } 243 244 /*ARGSUSED*/ 245 static void * 246 sc_realloc(void *p, size_t current, size_t n) 247 { 248 p = realloc(p, n); 249 if (p == NULL) { 250 syslog(LOG_ERR, OUT_OF_MEMORY_ERR); 251 } 252 return (p); 253 } 254 255 256 /*ARGSUSED*/ 257 static void 258 sc_free(void *p, size_t n) 259 { 260 free(p); 261 } 262 263 264 static char * 265 sc_strdup(char *cp) 266 { 267 char *new; 268 269 new = malloc((unsigned)(strlen(cp) + 1)); 270 if (new == NULL) { 271 syslog(LOG_ERR, OUT_OF_MEMORY_ERR); 272 return (NULL); 273 } 274 (void) strcpy(new, cp); 275 return (new); 276 } 277 278 279 static void 280 sc_strfree(char *s) 281 { 282 if (s) 283 free(s); 284 } 285 286 287 /* 288 * The following functions provide some simple dynamic string 289 * capability. This module has no hard-coded maximum string 290 * lengths and should be able to parse and generate arbitrarily 291 * long strings, macro expansion and command lines. 292 * 293 * Each string must be explicitly allocated and freed. 294 */ 295 296 /* 297 * Allocate a dynamic string, with a hint to indicate how 298 * much memory to dynamically add to the string as it grows 299 * beyond its existing bounds, so as to avoid excessive 300 * reallocs as a string grows. 301 */ 302 static str_t * 303 initstr(int hint) 304 { 305 str_t *str; 306 307 if ((str = sc_malloc(sizeof (str_t))) == NULL) 308 return (NULL); 309 str->s_str = NULL; 310 str->s_len = 0; 311 str->s_alloc = 0; 312 str->s_hint = hint; 313 return (str); 314 } 315 316 317 /* 318 * Free a dynamically-allocated string 319 */ 320 static void 321 freestr(str_t *str) 322 { 323 if (str->s_str) { 324 sc_free(str->s_str, str->s_alloc); 325 } 326 sc_free(str, sizeof (str_t)); 327 } 328 329 330 /* 331 * Reset a dynamically-allocated string, allows reuse 332 * rather than freeing the old and allocating a new one. 333 */ 334 static void 335 resetstr(str_t *str) 336 { 337 str->s_len = 0; 338 } 339 340 341 /* 342 * Copy a (simple) string onto a dynamically-allocated string 343 */ 344 static int 345 strcopys(str_t *str, char *s) 346 { 347 char *new_str; 348 int len = strlen(s) + 1; 349 350 if (str->s_alloc < len) { 351 new_str = (str->s_str == NULL) ? 352 sc_malloc(len+str->s_hint) : 353 sc_realloc(str->s_str, str->s_alloc, len+str->s_hint); 354 if (new_str == NULL) { 355 return (1); 356 } 357 str->s_str = new_str; 358 str->s_alloc = len + str->s_hint; 359 } 360 (void) strcpy(str->s_str, s); 361 str->s_len = len - 1; 362 return (0); 363 } 364 365 366 /* 367 * Concatenate a (simple) string onto a dynamically-allocated string 368 */ 369 static int 370 strcats(str_t *str, char *s) 371 { 372 char *new_str; 373 int len = str->s_len + strlen(s) + 1; 374 375 if (str->s_alloc < len) { 376 new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) : 377 sc_realloc(str->s_str, str->s_alloc, len+str->s_hint); 378 if (new_str == NULL) { 379 return (1); 380 } 381 str->s_str = new_str; 382 str->s_alloc = len + str->s_hint; 383 } 384 (void) strcpy(str->s_str + str->s_len, s); 385 str->s_len = len - 1; 386 return (0); 387 } 388 389 390 /* 391 * Concatenate a character onto a dynamically-allocated string 392 */ 393 static int 394 strcatc(str_t *str, int c) 395 { 396 char *new_str; 397 int len = str->s_len + 2; 398 399 if (str->s_alloc < len) { 400 new_str = (str->s_str == NULL) ? sc_malloc(len+str->s_hint) : 401 sc_realloc(str->s_str, str->s_alloc, len+str->s_hint); 402 if (new_str == NULL) { 403 return (1); 404 } 405 str->s_str = new_str; 406 str->s_alloc = len + str->s_hint; 407 } 408 *(str->s_str + str->s_len) = (char)c; 409 *(str->s_str + str->s_len + 1) = 0; 410 str->s_len++; 411 return (0); 412 } 413 414 /* 415 * fgets() equivalent using a dynamically-allocated string 416 */ 417 static char * 418 fstrgets(str_t *line, FILE *fp) 419 { 420 int c; 421 422 resetstr(line); 423 while ((c = fgetc(fp)) != EOF) { 424 if (strcatc(line, c)) 425 return (NULL); 426 if (c == '\n') 427 break; 428 } 429 if (line->s_len == 0) 430 return (NULL); 431 return (line->s_str); 432 } 433 434 /* 435 * Truncate a dynamically-allocated string at index position 'pos' 436 */ 437 static void 438 strtrunc(str_t *str, int pos) 439 { 440 if (str->s_len > pos) { 441 str->s_len = pos; 442 *(str->s_str + pos) = 0; 443 } 444 } 445 446 447 448 /* 449 * Parse a sysevent.conf file, adding each entry spec to the event table. 450 * 451 * The format of an entry in a sysevent.conf file is: 452 * 453 * class subclass vendor publisher user reserved1 reserved path arguments 454 * 455 * Fields are separated by either SPACE or TAB characters. A 456 * '#' (number sign) at the beginning of a line indicates a 457 * comment. Comment lines and blank lines are ignored. 458 * 459 * class 460 * The class of the event. 461 * 462 * subclass 463 * The subclass of the event. 464 * 465 * vendor 466 * The name of the vendor defining the event, usually the 467 * stock symbol. Events generated by system components 468 * provided by Sun Microsystems, Inc. always define vendor 469 * as 'SUNW'. 470 * 471 * publisher 472 * The name of the application, driver or system module 473 * producing the event. 474 * 475 * user 476 * The name of the user under which the command should be 477 * run. This allows commands to run with access privileges 478 * other than those for root. The user field should be '-' 479 * for commands to be run as root. 480 * 481 * reserved1 482 * Must be '-'. 483 * 484 * reserved2 485 * Must be '-'. 486 * 487 * path 488 * Pathname of the command to be invoked for matching events. 489 * 490 * arguments 491 * Optional argument with possible macro substitution to permit 492 * arbitrary command line construction with event-specific data. 493 */ 494 static void 495 parse_conf_file(char *conf_file) 496 { 497 char conf_path[PATH_MAX]; 498 FILE *fp; 499 char *lp; 500 str_t *line; 501 int lineno = 0; 502 char *vendor, *publisher; 503 char *class, *subclass; 504 char *user; 505 char *reserved1, *reserved2; 506 char *path, *args; 507 syseventtab_t *sep; 508 struct passwd pwd; 509 struct passwd *pwdp; 510 char pwdbuf[1024]; 511 int do_setuid; 512 pid_t saved_uid; 513 gid_t saved_gid; 514 int i, err; 515 516 (void) snprintf(conf_path, PATH_MAX, "%s/%s", 517 SYSEVENT_CONFIG_DIR, conf_file); 518 519 syseventd_print(DBG_CONF_FILE, "%s: reading %s\n", whoami, conf_path); 520 521 if ((fp = fopen(conf_path, "r")) == NULL) { 522 syslog(LOG_ERR, CANNOT_OPEN_ERR, conf_file, strerror(errno)); 523 return; 524 } 525 526 if ((line = initstr(128)) == NULL) 527 return; 528 529 while ((lp = fstrgets(line, fp)) != NULL) { 530 lineno++; 531 if (*lp == '\n' || *lp == '#') 532 continue; 533 *(lp + strlen(lp)-1) = 0; 534 535 syseventd_print(DBG_CONF_FILE, "[%d]: %s\n", 536 lineno, lp); 537 538 if ((class = next_field(&lp)) != NULL) { 539 subclass = next_field(&lp); 540 if (lp == NULL) 541 goto mal_formed; 542 vendor = next_field(&lp); 543 if (lp == NULL) 544 goto mal_formed; 545 publisher = next_field(&lp); 546 if (lp == NULL) 547 goto mal_formed; 548 user = next_field(&lp); 549 if (lp == NULL) 550 goto mal_formed; 551 reserved1 = next_field(&lp); 552 if (lp == NULL) 553 goto mal_formed; 554 reserved2 = next_field(&lp); 555 if (lp == NULL) 556 goto mal_formed; 557 path = next_field(&lp); 558 if (lp == NULL) 559 goto mal_formed; 560 args = skip_spaces(&lp); 561 } 562 563 /* 564 * validate user 565 */ 566 do_setuid = 0; 567 if ((strcmp(user, "-") != 0) && (strcmp(user, "root") != 0)) { 568 i = getpwnam_r(user, &pwd, pwdbuf, 569 sizeof (pwdbuf), &pwdp); 570 if (i != 0 || pwdp == NULL) { 571 syslog(LOG_ERR, NO_USER_ERR, 572 conf_file, lineno, user); 573 continue; 574 } 575 do_setuid = 1; 576 } 577 578 /* 579 * validate reserved fields 580 */ 581 if (strcmp(reserved1, "-") != 0) { 582 syslog(LOG_ERR, RESERVED_FIELD_ERR, 583 conf_file, lineno, reserved1); 584 continue; 585 } 586 if (strcmp(reserved2, "-") != 0) { 587 syslog(LOG_ERR, RESERVED_FIELD_ERR, 588 conf_file, lineno, reserved2); 589 continue; 590 } 591 592 /* 593 * ensure path is executable by user 594 */ 595 err = 0; 596 if (do_setuid) { 597 saved_uid = getuid(); 598 saved_gid = getgid(); 599 if (setregid(pwdp->pw_gid, -1) == -1) { 600 syslog(LOG_ERR, SETREGID_ERR, 601 whoami, pwdp->pw_gid, strerror(errno)); 602 err = -1; 603 } 604 if (setreuid(pwdp->pw_uid, -1) == -1) { 605 syslog(LOG_ERR, SETREUID_ERR, 606 whoami, pwdp->pw_uid, strerror(errno)); 607 err = -1; 608 } 609 } 610 if ((i = access(path, X_OK)) == -1) { 611 syslog(LOG_ERR, CANNOT_EXECUTE_ERR, 612 conf_file, lineno, path, strerror(errno)); 613 } 614 if (do_setuid) { 615 if (setreuid(saved_uid, -1) == -1) { 616 syslog(LOG_ERR, SETREUID_ERR, 617 whoami, saved_uid, strerror(errno)); 618 err = -1; 619 } 620 if (setregid(saved_gid, -1) == -1) { 621 syslog(LOG_ERR, SETREGID_ERR, 622 whoami, saved_gid, strerror(errno)); 623 err = -1; 624 } 625 } 626 if (i == -1 || err == -1) 627 continue; 628 629 /* 630 * all sanity tests successful - perform allocations 631 * to add entry to table 632 */ 633 if ((sep = sc_malloc(sizeof (syseventtab_t))) == NULL) 634 break; 635 636 sep->se_conf_file = conf_file; 637 sep->se_lineno = lineno; 638 sep->se_vendor = sc_strdup(vendor); 639 sep->se_publisher = sc_strdup(publisher); 640 sep->se_class = sc_strdup(class); 641 sep->se_subclass = sc_strdup(subclass); 642 sep->se_user = sc_strdup(user); 643 if (do_setuid) { 644 sep->se_uid = pwdp->pw_uid; 645 sep->se_gid = pwdp->pw_gid; 646 } else { 647 sep->se_uid = 0; 648 sep->se_gid = 0; 649 } 650 sep->se_reserved1 = sc_strdup(reserved1); 651 sep->se_reserved2 = sc_strdup(reserved2); 652 sep->se_path = sc_strdup(path); 653 sep->se_args = (args == NULL) ? NULL : sc_strdup(args); 654 sep->se_next = NULL; 655 656 if (sep->se_vendor == NULL || sep->se_publisher == NULL || 657 sep->se_class == NULL || sep->se_subclass == NULL || 658 sep->se_user == NULL || sep->se_reserved1 == NULL || 659 sep->se_reserved2 == NULL || sep->se_path == NULL || 660 (args && sep->se_args == NULL)) { 661 sc_strfree(sep->se_vendor); 662 sc_strfree(sep->se_publisher); 663 sc_strfree(sep->se_class); 664 sc_strfree(sep->se_subclass); 665 sc_strfree(sep->se_user); 666 sc_strfree(sep->se_reserved1); 667 sc_strfree(sep->se_reserved2); 668 sc_strfree(sep->se_path); 669 sc_strfree(sep->se_args); 670 sc_free(sep, sizeof (syseventtab_t)); 671 break; 672 } 673 674 /* 675 * link new entry into the table 676 */ 677 if (syseventtab == NULL) { 678 syseventtab = sep; 679 syseventtab_tail = sep; 680 } else { 681 syseventtab_tail->se_next = sep; 682 syseventtab_tail = sep; 683 } 684 685 if (debug_level >= DBG_DETAILED) { 686 syseventtab_t *sp; 687 for (sp = syseventtab; sp; sp = sp->se_next) { 688 syseventd_print(DBG_DETAILED, 689 " vendor=%s\n", sp->se_vendor); 690 syseventd_print(DBG_DETAILED, 691 " publisher=%s\n", sp->se_publisher); 692 syseventd_print(DBG_DETAILED, 693 " class=%s\n", sp->se_class); 694 syseventd_print(DBG_DETAILED, 695 " subclass=%s\n", sp->se_subclass); 696 syseventd_print(DBG_DETAILED, 697 " user=%s uid=%d gid=%d\n", 698 sp->se_user, sp->se_uid, sp->se_gid); 699 syseventd_print(DBG_DETAILED, 700 " reserved1=%s\n", sp->se_reserved1); 701 syseventd_print(DBG_DETAILED, 702 " reserved2=%s\n", sp->se_reserved2); 703 syseventd_print(DBG_DETAILED, 704 " path=%s\n", sp->se_path); 705 if (sp->se_args != NULL) { 706 syseventd_print(DBG_DETAILED, 707 " args=%s\n", sp->se_args); 708 } 709 } 710 } 711 712 continue; 713 714 mal_formed: 715 syslog(LOG_ERR, SYNTAX_ERR, conf_file, lineno); 716 } 717 718 freestr(line); 719 (void) fclose(fp); 720 } 721 722 723 /* 724 * Build the events specification table, a summation of all 725 * event specification found in the installed sysevent.conf 726 * configuration files. 727 * 728 * All sysevent.conf files reside in the /etc/sysevent/config 729 * and may contain zero or more event/command specifications. 730 * A sysevent.conf file should be named as follows: 731 * 732 * <vendor>,[<publisher>,][<class>,]sysevent.conf 733 * 734 * Event/command specifications delivered by the base Solaris 735 * system are provided in /etc/sysevent/config/SUNW,sysevent.conf. 736 * Event/command specifications delivered by optional 737 * Sun-supplied packages may install additional sysevent.conf 738 * files in /etc/sysevent/config using vendor SUNW, and additional 739 * publisher and/or event class naming to distinguish the 740 * events required for those products. Products provided 741 * by third-party hardware or software companies may 742 * distinguish their sysevent.conf files by vendor, and 743 * by publisher and/or event class within vendor. 744 * 745 * Files residing in /etc/sysevent/config with a '.' (period) 746 * as the first character of the name and files with a suffix 747 * of other than "sysevent.conf" are ignored. 748 */ 749 static void 750 build_event_table() 751 { 752 conftab_t *cfp = NULL; 753 DIR *dir; 754 struct dirent *result; 755 conftab_t *new_cfp; 756 char *str; 757 758 if ((dir = opendir(SYSEVENT_CONFIG_DIR)) == NULL) { 759 syslog(LOG_ERR, CANNOT_OPEN_ERR, 760 SYSEVENT_CONFIG_DIR, strerror(errno)); 761 return; 762 } 763 764 while ((result = readdir(dir)) != NULL) { 765 if (result->d_name[0] == '.') 766 continue; 767 768 /* 769 * file must have extension "sysevent.conf" 770 */ 771 if ((str = strrchr(result->d_name, ',')) != NULL) { 772 str++; 773 } else { 774 str = result->d_name; 775 } 776 if (strcmp(str, "sysevent.conf") != 0) { 777 syseventd_print(DBG_CONF_FILE, 778 "%s: ignoring %s\n", whoami, str); 779 continue; 780 } 781 782 /* 783 * Add to file table and parse this conf file 784 */ 785 if ((str = sc_strdup(result->d_name)) == NULL) 786 goto err; 787 if ((new_cfp = sc_malloc(sizeof (conftab_t))) == NULL) { 788 sc_strfree(str); 789 goto err; 790 } 791 if (conftab == NULL) { 792 conftab = new_cfp; 793 } else { 794 for (cfp = conftab; cfp->cf_next; cfp = cfp->cf_next) 795 ; 796 cfp->cf_next = new_cfp; 797 } 798 cfp = new_cfp; 799 cfp->cf_conf_file = str; 800 cfp->cf_next = NULL; 801 802 parse_conf_file(cfp->cf_conf_file); 803 } 804 805 err: 806 if (closedir(dir) == -1) { 807 if (errno == EAGAIN) 808 goto err; 809 syslog(LOG_ERR, CLOSEDIR_ERR, 810 SYSEVENT_CONFIG_DIR, strerror(errno)); 811 } 812 } 813 814 815 static int 816 enter_lock(char *lock_file) 817 { 818 struct flock lock; 819 int lock_fd; 820 821 (void) snprintf(lock_file, PATH_MAX, "%s/%s", 822 SYSEVENT_CONFIG_DIR, LOCK_FILENAME); 823 lock_fd = open(lock_file, O_CREAT|O_RDWR, 0644); 824 if (lock_fd < 0) { 825 syslog(LOG_ERR, MSG_LOCK_CREATE_ERR, 826 whoami, lock_file, strerror(errno)); 827 return (-1); 828 } 829 830 lock.l_type = F_WRLCK; 831 lock.l_whence = SEEK_SET; 832 lock.l_start = 0; 833 lock.l_len = 0; 834 835 retry: 836 if (fcntl(lock_fd, F_SETLKW, &lock) == -1) { 837 if (errno == EAGAIN || errno == EINTR) 838 goto retry; 839 (void) close(lock_fd); 840 syslog(LOG_ERR, MSG_LOCK_SET_ERR, 841 whoami, lock_file, strerror(errno)); 842 return (-1); 843 } 844 845 return (lock_fd); 846 } 847 848 849 static void 850 exit_lock(int lock_fd, char *lock_file) 851 { 852 struct flock lock; 853 854 lock.l_type = F_UNLCK; 855 lock.l_whence = SEEK_SET; 856 lock.l_start = 0; 857 lock.l_len = 0; 858 859 if (fcntl(lock_fd, F_SETLK, &lock) == -1) { 860 syslog(LOG_ERR, MSG_LOCK_CLR_ERR, 861 whoami, lock_file, strerror(errno)); 862 } 863 864 if (close(lock_fd) == -1) { 865 syslog(LOG_ERR, MSG_LOCK_CLOSE_ERR, 866 whoami, lock_file, strerror(errno)); 867 } 868 } 869 870 871 /* 872 * Free the events specification table, constructed by 873 * parsing all the sysevent.conf files found. 874 * 875 * The free of this table is in response to a HUP 876 * given to the syseventd daemon, permitting the 877 * table to be rebuilt after adding a new sysevent.conf 878 * file or changing an existing one without shutting 879 * down the daemon. 880 */ 881 static void 882 free_event_table() 883 { 884 syseventtab_t *sep; 885 syseventtab_t *sep_next; 886 conftab_t *cfp; 887 conftab_t *cfp_next; 888 889 sep = syseventtab; 890 while (sep) { 891 sc_strfree(sep->se_vendor); 892 sc_strfree(sep->se_publisher); 893 sc_strfree(sep->se_class); 894 sc_strfree(sep->se_subclass); 895 sc_strfree(sep->se_user); 896 sc_strfree(sep->se_reserved1); 897 sc_strfree(sep->se_reserved2); 898 sc_strfree(sep->se_path); 899 if (sep->se_args) 900 sc_strfree(sep->se_args); 901 sep_next = sep->se_next; 902 sc_free(sep, sizeof (syseventtab_t)); 903 sep = sep_next; 904 } 905 syseventtab = NULL; 906 907 cfp = conftab; 908 while (cfp) { 909 sc_strfree(cfp->cf_conf_file); 910 cfp_next = cfp->cf_next; 911 sc_free(cfp, sizeof (conftab_t)); 912 cfp = cfp_next; 913 } 914 conftab = NULL; 915 } 916 917 918 919 static char ident_chars[] = "_"; 920 921 /* 922 * Return a dynamically-allocated string containing the 923 * the next identifier in the string being parsed, pointed 924 * at by 'id'. 'end' returns a pointer to the character 925 * after the identifier. 926 * 927 * Identifiers are all alphanumeric ascii characters and 928 * those contained in ident_chars. 929 * 930 * The returned string must be explicitly freed via 931 * freestr(). 932 */ 933 static str_t * 934 snip_identifier(char *id, char **end) 935 { 936 str_t *token; 937 938 if ((token = initstr(32)) == NULL) 939 return (NULL); 940 941 while (*id != 0) { 942 if (isascii(*id) && 943 (isalnum(*id) || strchr(ident_chars, *id) != NULL)) { 944 if (strcatc(token, *id++)) { 945 freestr(token); 946 return (NULL); 947 } 948 } else { 949 *end = id; 950 return (token); 951 } 952 } 953 954 *end = id; 955 return (token); 956 } 957 958 959 /* 960 * Identical to snip_identifier(), but the identifier 961 * is delimited by the characters { and }. 962 */ 963 static str_t * 964 snip_delimited_identifier(char *id, char **end) 965 { 966 str_t *token; 967 968 if ((token = initstr(32)) == NULL) 969 return (NULL); 970 971 while (*id != 0) { 972 if (*id == '}') { 973 *end = id+1; 974 return (token); 975 } 976 if (strcatc(token, *id++)) { 977 freestr(token); 978 return (NULL); 979 } 980 } 981 982 if (*id == 0) { 983 freestr(token); 984 return (NULL); 985 } 986 987 *end = id; 988 return (token); 989 } 990 991 992 /* 993 * Return a string with the name of the attribute type 994 */ 995 static char *nv_attr_type_strings[] = { 996 "unknown", 997 "boolean", 998 "byte", 999 "int16", 1000 "uint16", 1001 "int32", 1002 "uint32", 1003 "int64", 1004 "uint64", 1005 "string", 1006 "byte-array", 1007 "int16-array", 1008 "uint16-array", 1009 "int32-array", 1010 "uint32-array", 1011 "int64-array", 1012 "uint64-array", 1013 "string-array", 1014 "hrtime" 1015 }; 1016 1017 static char * 1018 se_attr_type_to_str(int se_attr_type) 1019 { 1020 if (se_attr_type >= 0 && 1021 se_attr_type < sizeof (nv_attr_type_strings) / sizeof (char *)) { 1022 return (nv_attr_type_strings[se_attr_type]); 1023 } 1024 return (nv_attr_type_strings[DATA_TYPE_UNKNOWN]); 1025 } 1026 1027 1028 /* 1029 * Find and return the data matching the macro name 'token' 1030 * 1031 * Predefined macros are simply substituted with the 1032 * data from the event header: 1033 * 1034 * $vendor - the vendor string defining the event. 1035 * 1036 * $publisher - the publisher string defining the event. 1037 * 1038 * $class - the class string defining the event. 1039 * 1040 * $subclass - the subclass string defining the event. 1041 * 1042 * $sequence - the sequence number of the event. 1043 * 1044 * $timestamp - the timestamp of the event. 1045 * 1046 * Attributes with signed data types (DATA_TYPE_INT16, 1047 * DATA_TYPE_INT32 and DATA_TYPE_INT64) are expanded 1048 * as decimal digits. 1049 * 1050 * Attributes with unsigned data types (DATA_TYPE_BYTE, 1051 * DATA_TYPE_UINT16, DATA_TYPE_UINT32, DATA_TYPE_UINT64 and 1052 * DATA_TYPE_HTTIME) are expanded as hexadecimal digits 1053 * with a "0x" prefix. 1054 * 1055 * Attributes with string data type (DATA_TYPE_STRING) 1056 * are expanded with the string data. The data is 1057 * not quoted. If if it desired that the quoted strings 1058 * be generated on the command line, put quotes around 1059 * the macro call in the arguments. 1060 * 1061 * Array types are expanded with each element expanded 1062 * as defined for that scalar type, with a space separating 1063 * each element substitution. 1064 */ 1065 1066 static str_t * 1067 find_macro_definition(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep, 1068 char *token, sysevent_hdr_info_t *hdr) 1069 { 1070 nvpair_t *nvp; 1071 int nmatches; 1072 char num[64]; 1073 str_t *replacement; 1074 int i; 1075 uint_t nelems; 1076 union { 1077 uchar_t x_byte; 1078 int16_t x_int16; 1079 uint16_t x_uint16; 1080 int32_t x_int32; 1081 uint32_t x_uint32; 1082 int64_t x_int64; 1083 uint64_t x_uint64; 1084 hrtime_t x_time; 1085 char *x_string; 1086 uchar_t *x_byte_array; 1087 int16_t *x_int16_array; 1088 int32_t *x_int32_array; 1089 int64_t *x_int64_array; 1090 uint16_t *x_uint16_array; 1091 uint32_t *x_uint32_array; 1092 uint64_t *x_uint64_array; 1093 char **x_string_array; 1094 } x; 1095 1096 1097 if ((replacement = initstr(128)) == NULL) { 1098 return (NULL); 1099 } 1100 1101 if (strcmp(token, "vendor") == 0) { 1102 if (strcopys(replacement, hdr->vendor)) { 1103 freestr(replacement); 1104 return (NULL); 1105 } 1106 return (replacement); 1107 } 1108 1109 if (strcmp(token, "publisher") == 0) { 1110 if (strcopys(replacement, hdr->publisher)) { 1111 freestr(replacement); 1112 return (NULL); 1113 } 1114 return (replacement); 1115 } 1116 1117 if (strcmp(token, "class") == 0) { 1118 if (strcopys(replacement, hdr->class)) { 1119 freestr(replacement); 1120 return (NULL); 1121 } 1122 return (replacement); 1123 } 1124 1125 if (strcmp(token, "subclass") == 0) { 1126 if (strcopys(replacement, hdr->subclass)) { 1127 freestr(replacement); 1128 return (NULL); 1129 } 1130 return (replacement); 1131 } 1132 1133 if ((strcmp(token, "sequence") == 0) || 1134 (strcmp(token, "timestamp") == 0)) { 1135 if (strcmp(token, "sequence") == 0) { 1136 (void) snprintf(num, sizeof (num), 1137 "0x%llx", sysevent_get_seq(ev)); 1138 } else { 1139 hrtime_t ts; 1140 sysevent_get_time(ev, &ts); 1141 (void) snprintf(num, sizeof (num), "0x%llx", ts); 1142 } 1143 if (strcopys(replacement, num)) { 1144 freestr(replacement); 1145 return (NULL); 1146 } 1147 return (replacement); 1148 } 1149 1150 nmatches = 0; 1151 1152 if (nvlist) { 1153 nvpair_t *nvp_match; 1154 nvp = NULL; 1155 while ((nvp = nvlist_next_nvpair(nvlist, nvp)) != NULL) { 1156 if (debug_level >= DBG_DETAILED) { 1157 syseventd_print(DBG_DETAILED, 1158 " attribute: %s %s\n", nvpair_name(nvp), 1159 se_attr_type_to_str(nvpair_type(nvp))); 1160 } 1161 if (strcmp(token, nvpair_name(nvp)) == 0) { 1162 nmatches++; 1163 nvp_match = nvp; 1164 } 1165 } 1166 nvp = nvp_match; 1167 } 1168 1169 if (nmatches == 0) { 1170 syslog(LOG_ERR, MACRO_UNDEF_ERR, 1171 sep->se_conf_file, sep->se_lineno, token); 1172 freestr(replacement); 1173 return (NULL); 1174 } else if (nmatches > 1) { 1175 syslog(LOG_ERR, MACRO_MULT_DEF_ERR, 1176 sep->se_conf_file, sep->se_lineno, token); 1177 freestr(replacement); 1178 return (NULL); 1179 } 1180 1181 switch (nvpair_type(nvp)) { 1182 case DATA_TYPE_BYTE: 1183 (void) nvpair_value_byte(nvp, &x.x_byte); 1184 (void) snprintf(num, sizeof (num), "0x%x", x.x_byte); 1185 if (strcats(replacement, num)) { 1186 freestr(replacement); 1187 return (NULL); 1188 } 1189 break; 1190 case DATA_TYPE_INT16: 1191 (void) nvpair_value_int16(nvp, &x.x_int16); 1192 (void) snprintf(num, sizeof (num), "%d", x.x_int16); 1193 if (strcats(replacement, num)) { 1194 freestr(replacement); 1195 return (NULL); 1196 } 1197 break; 1198 case DATA_TYPE_UINT16: 1199 (void) nvpair_value_uint16(nvp, &x.x_uint16); 1200 (void) snprintf(num, sizeof (num), "0x%x", x.x_uint16); 1201 if (strcats(replacement, num)) { 1202 freestr(replacement); 1203 return (NULL); 1204 } 1205 break; 1206 case DATA_TYPE_INT32: 1207 (void) nvpair_value_int32(nvp, &x.x_int32); 1208 (void) snprintf(num, sizeof (num), "%d", x.x_int32); 1209 if (strcats(replacement, num)) { 1210 freestr(replacement); 1211 return (NULL); 1212 } 1213 break; 1214 case DATA_TYPE_UINT32: 1215 (void) nvpair_value_uint32(nvp, &x.x_uint32); 1216 (void) snprintf(num, sizeof (num), "0x%x", x.x_uint32); 1217 if (strcats(replacement, num)) { 1218 freestr(replacement); 1219 return (NULL); 1220 } 1221 break; 1222 case DATA_TYPE_INT64: 1223 (void) nvpair_value_int64(nvp, &x.x_int64); 1224 (void) snprintf(num, sizeof (num), "%lld", x.x_int64); 1225 if (strcats(replacement, num)) { 1226 freestr(replacement); 1227 return (NULL); 1228 } 1229 break; 1230 case DATA_TYPE_UINT64: 1231 (void) nvpair_value_uint64(nvp, &x.x_uint64); 1232 (void) snprintf(num, sizeof (num), "0x%llx", x.x_uint64); 1233 if (strcats(replacement, num)) { 1234 freestr(replacement); 1235 return (NULL); 1236 } 1237 break; 1238 case DATA_TYPE_STRING: 1239 (void) nvpair_value_string(nvp, &x.x_string); 1240 if (strcats(replacement, x.x_string)) { 1241 freestr(replacement); 1242 return (NULL); 1243 } 1244 break; 1245 case DATA_TYPE_BYTE_ARRAY: { 1246 uchar_t *p; 1247 (void) nvpair_value_byte_array(nvp, 1248 &x.x_byte_array, &nelems); 1249 p = x.x_byte_array; 1250 for (i = 0; i < nelems; i++) { 1251 (void) snprintf(num, sizeof (num), 1252 "0x%x ", *p++ & 0xff); 1253 if (strcats(replacement, num)) { 1254 freestr(replacement); 1255 return (NULL); 1256 } 1257 } 1258 } 1259 break; 1260 case DATA_TYPE_INT16_ARRAY: { 1261 int16_t *p; 1262 (void) nvpair_value_int16_array(nvp, 1263 &x.x_int16_array, &nelems); 1264 p = x.x_int16_array; 1265 for (i = 0; i < nelems; i++) { 1266 (void) snprintf(num, sizeof (num), "%d ", *p++); 1267 if (strcats(replacement, num)) { 1268 freestr(replacement); 1269 return (NULL); 1270 } 1271 } 1272 } 1273 break; 1274 1275 case DATA_TYPE_UINT16_ARRAY: { 1276 uint16_t *p; 1277 (void) nvpair_value_uint16_array(nvp, 1278 &x.x_uint16_array, &nelems); 1279 p = x.x_uint16_array; 1280 for (i = 0; i < nelems; i++) { 1281 (void) snprintf(num, sizeof (num), 1282 "0x%x ", *p++); 1283 if (strcats(replacement, num)) { 1284 freestr(replacement); 1285 return (NULL); 1286 } 1287 } 1288 } 1289 break; 1290 1291 case DATA_TYPE_INT32_ARRAY: { 1292 int32_t *p; 1293 (void) nvpair_value_int32_array(nvp, 1294 &x.x_int32_array, &nelems); 1295 p = x.x_int32_array; 1296 for (i = 0; i < nelems; i++) { 1297 (void) snprintf(num, sizeof (num), "%d ", *p++); 1298 if (strcats(replacement, num)) { 1299 freestr(replacement); 1300 return (NULL); 1301 } 1302 } 1303 } 1304 break; 1305 1306 case DATA_TYPE_UINT32_ARRAY: { 1307 uint32_t *p; 1308 (void) nvpair_value_uint32_array(nvp, 1309 &x.x_uint32_array, &nelems); 1310 p = x.x_uint32_array; 1311 for (i = 0; i < nelems; i++) { 1312 (void) snprintf(num, sizeof (num), 1313 "0x%x ", *p++); 1314 if (strcats(replacement, num)) { 1315 freestr(replacement); 1316 return (NULL); 1317 } 1318 } 1319 } 1320 break; 1321 1322 case DATA_TYPE_INT64_ARRAY: { 1323 int64_t *p; 1324 (void) nvpair_value_int64_array(nvp, 1325 &x.x_int64_array, &nelems); 1326 p = x.x_int64_array; 1327 for (i = 0; i < nelems; i++) { 1328 (void) snprintf(num, sizeof (num), 1329 "%lld ", *p++); 1330 if (strcats(replacement, num)) { 1331 freestr(replacement); 1332 return (NULL); 1333 } 1334 } 1335 } 1336 break; 1337 1338 case DATA_TYPE_UINT64_ARRAY: { 1339 uint64_t *p; 1340 (void) nvpair_value_uint64_array(nvp, 1341 &x.x_uint64_array, &nelems); 1342 p = x.x_uint64_array; 1343 for (i = 0; i < nelems; i++) { 1344 (void) snprintf(num, sizeof (num), 1345 "0x%llx ", *p++); 1346 if (strcats(replacement, num)) { 1347 freestr(replacement); 1348 return (NULL); 1349 } 1350 } 1351 } 1352 break; 1353 1354 case DATA_TYPE_STRING_ARRAY: { 1355 char **p; 1356 (void) nvpair_value_string_array(nvp, 1357 &x.x_string_array, &nelems); 1358 p = x.x_string_array; 1359 for (i = 0; i < nelems; i++) { 1360 if (strcats(replacement, *p++) || 1361 strcats(replacement, " ")) { 1362 freestr(replacement); 1363 return (NULL); 1364 } 1365 } 1366 } 1367 break; 1368 1369 case DATA_TYPE_HRTIME: 1370 (void) nvpair_value_hrtime(nvp, &x.x_time); 1371 (void) snprintf(num, sizeof (num), "0x%llx", x.x_time); 1372 if (strcats(replacement, num)) { 1373 freestr(replacement); 1374 return (NULL); 1375 } 1376 break; 1377 default: 1378 syslog(LOG_ERR, ATTR_UNSUPPORTED_ERR, 1379 sep->se_conf_file, sep->se_lineno, 1380 nvpair_type(nvp), token); 1381 freestr(replacement); 1382 return (NULL); 1383 } 1384 1385 return (replacement); 1386 } 1387 1388 /* 1389 * Expand macros in the command template provided in an event 1390 * specification with the data from the event or event attributes. 1391 * 1392 * Macros are introduced by the '$' character, with the macro 1393 * name being the following token separated by a SPACE or 1394 * TAB character. If the macro name is embedded in text, 1395 * it may be delineated by '${' and "}'. A backslash before 1396 * the '$' causes macro expansion not to occur. 1397 * 1398 * The following predefined macros are defined for each event: 1399 * 1400 * $vendor - the vendor string defining the event. 1401 * 1402 * $publisher - the publisher string defining the event. 1403 * 1404 * $class - the class string defining the event. 1405 * 1406 * $subclass - the subclass string defining the event. 1407 * 1408 * $sequence - the sequence number of the event. 1409 * 1410 * $timestamp - the timestamp of the event. 1411 * 1412 * 1413 * Macro names other than those predefined are compared against 1414 * the attribute list provided with the event. An attribute 1415 * with name matching the macro name causes the value of 1416 * of the attribute to be substituted as ASCII text on the 1417 * generated command line. 1418 * 1419 * Use of a macro for which no attribute with that name 1420 * is defined, or for which multiple attributes with that 1421 * name are provided, cause an error and the command is 1422 * not invoked. 1423 */ 1424 static int 1425 expand_macros(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep, 1426 str_t *line, sysevent_hdr_info_t *hdr) 1427 { 1428 char *p; 1429 int state; 1430 char *end; 1431 str_t *token; 1432 str_t *remainder; 1433 str_t *replacement; 1434 int count; 1435 int dollar_position; 1436 1437 syseventd_print(DBG_MACRO, " expanding macros: '%s'\n", line->s_str); 1438 1439 reset: 1440 state = 0; 1441 count = 0; 1442 for (p = line->s_str; *p != 0; p++, count++) { 1443 switch (state) { 1444 case 0: /* initial state */ 1445 if (*p == '\\') { 1446 state = 1; 1447 } else if (*p == '$') { 1448 dollar_position = count; 1449 state = 2; 1450 } 1451 break; 1452 case 1: /* skip characters */ 1453 state = 0; /* after backslash */ 1454 break; 1455 case 2: /* character after $ */ 1456 if (*p == '{') { 1457 token = snip_delimited_identifier(p+1, &end); 1458 } else { 1459 token = snip_identifier(p, &end); 1460 } 1461 if (token == NULL) 1462 goto failed; 1463 1464 if ((remainder = initstr(128)) == NULL) { 1465 freestr(token); 1466 return (1); 1467 } 1468 if (strcopys(remainder, end)) { 1469 freestr(token); 1470 freestr(remainder); 1471 return (1); 1472 } 1473 replacement = find_macro_definition(ev, nvlist, 1474 sep, token->s_str, hdr); 1475 if (replacement == NULL) { 1476 freestr(token); 1477 freestr(remainder); 1478 return (1); 1479 } 1480 syseventd_print(DBG_MACRO, 1481 " '%s' expands to '%s'\n", 1482 token->s_str, replacement->s_str); 1483 1484 strtrunc(line, dollar_position); 1485 if (strcats(line, replacement->s_str)) { 1486 freestr(token); 1487 freestr(replacement); 1488 freestr(remainder); 1489 return (1); 1490 } 1491 if (strcats(line, remainder->s_str)) { 1492 freestr(token); 1493 freestr(replacement); 1494 freestr(remainder); 1495 return (1); 1496 } 1497 1498 syseventd_print(DBG_MACRO, 1499 " with macro expanded: '%s'\n", line->s_str); 1500 1501 freestr(token); 1502 freestr(replacement); 1503 freestr(remainder); 1504 goto reset; 1505 } 1506 } 1507 1508 failed: 1509 if (state != 0) { 1510 syslog(LOG_ERR, SYNTAX_ERR, sep->se_conf_file, sep->se_lineno); 1511 return (1); 1512 } 1513 1514 return (0); 1515 } 1516 1517 1518 static void 1519 start_syseventconfd() 1520 { 1521 int err; 1522 1523 err = system1("/usr/lib/sysevent/syseventconfd", 1524 "/usr/lib/sysevent/syseventconfd"); 1525 1526 if (err != 0 && confd_err_msg_emitted == 0) { 1527 if (confd_state == CONFD_STATE_NOT_RUNNING) { 1528 syslog(LOG_ERR, SYSEVENTCONFD_START_ERR, 1529 strerror(errno)); 1530 } else { 1531 syslog(LOG_ERR, SYSEVENTCONFD_RESTART_ERR, 1532 strerror(errno)); 1533 } 1534 } 1535 } 1536 1537 1538 static int 1539 system1(const char *s_path, const char *s) 1540 { 1541 struct sigaction cbuf, ibuf, qbuf, ignore, dfl; 1542 sigset_t mask; 1543 sigset_t savemask; 1544 struct stat st; 1545 pid_t pid; 1546 int status, w; 1547 1548 /* Check the requested command */ 1549 if (s == NULL) { 1550 errno = EINVAL; 1551 return (-1); 1552 } 1553 1554 /* Check the ability to execute devfsadmd from this process */ 1555 if (stat(s_path, &st) < 0) { 1556 return (-1); 1557 } 1558 if (((geteuid() == st.st_uid) && ((st.st_mode & S_IXUSR) == 0)) || 1559 ((getegid() == st.st_gid) && ((st.st_mode & S_IXGRP) == 0)) || 1560 ((st.st_mode & S_IXOTH) == 0)) { 1561 errno = EPERM; 1562 return (-1); 1563 } 1564 1565 /* 1566 * Block SIGCHLD and set up a default handler for the duration of the 1567 * system1 call. 1568 */ 1569 (void) sigemptyset(&mask); 1570 (void) sigaddset(&mask, SIGCHLD); 1571 (void) sigprocmask(SIG_BLOCK, &mask, &savemask); 1572 (void) memset(&dfl, 0, sizeof (dfl)); 1573 dfl.sa_handler = SIG_DFL; 1574 (void) sigaction(SIGCHLD, &dfl, &cbuf); 1575 1576 /* Fork off the child process (using fork1(), because it's MT-safe) */ 1577 switch (pid = fork1()) { 1578 case -1: 1579 /* Error */ 1580 (void) sigaction(SIGCHLD, &cbuf, NULL); 1581 (void) sigprocmask(SIG_SETMASK, &savemask, NULL); 1582 return (-1); 1583 case 0: 1584 /* Set-up an initial signal mask for the child */ 1585 (void) sigemptyset(&mask); 1586 (void) sigprocmask(SIG_SETMASK, &mask, NULL); 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