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 /*
  23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
  24  */
  25 
  26 #include <sys/fm/protocol.h>
  27 #include <fm/fmd_snmp.h>
  28 #include <fm/fmd_msg.h>
  29 #include <fm/libfmevent.h>
  30 #include <net-snmp/net-snmp-config.h>
  31 #include <net-snmp/net-snmp-includes.h>
  32 #include <net-snmp/agent/net-snmp-agent-includes.h>
  33 #include <errno.h>
  34 #include <locale.h>
  35 #include <netdb.h>
  36 #include <signal.h>
  37 #include <strings.h>
  38 #include <stdlib.h>
  39 #include <unistd.h>
  40 #include <limits.h>
  41 #include <alloca.h>
  42 #include <priv_utils.h>
  43 #include <zone.h>
  44 #include "libfmnotify.h"
  45 
  46 /*
  47  * Debug messages can be enabled by setting the debug property to true
  48  *
  49  * # svccfg -s svc:/system/fm/snmp-notify setprop config/debug=true
  50  */
  51 #define SVCNAME         "system/fm/snmp-notify"
  52 
  53 typedef struct ireport_trap {
  54         char *host;
  55         char *msgid;
  56         char *desc;
  57         long long tstamp;
  58         char *fmri;
  59         uint32_t from_state;
  60         uint32_t to_state;
  61         char *reason;
  62         boolean_t is_stn_event;
  63 } ireport_trap_t;
  64 
  65 static nd_hdl_t *nhdl;
  66 static const char optstr[] = "dfR:";
  67 static const char SNMP_SUPPCONF[] = "fmd-trapgen";
  68 static char hostname[MAXHOSTNAMELEN + 1];
  69 
  70 static int
  71 usage(const char *pname)
  72 {
  73         (void) fprintf(stderr, "Usage: %s [-df] [-R <altroot>]\n", pname);
  74 
  75         (void) fprintf(stderr,
  76             "\t-d  enable debug mode\n"
  77             "\t-f  stay in foreground\n"
  78             "\t-R  specify alternate root\n");
  79 
  80         return (1);
  81 }
  82 
  83 /*
  84  * If someone does an "svcadm refresh" on us, then this function gets called,
  85  * which rereads our service configuration.
  86  */
  87 static void
  88 get_svc_config()
  89 {
  90         int s = 0;
  91         uint8_t val;
  92 
  93         s = nd_get_boolean_prop(nhdl, SVCNAME, "config", "debug", &val);
  94         nhdl->nh_debug = val;
  95 
  96         s += nd_get_astring_prop(nhdl, SVCNAME, "config", "rootdir",
  97             &(nhdl->nh_rootdir));
  98 
  99         if (s != 0)
 100                 nd_error(nhdl, "Failed to read retrieve service "
 101                     "properties");
 102 }
 103 
 104 static void
 105 nd_sighandler(int sig)
 106 {
 107         if (sig == SIGHUP)
 108                 get_svc_config();
 109         else
 110                 nd_cleanup(nhdl);
 111 }
 112 
 113 static int
 114 get_snmp_prefs(nd_hdl_t *nhdl, nvlist_t **pref_nvl, uint_t npref)
 115 {
 116         boolean_t *a1, *a2;
 117         uint_t n;
 118         int r;
 119 
 120         /*
 121          * For SMF state transition events, pref_nvl contain two sets of
 122          * preferences, which will have to be merged.
 123          *
 124          * The "snmp" nvlist currently only supports a single boolean member,
 125          * "active" which will be set to true, if it is true in either set
 126          */
 127         if (npref == 2) {
 128                 r = nvlist_lookup_boolean_array(pref_nvl[0], "active", &a1, &n);
 129                 r += nvlist_lookup_boolean_array(pref_nvl[1], "active", &a2,
 130                     &n);
 131                 if (r != 0) {
 132                         nd_debug(nhdl, "Malformed snmp notification "
 133                             "preferences");
 134                         nd_dump_nvlist(nhdl, pref_nvl[0]);
 135                         nd_dump_nvlist(nhdl, pref_nvl[1]);
 136                         return (-1);
 137                 } else if (!a1[0] && !a2[0]) {
 138                         nd_debug(nhdl, "SNMP notification is disabled");
 139                         return (-1);
 140                 }
 141         } else {
 142                 if (nvlist_lookup_boolean_array(pref_nvl[0], "active",
 143                     &a1, &n)) {
 144                         nd_debug(nhdl, "Malformed snmp notification "
 145                             "preferences");
 146                         nd_dump_nvlist(nhdl, pref_nvl[0]);
 147                         return (-1);
 148                 } else if (!a1[0]) {
 149                         nd_debug(nhdl, "SNMP notification is disabled");
 150                         return (-1);
 151                 }
 152         }
 153         return (0);
 154 }
 155 
 156 static void
 157 send_ireport_trap(ireport_trap_t *t)
 158 {
 159         static const oid sunIreportTrap_oid[] =
 160             { SUNIREPORTTRAP_OID };
 161         const size_t sunIreportTrap_len =
 162             OID_LENGTH(sunIreportTrap_oid);
 163 
 164         static const oid sunIreportHostname_oid[] =
 165             { SUNIREPORTHOSTNAME_OID };
 166         static const oid sunIreportMsgid_oid[] =
 167             { SUNIREPORTMSGID_OID };
 168         static const oid sunIreportDescription_oid[] =
 169             { SUNIREPORTDESCRIPTION_OID };
 170         static const oid sunIreportTime_oid[] =
 171             { SUNIREPORTTIME_OID };
 172 
 173         static const oid sunIreportSmfFmri_oid[] =
 174             { SUNIREPORTSMFFMRI_OID };
 175         static const oid sunIreportSmfFromState_oid[] =
 176             { SUNIREPORTSMFFROMSTATE_OID };
 177         static const oid sunIreportSmfToState_oid[] =
 178             { SUNIREPORTSMFTOSTATE_OID };
 179         static const oid sunIreportSmfTransitionReason_oid[] =
 180             { SUNIREPORTTRANSITIONREASON_OID };
 181         const size_t
 182             sunIreport_base_len = OID_LENGTH(sunIreportHostname_oid);
 183 
 184         size_t var_len = sunIreport_base_len + 1;
 185         oid var_name[MAX_OID_LEN];
 186 
 187         netsnmp_variable_list *notification_vars = NULL;
 188 
 189         size_t dt_len;
 190         uchar_t dt[11], *tdt;
 191         time_t ts = t->tstamp;
 192 
 193         tdt = date_n_time(&ts, &dt_len);
 194         /*
 195          * We know date_n_time is broken, it returns a buffer from
 196          * its stack. So we copy before we step over it!
 197          */
 198         for (int i = 0; i < dt_len; ++i)
 199                 dt[i] = tdt[i];
 200 
 201         if (var_len > MAX_OID_LEN) {
 202                 nd_error(nhdl, "var_len %d > MAX_OID_LEN %d\n", var_len,
 203                     MAX_OID_LEN);
 204                 return;
 205         }
 206 
 207         (void) memcpy(var_name, sunIreportHostname_oid, sunIreport_base_len *
 208             sizeof (oid));
 209         (void) snmp_varlist_add_variable(&notification_vars, var_name,
 210             sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->host,
 211             strlen(t->host));
 212 
 213         (void) memcpy(var_name, sunIreportMsgid_oid,
 214             sunIreport_base_len * sizeof (oid));
 215         (void) snmp_varlist_add_variable(&notification_vars, var_name,
 216             sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->msgid,
 217             strlen(t->msgid));
 218 
 219         (void) memcpy(var_name, sunIreportDescription_oid,
 220             sunIreport_base_len * sizeof (oid));
 221         (void) snmp_varlist_add_variable(&notification_vars, var_name,
 222             sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->desc,
 223             strlen(t->desc));
 224 
 225         (void) memcpy(var_name, sunIreportTime_oid, sunIreport_base_len *
 226             sizeof (oid));
 227         (void) snmp_varlist_add_variable(&notification_vars, var_name,
 228             sunIreport_base_len + 1, ASN_OCTET_STR, dt, dt_len);
 229 
 230         if (t->is_stn_event) {
 231                 (void) memcpy(var_name, sunIreportSmfFmri_oid,
 232                     sunIreport_base_len * sizeof (oid));
 233                 (void) snmp_varlist_add_variable(&notification_vars, var_name,
 234                     sunIreport_base_len + 1, ASN_OCTET_STR, (uchar_t *)t->fmri,
 235                     strlen(t->fmri));
 236 
 237                 (void) memcpy(var_name, sunIreportSmfFromState_oid,
 238                     sunIreport_base_len * sizeof (oid));
 239                 (void) snmp_varlist_add_variable(&notification_vars, var_name,
 240                     sunIreport_base_len + 1, ASN_INTEGER,
 241                     (uchar_t *)&t->from_state, sizeof (uint32_t));
 242 
 243                 (void) memcpy(var_name, sunIreportSmfToState_oid,
 244                     sunIreport_base_len * sizeof (oid));
 245                 (void) snmp_varlist_add_variable(&notification_vars, var_name,
 246                     sunIreport_base_len + 1, ASN_INTEGER,
 247                     (uchar_t *)&t->to_state, sizeof (uint32_t));
 248 
 249                 (void) memcpy(var_name, sunIreportSmfTransitionReason_oid,
 250                     sunIreport_base_len * sizeof (oid));
 251                 (void) snmp_varlist_add_variable(&notification_vars, var_name,
 252                     sunIreport_base_len + 1, ASN_OCTET_STR,
 253                     (uchar_t *)t->reason, strlen(t->reason));
 254         }
 255 
 256         /*
 257          * This function is capable of sending both v1 and v2/v3 traps.
 258          * Which is sent to a specific destination is determined by the
 259          * configuration file(s).
 260          */
 261         send_enterprise_trap_vars(SNMP_TRAP_ENTERPRISESPECIFIC,
 262             sunIreportTrap_oid[sunIreportTrap_len - 1],
 263             (oid *)sunIreportTrap_oid, sunIreportTrap_len - 2,
 264             notification_vars);
 265         nd_debug(nhdl, "Sent SNMP trap for %s", t->msgid);
 266 
 267         snmp_free_varbind(notification_vars);
 268 
 269 }
 270 
 271 /*ARGSUSED*/
 272 static void
 273 send_fm_trap(const char *uuid, const char *code, const char *url)
 274 {
 275         static const oid sunFmProblemTrap_oid[] = { SUNFMPROBLEMTRAP_OID };
 276         const size_t sunFmProblemTrap_len = OID_LENGTH(sunFmProblemTrap_oid);
 277 
 278         static const oid sunFmProblemUUID_oid[] =
 279             { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_UUID };
 280         static const oid sunFmProblemCode_oid[] =
 281             { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_CODE };
 282         static const oid sunFmProblemURL_oid[] =
 283             { SUNFMPROBLEMTABLE_OID, 1, SUNFMPROBLEM_COL_URL };
 284 
 285         const size_t sunFmProblem_base_len = OID_LENGTH(sunFmProblemUUID_oid);
 286 
 287         size_t uuid_len = strlen(uuid);
 288         size_t var_len = sunFmProblem_base_len + 1 + uuid_len;
 289         oid var_name[MAX_OID_LEN];
 290 
 291         netsnmp_variable_list *notification_vars = NULL;
 292 
 293         /*
 294          * The format of our trap varbinds' oids is as follows:
 295          *
 296          * +-----------------------+---+--------+----------+------+
 297          * | SUNFMPROBLEMTABLE_OID | 1 | column | uuid_len | uuid |
 298          * +-----------------------+---+--------+----------+------+
 299          *                                       \---- index ----/
 300          *
 301          * A common mistake here is to send the trap with varbinds that
 302          * do not contain the index.  All the indices are the same, and
 303          * all the oids are the same length, so the only thing we need to
 304          * do for each varbind is set the table and column parts of the
 305          * variable name.
 306          */
 307 
 308         if (var_len > MAX_OID_LEN)
 309                 return;
 310 
 311         var_name[sunFmProblem_base_len] = (oid)uuid_len;
 312         for (int i = 0; i < uuid_len; i++)
 313                 var_name[i + sunFmProblem_base_len + 1] = (oid)uuid[i];
 314 
 315         /*
 316          * Ordinarily, we would need to add the OID of the trap itself
 317          * to the head of the variable list; this is required by SNMP v2.
 318          * However, send_enterprise_trap_vars does this for us as a part
 319          * of converting between v1 and v2 traps, so we skip directly to
 320          * the objects we're sending.
 321          */
 322 
 323         (void) memcpy(var_name, sunFmProblemUUID_oid,
 324             sunFmProblem_base_len * sizeof (oid));
 325         (void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
 326             ASN_OCTET_STR, (uchar_t *)uuid, strlen(uuid));
 327         (void) memcpy(var_name, sunFmProblemCode_oid,
 328             sunFmProblem_base_len * sizeof (oid));
 329         (void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
 330             ASN_OCTET_STR, (uchar_t *)code, strlen(code));
 331         (void) memcpy(var_name, sunFmProblemURL_oid,
 332             sunFmProblem_base_len * sizeof (oid));
 333         (void) snmp_varlist_add_variable(&notification_vars, var_name, var_len,
 334             ASN_OCTET_STR, (uchar_t *)url, strlen(url));
 335 
 336         /*
 337          * This function is capable of sending both v1 and v2/v3 traps.
 338          * Which is sent to a specific destination is determined by the
 339          * configuration file(s).
 340          */
 341         send_enterprise_trap_vars(SNMP_TRAP_ENTERPRISESPECIFIC,
 342             sunFmProblemTrap_oid[sunFmProblemTrap_len - 1],
 343             (oid *)sunFmProblemTrap_oid, sunFmProblemTrap_len - 2,
 344             notification_vars);
 345         nd_debug(nhdl, "Sent SNMP trap for %s", code);
 346 
 347         snmp_free_varbind(notification_vars);
 348 }
 349 
 350 /*
 351  * The SUN-IREPORT-MIB declares the following enum to represent SMF service
 352  * states.
 353  *
 354  * offline(0), online(1), degraded(2), disabled(3), maintenance(4),
 355  * uninitialized(5)
 356  *
 357  * This function converts a string representation of an SMF service state
 358  * to it's corresponding enum val.
 359  */
 360 static int
 361 state_to_val(char *statestr, uint32_t *stateval)
 362 {
 363         if (strcmp(statestr, "offline") == 0)
 364                 *stateval = 0;
 365         else if (strcmp(statestr, "online") == 0)
 366                 *stateval = 1;
 367         else if (strcmp(statestr, "degraded") == 0)
 368                 *stateval = 2;
 369         else if (strcmp(statestr, "disabled") == 0)
 370                 *stateval = 3;
 371         else if (strcmp(statestr, "maintenance") == 0)
 372                 *stateval = 4;
 373         else if (strcmp(statestr, "uninitialized") == 0)
 374                 *stateval = 5;
 375         else
 376                 return (-1);
 377         return (0);
 378 }
 379 
 380 /*ARGSUSED*/
 381 static void
 382 ireport_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
 383 {
 384         nvlist_t **pref_nvl = NULL;
 385         nd_ev_info_t *ev_info = NULL;
 386         ireport_trap_t swtrap;
 387         uint_t npref;
 388         int ret;
 389 
 390         nd_debug(nhdl, "Received event of class %s", class);
 391 
 392         ret = nd_get_notify_prefs(nhdl, "snmp", ev, &pref_nvl, &npref);
 393         if (ret == SCF_ERROR_NOT_FOUND) {
 394                 /*
 395                  * No snmp notification preferences specified for this type of
 396                  * event, so we're done
 397                  */
 398                 return;
 399         } else if (ret != 0) {
 400                 nd_error(nhdl, "Failed to retrieve notification preferences "
 401                     "for this event");
 402                 return;
 403         }
 404 
 405         if (get_snmp_prefs(nhdl, pref_nvl, npref) != 0)
 406                 goto irpt_done;
 407 
 408         if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
 409                 goto irpt_done;
 410 
 411         swtrap.host = hostname;
 412         swtrap.msgid = ev_info->ei_diagcode;
 413         swtrap.desc = ev_info->ei_descr;
 414         swtrap.tstamp = (time_t)fmev_time_sec(ev);
 415 
 416         if (strncmp(class, "ireport.os.smf", 14) == 0) {
 417                 swtrap.fmri = ev_info->ei_fmri;
 418                 if (state_to_val(ev_info->ei_from_state, &swtrap.from_state)
 419                     < 0 ||
 420                     state_to_val(ev_info->ei_to_state, &swtrap.to_state) < 0) {
 421                         nd_error(nhdl, "Malformed event - invalid svc state");
 422                         nd_dump_nvlist(nhdl, ev_info->ei_payload);
 423                         goto irpt_done;
 424                 }
 425                 swtrap.reason = ev_info->ei_reason;
 426                 swtrap.is_stn_event = B_TRUE;
 427         }
 428         send_ireport_trap(&swtrap);
 429 irpt_done:
 430         if (ev_info)
 431                 nd_free_event_info(ev_info);
 432         nd_free_nvlarray(pref_nvl, npref);
 433 }
 434 
 435 /*ARGSUSED*/
 436 static void
 437 list_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg)
 438 {
 439         char *uuid;
 440         uint8_t version;
 441         nd_ev_info_t *ev_info = NULL;
 442         nvlist_t **pref_nvl = NULL;
 443         uint_t npref;
 444         int ret;
 445         boolean_t domsg;
 446 
 447         nd_debug(nhdl, "Received event of class %s", class);
 448 
 449         ret = nd_get_notify_prefs(nhdl, "snmp", ev, &pref_nvl, &npref);
 450         if (ret == SCF_ERROR_NOT_FOUND) {
 451                 /*
 452                  * No snmp notification preferences specified for this type of
 453                  * event, so we're done
 454                  */
 455                 return;
 456         } else if (ret != 0) {
 457                 nd_error(nhdl, "Failed to retrieve notification preferences "
 458                     "for this event");
 459                 return;
 460         }
 461 
 462         if (get_snmp_prefs(nhdl, pref_nvl, npref) != 0)
 463                 goto listcb_done;
 464 
 465         if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0)
 466                 goto listcb_done;
 467 
 468         /*
 469          * If the message payload member is set to 0, then it's an event we
 470          * typically suppress messaging on, so we won't send a trap for it.
 471          */
 472         if (nvlist_lookup_boolean_value(ev_info->ei_payload, FM_SUSPECT_MESSAGE,
 473             &domsg) == 0 && !domsg) {
 474                 nd_debug(nhdl, "Messaging suppressed for this event");
 475                 goto listcb_done;
 476         }
 477 
 478         if (nvlist_lookup_uint8(ev_info->ei_payload, FM_VERSION, &version)
 479             != 0 || version > FM_SUSPECT_VERSION) {
 480                 nd_error(nhdl, "invalid event version: %u", version);
 481                 goto listcb_done;
 482         }
 483 
 484         (void) nvlist_lookup_string(ev_info->ei_payload, FM_SUSPECT_UUID,
 485             &uuid);
 486 
 487         if (strcmp(ev_info->ei_url, ND_UNKNOWN) != 0)
 488                 send_fm_trap(uuid, ev_info->ei_diagcode, ev_info->ei_url);
 489         else
 490                 nd_error(nhdl, "failed to format url for %s", uuid);
 491 listcb_done:
 492         nd_free_nvlarray(pref_nvl, npref);
 493         if (ev_info)
 494                 nd_free_event_info(ev_info);
 495 }
 496 
 497 static int
 498 init_sma(void)
 499 {
 500         int err;
 501 
 502         /*
 503          * The only place we could possibly log is syslog, but the
 504          * full agent doesn't normally log there.  It would be confusing
 505          * if this agent did so; therefore we disable logging entirely.
 506          */
 507         snmp_disable_log();
 508 
 509         /*
 510          * Net-SNMP has a provision for reading an arbitrary number of
 511          * configuration files.  A configuration file is read if it has
 512          * had any handlers registered for it, or if it's the value in
 513          * of NETSNMP_DS_LIB_APPTYPE.  Our objective here is to read
 514          * both snmpd.conf and fmd-trapgen.conf.
 515          */
 516         if ((err = netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID,
 517             NETSNMP_DS_AGENT_ROLE, 0 /* MASTER_AGENT */)) != SNMPERR_SUCCESS)
 518                 return (err);
 519 
 520         init_agent_read_config("snmpd");
 521         if ((err = netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
 522             NETSNMP_DS_LIB_APPTYPE, SNMP_SUPPCONF)) != SNMPERR_SUCCESS)
 523                 return (err);
 524         if (register_app_config_handler("trapsink", snmpd_parse_config_trapsink,
 525             snmpd_free_trapsinks, "host [community] [port]") == NULL)
 526                 return (SNMPERR_MALLOC);
 527         if (register_app_config_handler("trap2sink",
 528             snmpd_parse_config_trap2sink, NULL, "host [community] [port]") ==
 529             NULL)
 530                 return (SNMPERR_MALLOC);
 531         if (register_app_config_handler("trapsess", snmpd_parse_config_trapsess,
 532             NULL, "[snmpcmdargs] host") == NULL)
 533                 return (SNMPERR_MALLOC);
 534 
 535         init_traps();
 536         init_snmp(SNMP_SUPPCONF);
 537 
 538         return (SNMPERR_SUCCESS);
 539 }
 540 
 541 int
 542 main(int argc, char *argv[])
 543 {
 544         struct rlimit rlim;
 545         struct sigaction act;
 546         sigset_t set;
 547         char c;
 548         boolean_t run_fg = B_FALSE;
 549 
 550         if ((nhdl = malloc(sizeof (nd_hdl_t))) == NULL) {
 551                 (void) fprintf(stderr, "Failed to allocate space for notifyd "
 552                     "handle (%s)", strerror(errno));
 553                 return (1);
 554         }
 555         bzero(nhdl, sizeof (nd_hdl_t));
 556         nhdl->nh_keep_running = B_TRUE;
 557         nhdl->nh_log_fd = stderr;
 558         nhdl->nh_pname = argv[0];
 559 
 560         get_svc_config();
 561 
 562         /*
 563          * In the case where we get started outside of SMF, args passed on the
 564          * command line override SMF property setting
 565          */
 566         while (optind < argc) {
 567                 while ((c = getopt(argc, argv, optstr)) != -1) {
 568                         switch (c) {
 569                         case 'd':
 570                                 nhdl->nh_debug = B_TRUE;
 571                                 break;
 572                         case 'f':
 573                                 run_fg = B_TRUE;
 574                                 break;
 575                         case 'R':
 576                                 nhdl->nh_rootdir = strdup(optarg);
 577                                 break;
 578                         default:
 579                                 free(nhdl);
 580                                 return (usage(nhdl->nh_pname));
 581                         }
 582                 }
 583         }
 584 
 585         /*
 586          * Set up a signal handler for SIGTERM (and SIGINT if we'll
 587          * be running in the foreground) to ensure sure we get a chance to exit
 588          * in an orderly fashion.  We also catch SIGHUP, which will be sent to
 589          * us by SMF if the service is refreshed.
 590          */
 591         (void) sigfillset(&set);
 592         (void) sigfillset(&act.sa_mask);
 593         act.sa_handler = nd_sighandler;
 594         act.sa_flags = 0;
 595 
 596         (void) sigaction(SIGTERM, &act, NULL);
 597         (void) sigdelset(&set, SIGTERM);
 598         (void) sigaction(SIGHUP, &act, NULL);
 599         (void) sigdelset(&set, SIGHUP);
 600 
 601         if (run_fg) {
 602                 (void) sigaction(SIGINT, &act, NULL);
 603                 (void) sigdelset(&set, SIGINT);
 604         } else
 605                 nd_daemonize(nhdl);
 606 
 607         rlim.rlim_cur = RLIM_INFINITY;
 608         rlim.rlim_max = RLIM_INFINITY;
 609         (void) setrlimit(RLIMIT_CORE, &rlim);
 610 
 611         /*
 612          * We need to be root initialize our libfmevent handle (because that
 613          * involves reading/writing to /dev/sysevent), so we do this before
 614          * calling __init_daemon_priv.
 615          */
 616         nhdl->nh_evhdl = fmev_shdl_init(LIBFMEVENT_VERSION_2, NULL, NULL, NULL);
 617         if (nhdl->nh_evhdl == NULL) {
 618                 (void) sleep(5);
 619                 nd_abort(nhdl, "failed to initialize libfmevent: %s",
 620                     fmev_strerror(fmev_errno));
 621         }
 622 
 623         /*
 624          * If we're in the global zone, reset all of our privilege sets to
 625          * the minimum set of required privileges.   We also change our
 626          * uid/gid to noaccess/noaccess
 627          *
 628          * __init_daemon_priv will also set the process core path for us
 629          *
 630          */
 631         if (getzoneid() == GLOBAL_ZONEID)
 632                 if (__init_daemon_priv(
 633                     PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS,
 634                     60002, 60002, PRIV_FILE_DAC_READ, NULL) != 0)
 635                         nd_abort(nhdl, "additional privileges required to run");
 636 
 637         nhdl->nh_msghdl = fmd_msg_init(nhdl->nh_rootdir, FMD_MSG_VERSION);
 638         if (nhdl->nh_msghdl == NULL)
 639                 nd_abort(nhdl, "failed to initialize libfmd_msg");
 640 
 641         if (init_sma() != SNMPERR_SUCCESS)
 642                 nd_abort(nhdl, "SNMP initialization failed");
 643 
 644         (void) gethostname(hostname, MAXHOSTNAMELEN + 1);
 645         /*
 646          * Set up our event subscriptions.  We subscribe to everything and then
 647          * consult libscf when we receive an event to determine what (if any)
 648          * notification to send.
 649          */
 650         nd_debug(nhdl, "Subscribing to ireport.os.smf.* events");
 651         if (fmev_shdl_subscribe(nhdl->nh_evhdl, "ireport.os.smf.*",
 652             ireport_cb, NULL) != FMEV_SUCCESS) {
 653                 nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
 654                     fmev_strerror(fmev_errno));
 655         }
 656 
 657         nd_debug(nhdl, "Subscribing to list.* events");
 658         if (fmev_shdl_subscribe(nhdl->nh_evhdl, "list.*", list_cb,
 659             NULL) != FMEV_SUCCESS) {
 660                 nd_abort(nhdl, "fmev_shdl_subscribe failed: %s",
 661                     fmev_strerror(fmev_errno));
 662         }
 663 
 664         /*
 665          * We run until someone kills us
 666          */
 667         while (nhdl->nh_keep_running)
 668                 (void) sigsuspend(&set);
 669 
 670         /*
 671          * snmp_shutdown, which we would normally use here, calls free_slots,
 672          * a callback that is supposed to tear down the pkcs11 state; however,
 673          * it abuses C_Finalize, causing fmd to drop core on shutdown.  Avoid
 674          * this by shutting down the library piecemeal.
 675          */
 676         snmp_store(SNMP_SUPPCONF);
 677         snmp_alarm_unregister_all();
 678         (void) snmp_close_sessions();
 679         shutdown_mib();
 680         unregister_all_config_handlers();
 681         netsnmp_ds_shutdown();
 682 
 683         free(nhdl->nh_rootdir);
 684         free(nhdl);
 685 
 686         return (0);
 687 }