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