4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
32 #include <priv_utils.h>
39 #include <sys/types.h>
41 #include <fm/fmd_msg.h>
42 #include <fm/libfmevent.h>
43 #include "libfmnotify.h"
45 #define SENDMAIL "/usr/sbin/sendmail"
46 #define SVCNAME "system/fm/smtp-notify"
48 #define XHDR_HOSTNAME "X-FMEV-HOSTNAME"
49 #define XHDR_CLASS "X-FMEV-CLASS"
50 #define XHDR_UUID "X-FMEV-UUID"
51 #define XHDR_MSGID "X-FMEV-CODE"
52 #define XHDR_SEVERITY "X-FMEV-SEVERITY"
53 #define XHDR_FMRI "X-FMEV-FMRI"
54 #define XHDR_FROM_STATE "X-FMEV-FROM-STATE"
55 #define XHDR_TO_STATE "X-FMEV-TO-STATE"
58 * Debug messages can be enabled by setting the debug property to true
60 * # svccfg -s svc:/system/fm/smtp-notify setprop config/debug=true
62 * Debug messages will be spooled to the service log at:
63 * <root>/var/svc/log/system-fm-smtp-notify:default.log
65 #define PP_SCRIPT "usr/lib/fm/notify/process_msg_template.sh"
67 typedef struct email_pref
72 char *ep_template_path
;
76 static nd_hdl_t
*nhdl
;
77 static char hostname
[MAXHOSTNAMELEN
+ 1];
78 static const char optstr
[] = "dfR:";
79 static const char DEF_SUBJ_TEMPLATE
[] = "smtp-notify-subject-template";
80 static const char SMF_SUBJ_TEMPLATE
[] = "smtp-notify-smf-subject-template";
81 static const char FM_SUBJ_TEMPLATE
[] = "smtp-notify-fm-subject-template";
82 static const char IREPORT_MSG_TEMPLATE
[] = "ireport-msg-template";
83 static const char SMF_MSG_TEMPLATE
[] = "ireport.os.smf-msg-template";
86 usage(const char *pname
)
88 (void) fprintf(stderr
, "Usage: %s [-df] [-R <altroot>]\n", pname
);
90 (void) fprintf(stderr
,
91 "\t-d enable debug mode\n"
92 "\t-f stay in foreground\n"
93 "\t-R specify alternate root\n");
99 * This function simply reads the file specified by "template" into a buffer
100 * and returns a pointer to that buffer (or NULL on failure). The caller is
101 * responsible for free'ing the returned buffer.
104 read_template(const char *template)
110 if (stat(template, &statb
) != 0) {
111 nd_error(nhdl
, "Failed to stat %s (%s)", template,
115 if ((fd
= open(template, O_RDONLY
)) < 0) {
116 nd_error(nhdl
, "Failed to open %s (%s)", template,
120 if ((buf
= malloc(statb
.st_size
+ 1)) == NULL
) {
121 nd_error(nhdl
, "Failed to allocate %d bytes", statb
.st_size
);
125 if (read(fd
, buf
, statb
.st_size
) < 0) {
126 nd_error(nhdl
, "Failed to read in template (%s)",
132 buf
[statb
.st_size
] = '\0';
138 * This function runs a user-supplied message body template through a script
139 * which replaces the "committed" expansion macros with actual libfmd_msg
143 process_template(nd_ev_info_t
*ev_info
, email_pref_t
*eprefs
)
145 char pp_script
[PATH_MAX
], tmpfile
[PATH_MAX
], pp_cli
[PATH_MAX
];
148 (void) snprintf(pp_script
, sizeof (pp_script
), "%s%s",
149 nhdl
->nh_rootdir
, PP_SCRIPT
);
150 (void) snprintf(tmpfile
, sizeof (tmpfile
), "%s%s",
151 nhdl
->nh_rootdir
, tmpnam(NULL
));
154 * If it's an SMF event, then the diagcode and severity won't be part
155 * of the event payload and so libfmd_msg won't be able to expand them.
156 * Therefore we pass the code and severity into the script and let the
157 * script do the expansion.
159 /* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
160 (void) sprintf(pp_cli
, "%s %s %s %s %s", pp_script
,
161 eprefs
->ep_template_path
, tmpfile
, ev_info
->ei_diagcode
,
162 ev_info
->ei_severity
);
164 nd_debug(nhdl
, "Executing %s", pp_cli
);
165 if (system(pp_cli
) != -1)
166 if ((eprefs
->ep_template
= read_template(tmpfile
)) != NULL
)
169 (void) unlink(tmpfile
);
174 * If someone does an "svcadm refresh" on us, then this function gets called,
175 * which rereads our service configuration.
183 s
= nd_get_boolean_prop(nhdl
, SVCNAME
, "config", "debug", &val
);
184 nhdl
->nh_debug
= val
;
186 s
+= nd_get_astring_prop(nhdl
, SVCNAME
, "config", "rootdir",
187 &(nhdl
->nh_rootdir
));
190 nd_error(nhdl
, "Failed to read retrieve service "
195 nd_sighandler(int sig
)
204 * This function constructs all the email headers and puts them into the
205 * "headers" buffer handle. The caller is responsible for free'ing this
209 build_headers(nd_hdl_t
*nhdl
, nd_ev_info_t
*ev_info
, email_pref_t
*eprefs
,
212 const char *subj_key
;
213 char *subj_fmt
, *subj
= NULL
;
215 boolean_t is_smf_event
= B_FALSE
, is_fm_event
= B_FALSE
;
218 * Fetch and format the email subject.
220 if (strncmp(ev_info
->ei_class
, "list.", 5) == 0) {
221 is_fm_event
= B_TRUE
;
222 subj_key
= FM_SUBJ_TEMPLATE
;
223 } else if (strncmp(ev_info
->ei_class
, "ireport.os.smf", 14) == 0) {
224 is_smf_event
= B_TRUE
;
225 subj_key
= SMF_SUBJ_TEMPLATE
;
227 subj_key
= DEF_SUBJ_TEMPLATE
;
230 if ((subj_fmt
= fmd_msg_gettext_key(nhdl
->nh_msghdl
, NULL
,
231 FMNOTIFY_MSG_DOMAIN
, subj_key
)) == NULL
) {
232 nd_error(nhdl
, "Failed to contruct subject format");
233 return (-1); /* libfmd_msg error */
237 /* LINTED: E_SEC_PRINTF_VAR_FMT */
238 len
= snprintf(NULL
, 0, subj_fmt
, hostname
,
239 ev_info
->ei_diagcode
);
240 subj
= alloca(len
+ 1);
241 /* LINTED: E_SEC_PRINTF_VAR_FMT */
242 (void) snprintf(subj
, len
+ 1, subj_fmt
, hostname
,
243 ev_info
->ei_diagcode
);
244 } else if (is_smf_event
) {
245 /* LINTED: E_SEC_PRINTF_VAR_FMT */
246 len
= snprintf(NULL
, 0, subj_fmt
, hostname
, ev_info
->ei_fmri
,
247 ev_info
->ei_from_state
, ev_info
->ei_to_state
);
248 subj
= alloca(len
+ 1);
249 /* LINTED: E_SEC_PRINTF_VAR_FMT */
250 (void) snprintf(subj
, len
+ 1, subj_fmt
, hostname
,
251 ev_info
->ei_fmri
, ev_info
->ei_from_state
,
252 ev_info
->ei_to_state
);
254 /* LINTED: E_SEC_PRINTF_VAR_FMT */
255 len
= snprintf(NULL
, 0, subj_fmt
, hostname
);
256 subj
= alloca(len
+ 1);
257 /* LINTED: E_SEC_PRINTF_VAR_FMT */
258 (void) snprintf(subj
, len
+ 1, subj_fmt
, hostname
);
262 * Here we add some X-headers to our mail message for use by mail
263 * filtering agents. We add headers for the following bits of event
264 * data for all events
272 * For SMF transition events, we'll have the following add'l X-headers
278 * We follow the X-headers with standard Reply-To and Subject headers.
281 len
= snprintf(NULL
, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
282 "%s: %s\nReply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME
,
283 hostname
, XHDR_CLASS
, ev_info
->ei_class
, XHDR_UUID
,
284 ev_info
->ei_uuid
, XHDR_MSGID
, ev_info
->ei_diagcode
,
285 XHDR_SEVERITY
, ev_info
->ei_severity
, eprefs
->ep_reply_to
,
288 *headers
= calloc(len
+ 1, sizeof (char));
290 (void) snprintf(*headers
, len
+ 1, "%s: %s\n%s: %s\n%s: %s\n"
291 "%s: %s\n%s: %s\nReply-To: %s\nSubject: %s\n\n",
292 XHDR_HOSTNAME
, hostname
, XHDR_CLASS
, ev_info
->ei_class
,
293 XHDR_UUID
, ev_info
->ei_uuid
, XHDR_MSGID
,
294 ev_info
->ei_diagcode
, XHDR_SEVERITY
, ev_info
->ei_severity
,
295 eprefs
->ep_reply_to
, subj
);
296 } else if (is_smf_event
) {
297 len
= snprintf(NULL
, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
298 "%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
299 "Subject: %s\n\n", XHDR_HOSTNAME
, hostname
, XHDR_CLASS
,
300 ev_info
->ei_class
, XHDR_MSGID
, ev_info
->ei_diagcode
,
301 XHDR_SEVERITY
, ev_info
->ei_severity
, XHDR_FMRI
,
302 ev_info
->ei_fmri
, XHDR_FROM_STATE
, ev_info
->ei_from_state
,
303 XHDR_TO_STATE
, ev_info
->ei_to_state
, eprefs
->ep_reply_to
,
306 *headers
= calloc(len
+ 1, sizeof (char));
308 (void) snprintf(*headers
, len
+ 1, "%s: %s\n%s: %s\n%s: %s\n"
309 "%s: %s\n%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
310 "Subject: %s\n\n", XHDR_HOSTNAME
, hostname
, XHDR_CLASS
,
311 ev_info
->ei_class
, XHDR_MSGID
, ev_info
->ei_diagcode
,
312 XHDR_SEVERITY
, ev_info
->ei_severity
, XHDR_FMRI
,
313 ev_info
->ei_fmri
, XHDR_FROM_STATE
, ev_info
->ei_from_state
,
314 XHDR_TO_STATE
, ev_info
->ei_to_state
, eprefs
->ep_reply_to
,
317 len
= snprintf(NULL
, 0, "%s: %s\n%s: %s\n%s: %s\n%s: %s\n"
318 "Reply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME
,
319 hostname
, XHDR_CLASS
, ev_info
->ei_class
, XHDR_MSGID
,
320 ev_info
->ei_diagcode
, XHDR_SEVERITY
, ev_info
->ei_severity
,
321 eprefs
->ep_reply_to
, subj
);
323 *headers
= calloc(len
+ 1, sizeof (char));
325 (void) snprintf(*headers
, len
+ 1, "%s: %s\n%s: %s\n%s: %s\n"
326 "%s: %s\nReply-To: %s\nSubject: %s\n\n",
327 XHDR_HOSTNAME
, hostname
, XHDR_CLASS
, ev_info
->ei_class
,
328 XHDR_MSGID
, ev_info
->ei_diagcode
, XHDR_SEVERITY
,
329 ev_info
->ei_severity
, eprefs
->ep_reply_to
, subj
);
335 send_email(nd_hdl_t
*nhdl
, const char *headers
, const char *body
,
339 char sm_cli
[PATH_MAX
];
342 * Open a pipe to sendmail and pump out the email message
344 (void) snprintf(sm_cli
, PATH_MAX
, "%s -t %s", SENDMAIL
, recip
);
346 nd_debug(nhdl
, "Sending email notification to %s", recip
);
347 if ((mp
= popen(sm_cli
, "w")) == NULL
) {
348 nd_error(nhdl
, "Failed to open pipe to %s (%s)", SENDMAIL
,
352 if (fprintf(mp
, "%s", headers
) < 0)
353 nd_error(nhdl
, "Failed to write to pipe (%s)", strerror(errno
));
355 if (fprintf(mp
, "%s\n.\n", body
) < 0)
356 nd_error(nhdl
, "Failed to write to pipe (%s)",
363 send_email_template(nd_hdl_t
*nhdl
, nd_ev_info_t
*ev_info
, email_pref_t
*eprefs
)
367 if (build_headers(nhdl
, ev_info
, eprefs
, &headers
) != 0)
371 * If the user specified a message body template, then we pass it
372 * through a private interface in libfmd_msg, which will return a string
373 * with any expansion tokens decoded.
375 if ((msg
= fmd_msg_decode_tokens(ev_info
->ei_payload
,
376 eprefs
->ep_template
, ev_info
->ei_url
)) == NULL
) {
377 nd_error(nhdl
, "Failed to parse msg template");
381 for (int i
= 0; i
< eprefs
->ep_num_recips
; i
++)
382 send_email(nhdl
, headers
, msg
, eprefs
->ep_recips
[i
]);
389 get_email_prefs(nd_hdl_t
*nhdl
, fmev_t ev
, email_pref_t
**eprefs
)
391 nvlist_t
**p_nvl
= NULL
;
393 uint_t npref
, tn1
= 0, tn2
= 0;
394 char **tmparr1
, **tmparr2
;
397 r
= nd_get_notify_prefs(nhdl
, "smtp", ev
, &p_nvl
, &npref
);
398 if (r
== SCF_ERROR_NOT_FOUND
) {
400 * No email notification preferences specified for this type of
401 * event, so we're done
405 nd_error(nhdl
, "Failed to retrieve notification preferences "
410 if ((ep
= malloc(sizeof (email_pref_t
))) == NULL
) {
411 nd_error(nhdl
, "Failed to allocate space for email preferences "
412 "(%s)", strerror(errno
));
415 (void) memset(ep
, 0, sizeof (email_pref_t
));
418 * For SMF state transition events, pref_nvl may contain two sets of
419 * preferences, which will have to be merged.
421 * The "smtp" nvlist can contain up to four members:
423 * "active" - boolean - used to toggle notfications
424 * "to" - a string array of email recipients
425 * "reply-to" - a string array containing the reply-to addresses
426 * - this is optional and defaults to root@localhost
427 * "msg_template" - the pathname of a user-supplied message body
430 * In the case that we have two sets of preferences, we will merge them
431 * using the following rules:
433 * "active" will be set to true, if it is true in either set
435 * The "reply-to" and "to" lists will be merged, with duplicate email
439 boolean_t
*act1
, *act2
;
440 char **arr1
, **arr2
, **strarr
, **reparr1
, **reparr2
;
441 uint_t n1
, n2
, arrsz
, repsz
;
443 r
= nvlist_lookup_boolean_array(p_nvl
[0], "active", &act1
, &n1
);
444 r
+= nvlist_lookup_boolean_array(p_nvl
[1], "active", &act2
,
446 r
+= nvlist_lookup_string_array(p_nvl
[0], "to", &arr1
, &n1
);
447 r
+= nvlist_lookup_string_array(p_nvl
[1], "to", &arr2
, &n2
);
450 nd_error(nhdl
, "Malformed email notification "
452 nd_dump_nvlist(nhdl
, p_nvl
[0]);
453 nd_dump_nvlist(nhdl
, p_nvl
[1]);
455 } else if (!act1
[0] && !act2
[0]) {
456 nd_debug(nhdl
, "Email notification is disabled");
460 if (nd_split_list(nhdl
, arr1
[0], ",", &tmparr1
, &tn1
) != 0 ||
461 nd_split_list(nhdl
, arr2
[0], ",", &tmparr2
, &tn2
) != 0) {
462 nd_error(nhdl
, "Error parsing \"to\" lists");
463 nd_dump_nvlist(nhdl
, p_nvl
[0]);
464 nd_dump_nvlist(nhdl
, p_nvl
[1]);
468 if ((ep
->ep_num_recips
= nd_merge_strarray(nhdl
, tmparr1
, tn1
,
469 tmparr2
, tn2
, &ep
->ep_recips
)) < 0) {
470 nd_error(nhdl
, "Error merging email recipient lists");
474 r
= nvlist_lookup_string_array(p_nvl
[0], "reply-to", &arr1
,
476 r
+= nvlist_lookup_string_array(p_nvl
[1], "reply-to", &arr2
,
480 nd_split_list(nhdl
, arr1
[0], ",", &reparr1
, &n1
) != 0 ||
481 nd_split_list(nhdl
, arr2
[0], ",", &reparr2
, &n2
) != 0 ||
482 (repsz
= nd_merge_strarray(nhdl
, tmparr1
, n1
, tmparr2
, n2
,
484 nd_join_strarray(nhdl
, strarr
, repsz
, &ep
->ep_reply_to
)
487 ep
->ep_reply_to
= strdup("root@localhost");
490 nd_free_strarray(reparr1
, n1
);
492 nd_free_strarray(reparr2
, n2
);
494 nd_free_strarray(strarr
, repsz
);
496 if (nvlist_lookup_string_array(p_nvl
[0], "msg_template",
497 &strarr
, &arrsz
) == 0)
498 ep
->ep_template_path
= strdup(strarr
[0]);
500 char **strarr
, **tmparr
;
505 * Both the "active" and "to" notification preferences are
506 * required, so if we have trouble looking either of these up
507 * we return an error. We will also return an error if "active"
508 * is set to false. Returning an error will cause us to not
509 * send a notification for this event.
511 r
= nvlist_lookup_boolean_array(p_nvl
[0], "active", &active
,
513 r
+= nvlist_lookup_string_array(p_nvl
[0], "to", &strarr
,
517 nd_error(nhdl
, "Malformed email notification "
519 nd_dump_nvlist(nhdl
, p_nvl
[0]);
521 } else if (!active
[0]) {
522 nd_debug(nhdl
, "Email notification is disabled");
526 if (nd_split_list(nhdl
, strarr
[0], ",", &tmparr
, &arrsz
)
528 nd_error(nhdl
, "Error parsing \"to\" list");
531 ep
->ep_num_recips
= arrsz
;
532 ep
->ep_recips
= tmparr
;
534 if (nvlist_lookup_string_array(p_nvl
[0], "msg_template",
535 &strarr
, &arrsz
) == 0)
536 ep
->ep_template_path
= strdup(strarr
[0]);
538 if (nvlist_lookup_string_array(p_nvl
[0], "reply-to", &strarr
,
540 ep
->ep_reply_to
= strdup(strarr
[0]);
542 ep
->ep_reply_to
= strdup("root@localhost");
549 nd_free_strarray(ep
->ep_recips
, ep
->ep_num_recips
);
551 free(ep
->ep_reply_to
);
555 nd_free_strarray(tmparr1
, tn1
);
557 nd_free_strarray(tmparr2
, tn2
);
558 nd_free_nvlarray(p_nvl
, npref
);
565 irpt_cbfunc(fmev_t ev
, const char *class, nvlist_t
*nvl
, void *arg
)
567 char *body_fmt
, *headers
= NULL
, *body
= NULL
, tstamp
[32];
570 nd_ev_info_t
*ev_info
= NULL
;
571 email_pref_t
*eprefs
;
573 nd_debug(nhdl
, "Received event of class %s", class);
575 if (get_email_prefs(nhdl
, ev
, &eprefs
) < 0)
578 if (nd_get_event_info(nhdl
, class, ev
, &ev_info
) != 0)
582 * If the user specified a template, then we pass it through a script,
583 * which post-processes any expansion macros. Then we attempt to read
584 * it in and then send the message. Otherwise we carry on with the rest
585 * of this function which will contruct the message body from one of the
588 if (eprefs
->ep_template
!= NULL
)
589 free(eprefs
->ep_template
);
591 if (eprefs
->ep_template_path
!= NULL
&&
592 process_template(ev_info
, eprefs
) == 0) {
593 send_email_template(nhdl
, ev_info
, eprefs
);
598 * Fetch and format the event timestamp
600 if (fmev_localtime(ev
, &ts
) == NULL
) {
601 nd_error(nhdl
, "Malformed event: failed to retrieve "
605 (void) strftime(tstamp
, sizeof (tstamp
), NULL
, &ts
);
608 * We have two message body templates to choose from. One for SMF
609 * service transition events and a generic one for any other
610 * uncommitted ireport.
612 if (strncmp(class, "ireport.os.smf", 14) == 0) {
614 * For SMF state transition events we have a standard message
615 * template that we fill in based on the payload of the event.
617 if ((body_fmt
= fmd_msg_gettext_key(nhdl
->nh_msghdl
, NULL
,
618 FMNOTIFY_MSG_DOMAIN
, SMF_MSG_TEMPLATE
)) == NULL
) {
619 nd_error(nhdl
, "Failed to format message body");
623 /* LINTED: E_SEC_PRINTF_VAR_FMT */
624 len
= snprintf(NULL
, 0, body_fmt
, hostname
, tstamp
,
625 ev_info
->ei_fmri
, ev_info
->ei_from_state
,
626 ev_info
->ei_to_state
, ev_info
->ei_descr
,
628 body
= calloc(len
, sizeof (char));
629 /* LINTED: E_SEC_PRINTF_VAR_FMT */
630 (void) snprintf(body
, len
, body_fmt
, hostname
, tstamp
,
631 ev_info
->ei_fmri
, ev_info
->ei_from_state
,
632 ev_info
->ei_to_state
, ev_info
->ei_descr
,
635 if ((body_fmt
= fmd_msg_gettext_key(nhdl
->nh_msghdl
, NULL
,
636 FMNOTIFY_MSG_DOMAIN
, IREPORT_MSG_TEMPLATE
)) == NULL
) {
637 nd_error(nhdl
, "Failed to format message body");
640 /* LINTED: E_SEC_PRINTF_VAR_FMT */
641 len
= snprintf(NULL
, 0, body_fmt
, hostname
, tstamp
, class);
642 body
= calloc(len
, sizeof (char));
643 /* LINTED: E_SEC_PRINTF_VAR_FMT */
644 (void) snprintf(body
, len
, body_fmt
, hostname
, tstamp
, class);
647 if (build_headers(nhdl
, ev_info
, eprefs
, &headers
) != 0)
651 * Everything is ready, so now we just iterate through the list of
652 * recipents, sending an email notification to each one.
654 for (int i
= 0; i
< eprefs
->ep_num_recips
; i
++)
655 send_email(nhdl
, headers
, body
, eprefs
->ep_recips
[i
]);
661 nd_free_event_info(ev_info
);
662 if (eprefs
->ep_recips
)
663 nd_free_strarray(eprefs
->ep_recips
, eprefs
->ep_num_recips
);
664 if (eprefs
->ep_reply_to
)
665 free(eprefs
->ep_reply_to
);
670 * There is a lack of uniformity in how the various entries in our diagnosis
671 * are terminated. Some end with one newline, others with two. This makes the
672 * output look a bit ugly. Therefore we postprocess the message before sending
673 * it, removing consecutive occurences of newlines.
676 postprocess_msg(char *msg
)
681 if ((buf
= malloc(strlen(msg
) + 1)) == NULL
)
685 for (i
= 1; i
< strlen(msg
); i
++) {
686 if (!(msg
[i
] == '\n' && msg
[i
- 1] == '\n'))
690 (void) strncpy(msg
, buf
, j
+1);
696 listev_cb(fmev_t ev
, const char *class, nvlist_t
*nvl
, void *arg
)
698 char *body
= NULL
, *headers
= NULL
;
699 nd_ev_info_t
*ev_info
= NULL
;
701 email_pref_t
*eprefs
;
703 nd_debug(nhdl
, "Received event of class %s", class);
705 if (get_email_prefs(nhdl
, ev
, &eprefs
) < 0)
708 if (nd_get_event_info(nhdl
, class, ev
, &ev_info
) != 0)
712 * If the message payload member is set to 0, then it's an event we
713 * typically suppress messaging on, so we won't send an email for it.
715 if (nvlist_lookup_boolean_value(ev_info
->ei_payload
, FM_SUSPECT_MESSAGE
,
716 &domsg
) == 0 && !domsg
) {
717 nd_debug(nhdl
, "Messaging suppressed for this event");
722 * If the user specified a template, then we pass it through a script,
723 * which post-processes any expansion macros. Then we attempt to read
724 * it in and then send the message. Otherwise we carry on with the rest
725 * of this function which will contruct the message body from one of the
728 if (eprefs
->ep_template
!= NULL
)
729 free(eprefs
->ep_template
);
731 if (eprefs
->ep_template_path
!= NULL
&&
732 process_template(ev_info
, eprefs
) == 0) {
733 send_email_template(nhdl
, ev_info
, eprefs
);
738 * Format the message body
740 * For FMA list.* events we use the same message that the
741 * syslog-msgs agent would emit as the message body
744 if ((body
= fmd_msg_gettext_nv(nhdl
->nh_msghdl
, NULL
,
745 ev_info
->ei_payload
)) == NULL
) {
746 nd_error(nhdl
, "Failed to format message body");
747 nd_dump_nvlist(nhdl
, ev_info
->ei_payload
);
750 postprocess_msg(body
);
752 if (build_headers(nhdl
, ev_info
, eprefs
, &headers
) != 0)
756 * Everything is ready, so now we just iterate through the list of
757 * recipents, sending an email notification to each one.
759 for (int i
= 0; i
< eprefs
->ep_num_recips
; i
++)
760 send_email(nhdl
, headers
, body
, eprefs
->ep_recips
[i
]);
766 nd_free_event_info(ev_info
);
767 if (eprefs
->ep_recips
)
768 nd_free_strarray(eprefs
->ep_recips
, eprefs
->ep_num_recips
);
769 if (eprefs
->ep_reply_to
)
770 free(eprefs
->ep_reply_to
);
775 main(int argc
, char *argv
[])
778 struct sigaction act
;
781 boolean_t run_fg
= B_FALSE
;
783 if ((nhdl
= malloc(sizeof (nd_hdl_t
))) == NULL
) {
784 (void) fprintf(stderr
, "Failed to allocate space for notifyd "
785 "handle (%s)", strerror(errno
));
788 (void) memset(nhdl
, 0, sizeof (nd_hdl_t
));
790 nhdl
->nh_keep_running
= B_TRUE
;
791 nhdl
->nh_log_fd
= stderr
;
792 nhdl
->nh_pname
= argv
[0];
797 * In the case where we get started outside of SMF, args passed on the
798 * command line override SMF property setting
800 while (optind
< argc
) {
801 while ((c
= getopt(argc
, argv
, optstr
)) != -1) {
804 nhdl
->nh_debug
= B_TRUE
;
810 nhdl
->nh_rootdir
= strdup(optarg
);
814 return (usage(nhdl
->nh_pname
));
820 * Set up a signal handler for SIGTERM (and SIGINT if we'll
821 * be running in the foreground) to ensure sure we get a chance to exit
822 * in an orderly fashion. We also catch SIGHUP, which will be sent to
823 * us by SMF if the service is refreshed.
825 (void) sigfillset(&set
);
826 (void) sigfillset(&act
.sa_mask
);
827 act
.sa_handler
= nd_sighandler
;
830 (void) sigaction(SIGTERM
, &act
, NULL
);
831 (void) sigdelset(&set
, SIGTERM
);
832 (void) sigaction(SIGHUP
, &act
, NULL
);
833 (void) sigdelset(&set
, SIGHUP
);
836 (void) sigaction(SIGINT
, &act
, NULL
);
837 (void) sigdelset(&set
, SIGINT
);
841 rlim
.rlim_cur
= RLIM_INFINITY
;
842 rlim
.rlim_max
= RLIM_INFINITY
;
843 (void) setrlimit(RLIMIT_CORE
, &rlim
);
846 * We need to be root to initialize our libfmevent handle (because that
847 * involves reading/writing to /dev/sysevent), so we do this before
848 * calling __init_daemon_priv.
850 nhdl
->nh_evhdl
= fmev_shdl_init(LIBFMEVENT_VERSION_2
, NULL
, NULL
, NULL
);
851 if (nhdl
->nh_evhdl
== NULL
) {
853 nd_abort(nhdl
, "failed to initialize libfmevent: %s",
854 fmev_strerror(fmev_errno
));
858 * If we're in the global zone, reset all of our privilege sets to
859 * the minimum set of required privileges. Since we've already
860 * initialized our libmevent handle, we no no longer need to run as
861 * root, so we change our uid/gid to noaccess (60002).
863 * __init_daemon_priv will also set the process core path for us
866 if (getzoneid() == GLOBAL_ZONEID
)
867 if (__init_daemon_priv(
868 PU_RESETGROUPS
| PU_LIMITPRIVS
| PU_INHERITPRIVS
,
869 60002, 60002, PRIV_PROC_SETID
, NULL
) != 0)
870 nd_abort(nhdl
, "additional privileges required to run");
872 nhdl
->nh_msghdl
= fmd_msg_init(nhdl
->nh_rootdir
, FMD_MSG_VERSION
);
873 if (nhdl
->nh_msghdl
== NULL
)
874 nd_abort(nhdl
, "failed to initialize libfmd_msg");
876 (void) gethostname(hostname
, MAXHOSTNAMELEN
+ 1);
878 * Set up our event subscriptions. We subscribe to everything and then
879 * consult libscf when we receive an event to determine whether to send
880 * an email notification.
882 nd_debug(nhdl
, "Subscribing to ireport.* events");
883 if (fmev_shdl_subscribe(nhdl
->nh_evhdl
, "ireport.*", irpt_cbfunc
,
884 NULL
) != FMEV_SUCCESS
) {
885 nd_abort(nhdl
, "fmev_shdl_subscribe failed: %s",
886 fmev_strerror(fmev_errno
));
889 nd_debug(nhdl
, "Subscribing to list.* events");
890 if (fmev_shdl_subscribe(nhdl
->nh_evhdl
, "list.*", listev_cb
,
891 NULL
) != FMEV_SUCCESS
) {
892 nd_abort(nhdl
, "fmev_shdl_subscribe failed: %s",
893 fmev_strerror(fmev_errno
));
897 * We run until someone kills us
899 while (nhdl
->nh_keep_running
)
900 (void) sigsuspend(&set
);
902 free(nhdl
->nh_rootdir
);