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