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,
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 }
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
|
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,
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 }
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
|