From a1dd93a24121e1b11406fdeb94954900bc9774aa Mon Sep 17 00:00:00 2001 From: Oleg King Date: Wed, 4 Jun 2008 15:22:45 +0200 Subject: [PATCH] notify_email plugin: Add plugin to send notifications via email. Hello, Collectd. I wrote a new plugin - notify_email. This plugin will notify you when threshold conditions are met - one email per one threshold notify. Email can be sent more then one recipient. Plugin does this by initiating SMTP session to specified SMTP server (it can also do SMTP AUTH if needed). Plugin uses libesmtp library. Please note: 1. SMTPFrom value sometimes is checked by SMTP server - you can get failure from SMTP server if you are using not-existent email. 2. SMTPSubject can contain two '%s' - first for severity and second for hostname. 3. Hostname used in SMTP connection - is hostname_g, hostname that is used for all collectd host identification. If it cannot be resolved by internet DNS - you can expect problems when sending emails. Patch and new file are included as attach. --- configure.in | 40 +++++++ src/Makefile.am | 8 ++ src/collectd.conf.in | 12 ++ src/notify_email.c | 276 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 336 insertions(+) create mode 100644 src/notify_email.c diff --git a/configure.in b/configure.in index cac633ba..856aa827 100644 --- a/configure.in +++ b/configure.in @@ -1489,6 +1489,44 @@ AC_DEFINE_UNQUOTED(COLLECT_LIBPCAP, [$collect_libpcap], [Wether or not to use the pcap library]) AM_CONDITIONAL(BUILD_WITH_LIBPCAP, test "x$with_libpcap" = "xyes") +AC_ARG_WITH(libesmtp, [AS_HELP_STRING([--with-libesmtp@<:@=PREFIX@:>@], [Path to libesmtp.])], +[ + if test "x$withval" != "xno" && test "x$withval" != "xyes" + then + LDFLAGS="$LDFLAGS -L$withval/lib" + CPPFLAGS="$CPPFLAGS -I$withval/include -D_THREAD_SAFE" + with_libesmtp="yes" + else + with_libesmtp="$withval" + fi +], +[ + with_libesmtp="yes" +]) +if test "x$with_libesmtp" = "xyes" +then + AC_CHECK_LIB(esmtp, smtp_create_session, + [ + AC_DEFINE(HAVE_LIBESMTP, 1, [Define to 1 if you have the esmtp library (-lesmtp).]) + ], [with_libesmtp="no (libesmtp not found)"]) +fi +if test "x$with_libesmtp" = "xyes" +then + AC_CHECK_HEADERS(libesmtp.h, + [ + AC_DEFINE(HAVE_LIBESMTP_H, 1, [Define to 1 if you have the header file.]) + ], [with_libesmtp="no (libesmtp.h not found)"]) +fi +if test "x$with_libesmtp" = "xyes" +then + collect_libesmtp=1 +else + collect_libesmtp=0 +fi +AC_DEFINE_UNQUOTED(COLLECT_LIBESMTP, [$collect_libesmtp], + [Wether or not to use the esmtp library]) +AM_CONDITIONAL(BUILD_WITH_LIBESMTP, test "x$with_libesmtp" = "xyes") + perl_interpreter="perl" AC_ARG_WITH(libperl, [AS_HELP_STRING([--with-libperl@<:@=PREFIX@:>@], [Path to libperl.])], [ @@ -2499,6 +2537,7 @@ AC_PLUGIN([netlink], [$with_libnetlink], [Enhanced Linux network statistic AC_PLUGIN([network], [yes], [Network communication plugin]) AC_PLUGIN([nfs], [$plugin_nfs], [NFS statistics]) AC_PLUGIN([nginx], [$with_libcurl], [nginx statistics]) +AC_PLUGIN([notify_email], [$with_libesmtp], [Email notifier]) AC_PLUGIN([ntpd], [yes], [NTPd statistics]) AC_PLUGIN([nut], [$with_libupsclient], [Network UPS tools statistics]) AC_PLUGIN([perl], [$plugin_perl], [Embed a Perl interpreter]) @@ -2660,6 +2699,7 @@ Configuration: network . . . . . . . $enable_network nfs . . . . . . . . . $enable_nfs nginx . . . . . . . . $enable_nginx + notify_email . . . . $enable_notify_email ntpd . . . . . . . . $enable_ntpd nut . . . . . . . . . $enable_nut perl . . . . . . . . $enable_perl diff --git a/src/Makefile.am b/src/Makefile.am index 42a1124a..149ecb32 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -478,6 +478,14 @@ collectd_LDADD += "-dlopen" nginx.la collectd_DEPENDENCIES += nginx.la endif +if BUILD_PLUGIN_NOTIFY_EMAIL +pkglib_LTLIBRARIES += notify_email.la +notify_email_la_SOURCES = notify_email.c +notify_email_la_LDFLAGS = -L/usr/local/lib -lesmtp -lssl -lcrypto -pthread -module -avoid-version +collectd_LDADD += "-dlopen" notify_email.la +collectd_DEPENDENCIES += notify_email.la +endif + if BUILD_PLUGIN_NTPD pkglib_LTLIBRARIES += ntpd.la ntpd_la_SOURCES = ntpd.c diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 25bc09e6..06f9a92c 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -46,6 +46,7 @@ FQDNLookup true @BUILD_PLUGIN_NETWORK_TRUE@LoadPlugin network @BUILD_PLUGIN_NFS_TRUE@LoadPlugin nfs @BUILD_PLUGIN_NGINX_TRUE@LoadPlugin nginx +@BUILD_PLUGIN_NOTIFY_EMAIL_TRUE@LoadPlugin notify_email @BUILD_PLUGIN_NTPD_TRUE@LoadPlugin ntpd @BUILD_PLUGIN_NUT_TRUE@LoadPlugin nut @BUILD_PLUGIN_PERL_TRUE@LoadPlugin perl @@ -211,6 +212,17 @@ FQDNLookup true # CACert "/etc/ssl/ca.crt" # +# +# SMTPHost "localhost" +# SMTPPort 25 +# SMTPSubject "Aaaaaa!! %s on %s!!!!!" # on . beware! do not use not more than two %s in this string!!! +# SMTPFrom "collectd@main0server.com" +# SMTPUser "my-username" +# SMTPPassword "my-password" +# EmailTo "email1@domain1.net" +# EmailTo "email2@domain2.com" +# + # # Host "localhost" # Port 123 diff --git a/src/notify_email.c b/src/notify_email.c new file mode 100644 index 00000000..f6c628e1 --- /dev/null +++ b/src/notify_email.c @@ -0,0 +1,276 @@ +/** + * collectd - src/notify_email.c + * Copyright (C) 2008 Oleg King + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Authors: + * Oleg King + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" + +#include +#include + +#define MAXSTRING 256 + +static const char *config_keys[] = +{ + "SMTPHost", + "SMTPPort", + "SMTPUser", + "SMTPPassword", + "SMTPFrom", + "SMTPSubject", + "EmailTo" +}; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static char **emails; +static int emails_len = 0; + +static smtp_session_t session; +static smtp_message_t message; +static auth_context_t authctx = NULL; + +static int smtp_port = 25; +static char *smtp_host = NULL; +static char *smtp_user = NULL; +static char *smtp_password = NULL; +static char *smtp_from = NULL; +static char *smtp_subject = NULL; + +#define DEFAULT_SMTP_HOST "localhost" +#define DEFAULT_SMTP_FROM "root@localhost" +#define DEFAULT_SMTP_SUBJECT "Collectd notify: %s@%s" + + +/* Callback to get username and password */ +static int authinteract (auth_client_request_t request, char **result, int fields, void *arg) +{ + int i; + for (i = 0; i < fields; i++) + { + if (request[i].flags & AUTH_USER) + result[i] = smtp_user; + else if (request[i].flags & AUTH_PASS) + result[i] = smtp_password; + else + return 0; + } + return 1; +} + +/* Callback to print the recipient status */ +static void print_recipient_status (smtp_recipient_t recipient, const char *mailbox, void *arg) +{ + const smtp_status_t *status; + + status = smtp_recipient_status (recipient); + if (status->text[strlen(status->text) - 2] == '\r') + status->text[strlen(status->text) - 2] = 0; + INFO ("notify_email: notify sent to %s: %d %s", mailbox, status->code, status->text); +} + +/* Callback to monitor SMTP activity */ +static void monitor_cb (const char *buf, int buflen, int writing, void *arg) +{ + char log_str[MAXSTRING]; + + strncpy(log_str, buf, buflen); + if (buflen > 2) + log_str[buflen - 2] = 0; /* replace \n with \0 */ + + if (writing == SMTP_CB_HEADERS) { + DEBUG ("SMTP --- H: %s", log_str); + return; + } + DEBUG (writing ? "SMTP >>> C: %s" : "SMTP <<< S: %s", log_str); +} + +static int notify_email_init() +{ + char server[MAXSTRING]; + + auth_client_init(); + if ( !(session = smtp_create_session()) ) { + ERROR ("notify_email plugin: cannot create SMTP session"); + return (-1); + } + + smtp_set_monitorcb (session, monitor_cb, NULL, 1); + smtp_set_hostname (session, hostname_g); + sprintf(server, "%s:%i", smtp_host == NULL ? DEFAULT_SMTP_HOST : smtp_host, smtp_port); + smtp_set_server (session, server); + + if (smtp_user && smtp_password) { + authctx = auth_create_context (); + auth_set_mechanism_flags (authctx, AUTH_PLUGIN_PLAIN, 0); + auth_set_interact_cb (authctx, authinteract, NULL); + } + + if ( !smtp_auth_set_context (session, authctx)) { + ERROR ("notify_email plugin: cannot set SMTP auth context"); + return (-1); + } + + return (0); +} + + +static int notify_email_shutdown() +{ + smtp_destroy_session (session); + auth_destroy_context (authctx); + auth_client_exit(); + return (0); +} + + +static int notify_email_config (const char *key, const char *value) +{ + if (strcasecmp (key, "EmailTo") == 0) + { + char **tmp; + + tmp = (char **) realloc ((void *) emails, (emails_len + 1) * sizeof (char *)); + if (tmp == NULL) { + ERROR ("notify_email: realloc failed."); + return (-1); + } + + emails = tmp; + emails[emails_len] = strdup (value); + if (emails[emails_len] == NULL) { + ERROR ("notify_email: strdup failed."); + return (-1); + } + emails_len++; + } + else if (0 == strcasecmp (key, "SMTPHost")) { + sfree (smtp_host); + smtp_host = strdup (value); + } + else if (0 == strcasecmp (key, "SMTPPort")) { + int port_tmp = atoi (value); + if (port_tmp < 1 || port_tmp > 65535) + { + WARNING ("notify_email plugin: Invalid SMTP port: %i", port_tmp); + return (1); + } + smtp_port = port_tmp; + } + else if (0 == strcasecmp (key, "SMTPUser")) { + sfree (smtp_user); + smtp_user = strdup (value); + } + else if (0 == strcasecmp (key, "SMTPPassword")) { + sfree (smtp_password); + smtp_password = strdup (value); + } + else if (0 == strcasecmp (key, "SMTPFrom")) { + sfree (smtp_from); + smtp_from = strdup (value); + } + else if (0 == strcasecmp (key, "SMTPSubject")) { + sfree (smtp_subject); + smtp_subject = strdup (value); + } + else { + return -1; + } + return 0; +} /* int notify_email_config (const char *, const char *) */ + + +static int notify_email_notification (const notification_t *n) +{ + smtp_recipient_t recipient; + + struct tm timestamp_tm; + char timestamp_str[64]; + + char severity[MAXSTRING]; + char subject[MAXSTRING]; + + char buf[4096] = ""; + int buf_len = sizeof (buf); + int i; + + sprintf (severity, "%s", + (n->severity == NOTIF_FAILURE) ? "FAILURE" + : ((n->severity == NOTIF_WARNING) ? "WARNING" + : ((n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN"))); + + sprintf (subject, smtp_subject == NULL ? DEFAULT_SMTP_SUBJECT : smtp_subject, severity, n->host); + + localtime_r (&n->time, ×tamp_tm); + strftime (timestamp_str, sizeof (timestamp_str), "%Y-%m-%d %H:%M:%S", ×tamp_tm); + timestamp_str[sizeof (timestamp_str) - 1] = '\0'; + + /* Let's make RFC822 message text with \r\n EOLs */ + snprintf (buf, buf_len, + "MIME-Version: 1.0\r\n" + "Content-Type: text/plain;\r\n" + "Content-Transfer-Encoding: 8bit\r\n" + "Subject: %s\r\n" + "\r\n" + "%s - %s@%s\r\n" + "\r\n" + "Message: %s", + subject, + timestamp_str, + severity, + n->host, + n->message); + + if ( !(message = smtp_add_message (session))) { + ERROR ("notify_email plugin: cannot set SMTP message"); + return (-1); + } + smtp_set_reverse_path (message, smtp_from); + smtp_set_header (message, "To", NULL, NULL); + smtp_set_message_str (message, buf); + + for (i = 0; i < emails_len; i++) + recipient = smtp_add_recipient (message, emails[i]); + + /* Initiate a connection to the SMTP server and transfer the message. */ + if (!smtp_start_session (session)) { + char buf[MAXSTRING]; + ERROR ("SMTP server problem: %s", smtp_strerror (smtp_errno (), buf, sizeof buf)); + return (-1); + } else { + const smtp_status_t *status; + /* Report on the success or otherwise of the mail transfer. */ + status = smtp_message_transfer_status (message); + DEBUG ("SMTP server report: %d %s", status->code, (status->text != NULL) ? status->text : "\n"); + smtp_enumerate_recipients (message, print_recipient_status, NULL); + } + + return (0); +} /* int notify_email_notification */ + +void module_register (void) +{ + plugin_register_init ("notify_email", notify_email_init); + plugin_register_shutdown ("notify_email", notify_email_shutdown); + plugin_register_config ("notify_email", notify_email_config, + config_keys, config_keys_num); + plugin_register_notification ("notify_email", notify_email_notification); +} /* void module_register (void) */ -- 2.30.2