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