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