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 goto mal_formed; 539 if ((subclass = next_field(&lp)) == NULL) 540 goto mal_formed; 541 if ((vendor = next_field(&lp)) == NULL) 542 goto mal_formed; 543 if ((publisher = next_field(&lp)) == NULL) 544 goto mal_formed; 545 if ((user = next_field(&lp)) == NULL) 546 goto mal_formed; 547 if ((reserved1 = next_field(&lp)) == NULL) 548 goto mal_formed; 549 if ((reserved2 = next_field(&lp)) == NULL) 550 goto mal_formed; 551 if ((path = next_field(&lp)) == NULL) 552 goto mal_formed; 553 args = skip_spaces(&lp); 554 555 /* 556 * validate user 557 */ 558 do_setuid = 0; 559 if ((strcmp(user, "-") != 0) && (strcmp(user, "root") != 0)) { 560 i = getpwnam_r(user, &pwd, pwdbuf, 561 sizeof (pwdbuf), &pwdp); 562 if (i != 0 || pwdp == NULL) { 563 syslog(LOG_ERR, NO_USER_ERR, 564 conf_file, lineno, user); 565 continue; 566 } 567 do_setuid = 1; 568 } 569 570 /* 571 * validate reserved fields 572 */ 573 if (strcmp(reserved1, "-") != 0) { 574 syslog(LOG_ERR, RESERVED_FIELD_ERR, 575 conf_file, lineno, reserved1); 576 continue; 577 } 578 if (strcmp(reserved2, "-") != 0) { 579 syslog(LOG_ERR, RESERVED_FIELD_ERR, 580 conf_file, lineno, reserved2); 581 continue; 582 } 583 584 /* 585 * ensure path is executable by user 586 */ 587 err = 0; 588 if (do_setuid) { 589 saved_uid = getuid(); 590 saved_gid = getgid(); 591 if (setregid(pwdp->pw_gid, -1) == -1) { 592 syslog(LOG_ERR, SETREGID_ERR, 593 whoami, pwdp->pw_gid, strerror(errno)); 594 err = -1; 595 } 596 if (setreuid(pwdp->pw_uid, -1) == -1) { 597 syslog(LOG_ERR, SETREUID_ERR, 598 whoami, pwdp->pw_uid, strerror(errno)); 599 err = -1; 600 } 601 } 602 if ((i = access(path, X_OK)) == -1) { 603 syslog(LOG_ERR, CANNOT_EXECUTE_ERR, 604 conf_file, lineno, path, strerror(errno)); 605 } 606 if (do_setuid) { 607 if (setreuid(saved_uid, -1) == -1) { 608 syslog(LOG_ERR, SETREUID_ERR, 609 whoami, saved_uid, strerror(errno)); 610 err = -1; 611 } 612 if (setregid(saved_gid, -1) == -1) { 613 syslog(LOG_ERR, SETREGID_ERR, 614 whoami, saved_gid, strerror(errno)); 615 err = -1; 616 } 617 } 618 if (i == -1 || err == -1) 619 continue; 620 621 /* 622 * all sanity tests successful - perform allocations 623 * to add entry to table 624 */ 625 if ((sep = sc_malloc(sizeof (syseventtab_t))) == NULL) 626 break; 627 628 sep->se_conf_file = conf_file; 629 sep->se_lineno = lineno; 630 sep->se_vendor = sc_strdup(vendor); 631 sep->se_publisher = sc_strdup(publisher); 632 sep->se_class = sc_strdup(class); 633 sep->se_subclass = sc_strdup(subclass); 634 sep->se_user = sc_strdup(user); 635 if (do_setuid) { 636 sep->se_uid = pwdp->pw_uid; 637 sep->se_gid = pwdp->pw_gid; 638 } else { 639 sep->se_uid = 0; 640 sep->se_gid = 0; 641 } 642 sep->se_reserved1 = sc_strdup(reserved1); 643 sep->se_reserved2 = sc_strdup(reserved2); 644 sep->se_path = sc_strdup(path); 645 sep->se_args = (args == NULL) ? NULL : sc_strdup(args); 646 sep->se_next = NULL; 647 648 if (sep->se_vendor == NULL || sep->se_publisher == NULL || 649 sep->se_class == NULL || sep->se_subclass == NULL || 650 sep->se_user == NULL || sep->se_reserved1 == NULL || 651 sep->se_reserved2 == NULL || sep->se_path == NULL || 652 (args && sep->se_args == NULL)) { 653 sc_strfree(sep->se_vendor); 654 sc_strfree(sep->se_publisher); 655 sc_strfree(sep->se_class); 656 sc_strfree(sep->se_subclass); 657 sc_strfree(sep->se_user); 658 sc_strfree(sep->se_reserved1); 659 sc_strfree(sep->se_reserved2); 660 sc_strfree(sep->se_path); 661 sc_strfree(sep->se_args); 662 sc_free(sep, sizeof (syseventtab_t)); 663 break; 664 } 665 666 /* 667 * link new entry into the table 668 */ 669 if (syseventtab == NULL) { 670 syseventtab = sep; 671 syseventtab_tail = sep; 672 } else { 673 syseventtab_tail->se_next = sep; 674 syseventtab_tail = sep; 675 } 676 677 if (debug_level >= DBG_DETAILED) { 678 syseventtab_t *sp; 679 for (sp = syseventtab; sp; sp = sp->se_next) { 680 syseventd_print(DBG_DETAILED, 681 " vendor=%s\n", sp->se_vendor); 682 syseventd_print(DBG_DETAILED, 683 " publisher=%s\n", sp->se_publisher); 684 syseventd_print(DBG_DETAILED, 685 " class=%s\n", sp->se_class); 686 syseventd_print(DBG_DETAILED, 687 " subclass=%s\n", sp->se_subclass); 688 syseventd_print(DBG_DETAILED, 689 " user=%s uid=%d gid=%d\n", 690 sp->se_user, sp->se_uid, sp->se_gid); 691 syseventd_print(DBG_DETAILED, 692 " reserved1=%s\n", sp->se_reserved1); 693 syseventd_print(DBG_DETAILED, 694 " reserved2=%s\n", sp->se_reserved2); 695 syseventd_print(DBG_DETAILED, 696 " path=%s\n", sp->se_path); 697 if (sp->se_args != NULL) { 698 syseventd_print(DBG_DETAILED, 699 " args=%s\n", sp->se_args); 700 } 701 } 702 } 703 704 continue; 705 706 mal_formed: 707 syslog(LOG_ERR, SYNTAX_ERR, conf_file, lineno); 708 } 709 710 freestr(line); 711 (void) fclose(fp); 712 } 713 714 715 /* 716 * Build the events specification table, a summation of all 717 * event specification found in the installed sysevent.conf 718 * configuration files. 719 * 720 * All sysevent.conf files reside in the /etc/sysevent/config 721 * and may contain zero or more event/command specifications. 722 * A sysevent.conf file should be named as follows: 723 * 724 * <vendor>,[<publisher>,][<class>,]sysevent.conf 725 * 726 * Event/command specifications delivered by the base Solaris 727 * system are provided in /etc/sysevent/config/SUNW,sysevent.conf. 728 * Event/command specifications delivered by optional 729 * Sun-supplied packages may install additional sysevent.conf 730 * files in /etc/sysevent/config using vendor SUNW, and additional 731 * publisher and/or event class naming to distinguish the 732 * events required for those products. Products provided 733 * by third-party hardware or software companies may 734 * distinguish their sysevent.conf files by vendor, and 735 * by publisher and/or event class within vendor. 736 * 737 * Files residing in /etc/sysevent/config with a '.' (period) 738 * as the first character of the name and files with a suffix 739 * of other than "sysevent.conf" are ignored. 740 */ 741 static void 742 build_event_table() 743 { 744 conftab_t *cfp = NULL; 745 DIR *dir; 746 struct dirent *result; 747 conftab_t *new_cfp; 748 char *str; 749 750 if ((dir = opendir(SYSEVENT_CONFIG_DIR)) == NULL) { 751 syslog(LOG_ERR, CANNOT_OPEN_ERR, 752 SYSEVENT_CONFIG_DIR, strerror(errno)); 753 return; 754 } 755 756 while ((result = readdir(dir)) != NULL) { 757 if (result->d_name[0] == '.') 758 continue; 759 760 /* 761 * file must have extension "sysevent.conf" 762 */ 763 if ((str = strrchr(result->d_name, ',')) != NULL) { 764 str++; 765 } else { 766 str = result->d_name; 767 } 768 if (strcmp(str, "sysevent.conf") != 0) { 769 syseventd_print(DBG_CONF_FILE, 770 "%s: ignoring %s\n", whoami, str); 771 continue; 772 } 773 774 /* 775 * Add to file table and parse this conf file 776 */ 777 if ((str = sc_strdup(result->d_name)) == NULL) 778 goto err; 779 if ((new_cfp = sc_malloc(sizeof (conftab_t))) == NULL) { 780 sc_strfree(str); 781 goto err; 782 } 783 if (conftab == NULL) { 784 conftab = new_cfp; 785 } else { 786 for (cfp = conftab; cfp->cf_next; cfp = cfp->cf_next) 787 ; 788 cfp->cf_next = new_cfp; 789 } 790 cfp = new_cfp; 791 cfp->cf_conf_file = str; 792 cfp->cf_next = NULL; 793 794 parse_conf_file(cfp->cf_conf_file); 795 } 796 797 err: 798 if (closedir(dir) == -1) { 799 if (errno == EAGAIN) 800 goto err; 801 syslog(LOG_ERR, CLOSEDIR_ERR, 802 SYSEVENT_CONFIG_DIR, strerror(errno)); 803 } 804 } 805 806 807 static int 808 enter_lock(char *lock_file) 809 { 810 struct flock lock; 811 int lock_fd; 812 813 (void) snprintf(lock_file, PATH_MAX, "%s/%s", 814 SYSEVENT_CONFIG_DIR, LOCK_FILENAME); 815 lock_fd = open(lock_file, O_CREAT|O_RDWR, 0644); 816 if (lock_fd < 0) { 817 syslog(LOG_ERR, MSG_LOCK_CREATE_ERR, 818 whoami, lock_file, strerror(errno)); 819 return (-1); 820 } 821 822 lock.l_type = F_WRLCK; 823 lock.l_whence = SEEK_SET; 824 lock.l_start = 0; 825 lock.l_len = 0; 826 827 retry: 828 if (fcntl(lock_fd, F_SETLKW, &lock) == -1) { 829 if (errno == EAGAIN || errno == EINTR) 830 goto retry; 831 (void) close(lock_fd); 832 syslog(LOG_ERR, MSG_LOCK_SET_ERR, 833 whoami, lock_file, strerror(errno)); 834 return (-1); 835 } 836 837 return (lock_fd); 838 } 839 840 841 static void 842 exit_lock(int lock_fd, char *lock_file) 843 { 844 struct flock lock; 845 846 lock.l_type = F_UNLCK; 847 lock.l_whence = SEEK_SET; 848 lock.l_start = 0; 849 lock.l_len = 0; 850 851 if (fcntl(lock_fd, F_SETLK, &lock) == -1) { 852 syslog(LOG_ERR, MSG_LOCK_CLR_ERR, 853 whoami, lock_file, strerror(errno)); 854 } 855 856 if (close(lock_fd) == -1) { 857 syslog(LOG_ERR, MSG_LOCK_CLOSE_ERR, 858 whoami, lock_file, strerror(errno)); 859 } 860 } 861 862 863 /* 864 * Free the events specification table, constructed by 865 * parsing all the sysevent.conf files found. 866 * 867 * The free of this table is in response to a HUP 868 * given to the syseventd daemon, permitting the 869 * table to be rebuilt after adding a new sysevent.conf 870 * file or changing an existing one without shutting 871 * down the daemon. 872 */ 873 static void 874 free_event_table() 875 { 876 syseventtab_t *sep; 877 syseventtab_t *sep_next; 878 conftab_t *cfp; 879 conftab_t *cfp_next; 880 881 sep = syseventtab; 882 while (sep) { 883 sc_strfree(sep->se_vendor); 884 sc_strfree(sep->se_publisher); 885 sc_strfree(sep->se_class); 886 sc_strfree(sep->se_subclass); 887 sc_strfree(sep->se_user); 888 sc_strfree(sep->se_reserved1); 889 sc_strfree(sep->se_reserved2); 890 sc_strfree(sep->se_path); 891 if (sep->se_args) 892 sc_strfree(sep->se_args); 893 sep_next = sep->se_next; 894 sc_free(sep, sizeof (syseventtab_t)); 895 sep = sep_next; 896 } 897 syseventtab = NULL; 898 899 cfp = conftab; 900 while (cfp) { 901 sc_strfree(cfp->cf_conf_file); 902 cfp_next = cfp->cf_next; 903 sc_free(cfp, sizeof (conftab_t)); 904 cfp = cfp_next; 905 } 906 conftab = NULL; 907 } 908 909 910 911 static char ident_chars[] = "_"; 912 913 /* 914 * Return a dynamically-allocated string containing the 915 * the next identifier in the string being parsed, pointed 916 * at by 'id'. 'end' returns a pointer to the character 917 * after the identifier. 918 * 919 * Identifiers are all alphanumeric ascii characters and 920 * those contained in ident_chars. 921 * 922 * The returned string must be explicitly freed via 923 * freestr(). 924 */ 925 static str_t * 926 snip_identifier(char *id, char **end) 927 { 928 str_t *token; 929 930 if ((token = initstr(32)) == NULL) 931 return (NULL); 932 933 while (*id != 0) { 934 if (isascii(*id) && 935 (isalnum(*id) || strchr(ident_chars, *id) != NULL)) { 936 if (strcatc(token, *id++)) { 937 freestr(token); 938 return (NULL); 939 } 940 } else { 941 *end = id; 942 return (token); 943 } 944 } 945 946 *end = id; 947 return (token); 948 } 949 950 951 /* 952 * Identical to snip_identifier(), but the identifier 953 * is delimited by the characters { and }. 954 */ 955 static str_t * 956 snip_delimited_identifier(char *id, char **end) 957 { 958 str_t *token; 959 960 if ((token = initstr(32)) == NULL) 961 return (NULL); 962 963 while (*id != 0) { 964 if (*id == '}') { 965 *end = id+1; 966 return (token); 967 } 968 if (strcatc(token, *id++)) { 969 freestr(token); 970 return (NULL); 971 } 972 } 973 974 if (*id == 0) { 975 freestr(token); 976 return (NULL); 977 } 978 979 *end = id; 980 return (token); 981 } 982 983 984 /* 985 * Return a string with the name of the attribute type 986 */ 987 static char *nv_attr_type_strings[] = { 988 "unknown", 989 "boolean", 990 "byte", 991 "int16", 992 "uint16", 993 "int32", 994 "uint32", 995 "int64", 996 "uint64", 997 "string", 998 "byte-array", 999 "int16-array", 1000 "uint16-array", 1001 "int32-array", 1002 "uint32-array", 1003 "int64-array", 1004 "uint64-array", 1005 "string-array", 1006 "hrtime" 1007 }; 1008 1009 static char * 1010 se_attr_type_to_str(int se_attr_type) 1011 { 1012 if (se_attr_type >= 0 && 1013 se_attr_type < sizeof (nv_attr_type_strings) / sizeof (char *)) { 1014 return (nv_attr_type_strings[se_attr_type]); 1015 } 1016 return (nv_attr_type_strings[DATA_TYPE_UNKNOWN]); 1017 } 1018 1019 1020 /* 1021 * Find and return the data matching the macro name 'token' 1022 * 1023 * Predefined macros are simply substituted with the 1024 * data from the event header: 1025 * 1026 * $vendor - the vendor string defining the event. 1027 * 1028 * $publisher - the publisher string defining the event. 1029 * 1030 * $class - the class string defining the event. 1031 * 1032 * $subclass - the subclass string defining the event. 1033 * 1034 * $sequence - the sequence number of the event. 1035 * 1036 * $timestamp - the timestamp of the event. 1037 * 1038 * Attributes with signed data types (DATA_TYPE_INT16, 1039 * DATA_TYPE_INT32 and DATA_TYPE_INT64) are expanded 1040 * as decimal digits. 1041 * 1042 * Attributes with unsigned data types (DATA_TYPE_BYTE, 1043 * DATA_TYPE_UINT16, DATA_TYPE_UINT32, DATA_TYPE_UINT64 and 1044 * DATA_TYPE_HTTIME) are expanded as hexadecimal digits 1045 * with a "0x" prefix. 1046 * 1047 * Attributes with string data type (DATA_TYPE_STRING) 1048 * are expanded with the string data. The data is 1049 * not quoted. If if it desired that the quoted strings 1050 * be generated on the command line, put quotes around 1051 * the macro call in the arguments. 1052 * 1053 * Array types are expanded with each element expanded 1054 * as defined for that scalar type, with a space separating 1055 * each element substitution. 1056 */ 1057 1058 static str_t * 1059 find_macro_definition(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep, 1060 char *token, sysevent_hdr_info_t *hdr) 1061 { 1062 nvpair_t *nvp; 1063 int nmatches; 1064 char num[64]; 1065 str_t *replacement; 1066 int i; 1067 uint_t nelems; 1068 union { 1069 uchar_t x_byte; 1070 int16_t x_int16; 1071 uint16_t x_uint16; 1072 int32_t x_int32; 1073 uint32_t x_uint32; 1074 int64_t x_int64; 1075 uint64_t x_uint64; 1076 hrtime_t x_time; 1077 char *x_string; 1078 uchar_t *x_byte_array; 1079 int16_t *x_int16_array; 1080 int32_t *x_int32_array; 1081 int64_t *x_int64_array; 1082 uint16_t *x_uint16_array; 1083 uint32_t *x_uint32_array; 1084 uint64_t *x_uint64_array; 1085 char **x_string_array; 1086 } x; 1087 1088 1089 if ((replacement = initstr(128)) == NULL) { 1090 return (NULL); 1091 } 1092 1093 if (strcmp(token, "vendor") == 0) { 1094 if (strcopys(replacement, hdr->vendor)) { 1095 freestr(replacement); 1096 return (NULL); 1097 } 1098 return (replacement); 1099 } 1100 1101 if (strcmp(token, "publisher") == 0) { 1102 if (strcopys(replacement, hdr->publisher)) { 1103 freestr(replacement); 1104 return (NULL); 1105 } 1106 return (replacement); 1107 } 1108 1109 if (strcmp(token, "class") == 0) { 1110 if (strcopys(replacement, hdr->class)) { 1111 freestr(replacement); 1112 return (NULL); 1113 } 1114 return (replacement); 1115 } 1116 1117 if (strcmp(token, "subclass") == 0) { 1118 if (strcopys(replacement, hdr->subclass)) { 1119 freestr(replacement); 1120 return (NULL); 1121 } 1122 return (replacement); 1123 } 1124 1125 if ((strcmp(token, "sequence") == 0) || 1126 (strcmp(token, "timestamp") == 0)) { 1127 if (strcmp(token, "sequence") == 0) { 1128 (void) snprintf(num, sizeof (num), 1129 "0x%llx", sysevent_get_seq(ev)); 1130 } else { 1131 hrtime_t ts; 1132 sysevent_get_time(ev, &ts); 1133 (void) snprintf(num, sizeof (num), "0x%llx", ts); 1134 } 1135 if (strcopys(replacement, num)) { 1136 freestr(replacement); 1137 return (NULL); 1138 } 1139 return (replacement); 1140 } 1141 1142 nmatches = 0; 1143 1144 if (nvlist) { 1145 nvpair_t *nvp_match; 1146 nvp = NULL; 1147 while ((nvp = nvlist_next_nvpair(nvlist, nvp)) != NULL) { 1148 if (debug_level >= DBG_DETAILED) { 1149 syseventd_print(DBG_DETAILED, 1150 " attribute: %s %s\n", nvpair_name(nvp), 1151 se_attr_type_to_str(nvpair_type(nvp))); 1152 } 1153 if (strcmp(token, nvpair_name(nvp)) == 0) { 1154 nmatches++; 1155 nvp_match = nvp; 1156 } 1157 } 1158 nvp = nvp_match; 1159 } 1160 1161 if (nmatches == 0) { 1162 syslog(LOG_ERR, MACRO_UNDEF_ERR, 1163 sep->se_conf_file, sep->se_lineno, token); 1164 freestr(replacement); 1165 return (NULL); 1166 } else if (nmatches > 1) { 1167 syslog(LOG_ERR, MACRO_MULT_DEF_ERR, 1168 sep->se_conf_file, sep->se_lineno, token); 1169 freestr(replacement); 1170 return (NULL); 1171 } 1172 1173 switch (nvpair_type(nvp)) { 1174 case DATA_TYPE_BYTE: 1175 (void) nvpair_value_byte(nvp, &x.x_byte); 1176 (void) snprintf(num, sizeof (num), "0x%x", x.x_byte); 1177 if (strcats(replacement, num)) { 1178 freestr(replacement); 1179 return (NULL); 1180 } 1181 break; 1182 case DATA_TYPE_INT16: 1183 (void) nvpair_value_int16(nvp, &x.x_int16); 1184 (void) snprintf(num, sizeof (num), "%d", x.x_int16); 1185 if (strcats(replacement, num)) { 1186 freestr(replacement); 1187 return (NULL); 1188 } 1189 break; 1190 case DATA_TYPE_UINT16: 1191 (void) nvpair_value_uint16(nvp, &x.x_uint16); 1192 (void) snprintf(num, sizeof (num), "0x%x", x.x_uint16); 1193 if (strcats(replacement, num)) { 1194 freestr(replacement); 1195 return (NULL); 1196 } 1197 break; 1198 case DATA_TYPE_INT32: 1199 (void) nvpair_value_int32(nvp, &x.x_int32); 1200 (void) snprintf(num, sizeof (num), "%d", x.x_int32); 1201 if (strcats(replacement, num)) { 1202 freestr(replacement); 1203 return (NULL); 1204 } 1205 break; 1206 case DATA_TYPE_UINT32: 1207 (void) nvpair_value_uint32(nvp, &x.x_uint32); 1208 (void) snprintf(num, sizeof (num), "0x%x", x.x_uint32); 1209 if (strcats(replacement, num)) { 1210 freestr(replacement); 1211 return (NULL); 1212 } 1213 break; 1214 case DATA_TYPE_INT64: 1215 (void) nvpair_value_int64(nvp, &x.x_int64); 1216 (void) snprintf(num, sizeof (num), "%lld", x.x_int64); 1217 if (strcats(replacement, num)) { 1218 freestr(replacement); 1219 return (NULL); 1220 } 1221 break; 1222 case DATA_TYPE_UINT64: 1223 (void) nvpair_value_uint64(nvp, &x.x_uint64); 1224 (void) snprintf(num, sizeof (num), "0x%llx", x.x_uint64); 1225 if (strcats(replacement, num)) { 1226 freestr(replacement); 1227 return (NULL); 1228 } 1229 break; 1230 case DATA_TYPE_STRING: 1231 (void) nvpair_value_string(nvp, &x.x_string); 1232 if (strcats(replacement, x.x_string)) { 1233 freestr(replacement); 1234 return (NULL); 1235 } 1236 break; 1237 case DATA_TYPE_BYTE_ARRAY: { 1238 uchar_t *p; 1239 (void) nvpair_value_byte_array(nvp, 1240 &x.x_byte_array, &nelems); 1241 p = x.x_byte_array; 1242 for (i = 0; i < nelems; i++) { 1243 (void) snprintf(num, sizeof (num), 1244 "0x%x ", *p++ & 0xff); 1245 if (strcats(replacement, num)) { 1246 freestr(replacement); 1247 return (NULL); 1248 } 1249 } 1250 } 1251 break; 1252 case DATA_TYPE_INT16_ARRAY: { 1253 int16_t *p; 1254 (void) nvpair_value_int16_array(nvp, 1255 &x.x_int16_array, &nelems); 1256 p = x.x_int16_array; 1257 for (i = 0; i < nelems; i++) { 1258 (void) snprintf(num, sizeof (num), "%d ", *p++); 1259 if (strcats(replacement, num)) { 1260 freestr(replacement); 1261 return (NULL); 1262 } 1263 } 1264 } 1265 break; 1266 1267 case DATA_TYPE_UINT16_ARRAY: { 1268 uint16_t *p; 1269 (void) nvpair_value_uint16_array(nvp, 1270 &x.x_uint16_array, &nelems); 1271 p = x.x_uint16_array; 1272 for (i = 0; i < nelems; i++) { 1273 (void) snprintf(num, sizeof (num), 1274 "0x%x ", *p++); 1275 if (strcats(replacement, num)) { 1276 freestr(replacement); 1277 return (NULL); 1278 } 1279 } 1280 } 1281 break; 1282 1283 case DATA_TYPE_INT32_ARRAY: { 1284 int32_t *p; 1285 (void) nvpair_value_int32_array(nvp, 1286 &x.x_int32_array, &nelems); 1287 p = x.x_int32_array; 1288 for (i = 0; i < nelems; i++) { 1289 (void) snprintf(num, sizeof (num), "%d ", *p++); 1290 if (strcats(replacement, num)) { 1291 freestr(replacement); 1292 return (NULL); 1293 } 1294 } 1295 } 1296 break; 1297 1298 case DATA_TYPE_UINT32_ARRAY: { 1299 uint32_t *p; 1300 (void) nvpair_value_uint32_array(nvp, 1301 &x.x_uint32_array, &nelems); 1302 p = x.x_uint32_array; 1303 for (i = 0; i < nelems; i++) { 1304 (void) snprintf(num, sizeof (num), 1305 "0x%x ", *p++); 1306 if (strcats(replacement, num)) { 1307 freestr(replacement); 1308 return (NULL); 1309 } 1310 } 1311 } 1312 break; 1313 1314 case DATA_TYPE_INT64_ARRAY: { 1315 int64_t *p; 1316 (void) nvpair_value_int64_array(nvp, 1317 &x.x_int64_array, &nelems); 1318 p = x.x_int64_array; 1319 for (i = 0; i < nelems; i++) { 1320 (void) snprintf(num, sizeof (num), 1321 "%lld ", *p++); 1322 if (strcats(replacement, num)) { 1323 freestr(replacement); 1324 return (NULL); 1325 } 1326 } 1327 } 1328 break; 1329 1330 case DATA_TYPE_UINT64_ARRAY: { 1331 uint64_t *p; 1332 (void) nvpair_value_uint64_array(nvp, 1333 &x.x_uint64_array, &nelems); 1334 p = x.x_uint64_array; 1335 for (i = 0; i < nelems; i++) { 1336 (void) snprintf(num, sizeof (num), 1337 "0x%llx ", *p++); 1338 if (strcats(replacement, num)) { 1339 freestr(replacement); 1340 return (NULL); 1341 } 1342 } 1343 } 1344 break; 1345 1346 case DATA_TYPE_STRING_ARRAY: { 1347 char **p; 1348 (void) nvpair_value_string_array(nvp, 1349 &x.x_string_array, &nelems); 1350 p = x.x_string_array; 1351 for (i = 0; i < nelems; i++) { 1352 if (strcats(replacement, *p++) || 1353 strcats(replacement, " ")) { 1354 freestr(replacement); 1355 return (NULL); 1356 } 1357 } 1358 } 1359 break; 1360 1361 case DATA_TYPE_HRTIME: 1362 (void) nvpair_value_hrtime(nvp, &x.x_time); 1363 (void) snprintf(num, sizeof (num), "0x%llx", x.x_time); 1364 if (strcats(replacement, num)) { 1365 freestr(replacement); 1366 return (NULL); 1367 } 1368 break; 1369 default: 1370 syslog(LOG_ERR, ATTR_UNSUPPORTED_ERR, 1371 sep->se_conf_file, sep->se_lineno, 1372 nvpair_type(nvp), token); 1373 freestr(replacement); 1374 return (NULL); 1375 } 1376 1377 return (replacement); 1378 } 1379 1380 /* 1381 * Expand macros in the command template provided in an event 1382 * specification with the data from the event or event attributes. 1383 * 1384 * Macros are introduced by the '$' character, with the macro 1385 * name being the following token separated by a SPACE or 1386 * TAB character. If the macro name is embedded in text, 1387 * it may be delineated by '${' and "}'. A backslash before 1388 * the '$' causes macro expansion not to occur. 1389 * 1390 * The following predefined macros are defined for each event: 1391 * 1392 * $vendor - the vendor string defining the event. 1393 * 1394 * $publisher - the publisher string defining the event. 1395 * 1396 * $class - the class string defining the event. 1397 * 1398 * $subclass - the subclass string defining the event. 1399 * 1400 * $sequence - the sequence number of the event. 1401 * 1402 * $timestamp - the timestamp of the event. 1403 * 1404 * 1405 * Macro names other than those predefined are compared against 1406 * the attribute list provided with the event. An attribute 1407 * with name matching the macro name causes the value of 1408 * of the attribute to be substituted as ASCII text on the 1409 * generated command line. 1410 * 1411 * Use of a macro for which no attribute with that name 1412 * is defined, or for which multiple attributes with that 1413 * name are provided, cause an error and the command is 1414 * not invoked. 1415 */ 1416 static int 1417 expand_macros(sysevent_t *ev, nvlist_t *nvlist, syseventtab_t *sep, 1418 str_t *line, sysevent_hdr_info_t *hdr) 1419 { 1420 char *p; 1421 int state; 1422 char *end; 1423 str_t *token; 1424 str_t *remainder; 1425 str_t *replacement; 1426 int count; 1427 int dollar_position; 1428 1429 syseventd_print(DBG_MACRO, " expanding macros: '%s'\n", line->s_str); 1430 1431 reset: 1432 state = 0; 1433 count = 0; 1434 for (p = line->s_str; *p != 0; p++, count++) { 1435 switch (state) { 1436 case 0: /* initial state */ 1437 if (*p == '\\') { 1438 state = 1; 1439 } else if (*p == '$') { 1440 dollar_position = count; 1441 state = 2; 1442 } 1443 break; 1444 case 1: /* skip characters */ 1445 state = 0; /* after backslash */ 1446 break; 1447 case 2: /* character after $ */ 1448 if (*p == '{') { 1449 token = snip_delimited_identifier(p+1, &end); 1450 } else { 1451 token = snip_identifier(p, &end); 1452 } 1453 if (token == NULL) 1454 goto failed; 1455 1456 if ((remainder = initstr(128)) == NULL) { 1457 freestr(token); 1458 return (1); 1459 } 1460 if (strcopys(remainder, end)) { 1461 freestr(token); 1462 freestr(remainder); 1463 return (1); 1464 } 1465 replacement = find_macro_definition(ev, nvlist, 1466 sep, token->s_str, hdr); 1467 if (replacement == NULL) { 1468 freestr(token); 1469 freestr(remainder); 1470 return (1); 1471 } 1472 syseventd_print(DBG_MACRO, 1473 " '%s' expands to '%s'\n", 1474 token->s_str, replacement->s_str); 1475 1476 strtrunc(line, dollar_position); 1477 if (strcats(line, replacement->s_str)) { 1478 freestr(token); 1479 freestr(replacement); 1480 freestr(remainder); 1481 return (1); 1482 } 1483 if (strcats(line, remainder->s_str)) { 1484 freestr(token); 1485 freestr(replacement); 1486 freestr(remainder); 1487 return (1); 1488 } 1489 1490 syseventd_print(DBG_MACRO, 1491 " with macro expanded: '%s'\n", line->s_str); 1492 1493 freestr(token); 1494 freestr(replacement); 1495 freestr(remainder); 1496 goto reset; 1497 } 1498 } 1499 1500 failed: 1501 if (state != 0) { 1502 syslog(LOG_ERR, SYNTAX_ERR, sep->se_conf_file, sep->se_lineno); 1503 return (1); 1504 } 1505 1506 return (0); 1507 } 1508 1509 1510 static void 1511 start_syseventconfd() 1512 { 1513 int err; 1514 1515 err = system1("/usr/lib/sysevent/syseventconfd", 1516 "/usr/lib/sysevent/syseventconfd"); 1517 1518 if (err != 0 && confd_err_msg_emitted == 0) { 1519 if (confd_state == CONFD_STATE_NOT_RUNNING) { 1520 syslog(LOG_ERR, SYSEVENTCONFD_START_ERR, 1521 strerror(errno)); 1522 } else { 1523 syslog(LOG_ERR, SYSEVENTCONFD_RESTART_ERR, 1524 strerror(errno)); 1525 } 1526 } 1527 } 1528 1529 1530 static int 1531 system1(const char *s_path, const char *s) 1532 { 1533 struct sigaction cbuf, ibuf, qbuf, ignore, dfl; 1534 sigset_t mask; 1535 sigset_t savemask; 1536 struct stat st; 1537 pid_t pid; 1538 int status, w; 1539 1540 /* Check the requested command */ 1541 if (s == NULL) { 1542 errno = EINVAL; 1543 return (-1); 1544 } 1545 1546 /* Check the ability to execute devfsadmd from this process */ 1547 if (stat(s_path, &st) < 0) { 1548 return (-1); 1549 } 1550 if (((geteuid() == st.st_uid) && ((st.st_mode & S_IXUSR) == 0)) || 1551 ((getegid() == st.st_gid) && ((st.st_mode & S_IXGRP) == 0)) || 1552 ((st.st_mode & S_IXOTH) == 0)) { 1553 errno = EPERM; 1554 return (-1); 1555 } 1556 1557 /* 1558 * Block SIGCHLD and set up a default handler for the duration of the 1559 * system1 call. 1560 */ 1561 (void) sigemptyset(&mask); 1562 (void) sigaddset(&mask, SIGCHLD); 1563 (void) sigprocmask(SIG_BLOCK, &mask, &savemask); 1564 (void) memset(&dfl, 0, sizeof (dfl)); 1565 dfl.sa_handler = SIG_DFL; 1566 (void) sigaction(SIGCHLD, &dfl, &cbuf); 1567 1568 /* Fork off the child process (using fork1(), because it's MT-safe) */ 1569 switch (pid = fork1()) { 1570 case -1: 1571 /* Error */ 1572 (void) sigaction(SIGCHLD, &cbuf, NULL); 1573 (void) sigprocmask(SIG_SETMASK, &savemask, NULL); 1574 return (-1); 1575 case 0: 1576 /* Set-up an initial signal mask for the child */ 1577 (void) sigemptyset(&mask); 1578 (void) sigprocmask(SIG_SETMASK, &mask, NULL); 1579 closefrom(3); 1580 (void) execl(s_path, s, (char *)0); 1581 _exit(-1); 1582 break; 1583 default: 1584 /* Parent */ 1585 break; 1586 } 1587 1588 (void) memset(&ignore, 0, sizeof (ignore)); 1589 ignore.sa_handler = SIG_IGN; 1590 (void) sigaction(SIGINT, &ignore, &ibuf); 1591 (void) sigaction(SIGQUIT, &ignore, &qbuf); 1592 1593 do { 1594 w = waitpid(pid, &status, 0); 1595 } while (w == -1 && errno == EINTR); 1596 1597 (void) sigaction(SIGINT, &ibuf, NULL); 1598 (void) sigaction(SIGQUIT, &qbuf, NULL); 1599 1600 (void) sigaction(SIGCHLD, &cbuf, NULL); 1601 (void) sigprocmask(SIG_SETMASK, &savemask, NULL); 1602 1603 return ((w == -1)? w: status); 1604 } 1605 1606 /* 1607 * Free all commands on the cmd queue 1608 */ 1609 static void 1610 abort_cmd_queue() 1611 { 1612 cmdqueue_t *cmd; 1613 cmdqueue_t *next; 1614 int nevents = 0; 1615 1616 while ((cmd = cmdq) != NULL) { 1617 next = cmd->next; 1618 cmdq_cnt--; 1619 sysevent_free(cmd->event); 1620 sc_free(cmd, sizeof (cmdqueue_t)); 1621 cmdq = next; 1622 nevents++; 1623 } 1624 cmdq_tail = NULL; 1625 1626 /* 1627 * Generate error msgs if events were discarded or 1628 * we are entering the disabled state. 1629 */ 1630 if (nevents > 0) { 1631 syslog(LOG_ERR, N_EVENTS_DISCARDED_ERR, nevents); 1632 } 1633 if (want_fini == 0) { 1634 confd_state = CONFD_STATE_DISABLED; 1635 syslog(LOG_ERR, SERVICE_DISABLED_MSG); 1636 } 1637 } 1638 1639 /* 1640 * For a matching event specification, build the command to be 1641 * invoked in response to the event. Building the command involves 1642 * expanding macros supplied in the event specification command 1643 * with values from the actual event. These macros can be 1644 * the class/subclass/vendor/publisher strings, or arbitrary 1645 * attribute data attached to the event. 1646 * 1647 * This module does not invoke (fork/exec) the command itself, 1648 * since this module is running in the context of the syseventd 1649 * daemon, and fork/exec's done here interfere with the door 1650 * upcall delivering events from the kernel to the daemon. 1651 * Instead, we build a separate event and nvlist with the 1652 * attributes of the command to be invoked, and pass that on 1653 * to the syseventconfd daemon, which is basically a fork/exec 1654 * server on our behalf. 1655 * 1656 * Errors queuing the event are returned to syseventd with 1657 * EAGAIN, allowing syseventd to manage a limited number of 1658 * retries after a short delay. 1659 */ 1660 static int 1661 queue_event(sysevent_t *ev, syseventtab_t *sep, sysevent_hdr_info_t *hdr) 1662 { 1663 str_t *line; 1664 nvlist_t *nvlist; 1665 char *argv0; 1666 sysevent_t *cmd_event; 1667 nvlist_t *cmd_nvlist; 1668 cmdqueue_t *new_cmd; 1669 1670 if ((line = initstr(128)) == NULL) 1671 return (1); 1672 1673 if ((argv0 = strrchr(sep->se_path, '/')) == NULL) { 1674 argv0 = sep->se_path; 1675 } else { 1676 argv0++; 1677 } 1678 if (strcopys(line, argv0)) { 1679 freestr(line); 1680 return (1); 1681 } 1682 1683 if (sep->se_args) { 1684 if (strcats(line, " ")) { 1685 freestr(line); 1686 return (1); 1687 } 1688 if (strcats(line, sep->se_args)) { 1689 freestr(line); 1690 return (1); 1691 } 1692 1693 if (sysevent_get_attr_list(ev, &nvlist) != 0) { 1694 syslog(LOG_ERR, GET_ATTR_LIST_ERR, 1695 sep->se_conf_file, sep->se_lineno, 1696 strerror(errno)); 1697 freestr(line); 1698 return (1); 1699 } 1700 if (expand_macros(ev, nvlist, sep, line, hdr)) { 1701 freestr(line); 1702 if (nvlist) 1703 nvlist_free(nvlist); 1704 return (1); 1705 } 1706 if (nvlist) 1707 nvlist_free(nvlist); 1708 } 1709 1710 if (debug_level >= DBG_EXEC) { 1711 syseventd_print(DBG_EXEC, "%s, line %d: path = %s\n", 1712 sep->se_conf_file, sep->se_lineno, sep->se_path); 1713 syseventd_print(DBG_EXEC, " cmd = %s\n", line->s_str); 1714 } 1715 1716 cmd_nvlist = NULL; 1717 if ((errno = nvlist_alloc(&cmd_nvlist, NV_UNIQUE_NAME, 0)) != 0) { 1718 freestr(line); 1719 syslog(LOG_ERR, NVLIST_ALLOC_ERR, 1720 sep->se_conf_file, sep->se_lineno, 1721 strerror(errno)); 1722 return (1); 1723 } 1724 1725 if ((errno = nvlist_add_string(cmd_nvlist, "path", sep->se_path)) != 0) 1726 goto err; 1727 if ((errno = nvlist_add_string(cmd_nvlist, "cmd", line->s_str)) != 0) 1728 goto err; 1729 if ((errno = nvlist_add_string(cmd_nvlist, "file", 1730 sep->se_conf_file)) != 0) 1731 goto err; 1732 if ((errno = nvlist_add_int32(cmd_nvlist, "line", sep->se_lineno)) != 0) 1733 goto err; 1734 if ((errno = nvlist_add_string(cmd_nvlist, "user", sep->se_user)) != 0) 1735 goto err; 1736 1737 if (sep->se_uid != (uid_t)0) { 1738 if ((errno = nvlist_add_int32(cmd_nvlist, "uid", 1739 sep->se_uid)) != 0) 1740 goto err; 1741 if ((errno = nvlist_add_int32(cmd_nvlist, "gid", 1742 sep->se_gid)) != 0) 1743 goto err; 1744 } 1745 1746 cmd_event = sysevent_alloc_event(hdr->class, hdr->subclass, hdr->vendor, 1747 hdr->publisher, cmd_nvlist); 1748 if (cmd_event == NULL) { 1749 syslog(LOG_ERR, SYSEVENT_ALLOC_ERR, 1750 sep->se_conf_file, sep->se_lineno, 1751 strerror(errno)); 1752 nvlist_free(cmd_nvlist); 1753 freestr(line); 1754 return (1); 1755 } 1756 1757 nvlist_free(cmd_nvlist); 1758 freestr(line); 1759 1760 /* 1761 * Place cmd_event on queue to be transported to syseventconfd 1762 */ 1763 if ((new_cmd = sc_malloc(sizeof (cmdqueue_t))) == NULL) { 1764 sysevent_free(cmd_event); 1765 return (1); 1766 } 1767 new_cmd->event = cmd_event; 1768 new_cmd->next = NULL; 1769 (void) mutex_lock(&cmdq_lock); 1770 if (cmdq == NULL) { 1771 cmdq = new_cmd; 1772 } else { 1773 cmdq_tail->next = new_cmd; 1774 } 1775 cmdq_cnt++; 1776 cmdq_tail = new_cmd; 1777 1778 /* 1779 * signal queue flush thread 1780 */ 1781 (void) cond_signal(&cmdq_cv); 1782 1783 (void) mutex_unlock(&cmdq_lock); 1784 1785 return (0); 1786 1787 err: 1788 syslog(LOG_ERR, NVLIST_BUILD_ERR, 1789 sep->se_conf_file, sep->se_lineno, strerror(errno)); 1790 nvlist_free(cmd_nvlist); 1791 freestr(line); 1792 return (1); 1793 } 1794 1795 1796 static int 1797 transport_event(sysevent_t *event) 1798 { 1799 int rval; 1800 1801 rval = sysevent_send_event(confd_handle, event); 1802 if (rval != 0) { 1803 switch (errno) { 1804 case EAGAIN: 1805 case EINTR: 1806 /* 1807 * syseventconfd daemon may be forking, stop 1808 * attempting to empty the queue momentarily. 1809 */ 1810 rval = errno; 1811 break; 1812 case ENOENT: 1813 case EBADF: 1814 /* 1815 * start/restart the syseventconfd daemon, 1816 * allowing for some delay when starting 1817 * up before it begins to reply. 1818 */ 1819 if (confd_state == CONFD_STATE_NOT_RUNNING || 1820 confd_state == CONFD_STATE_OK) { 1821 confd_state = CONFD_STATE_STARTED; 1822 start_syseventconfd(); 1823 confd_retries = 0; 1824 rval = EAGAIN; 1825 } else if (confd_state == CONFD_STATE_STARTED && 1826 confd_retries < 16) { 1827 if (++confd_retries == 16) { 1828 confd_state = CONFD_STATE_ERR; 1829 if (confd_err_msg_emitted == 0) { 1830 syslog(LOG_ERR, 1831 SYSEVENTCONFD_ERR); 1832 confd_err_msg_emitted = 1; 1833 } 1834 } 1835 rval = EAGAIN; 1836 } else { 1837 rval = errno; 1838 } 1839 break; 1840 default: 1841 syslog(LOG_ERR, SYSEVENTCONFD_TRAN_ERR, 1842 strerror(errno)); 1843 rval = errno; 1844 break; 1845 } 1846 } else if (confd_state != CONFD_STATE_OK) { 1847 if (confd_state == CONFD_STATE_ERR) { 1848 syslog(LOG_ERR, SYSEVENTCONFD_OK); 1849 confd_err_msg_emitted = 0; 1850 } 1851 confd_state = CONFD_STATE_OK; 1852 confd_retries = 0; 1853 confd_err_msg_emitted = 0; 1854 } 1855 return (rval); 1856 } 1857 1858 1859 /* 1860 * Send events on queue to syseventconfd daemon. We queue events 1861 * here since the daemon is unable to handle events during an 1862 * active fork/exec, returning EAGAIN as a result. It is grossly 1863 * inefficient to bounce these events back to syseventd, so 1864 * we queue them here for delivery. 1865 * 1866 * EAGAIN/EINTR don't indicate errors with the transport to 1867 * syseventconfd itself, just the daemon is busy or some 1868 * other transient difficulty. We retry EBADF and other errors 1869 * for some time, then eventually give up - something's broken. 1870 * 1871 * Error handling strategy: 1872 * If we're trying to shut down and the syseventconfd daemon isn't 1873 * responding, abort the queue so we don't cause the fini to hang 1874 * forever. Otherwise, EAGAIN/EINTR are retried forever, as 1875 * we presume the daemon is active but either busy or some transient 1876 * state is preventing the transport. We make considerable effort 1877 * to retry EBADF since the daemon may take some time to come up when 1878 * restarted so don't want to give up too easily. Once we enter 1879 * the DISABLED state, we stop handling events altogther to 1880 * avoid thrashing the system if the syseventconfd binary is 1881 * corrupted or missing. This state can be cleared by issuing 1882 * a HUP signal to the syseventd daemon. For errors other than 1883 * EAGAIN/EINTR/EBADF, we just drop the event and if we get 1884 * a certain number of these in a row, we enter the DISABLED 1885 * state. 1886 */ 1887 1888 static void 1889 transport_queued_events() 1890 { 1891 int rval; 1892 cmdqueue_t *cmd; 1893 1894 (void) mutex_lock(&cmdq_lock); 1895 while (cmdq != NULL) { 1896 cmd = cmdq; 1897 (void) mutex_unlock(&cmdq_lock); 1898 rval = transport_event(cmd->event); 1899 (void) mutex_lock(&cmdq_lock); 1900 if (rval != 0) { 1901 switch (rval) { 1902 case EAGAIN: 1903 case EINTR: 1904 /* 1905 * Limit retries in the case of fini 1906 */ 1907 if (want_fini) { 1908 if (++transport_retries == 16) { 1909 abort_cmd_queue(); 1910 } 1911 } 1912 (void) mutex_unlock(&cmdq_lock); 1913 return; 1914 case EBADF: 1915 /* 1916 * retry up to 16 times 1917 */ 1918 if (want_fini || ++transport_retries == 16) { 1919 abort_cmd_queue(); 1920 } 1921 (void) mutex_unlock(&cmdq_lock); 1922 return; 1923 default: 1924 /* 1925 * After 16 sequential errors, give up 1926 */ 1927 if (++transport_retries == 16) { 1928 abort_cmd_queue(); 1929 (void) mutex_unlock(&cmdq_lock); 1930 return; 1931 } 1932 /* 1933 * We don't retry these errors, we 1934 * fall through to remove this event 1935 * from the queue. 1936 */ 1937 break; 1938 } 1939 } else { 1940 transport_retries = 0; 1941 } 1942 1943 /* 1944 * Take completed event off queue 1945 */ 1946 cmdq_cnt--; 1947 cmdq = cmdq->next; 1948 if (cmdq == NULL) { 1949 cmdq_tail = NULL; 1950 } 1951 (void) mutex_unlock(&cmdq_lock); 1952 sysevent_free(cmd->event); 1953 sc_free(cmd, sizeof (cmdqueue_t)); 1954 (void) mutex_lock(&cmdq_lock); 1955 } 1956 1957 (void) mutex_unlock(&cmdq_lock); 1958 } 1959 1960 1961 static void 1962 queue_flush_thr() 1963 { 1964 int n; 1965 1966 (void) mutex_lock(&cmdq_lock); 1967 for (;;) { 1968 while (cmdq_cnt == 0 && want_fini == 0) { 1969 (void) cond_wait(&cmdq_cv, &cmdq_lock); 1970 } 1971 if (cmdq_cnt == 0 && want_fini) { 1972 (void) cond_signal(&cmdq_thr_cv); 1973 (void) mutex_unlock(&cmdq_lock); 1974 thr_exit(NULL); 1975 /*NOTREACHED*/ 1976 } 1977 (void) mutex_unlock(&cmdq_lock); 1978 transport_queued_events(); 1979 (void) mutex_lock(&cmdq_lock); 1980 if (cmdq_cnt != 0) { 1981 (void) mutex_unlock(&cmdq_lock); 1982 if (want_fini == 0 && confd_err_msg_emitted) { 1983 for (n = 0; n < 60; n++) { 1984 (void) sleep(1); 1985 if (want_fini) 1986 break; 1987 } 1988 } else { 1989 (void) sleep(1); 1990 } 1991 (void) mutex_lock(&cmdq_lock); 1992 } 1993 } 1994 } 1995 1996 1997 /* 1998 * syseventd daemon module event handler 1999 * 2000 * The syseventd daemon calls this handler with each event 2001 * for this module to handle the event as appropriate. 2002 * The task of this module is to compare the event's 2003 * class/subclass/publisher/vendor against the list of 2004 * event specifications provided in the installed 2005 * sysevent.conf files. Build and execute the 2006 * defined command for that event specification 2007 * for each match. 2008 * 2009 * Events are matched against the class, subclass, vendor 2010 * and publisher specifications. Any field not to be matched 2011 * against an event should be set to '-'. A specification 2012 * of '- - - -' generates a match against every event. 2013 */ 2014 /*ARGSUSED*/ 2015 static int 2016 sysevent_conf_event(sysevent_t *ev, int flag) 2017 { 2018 int ret = 0; 2019 char *vendor; 2020 char *publisher; 2021 char *class; 2022 char *subclass; 2023 syseventtab_t *sep; 2024 sysevent_hdr_info_t hdr; 2025 uint64_t seq; 2026 hrtime_t ts; 2027 2028 /* 2029 * If we've been completely unable to communicate with 2030 * syseventconfd, there's not much we can do. 2031 */ 2032 if (confd_state == CONFD_STATE_DISABLED) { 2033 return (0); 2034 } 2035 2036 /* 2037 * sysevent_get_seq(ev) < ev_seq): 2038 * an event we have played before, ignore it 2039 * sysevent_get_seq(ev) == ev_seq): 2040 * ev_nretries > 0, an event being retried 2041 * sysevent_get_seq(ev) > ev_seq): 2042 * a new event 2043 */ 2044 if (debug_level >= DBG_EVENTS) { 2045 if (sysevent_get_seq(ev) == ev_seq && ev_nretries > 0) { 2046 syseventd_print(DBG_EVENTS, 2047 "sequence: %lld/%lld, retry %d\n", 2048 sysevent_get_seq(ev), ev_seq, ev_nretries); 2049 } else if (sysevent_get_seq(ev) > ev_seq) { 2050 syseventd_print(DBG_EVENTS, 2051 "sequence: %lld/%lld\n", 2052 sysevent_get_seq(ev), ev_seq); 2053 } 2054 } 2055 2056 seq = sysevent_get_seq(ev); 2057 sysevent_get_time(ev, &ts); 2058 2059 if (seq > ev_seq || ts > ev_ts) { 2060 ev_nretries = 0; 2061 } else if (first_event == 0 && 2062 (((seq < ev_seq) || (seq == 0 && ts > ev_ts)) || 2063 (seq == ev_seq && ev_nretries == 0))) { 2064 syseventd_print(DBG_TEST, 2065 "out-of-order sequence: received %lld/0x%llx, " 2066 "expected %lld/0x%llx\n", seq, ts, ev_seq+1, ev_ts); 2067 return (ret); 2068 } 2069 2070 ev_ts = ts; 2071 ev_seq = seq; 2072 first_event = 0; 2073 2074 /* 2075 * sysevent_get_vendor_name() and sysevent_get_pub_name() 2076 * allocate strings which must be freed. 2077 */ 2078 vendor = sysevent_get_vendor_name(ev); 2079 publisher = sysevent_get_pub_name(ev); 2080 class = sysevent_get_class_name(ev); 2081 subclass = sysevent_get_subclass_name(ev); 2082 2083 if (vendor == NULL || publisher == NULL) { 2084 syseventd_print(DBG_EVENTS, "Short on memory with vendor " 2085 "and/or publisher string generation\n"); 2086 /* Temporary short on memory */ 2087 ev_nretries++; 2088 free(publisher); 2089 free(vendor); 2090 return (EAGAIN); 2091 } 2092 2093 syseventd_print(DBG_EVENTS, 2094 "%s event %lld: vendor='%s' publisher='%s' class='%s' " 2095 "subclass='%s'\n", whoami, sysevent_get_seq(ev), vendor, 2096 publisher, class, subclass); 2097 2098 for (sep = syseventtab; sep; sep = sep->se_next) { 2099 if (strcmp(sep->se_vendor, "-") != 0) { 2100 if (strcmp(sep->se_vendor, vendor) != 0) 2101 continue; 2102 } 2103 if (strcmp(sep->se_publisher, "-") != 0) { 2104 if (strcmp(sep->se_publisher, publisher) != 0) 2105 continue; 2106 } 2107 if (strcmp(sep->se_class, "-") != 0) { 2108 if (strcmp(sep->se_class, class) != 0) 2109 continue; 2110 } 2111 if (strcmp(sep->se_subclass, "-") != 0) { 2112 if (strcmp(sep->se_subclass, subclass) != 0) 2113 continue; 2114 } 2115 syseventd_print(DBG_MATCHES, " event match: %s, line %d\n", 2116 sep->se_conf_file, sep->se_lineno); 2117 hdr.class = class; 2118 hdr.subclass = subclass; 2119 hdr.vendor = vendor; 2120 hdr.publisher = publisher; 2121 if ((ret = queue_event(ev, sep, &hdr)) != 0) 2122 break; 2123 } 2124 2125 if (ret == 0) { 2126 ev_nretries = 0; 2127 } else { 2128 /* 2129 * Ask syseventd to retry any failed event. If we have 2130 * reached the limit on retries, emit a msg that we're 2131 * not going to be able to service it. 2132 */ 2133 if (ev_nretries == SE_MAX_RETRY_LIMIT) { 2134 syslog(LOG_ERR, SYSEVENT_SEND_ERR, 2135 sep->se_conf_file, sep->se_lineno, errno); 2136 } else { 2137 syseventd_print(DBG_TEST, "%s event %lld: " 2138 "'%s' '%s' '%s' '%s - errno %d, retry %d\n", 2139 whoami, sysevent_get_seq(ev), vendor, 2140 publisher, class, subclass, errno, ev_nretries); 2141 } 2142 ret = EAGAIN; 2143 ev_nretries++; 2144 } 2145 2146 free(publisher); 2147 free(vendor); 2148 2149 return (ret); 2150 } 2151 2152 /* 2153 * syseventd daemon module initialization 2154 */ 2155 struct slm_mod_ops * 2156 slm_init() 2157 { 2158 char lock_file[PATH_MAX+1]; 2159 int lock_fd; 2160 int err; 2161 2162 /* 2163 * This functionality is not supported in the mini-root 2164 * environment, ie install. If root_dir is set, implying 2165 * install, we quietly fail. Return dummy ops rather 2166 * than NULL to avoid error msgs out of syseventd. 2167 */ 2168 if (strcmp(root_dir, "") != 0) { 2169 return (&sysevent_conf_dummy_mod_ops); 2170 } 2171 2172 ev_nretries = 0; 2173 first_event = 1; 2174 2175 /* 2176 * Initialize the channel to syseventconfd 2177 */ 2178 confd_handle = sysevent_open_channel_alt(SYSEVENTCONFD_SERVICE_DOOR); 2179 if (confd_handle == NULL) { 2180 syslog(LOG_ERR, CHANNEL_OPEN_ERR); 2181 return (NULL); 2182 } 2183 2184 if (sysevent_bind_publisher(confd_handle) != 0) { 2185 if (errno == EBUSY) { 2186 sysevent_cleanup_publishers(confd_handle); 2187 if (sysevent_bind_publisher(confd_handle) != 0) { 2188 sysevent_close_channel(confd_handle); 2189 return (NULL); 2190 } 2191 } 2192 } 2193 2194 sysevent_cleanup_subscribers(confd_handle); 2195 2196 cmdq = NULL; 2197 cmdq_tail = NULL; 2198 cmdq_cnt = 0; 2199 want_fini = 0; 2200 confd_err_msg_emitted = 0; 2201 if (confd_state != CONFD_STATE_OK) { 2202 confd_state = CONFD_STATE_NOT_RUNNING; 2203 } 2204 2205 confd_retries = 0; 2206 transport_retries = 0; 2207 2208 (void) mutex_init(&cmdq_lock, USYNC_THREAD, NULL); 2209 (void) cond_init(&cmdq_cv, USYNC_THREAD, NULL); 2210 (void) cond_init(&cmdq_thr_cv, USYNC_THREAD, NULL); 2211 2212 /* 2213 * Create thread to flush cmd queue 2214 */ 2215 if ((err = thr_create(NULL, NULL, (void *(*)(void*))queue_flush_thr, 2216 (void *)NULL, 0, &cmdq_thr_id)) != 0) { 2217 syslog(LOG_ERR, THR_CREATE_ERR, strerror(err)); 2218 sysevent_close_channel(confd_handle); 2219 confd_handle = NULL; 2220 (void) mutex_destroy(&cmdq_lock); 2221 (void) cond_destroy(&cmdq_cv); 2222 (void) cond_destroy(&cmdq_thr_cv); 2223 return (NULL); 2224 } 2225 2226 if ((lock_fd = enter_lock(lock_file)) == -1) { 2227 (void) thr_join(cmdq_thr_id, NULL, NULL); 2228 sysevent_close_channel(confd_handle); 2229 confd_handle = NULL; 2230 (void) mutex_destroy(&cmdq_lock); 2231 (void) cond_destroy(&cmdq_cv); 2232 (void) cond_destroy(&cmdq_thr_cv); 2233 return (NULL); 2234 } 2235 2236 build_event_table(); 2237 exit_lock(lock_fd, lock_file); 2238 return (&sysevent_conf_mod_ops); 2239 } 2240 2241 /* 2242 * syseventd daemon module tear-down 2243 */ 2244 void 2245 slm_fini() 2246 { 2247 int err; 2248 2249 /* 2250 * Nothing to clean up if we're in the install environment 2251 */ 2252 if (strcmp(root_dir, "") != 0) { 2253 return; 2254 } 2255 2256 /* 2257 * Wait for the queue to drain 2258 */ 2259 (void) mutex_lock(&cmdq_lock); 2260 want_fini = 1; 2261 (void) cond_signal(&cmdq_cv); 2262 (void) cond_wait(&cmdq_thr_cv, &cmdq_lock); 2263 (void) mutex_unlock(&cmdq_lock); 2264 2265 /* 2266 * Shut down the the queue flush thread 2267 */ 2268 if ((err = thr_join(cmdq_thr_id, NULL, NULL)) != 0) { 2269 syslog(LOG_ERR, THR_JOIN_ERR, strerror(err)); 2270 } 2271 2272 sysevent_close_channel(confd_handle); 2273 confd_handle = NULL; 2274 (void) mutex_destroy(&cmdq_lock); 2275 (void) cond_destroy(&cmdq_cv); 2276 (void) cond_destroy(&cmdq_thr_cv); 2277 free_event_table(); 2278 } 2279 2280 /*ARGSUSED*/ 2281 static int 2282 sysevent_conf_dummy_event(sysevent_t *ev, int flag) 2283 { 2284 return (0); 2285 } 2286