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