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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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(¬ification_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 } 688