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