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