Mercurial > illumos > illumos-gate
comparison usr/src/cmd/fm/notify/smtp-notify/common/smtp-notify.c @ 12979:ab9ae749152f
PSARC/2009/617 Software Events Notification Parameters CLI
PSARC/2009/618 snmp-notify: SNMP Notification Daemon for Software Events
PSARC/2009/619 smtp-notify: Email Notification Daemon for Software Events
PSARC/2010/225 fmd for non-global Solaris zones
PSARC/2010/226 Solaris Instance UUID
PSARC/2010/227 nvlist_nvflag(3NVPAIR)
PSARC/2010/228 libfmevent additions
PSARC/2010/257 sysevent_evc_setpropnvl and sysevent_evc_getpropnvl
PSARC/2010/265 FMRI and FMA Event Stabilty, 'ireport' category 1 event class, and the 'sw' FMRI scheme
PSARC/2010/278 FMA/SMF integration: instance state transitions
PSARC/2010/279 Modelling panics within FMA
PSARC/2010/290 logadm.conf upgrade
6392476 fmdump needs to pretty-print
6393375 userland ereport/ireport event generation interfaces
6445732 Add email notification agent for FMA and software events
6804168 RFE: Allow an efficient means to monitor SMF services status changes
6866661 scf_values_destroy(3SCF) will segfault if is passed NULL
6884709 Add snmp notification agent for FMA and software events
6884712 Add private interface to tap into libfmd_msg macro expansion capabilities
6897919 fmd to run in a non-global zone
6897937 fmd use of non-private doors is not safe
6900081 add a UUID to Solaris kernel image for use in crashdump identification
6914884 model panic events as a defect diagnosis in FMA
6944862 fmd_case_open_uuid, fmd_case_uuisresolved, fmd_nvl_create_defect
6944866 log legacy sysevents in fmd
6944867 enumerate svc scheme in topo
6944868 software-diagnosis and software-response fmd modules
6944870 model SMF maintenance state as a defect diagnosis in FMA
6944876 savecore runs in foreground for systems with zfs root and dedicated dump
6965796 Implement notification parameters for SMF state transitions and FMA events
6968287 SUN-FM-MIB.mib needs to be updated to reflect Oracle information
6972331 logadm.conf upgrade PSARC/2010/290
author | Gavin Maltby <gavin.maltby@oracle.com> |
---|---|
date | Fri, 30 Jul 2010 17:04:17 +1000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
12978:19d842faf8e4 | 12979:ab9ae749152f |
---|---|
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 #include <stdio.h> | |
26 #include <stdlib.h> | |
27 #include <string.h> | |
28 #include <alloca.h> | |
29 #include <errno.h> | |
30 #include <fcntl.h> | |
31 #include <libscf.h> | |
32 #include <priv_utils.h> | |
33 #include <netdb.h> | |
34 #include <signal.h> | |
35 #include <strings.h> | |
36 #include <time.h> | |
37 #include <unistd.h> | |
38 #include <zone.h> | |
39 #include <sys/types.h> | |
40 #include <sys/stat.h> | |
41 #include <fm/fmd_msg.h> | |
42 #include <fm/libfmevent.h> | |
43 #include "libfmnotify.h" | |
44 | |
45 #define SENDMAIL "/usr/sbin/sendmail" | |
46 #define SVCNAME "system/fm/smtp-notify" | |
47 | |
48 #define XHDR_HOSTNAME "X-FMEV-HOSTNAME" | |
49 #define XHDR_CLASS "X-FMEV-CLASS" | |
50 #define XHDR_UUID "X-FMEV-UUID" | |
51 #define XHDR_MSGID "X-FMEV-CODE" | |
52 #define XHDR_SEVERITY "X-FMEV-SEVERITY" | |
53 #define XHDR_FMRI "X-FMEV-FMRI" | |
54 #define XHDR_FROM_STATE "X-FMEV-FROM-STATE" | |
55 #define XHDR_TO_STATE "X-FMEV-TO-STATE" | |
56 | |
57 /* | |
58 * Debug messages can be enabled by setting the debug property to true | |
59 * | |
60 * # svccfg -s svc:/system/fm/smtp-notify setprop config/debug=true | |
61 * | |
62 * Debug messages will be spooled to the service log at: | |
63 * <root>/var/svc/log/system-fm-smtp-notify:default.log | |
64 */ | |
65 #define PP_SCRIPT "usr/lib/fm/notify/process_msg_template.sh" | |
66 | |
67 typedef struct email_pref | |
68 { | |
69 int ep_num_recips; | |
70 char **ep_recips; | |
71 char *ep_reply_to; | |
72 char *ep_template_path; | |
73 char *ep_template; | |
74 } email_pref_t; | |
75 | |
76 static nd_hdl_t *nhdl; | |
77 static char hostname[MAXHOSTNAMELEN + 1]; | |
78 static const char optstr[] = "dfR:"; | |
79 static const char DEF_SUBJ_TEMPLATE[] = "smtp-notify-subject-template"; | |
80 static const char SMF_SUBJ_TEMPLATE[] = "smtp-notify-smf-subject-template"; | |
81 static const char FM_SUBJ_TEMPLATE[] = "smtp-notify-fm-subject-template"; | |
82 static const char IREPORT_MSG_TEMPLATE[] = "ireport-msg-template"; | |
83 static const char SMF_MSG_TEMPLATE[] = "ireport.os.smf-msg-template"; | |
84 | |
85 static int | |
86 usage(const char *pname) | |
87 { | |
88 (void) fprintf(stderr, "Usage: %s [-df] [-R <altroot>]\n", pname); | |
89 | |
90 (void) fprintf(stderr, | |
91 "\t-d enable debug mode\n" | |
92 "\t-f stay in foreground\n" | |
93 "\t-R specify alternate root\n"); | |
94 | |
95 return (1); | |
96 } | |
97 | |
98 /* | |
99 * This function simply reads the file specified by "template" into a buffer | |
100 * and returns a pointer to that buffer (or NULL on failure). The caller is | |
101 * responsible for free'ing the returned buffer. | |
102 */ | |
103 static char * | |
104 read_template(const char *template) | |
105 { | |
106 int fd; | |
107 struct stat statb; | |
108 char *buf; | |
109 | |
110 if (stat(template, &statb) != 0) { | |
111 nd_error(nhdl, "Failed to stat %s (%s)", template, | |
112 strerror(errno)); | |
113 return (NULL); | |
114 } | |
115 if ((fd = open(template, O_RDONLY)) < 0) { | |
116 nd_error(nhdl, "Failed to open %s (%s)", template, | |
117 strerror(errno)); | |
118 return (NULL); | |
119 } | |
120 if ((buf = malloc(statb.st_size + 1)) == NULL) { | |
121 nd_error(nhdl, "Failed to allocate %d bytes", statb.st_size); | |
122 (void) close(fd); | |
123 return (NULL); | |
124 } | |
125 if (read(fd, buf, statb.st_size) < 0) { | |
126 nd_error(nhdl, "Failed to read in template (%s)", | |
127 strerror(errno)); | |
128 free(buf); | |
129 (void) close(fd); | |
130 return (NULL); | |
131 } | |
132 buf[statb.st_size] = '\0'; | |
133 (void) close(fd); | |
134 return (buf); | |
135 } | |
136 | |
137 /* | |
138 * This function runs a user-supplied message body template through a script | |
139 * which replaces the "committed" expansion macros with actual libfmd_msg | |
140 * expansion macros. | |
141 */ | |
142 static int | |
143 process_template(nd_ev_info_t *ev_info, email_pref_t *eprefs) | |
144 { | |
145 char pp_script[PATH_MAX], tmpfile[PATH_MAX], pp_cli[PATH_MAX]; | |
146 int ret = -1; | |
147 | |
148 (void) snprintf(pp_script, sizeof (pp_script), "%s%s", | |
149 nhdl->nh_rootdir, PP_SCRIPT); | |
150 (void) snprintf(tmpfile, sizeof (tmpfile), "%s%s", | |
151 nhdl->nh_rootdir, tmpnam(NULL)); | |
152 | |
153 /* | |
154 * If it's an SMF event, then the diagcode and severity won't be part | |
155 * of the event payload and so libfmd_msg won't be able to expand them. | |
156 * Therefore we pass the code and severity into the script and let the | |
157 * script do the expansion. | |
158 */ | |
159 /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */ | |
160 (void) sprintf(pp_cli, "%s %s %s %s %s", pp_script, | |
161 eprefs->ep_template_path, tmpfile, ev_info->ei_diagcode, | |
162 ev_info->ei_severity); | |
163 | |
164 nd_debug(nhdl, "Executing %s", pp_cli); | |
165 if (system(pp_cli) != -1) | |
166 if ((eprefs->ep_template = read_template(tmpfile)) != NULL) | |
167 ret = 0; | |
168 | |
169 (void) unlink(tmpfile); | |
170 return (ret); | |
171 } | |
172 | |
173 /* | |
174 * If someone does an "svcadm refresh" on us, then this function gets called, | |
175 * which rereads our service configuration. | |
176 */ | |
177 static void | |
178 get_svc_config() | |
179 { | |
180 int s = 0; | |
181 uint8_t val; | |
182 | |
183 s = nd_get_boolean_prop(nhdl, SVCNAME, "config", "debug", &val); | |
184 nhdl->nh_debug = val; | |
185 | |
186 s += nd_get_astring_prop(nhdl, SVCNAME, "config", "rootdir", | |
187 &(nhdl->nh_rootdir)); | |
188 | |
189 if (s != 0) | |
190 nd_error(nhdl, "Failed to read retrieve service " | |
191 "properties\n"); | |
192 } | |
193 | |
194 static void | |
195 nd_sighandler(int sig) | |
196 { | |
197 if (sig == SIGHUP) | |
198 get_svc_config(); | |
199 else | |
200 nd_cleanup(nhdl); | |
201 } | |
202 | |
203 /* | |
204 * This function constructs all the email headers and puts them into the | |
205 * "headers" buffer handle. The caller is responsible for free'ing this | |
206 * buffer. | |
207 */ | |
208 static int | |
209 build_headers(nd_hdl_t *nhdl, nd_ev_info_t *ev_info, email_pref_t *eprefs, | |
210 char **headers) | |
211 { | |
212 const char *subj_key; | |
213 char *subj_fmt, *subj = NULL; | |
214 size_t len; | |
215 boolean_t is_smf_event = B_FALSE, is_fm_event = B_FALSE; | |
216 | |
217 /* | |
218 * Fetch and format the email subject. | |
219 */ | |
220 if (strncmp(ev_info->ei_class, "list.", 5) == 0) { | |
221 is_fm_event = B_TRUE; | |
222 subj_key = FM_SUBJ_TEMPLATE; | |
223 } else if (strncmp(ev_info->ei_class, "ireport.os.smf", 14) == 0) { | |
224 is_smf_event = B_TRUE; | |
225 subj_key = SMF_SUBJ_TEMPLATE; | |
226 } else { | |
227 subj_key = DEF_SUBJ_TEMPLATE; | |
228 } | |
229 | |
230 if ((subj_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL, | |
231 FMNOTIFY_MSG_DOMAIN, subj_key)) == NULL) { | |
232 nd_error(nhdl, "Failed to contruct subject format"); | |
233 return (-1); /* libfmd_msg error */ | |
234 } | |
235 | |
236 if (is_fm_event) { | |
237 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
238 len = snprintf(NULL, 0, subj_fmt, hostname, | |
239 ev_info->ei_diagcode); | |
240 subj = alloca(len + 1); | |
241 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
242 (void) snprintf(subj, len + 1, subj_fmt, hostname, | |
243 ev_info->ei_diagcode); | |
244 } else if (is_smf_event) { | |
245 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
246 len = snprintf(NULL, 0, subj_fmt, hostname, ev_info->ei_fmri, | |
247 ev_info->ei_from_state, ev_info->ei_to_state); | |
248 subj = alloca(len + 1); | |
249 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
250 (void) snprintf(subj, len + 1, subj_fmt, hostname, | |
251 ev_info->ei_fmri, ev_info->ei_from_state, | |
252 ev_info->ei_to_state); | |
253 } else { | |
254 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
255 len = snprintf(NULL, 0, subj_fmt, hostname); | |
256 subj = alloca(len + 1); | |
257 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
258 (void) snprintf(subj, len + 1, subj_fmt, hostname); | |
259 } | |
260 | |
261 /* | |
262 * Here we add some X-headers to our mail message for use by mail | |
263 * filtering agents. We add headers for the following bits of event | |
264 * data for all events | |
265 * | |
266 * hostname | |
267 * msg id (diagcode) | |
268 * event class | |
269 * event severity | |
270 * event uuid | |
271 * | |
272 * For SMF transition events, we'll have the following add'l X-headers | |
273 * | |
274 * from-state | |
275 * to-state | |
276 * service fmri | |
277 * | |
278 * We follow the X-headers with standard Reply-To and Subject headers. | |
279 */ | |
280 if (is_fm_event) { | |
281 len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n" | |
282 "%s: %s\nReply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME, | |
283 hostname, XHDR_CLASS, ev_info->ei_class, XHDR_UUID, | |
284 ev_info->ei_uuid, XHDR_MSGID, ev_info->ei_diagcode, | |
285 XHDR_SEVERITY, ev_info->ei_severity, eprefs->ep_reply_to, | |
286 subj); | |
287 | |
288 *headers = calloc(len + 1, sizeof (char)); | |
289 | |
290 (void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n" | |
291 "%s: %s\n%s: %s\nReply-To: %s\nSubject: %s\n\n", | |
292 XHDR_HOSTNAME, hostname, XHDR_CLASS, ev_info->ei_class, | |
293 XHDR_UUID, ev_info->ei_uuid, XHDR_MSGID, | |
294 ev_info->ei_diagcode, XHDR_SEVERITY, ev_info->ei_severity, | |
295 eprefs->ep_reply_to, subj); | |
296 } else if (is_smf_event) { | |
297 len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n" | |
298 "%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n" | |
299 "Subject: %s\n\n", XHDR_HOSTNAME, hostname, XHDR_CLASS, | |
300 ev_info->ei_class, XHDR_MSGID, ev_info->ei_diagcode, | |
301 XHDR_SEVERITY, ev_info->ei_severity, XHDR_FMRI, | |
302 ev_info->ei_fmri, XHDR_FROM_STATE, ev_info->ei_from_state, | |
303 XHDR_TO_STATE, ev_info->ei_to_state, eprefs->ep_reply_to, | |
304 subj); | |
305 | |
306 *headers = calloc(len + 1, sizeof (char)); | |
307 | |
308 (void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n" | |
309 "%s: %s\n%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n" | |
310 "Subject: %s\n\n", XHDR_HOSTNAME, hostname, XHDR_CLASS, | |
311 ev_info->ei_class, XHDR_MSGID, ev_info->ei_diagcode, | |
312 XHDR_SEVERITY, ev_info->ei_severity, XHDR_FMRI, | |
313 ev_info->ei_fmri, XHDR_FROM_STATE, ev_info->ei_from_state, | |
314 XHDR_TO_STATE, ev_info->ei_to_state, eprefs->ep_reply_to, | |
315 subj); | |
316 } else { | |
317 len = snprintf(NULL, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n" | |
318 "Reply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME, | |
319 hostname, XHDR_CLASS, ev_info->ei_class, XHDR_MSGID, | |
320 ev_info->ei_diagcode, XHDR_SEVERITY, ev_info->ei_severity, | |
321 eprefs->ep_reply_to, subj); | |
322 | |
323 *headers = calloc(len + 1, sizeof (char)); | |
324 | |
325 (void) snprintf(*headers, len + 1, "%s: %s\n%s: %s\n%s: %s\n" | |
326 "%s: %s\nReply-To: %s\nSubject: %s\n\n", | |
327 XHDR_HOSTNAME, hostname, XHDR_CLASS, ev_info->ei_class, | |
328 XHDR_MSGID, ev_info->ei_diagcode, XHDR_SEVERITY, | |
329 ev_info->ei_severity, eprefs->ep_reply_to, subj); | |
330 } | |
331 return (0); | |
332 } | |
333 | |
334 static void | |
335 send_email(nd_hdl_t *nhdl, const char *headers, const char *body, | |
336 const char *recip) | |
337 { | |
338 FILE *mp; | |
339 char sm_cli[PATH_MAX]; | |
340 | |
341 /* | |
342 * Open a pipe to sendmail and pump out the email message | |
343 */ | |
344 (void) snprintf(sm_cli, PATH_MAX, "%s -t %s", SENDMAIL, recip); | |
345 | |
346 nd_debug(nhdl, "Sending email notification to %s", recip); | |
347 if ((mp = popen(sm_cli, "w")) == NULL) { | |
348 nd_error(nhdl, "Failed to open pipe to %s (%s)", SENDMAIL, | |
349 strerror(errno)); | |
350 return; | |
351 } | |
352 if (fprintf(mp, "%s", headers) < 0) | |
353 nd_error(nhdl, "Failed to write to pipe (%s)", strerror(errno)); | |
354 | |
355 if (fprintf(mp, "%s\n.\n", body) < 0) | |
356 nd_error(nhdl, "Failed to write to pipe (%s)", | |
357 strerror(errno)); | |
358 | |
359 (void) pclose(mp); | |
360 } | |
361 | |
362 static void | |
363 send_email_template(nd_hdl_t *nhdl, nd_ev_info_t *ev_info, email_pref_t *eprefs) | |
364 { | |
365 char *msg, *headers; | |
366 | |
367 if (build_headers(nhdl, ev_info, eprefs, &headers) != 0) | |
368 return; | |
369 | |
370 /* | |
371 * If the user specified a message body template, then we pass it | |
372 * through a private interface in libfmd_msg, which will return a string | |
373 * with any expansion tokens decoded. | |
374 */ | |
375 if ((msg = fmd_msg_decode_tokens(ev_info->ei_payload, | |
376 eprefs->ep_template, ev_info->ei_url)) == NULL) { | |
377 nd_error(nhdl, "Failed to parse msg template"); | |
378 free(headers); | |
379 return; | |
380 } | |
381 for (int i = 0; i < eprefs->ep_num_recips; i++) | |
382 send_email(nhdl, headers, msg, eprefs->ep_recips[i]); | |
383 | |
384 free(msg); | |
385 free(headers); | |
386 } | |
387 | |
388 static int | |
389 get_email_prefs(nd_hdl_t *nhdl, fmev_t ev, email_pref_t **eprefs) | |
390 { | |
391 nvlist_t **p_nvl = NULL; | |
392 email_pref_t *ep; | |
393 uint_t npref, tn1 = 0, tn2 = 0; | |
394 char **tmparr1, **tmparr2; | |
395 int r, ret = -1; | |
396 | |
397 r = nd_get_notify_prefs(nhdl, "smtp", ev, &p_nvl, &npref); | |
398 if (r == SCF_ERROR_NOT_FOUND) { | |
399 /* | |
400 * No email notification preferences specified for this type of | |
401 * event, so we're done | |
402 */ | |
403 return (-1); | |
404 } else if (r != 0) { | |
405 nd_error(nhdl, "Failed to retrieve notification preferences " | |
406 "for this event"); | |
407 return (-1); | |
408 } | |
409 | |
410 if ((ep = malloc(sizeof (email_pref_t))) == NULL) { | |
411 nd_error(nhdl, "Failed to allocate space for email preferences " | |
412 "(%s)", strerror(errno)); | |
413 goto eprefs_done; | |
414 } | |
415 (void) memset(ep, 0, sizeof (email_pref_t)); | |
416 | |
417 /* | |
418 * For SMF state transition events, pref_nvl may contain two sets of | |
419 * preferences, which will have to be merged. | |
420 * | |
421 * The "smtp" nvlist can contain up to four members: | |
422 * | |
423 * "active" - boolean - used to toggle notfications | |
424 * "to" - a string array of email recipients | |
425 * "reply-to" - a string array containing the reply-to addresses | |
426 * - this is optional and defaults to root@localhost | |
427 * "msg_template" - the pathname of a user-supplied message body | |
428 * template | |
429 * | |
430 * In the case that we have two sets of preferences, we will merge them | |
431 * using the following rules: | |
432 * | |
433 * "active" will be set to true, if it is true in either set | |
434 * | |
435 * The "reply-to" and "to" lists will be merged, with duplicate email | |
436 * addresses removed. | |
437 */ | |
438 if (npref == 2) { | |
439 boolean_t *act1, *act2; | |
440 char **arr1, **arr2, **strarr, **reparr1, **reparr2; | |
441 uint_t n1, n2, arrsz, repsz; | |
442 | |
443 r = nvlist_lookup_boolean_array(p_nvl[0], "active", &act1, &n1); | |
444 r += nvlist_lookup_boolean_array(p_nvl[1], "active", &act2, | |
445 &n2); | |
446 r += nvlist_lookup_string_array(p_nvl[0], "to", &arr1, &n1); | |
447 r += nvlist_lookup_string_array(p_nvl[1], "to", &arr2, &n2); | |
448 | |
449 if (r != 0) { | |
450 nd_error(nhdl, "Malformed email notification " | |
451 "preferences"); | |
452 nd_dump_nvlist(nhdl, p_nvl[0]); | |
453 nd_dump_nvlist(nhdl, p_nvl[1]); | |
454 goto eprefs_done; | |
455 } else if (!act1[0] && !act2[0]) { | |
456 nd_debug(nhdl, "Email notification is disabled"); | |
457 goto eprefs_done; | |
458 } | |
459 | |
460 if (nd_split_list(nhdl, arr1[0], ",", &tmparr1, &tn1) != 0 || | |
461 nd_split_list(nhdl, arr2[0], ",", &tmparr2, &tn2) != 0) { | |
462 nd_error(nhdl, "Error parsing \"to\" lists"); | |
463 nd_dump_nvlist(nhdl, p_nvl[0]); | |
464 nd_dump_nvlist(nhdl, p_nvl[1]); | |
465 goto eprefs_done; | |
466 } | |
467 | |
468 if ((ep->ep_num_recips = nd_merge_strarray(nhdl, tmparr1, tn1, | |
469 tmparr2, tn2, &ep->ep_recips)) < 0) { | |
470 nd_error(nhdl, "Error merging email recipient lists"); | |
471 goto eprefs_done; | |
472 } | |
473 | |
474 r = nvlist_lookup_string_array(p_nvl[0], "reply-to", &arr1, | |
475 &n1); | |
476 r += nvlist_lookup_string_array(p_nvl[1], "reply-to", &arr2, | |
477 &n2); | |
478 repsz = n1 = n2 = 0; | |
479 if (!r && | |
480 nd_split_list(nhdl, arr1[0], ",", &reparr1, &n1) != 0 || | |
481 nd_split_list(nhdl, arr2[0], ",", &reparr2, &n2) != 0 || | |
482 (repsz = nd_merge_strarray(nhdl, tmparr1, n1, tmparr2, n2, | |
483 &strarr)) != 0 || | |
484 nd_join_strarray(nhdl, strarr, repsz, &ep->ep_reply_to) | |
485 != 0) { | |
486 | |
487 ep->ep_reply_to = strdup("root@localhost"); | |
488 } | |
489 if (n1) | |
490 nd_free_strarray(reparr1, n1); | |
491 if (n2) | |
492 nd_free_strarray(reparr2, n2); | |
493 if (repsz > 0) | |
494 nd_free_strarray(strarr, repsz); | |
495 | |
496 if (nvlist_lookup_string_array(p_nvl[0], "msg_template", | |
497 &strarr, &arrsz) == 0) | |
498 ep->ep_template_path = strdup(strarr[0]); | |
499 } else { | |
500 char **strarr, **tmparr; | |
501 uint_t arrsz; | |
502 boolean_t *active; | |
503 | |
504 /* | |
505 * Both the "active" and "to" notification preferences are | |
506 * required, so if we have trouble looking either of these up | |
507 * we return an error. We will also return an error if "active" | |
508 * is set to false. Returning an error will cause us to not | |
509 * send a notification for this event. | |
510 */ | |
511 r = nvlist_lookup_boolean_array(p_nvl[0], "active", &active, | |
512 &arrsz); | |
513 r += nvlist_lookup_string_array(p_nvl[0], "to", &strarr, | |
514 &arrsz); | |
515 | |
516 if (r != 0) { | |
517 nd_error(nhdl, "Malformed email notification " | |
518 "preferences"); | |
519 nd_dump_nvlist(nhdl, p_nvl[0]); | |
520 goto eprefs_done; | |
521 } else if (!active[0]) { | |
522 nd_debug(nhdl, "Email notification is disabled"); | |
523 goto eprefs_done; | |
524 } | |
525 | |
526 if (nd_split_list(nhdl, strarr[0], ",", &tmparr, &arrsz) | |
527 != 0) { | |
528 nd_error(nhdl, "Error parsing \"to\" list"); | |
529 goto eprefs_done; | |
530 } | |
531 ep->ep_num_recips = arrsz; | |
532 ep->ep_recips = tmparr; | |
533 | |
534 if (nvlist_lookup_string_array(p_nvl[0], "msg_template", | |
535 &strarr, &arrsz) == 0) | |
536 ep->ep_template_path = strdup(strarr[0]); | |
537 | |
538 if (nvlist_lookup_string_array(p_nvl[0], "reply-to", &strarr, | |
539 &arrsz) == 0) | |
540 ep->ep_reply_to = strdup(strarr[0]); | |
541 else | |
542 ep->ep_reply_to = strdup("root@localhost"); | |
543 } | |
544 ret = 0; | |
545 *eprefs = ep; | |
546 eprefs_done: | |
547 if (ret != 0) { | |
548 if (ep->ep_recips) | |
549 nd_free_strarray(ep->ep_recips, ep->ep_num_recips); | |
550 if (ep->ep_reply_to) | |
551 free(ep->ep_reply_to); | |
552 free(ep); | |
553 } | |
554 if (tn1) | |
555 nd_free_strarray(tmparr1, tn1); | |
556 if (tn2) | |
557 nd_free_strarray(tmparr2, tn2); | |
558 nd_free_nvlarray(p_nvl, npref); | |
559 | |
560 return (ret); | |
561 } | |
562 | |
563 /*ARGSUSED*/ | |
564 static void | |
565 irpt_cbfunc(fmev_t ev, const char *class, nvlist_t *nvl, void *arg) | |
566 { | |
567 char *body_fmt, *headers = NULL, *body = NULL, tstamp[32]; | |
568 struct tm ts; | |
569 size_t len; | |
570 nd_ev_info_t *ev_info = NULL; | |
571 email_pref_t *eprefs; | |
572 | |
573 nd_debug(nhdl, "Received event of class %s", class); | |
574 | |
575 if (get_email_prefs(nhdl, ev, &eprefs) < 0) | |
576 return; | |
577 | |
578 if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0) | |
579 goto irpt_done; | |
580 | |
581 /* | |
582 * If the user specified a template, then we pass it through a script, | |
583 * which post-processes any expansion macros. Then we attempt to read | |
584 * it in and then send the message. Otherwise we carry on with the rest | |
585 * of this function which will contruct the message body from one of the | |
586 * default templates. | |
587 */ | |
588 if (eprefs->ep_template != NULL) | |
589 free(eprefs->ep_template); | |
590 | |
591 if (eprefs->ep_template_path != NULL && | |
592 process_template(ev_info, eprefs) == 0) { | |
593 send_email_template(nhdl, ev_info, eprefs); | |
594 goto irpt_done; | |
595 } | |
596 | |
597 /* | |
598 * Fetch and format the event timestamp | |
599 */ | |
600 if (fmev_localtime(ev, &ts) == NULL) { | |
601 nd_error(nhdl, "Malformed event: failed to retrieve " | |
602 "timestamp"); | |
603 goto irpt_done; | |
604 } | |
605 (void) strftime(tstamp, sizeof (tstamp), NULL, &ts); | |
606 | |
607 /* | |
608 * We have two message body templates to choose from. One for SMF | |
609 * service transition events and a generic one for any other | |
610 * uncommitted ireport. | |
611 */ | |
612 if (strncmp(class, "ireport.os.smf", 14) == 0) { | |
613 /* | |
614 * For SMF state transition events we have a standard message | |
615 * template that we fill in based on the payload of the event. | |
616 */ | |
617 if ((body_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL, | |
618 FMNOTIFY_MSG_DOMAIN, SMF_MSG_TEMPLATE)) == NULL) { | |
619 nd_error(nhdl, "Failed to format message body"); | |
620 goto irpt_done; | |
621 } | |
622 | |
623 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
624 len = snprintf(NULL, 0, body_fmt, hostname, tstamp, | |
625 ev_info->ei_fmri, ev_info->ei_from_state, | |
626 ev_info->ei_to_state, ev_info->ei_descr, | |
627 ev_info->ei_reason); | |
628 body = calloc(len, sizeof (char)); | |
629 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
630 (void) snprintf(body, len, body_fmt, hostname, tstamp, | |
631 ev_info->ei_fmri, ev_info->ei_from_state, | |
632 ev_info->ei_to_state, ev_info->ei_descr, | |
633 ev_info->ei_reason); | |
634 } else { | |
635 if ((body_fmt = fmd_msg_gettext_key(nhdl->nh_msghdl, NULL, | |
636 FMNOTIFY_MSG_DOMAIN, IREPORT_MSG_TEMPLATE)) == NULL) { | |
637 nd_error(nhdl, "Failed to format message body"); | |
638 goto irpt_done; | |
639 } | |
640 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
641 len = snprintf(NULL, 0, body_fmt, hostname, tstamp, class); | |
642 body = calloc(len, sizeof (char)); | |
643 /* LINTED: E_SEC_PRINTF_VAR_FMT */ | |
644 (void) snprintf(body, len, body_fmt, hostname, tstamp, class); | |
645 } | |
646 | |
647 if (build_headers(nhdl, ev_info, eprefs, &headers) != 0) | |
648 goto irpt_done; | |
649 | |
650 /* | |
651 * Everything is ready, so now we just iterate through the list of | |
652 * recipents, sending an email notification to each one. | |
653 */ | |
654 for (int i = 0; i < eprefs->ep_num_recips; i++) | |
655 send_email(nhdl, headers, body, eprefs->ep_recips[i]); | |
656 | |
657 irpt_done: | |
658 free(headers); | |
659 free(body); | |
660 if (ev_info) | |
661 nd_free_event_info(ev_info); | |
662 if (eprefs->ep_recips) | |
663 nd_free_strarray(eprefs->ep_recips, eprefs->ep_num_recips); | |
664 if (eprefs->ep_reply_to) | |
665 free(eprefs->ep_reply_to); | |
666 free(eprefs); | |
667 } | |
668 | |
669 /* | |
670 * There is a lack of uniformity in how the various entries in our diagnosis | |
671 * are terminated. Some end with one newline, others with two. This makes the | |
672 * output look a bit ugly. Therefore we postprocess the message before sending | |
673 * it, removing consecutive occurences of newlines. | |
674 */ | |
675 static void | |
676 postprocess_msg(char *msg) | |
677 { | |
678 int i = 0, j = 0; | |
679 char *buf; | |
680 | |
681 if ((buf = malloc(strlen(msg) + 1)) == NULL) | |
682 return; | |
683 | |
684 buf[j++] = msg[i++]; | |
685 for (i = 1; i < strlen(msg); i++) { | |
686 if (!(msg[i] == '\n' && msg[i - 1] == '\n')) | |
687 buf[j++] = msg[i]; | |
688 } | |
689 buf[j] = '\0'; | |
690 (void) strncpy(msg, buf, j+1); | |
691 free(buf); | |
692 } | |
693 | |
694 /*ARGSUSED*/ | |
695 static void | |
696 listev_cb(fmev_t ev, const char *class, nvlist_t *nvl, void *arg) | |
697 { | |
698 char *body = NULL, *headers = NULL; | |
699 nd_ev_info_t *ev_info = NULL; | |
700 boolean_t domsg; | |
701 email_pref_t *eprefs; | |
702 | |
703 nd_debug(nhdl, "Received event of class %s", class); | |
704 | |
705 if (get_email_prefs(nhdl, ev, &eprefs) < 0) | |
706 return; | |
707 | |
708 if (nd_get_event_info(nhdl, class, ev, &ev_info) != 0) | |
709 goto listcb_done; | |
710 | |
711 /* | |
712 * If the message payload member is set to 0, then it's an event we | |
713 * typically suppress messaging on, so we won't send an email for it. | |
714 */ | |
715 if (nvlist_lookup_boolean_value(ev_info->ei_payload, FM_SUSPECT_MESSAGE, | |
716 &domsg) == 0 && !domsg) { | |
717 nd_debug(nhdl, "Messaging suppressed for this event"); | |
718 goto listcb_done; | |
719 } | |
720 | |
721 /* | |
722 * If the user specified a template, then we pass it through a script, | |
723 * which post-processes any expansion macros. Then we attempt to read | |
724 * it in and then send the message. Otherwise we carry on with the rest | |
725 * of this function which will contruct the message body from one of the | |
726 * default templates. | |
727 */ | |
728 if (eprefs->ep_template != NULL) | |
729 free(eprefs->ep_template); | |
730 | |
731 if (eprefs->ep_template_path != NULL && | |
732 process_template(ev_info, eprefs) == 0) { | |
733 send_email_template(nhdl, ev_info, eprefs); | |
734 goto listcb_done; | |
735 } | |
736 | |
737 /* | |
738 * Format the message body | |
739 * | |
740 * For FMA list.* events we use the same message that the | |
741 * syslog-msgs agent would emit as the message body | |
742 * | |
743 */ | |
744 if ((body = fmd_msg_gettext_nv(nhdl->nh_msghdl, NULL, | |
745 ev_info->ei_payload)) == NULL) { | |
746 nd_error(nhdl, "Failed to format message body"); | |
747 nd_dump_nvlist(nhdl, ev_info->ei_payload); | |
748 goto listcb_done; | |
749 } | |
750 postprocess_msg(body); | |
751 | |
752 if (build_headers(nhdl, ev_info, eprefs, &headers) != 0) | |
753 goto listcb_done; | |
754 | |
755 /* | |
756 * Everything is ready, so now we just iterate through the list of | |
757 * recipents, sending an email notification to each one. | |
758 */ | |
759 for (int i = 0; i < eprefs->ep_num_recips; i++) | |
760 send_email(nhdl, headers, body, eprefs->ep_recips[i]); | |
761 | |
762 listcb_done: | |
763 free(headers); | |
764 free(body); | |
765 if (ev_info) | |
766 nd_free_event_info(ev_info); | |
767 if (eprefs->ep_recips) | |
768 nd_free_strarray(eprefs->ep_recips, eprefs->ep_num_recips); | |
769 if (eprefs->ep_reply_to) | |
770 free(eprefs->ep_reply_to); | |
771 free(eprefs); | |
772 } | |
773 | |
774 int | |
775 main(int argc, char *argv[]) | |
776 { | |
777 struct rlimit rlim; | |
778 struct sigaction act; | |
779 sigset_t set; | |
780 char c; | |
781 boolean_t run_fg = B_FALSE; | |
782 | |
783 if ((nhdl = malloc(sizeof (nd_hdl_t))) == NULL) { | |
784 (void) fprintf(stderr, "Failed to allocate space for notifyd " | |
785 "handle (%s)", strerror(errno)); | |
786 return (1); | |
787 } | |
788 (void) memset(nhdl, 0, sizeof (nd_hdl_t)); | |
789 | |
790 nhdl->nh_keep_running = B_TRUE; | |
791 nhdl->nh_log_fd = stderr; | |
792 nhdl->nh_pname = argv[0]; | |
793 | |
794 get_svc_config(); | |
795 | |
796 /* | |
797 * In the case where we get started outside of SMF, args passed on the | |
798 * command line override SMF property setting | |
799 */ | |
800 while (optind < argc) { | |
801 while ((c = getopt(argc, argv, optstr)) != -1) { | |
802 switch (c) { | |
803 case 'd': | |
804 nhdl->nh_debug = B_TRUE; | |
805 break; | |
806 case 'f': | |
807 run_fg = B_TRUE; | |
808 break; | |
809 case 'R': | |
810 nhdl->nh_rootdir = strdup(optarg); | |
811 break; | |
812 default: | |
813 free(nhdl); | |
814 return (usage(nhdl->nh_pname)); | |
815 } | |
816 } | |
817 } | |
818 | |
819 /* | |
820 * Set up a signal handler for SIGTERM (and SIGINT if we'll | |
821 * be running in the foreground) to ensure sure we get a chance to exit | |
822 * in an orderly fashion. We also catch SIGHUP, which will be sent to | |
823 * us by SMF if the service is refreshed. | |
824 */ | |
825 (void) sigfillset(&set); | |
826 (void) sigfillset(&act.sa_mask); | |
827 act.sa_handler = nd_sighandler; | |
828 act.sa_flags = 0; | |
829 | |
830 (void) sigaction(SIGTERM, &act, NULL); | |
831 (void) sigdelset(&set, SIGTERM); | |
832 (void) sigaction(SIGHUP, &act, NULL); | |
833 (void) sigdelset(&set, SIGHUP); | |
834 | |
835 if (run_fg) { | |
836 (void) sigaction(SIGINT, &act, NULL); | |
837 (void) sigdelset(&set, SIGINT); | |
838 } else | |
839 nd_daemonize(nhdl); | |
840 | |
841 rlim.rlim_cur = RLIM_INFINITY; | |
842 rlim.rlim_max = RLIM_INFINITY; | |
843 (void) setrlimit(RLIMIT_CORE, &rlim); | |
844 | |
845 /* | |
846 * We need to be root to initialize our libfmevent handle (because that | |
847 * involves reading/writing to /dev/sysevent), so we do this before | |
848 * calling __init_daemon_priv. | |
849 */ | |
850 nhdl->nh_evhdl = fmev_shdl_init(LIBFMEVENT_VERSION_2, NULL, NULL, NULL); | |
851 if (nhdl->nh_evhdl == NULL) { | |
852 (void) sleep(5); | |
853 nd_abort(nhdl, "failed to initialize libfmevent: %s", | |
854 fmev_strerror(fmev_errno)); | |
855 } | |
856 | |
857 /* | |
858 * If we're in the global zone, reset all of our privilege sets to | |
859 * the minimum set of required privileges. Since we've already | |
860 * initialized our libmevent handle, we no no longer need to run as | |
861 * root, so we change our uid/gid to noaccess (60002). | |
862 * | |
863 * __init_daemon_priv will also set the process core path for us | |
864 * | |
865 */ | |
866 if (getzoneid() == GLOBAL_ZONEID) | |
867 if (__init_daemon_priv( | |
868 PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS, | |
869 60002, 60002, PRIV_PROC_SETID, NULL) != 0) | |
870 nd_abort(nhdl, "additional privileges required to run"); | |
871 | |
872 nhdl->nh_msghdl = fmd_msg_init(nhdl->nh_rootdir, FMD_MSG_VERSION); | |
873 if (nhdl->nh_msghdl == NULL) | |
874 nd_abort(nhdl, "failed to initialize libfmd_msg"); | |
875 | |
876 (void) gethostname(hostname, MAXHOSTNAMELEN + 1); | |
877 /* | |
878 * Set up our event subscriptions. We subscribe to everything and then | |
879 * consult libscf when we receive an event to determine whether to send | |
880 * an email notification. | |
881 */ | |
882 nd_debug(nhdl, "Subscribing to ireport.* events"); | |
883 if (fmev_shdl_subscribe(nhdl->nh_evhdl, "ireport.*", irpt_cbfunc, | |
884 NULL) != FMEV_SUCCESS) { | |
885 nd_abort(nhdl, "fmev_shdl_subscribe failed: %s", | |
886 fmev_strerror(fmev_errno)); | |
887 } | |
888 | |
889 nd_debug(nhdl, "Subscribing to list.* events"); | |
890 if (fmev_shdl_subscribe(nhdl->nh_evhdl, "list.*", listev_cb, | |
891 NULL) != FMEV_SUCCESS) { | |
892 nd_abort(nhdl, "fmev_shdl_subscribe failed: %s", | |
893 fmev_strerror(fmev_errno)); | |
894 } | |
895 | |
896 /* | |
897 * We run until someone kills us | |
898 */ | |
899 while (nhdl->nh_keep_running) | |
900 (void) sigsuspend(&set); | |
901 | |
902 free(nhdl->nh_rootdir); | |
903 free(nhdl); | |
904 | |
905 return (0); | |
906 } |