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