xref: /illumos-gate/usr/src/cmd/fm/notify/snmp-notify/common/snmp-notify.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 }
688