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
usage(const char * pname)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
get_svc_config()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
nd_sighandler(int sig)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
get_snmp_prefs(nd_hdl_t * nhdl,nvlist_t ** pref_nvl,uint_t npref)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
send_ireport_trap(ireport_trap_t * t)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
send_fm_trap(const char * uuid,const char * code,const char * url)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
state_to_val(char * statestr,uint32_t * stateval)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
ireport_cb(fmev_t ev,const char * class,nvlist_t * nvl,void * arg)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
list_cb(fmev_t ev,const char * class,nvlist_t * nvl,void * arg)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
init_sma(void)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
main(int argc,char * argv[])546 main(int argc, char *argv[])
547 {
548 	struct rlimit rlim;
549 	struct sigaction act;
550 	sigset_t set;
551 	int 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 }
692