/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <errno.h>
#include <fcntl.h>
#include <libscf.h>
#include <priv_utils.h>
#include <netdb.h>
#include <signal.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include <zone.h>
#include <fm/libfmevent.h>
#include "libfmnotify.h"
/*
* Debug messages can be enabled by setting the debug property to true
*
* # svccfg -s svc:/system/fm/smtp-notify setprop config/debug=true
*
* Debug messages will be spooled to the service log at:
* <root>/var/svc/log/system-fm-smtp-notify:default.log
*/
typedef struct email_pref
{
int ep_num_recips;
char **ep_recips;
char *ep_reply_to;
char *ep_template_path;
char *ep_template;
} email_pref_t;
static int
{
"\t-d enable debug mode\n"
"\t-f stay in foreground\n"
"\t-R specify alternate root\n");
return (1);
}
/*
* This function simply reads the file specified by "template" into a buffer
* and returns a pointer to that buffer (or NULL on failure). The caller is
* responsible for free'ing the returned buffer.
*/
static char *
{
int fd;
char *buf;
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (NULL);
}
return (buf);
}
/*
* This function runs a user-supplied message body template through a script
* which replaces the "committed" expansion macros with actual libfmd_msg
* expansion macros.
*/
static int
{
/*
* If it's an SMF event, then the diagcode and severity won't be part
* of the event payload and so libfmd_msg won't be able to expand them.
* Therefore we pass the code and severity into the script and let the
* script do the expansion.
*/
/* LINTED: E_SEC_SPRINTF_UNBOUNDED_COPY */
ret = 0;
return (ret);
}
/*
* If someone does an "svcadm refresh" on us, then this function gets called,
* which rereads our service configuration.
*/
static void
{
int s = 0;
&(nhdl->nh_rootdir));
if (s != 0)
"properties\n");
}
static void
{
else
}
/*
* This function constructs all the email headers and puts them into the
* "headers" buffer handle. The caller is responsible for free'ing this
* buffer.
*/
static int
char **headers)
{
const char *subj_key;
/*
* Fetch and format the email subject.
*/
} else {
}
return (-1); /* libfmd_msg error */
}
if (is_fm_event) {
/* LINTED: E_SEC_PRINTF_VAR_FMT */
/* LINTED: E_SEC_PRINTF_VAR_FMT */
} else if (is_smf_event) {
/* LINTED: E_SEC_PRINTF_VAR_FMT */
/* LINTED: E_SEC_PRINTF_VAR_FMT */
} else {
/* LINTED: E_SEC_PRINTF_VAR_FMT */
/* LINTED: E_SEC_PRINTF_VAR_FMT */
}
/*
* Here we add some X-headers to our mail message for use by mail
* filtering agents. We add headers for the following bits of event
* data for all events
*
* hostname
* msg id (diagcode)
* event class
* event severity
* event uuid
*
* For SMF transition events, we'll have the following add'l X-headers
*
* from-state
* to-state
* service fmri
*
* We follow the X-headers with standard Reply-To and Subject headers.
*/
if (is_fm_event) {
"%s: %s\nReply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME,
subj);
"%s: %s\n%s: %s\nReply-To: %s\nSubject: %s\n\n",
} else if (is_smf_event) {
"%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
subj);
"%s: %s\n%s: %s\n%s: %s\n%s: %s\nReply-To: %s\n"
subj);
} else {
"Reply-To: %s\nSubject: %s\n\n", XHDR_HOSTNAME,
"%s: %s\nReply-To: %s\nSubject: %s\n\n",
}
return (0);
}
static void
const char *recip)
{
/*
* Open a pipe to sendmail and pump out the email message
*/
return;
}
}
static void
{
return;
/*
* If the user specified a message body template, then we pass it
* through a private interface in libfmd_msg, which will return a string
* with any expansion tokens decoded.
*/
return;
}
for (int i = 0; i < eprefs->ep_num_recips; i++)
}
static int
{
if (r == SCF_ERROR_NOT_FOUND) {
/*
* No email notification preferences specified for this type of
* event, so we're done
*/
return (-1);
} else if (r != 0) {
"for this event");
return (-1);
}
goto eprefs_done;
}
/*
* For SMF state transition events, pref_nvl may contain two sets of
* preferences, which will have to be merged.
*
* The "smtp" nvlist can contain up to four members:
*
* "active" - boolean - used to toggle notfications
* "to" - a string array of email recipients
* "reply-to" - a string array containing the reply-to addresses
* - this is optional and defaults to root@localhost
* "msg_template" - the pathname of a user-supplied message body
* template
*
* In the case that we have two sets of preferences, we will merge them
* using the following rules:
*
* "active" will be set to true, if it is true in either set
*
* The "reply-to" and "to" lists will be merged, with duplicate email
* addresses removed.
*/
if (npref == 2) {
&n2);
if (r != 0) {
"preferences");
goto eprefs_done;
goto eprefs_done;
}
goto eprefs_done;
}
goto eprefs_done;
}
&n1);
&n2);
if (!r &&
&strarr)) != 0 ||
!= 0) {
}
if (n1)
if (n2)
if (repsz > 0)
} else {
/*
* Both the "active" and "to" notification preferences are
* required, so if we have trouble looking either of these up
* we return an error. We will also return an error if "active"
* is set to false. Returning an error will cause us to not
* send a notification for this event.
*/
&arrsz);
&arrsz);
if (r != 0) {
"preferences");
goto eprefs_done;
} else if (!active[0]) {
goto eprefs_done;
}
!= 0) {
goto eprefs_done;
}
&arrsz) == 0)
else
}
ret = 0;
if (ret != 0) {
if (ep->ep_reply_to)
}
if (tn1)
if (tn2)
return (ret);
}
/*ARGSUSED*/
static void
{
return;
goto irpt_done;
/*
* If the user specified a template, then we pass it through a script,
* which post-processes any expansion macros. Then we attempt to read
* it in and then send the message. Otherwise we carry on with the rest
* of this function which will contruct the message body from one of the
* default templates.
*/
goto irpt_done;
}
/*
* Fetch and format the event timestamp
*/
"timestamp");
goto irpt_done;
}
/*
* We have two message body templates to choose from. One for SMF
* service transition events and a generic one for any other
* uncommitted ireport.
*/
/*
* For SMF state transition events we have a standard message
* template that we fill in based on the payload of the event.
*/
goto irpt_done;
}
/* LINTED: E_SEC_PRINTF_VAR_FMT */
/* LINTED: E_SEC_PRINTF_VAR_FMT */
} else {
goto irpt_done;
}
/* LINTED: E_SEC_PRINTF_VAR_FMT */
/* LINTED: E_SEC_PRINTF_VAR_FMT */
}
goto irpt_done;
/*
* Everything is ready, so now we just iterate through the list of
* recipents, sending an email notification to each one.
*/
for (int i = 0; i < eprefs->ep_num_recips; i++)
if (ev_info)
if (eprefs->ep_reply_to)
}
/*
* There is a lack of uniformity in how the various entries in our diagnosis
* are terminated. Some end with one newline, others with two. This makes the
* output look a bit ugly. Therefore we postprocess the message before sending
* it, removing consecutive occurences of newlines.
*/
static void
{
int i = 0, j = 0;
char *buf;
return;
}
buf[j] = '\0';
}
/*ARGSUSED*/
static void
{
return;
goto listcb_done;
/*
* If the message payload member is set to 0, then it's an event we
* typically suppress messaging on, so we won't send an email for it.
*/
goto listcb_done;
}
/*
* If the user specified a template, then we pass it through a script,
* which post-processes any expansion macros. Then we attempt to read
* it in and then send the message. Otherwise we carry on with the rest
* of this function which will contruct the message body from one of the
* default templates.
*/
goto listcb_done;
}
/*
* Format the message body
*
* For FMA list.* events we use the same message that the
* syslog-msgs agent would emit as the message body
*
*/
goto listcb_done;
}
goto listcb_done;
/*
* Everything is ready, so now we just iterate through the list of
* recipents, sending an email notification to each one.
*/
for (int i = 0; i < eprefs->ep_num_recips; i++)
if (ev_info)
if (eprefs->ep_reply_to)
}
int
{
char c;
return (1);
}
/*
* In the case where we get started outside of SMF, args passed on the
* command line override SMF property setting
*/
switch (c) {
case 'd':
break;
case 'f':
break;
case 'R':
break;
default:
}
}
}
/*
* Set up a signal handler for SIGTERM (and SIGINT if we'll
* be running in the foreground) to ensure sure we get a chance to exit
* in an orderly fashion. We also catch SIGHUP, which will be sent to
* us by SMF if the service is refreshed.
*/
(void) sigfillset(&set);
if (run_fg) {
} else
/*
* We need to be root to initialize our libfmevent handle (because that
* calling __init_daemon_priv.
*/
(void) sleep(5);
}
/*
* If we're in the global zone, reset all of our privilege sets to
* the minimum set of required privileges. Since we've already
* initialized our libmevent handle, we no no longer need to run as
*
* __init_daemon_priv will also set the process core path for us
*
*/
if (getzoneid() == GLOBAL_ZONEID)
if (__init_daemon_priv(
/*
* Set up our event subscriptions. We subscribe to everything and then
* consult libscf when we receive an event to determine whether to send
* an email notification.
*/
NULL) != FMEV_SUCCESS) {
}
NULL) != FMEV_SUCCESS) {
}
/*
* We run until someone kills us
*/
while (nhdl->nh_keep_running)
(void) sigsuspend(&set);
return (0);
}