eb9ec00c1dab97ad88f6dd1f9a486071ede4c577
[collectd.git] / src / notify_email.c
1 /**
2  * collectd - src/notify_email.c
3  * Copyright (C) 2008  Oleg King
4  * Copyright (C) 2010  Florian Forster
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; either version 2 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19  *
20  * Authors:
21  *   Oleg King <king2 at kaluga.ru>
22  *   Florian Forster <octo at collectd.org>
23  **/
24
25 #include "collectd.h"
26
27 #include "common.h"
28 #include "plugin.h"
29
30 #include <auth-client.h>
31 #include <libesmtp.h>
32
33 #define MAXSTRING 256
34
35 static const char *config_keys[] = {"SMTPServer",   "SMTPPort", "SMTPUser",
36                                     "SMTPPassword", "From",     "Recipient",
37                                     "Subject"};
38 static int config_keys_num = STATIC_ARRAY_SIZE(config_keys);
39
40 static char **recipients;
41 static int recipients_len = 0;
42
43 static smtp_session_t session;
44 static pthread_mutex_t session_lock = PTHREAD_MUTEX_INITIALIZER;
45 static smtp_message_t message;
46 static auth_context_t authctx;
47
48 static int smtp_port = 25;
49 static char *smtp_host;
50 static char *smtp_user;
51 static char *smtp_password;
52 static char *email_from;
53 static char *email_subject;
54
55 #define DEFAULT_SMTP_HOST "localhost"
56 #define DEFAULT_SMTP_FROM "root@localhost"
57 #define DEFAULT_SMTP_SUBJECT "Collectd notify: %s@%s"
58
59 /* Callback to get username and password */
60 static int authinteract(auth_client_request_t request, char **result,
61                         int fields, void __attribute__((unused)) * arg) {
62   for (int i = 0; i < fields; i++) {
63     if (request[i].flags & AUTH_USER)
64       result[i] = smtp_user;
65     else if (request[i].flags & AUTH_PASS)
66       result[i] = smtp_password;
67     else
68       return 0;
69   }
70   return 1;
71 } /* int authinteract */
72
73 /* Callback to print the recipient status */
74 static void print_recipient_status(smtp_recipient_t recipient,
75                                    const char *mailbox,
76                                    void __attribute__((unused)) * arg) {
77   const smtp_status_t *status;
78
79   status = smtp_recipient_status(recipient);
80   if (status->text[strlen(status->text) - 2] == '\r')
81     status->text[strlen(status->text) - 2] = 0;
82   INFO("notify_email: notify sent to %s: %d %s", mailbox, status->code,
83        status->text);
84 } /* void print_recipient_status */
85
86 /* Callback to monitor SMTP activity */
87 static void monitor_cb(const char *buf, int buflen, int writing,
88                        void __attribute__((unused)) * arg) {
89   char log_str[MAXSTRING];
90
91   sstrncpy(log_str, buf, sizeof(log_str));
92   if (buflen > 2)
93     log_str[buflen - 2] = 0; /* replace \n with \0 */
94
95   if (writing == SMTP_CB_HEADERS) {
96     DEBUG("notify_email plugin: SMTP --- H: %s", log_str);
97     return;
98   }
99   DEBUG(writing ? "notify_email plugin: SMTP >>> C: %s"
100                 : "notify_email plugin: SMTP <<< S: %s",
101         log_str);
102 } /* void monitor_cb */
103
104 static int notify_email_init(void) {
105   char server[MAXSTRING];
106
107   snprintf(server, sizeof(server), "%s:%i",
108            (smtp_host == NULL) ? DEFAULT_SMTP_HOST : smtp_host, smtp_port);
109
110   pthread_mutex_lock(&session_lock);
111
112   auth_client_init();
113
114   session = smtp_create_session();
115   if (session == NULL) {
116     pthread_mutex_unlock(&session_lock);
117     ERROR("notify_email plugin: cannot create SMTP session");
118     return -1;
119   }
120
121   smtp_set_monitorcb(session, monitor_cb, NULL, 1);
122   smtp_set_hostname(session, hostname_g);
123   smtp_set_server(session, server);
124
125   if (smtp_user && smtp_password) {
126     authctx = auth_create_context();
127     auth_set_mechanism_flags(authctx, AUTH_PLUGIN_PLAIN, 0);
128     auth_set_interact_cb(authctx, authinteract, NULL);
129   }
130
131   if (!smtp_auth_set_context(session, authctx)) {
132     pthread_mutex_unlock(&session_lock);
133     ERROR("notify_email plugin: cannot set SMTP auth context");
134     return -1;
135   }
136
137   pthread_mutex_unlock(&session_lock);
138   return 0;
139 } /* int notify_email_init */
140
141 static int notify_email_shutdown(void) {
142   pthread_mutex_lock(&session_lock);
143
144   if (session != NULL)
145     smtp_destroy_session(session);
146   session = NULL;
147
148   if (authctx != NULL)
149     auth_destroy_context(authctx);
150   authctx = NULL;
151
152   auth_client_exit();
153
154   pthread_mutex_unlock(&session_lock);
155   return 0;
156 } /* int notify_email_shutdown */
157
158 static int notify_email_config(const char *key, const char *value) {
159   if (strcasecmp(key, "Recipient") == 0) {
160     char **tmp;
161
162     tmp = realloc(recipients, (recipients_len + 1) * sizeof(char *));
163     if (tmp == NULL) {
164       ERROR("notify_email: realloc failed.");
165       return -1;
166     }
167
168     recipients = tmp;
169     recipients[recipients_len] = strdup(value);
170     if (recipients[recipients_len] == NULL) {
171       ERROR("notify_email: strdup failed.");
172       return -1;
173     }
174     recipients_len++;
175   } else if (0 == strcasecmp(key, "SMTPServer")) {
176     sfree(smtp_host);
177     smtp_host = strdup(value);
178   } else if (0 == strcasecmp(key, "SMTPPort")) {
179     int port_tmp = atoi(value);
180     if (port_tmp < 1 || port_tmp > 65535) {
181       WARNING("notify_email plugin: Invalid SMTP port: %i", port_tmp);
182       return 1;
183     }
184     smtp_port = port_tmp;
185   } else if (0 == strcasecmp(key, "SMTPUser")) {
186     sfree(smtp_user);
187     smtp_user = strdup(value);
188   } else if (0 == strcasecmp(key, "SMTPPassword")) {
189     sfree(smtp_password);
190     smtp_password = strdup(value);
191   } else if (0 == strcasecmp(key, "From")) {
192     sfree(email_from);
193     email_from = strdup(value);
194   } else if (0 == strcasecmp(key, "Subject")) {
195     sfree(email_subject);
196     email_subject = strdup(value);
197   } else {
198     return -1;
199   }
200   return 0;
201 } /* int notify_email_config (const char *, const char *) */
202
203 static int notify_email_notification(const notification_t *n,
204                                      user_data_t __attribute__((unused)) *
205                                          user_data) {
206
207   struct tm timestamp_tm;
208   char timestamp_str[64];
209
210   char severity[32];
211   char subject[MAXSTRING];
212
213   char buf[4096] = "";
214   int buf_len = sizeof(buf);
215   int i;
216
217   snprintf(severity, sizeof(severity), "%s",
218            (n->severity == NOTIF_FAILURE)
219                ? "FAILURE"
220                : ((n->severity == NOTIF_WARNING)
221                       ? "WARNING"
222                       : ((n->severity == NOTIF_OKAY) ? "OKAY" : "UNKNOWN")));
223
224   snprintf(subject, sizeof(subject),
225            (email_subject == NULL) ? DEFAULT_SMTP_SUBJECT : email_subject,
226            severity, n->host);
227
228   localtime_r(&CDTIME_T_TO_TIME_T(n->time), &timestamp_tm);
229   strftime(timestamp_str, sizeof(timestamp_str), "%Y-%m-%d %H:%M:%S",
230            &timestamp_tm);
231   timestamp_str[sizeof(timestamp_str) - 1] = '\0';
232
233   /* Let's make RFC822 message text with \r\n EOLs */
234   snprintf(buf, buf_len, "MIME-Version: 1.0\r\n"
235                          "Content-Type: text/plain; charset=\"US-ASCII\"\r\n"
236                          "Content-Transfer-Encoding: 8bit\r\n"
237                          "Subject: %s\r\n"
238                          "\r\n"
239                          "%s - %s@%s\r\n"
240                          "\r\n"
241                          "Message: %s",
242            subject, timestamp_str, severity, n->host, n->message);
243
244   pthread_mutex_lock(&session_lock);
245
246   if (session == NULL) {
247     /* Initialization failed or we're in the process of shutting down. */
248     pthread_mutex_unlock(&session_lock);
249     return -1;
250   }
251
252   if (!(message = smtp_add_message(session))) {
253     pthread_mutex_unlock(&session_lock);
254     ERROR("notify_email plugin: cannot set SMTP message");
255     return -1;
256   }
257   smtp_set_reverse_path(message, email_from);
258   smtp_set_header(message, "To", NULL, NULL);
259   smtp_set_message_str(message, buf);
260
261   for (i = 0; i < recipients_len; i++)
262     smtp_add_recipient(message, recipients[i]);
263
264   /* Initiate a connection to the SMTP server and transfer the message. */
265   if (!smtp_start_session(session)) {
266     ERROR("notify_email plugin: SMTP server problem: %s",
267           smtp_strerror(smtp_errno(), buf, sizeof buf));
268     pthread_mutex_unlock(&session_lock);
269     return -1;
270   } else {
271 #if COLLECT_DEBUG
272     const smtp_status_t *status;
273     /* Report on the success or otherwise of the mail transfer. */
274     status = smtp_message_transfer_status(message);
275     DEBUG("notify_email plugin: SMTP server report: %d %s", status->code,
276           (status->text != NULL) ? status->text : "\n");
277 #endif
278     smtp_enumerate_recipients(message, print_recipient_status, NULL);
279   }
280
281   pthread_mutex_unlock(&session_lock);
282   return 0;
283 } /* int notify_email_notification */
284
285 void module_register(void) {
286   plugin_register_init("notify_email", notify_email_init);
287   plugin_register_shutdown("notify_email", notify_email_shutdown);
288   plugin_register_config("notify_email", notify_email_config, config_keys,
289                          config_keys_num);
290   plugin_register_notification("notify_email", notify_email_notification,
291                                /* user_data = */ NULL);
292 } /* void module_register (void) */