blob: 80dc7d6f65dd8aa6ba5825d581e42bec8caa6557 [file] [log] [blame]
Simon Horman0d16a402015-01-30 11:22:58 +09001/*
2 * Mailer management.
3 *
4 * Copyright 2015 Horms Solutions Ltd, Simon Horman <horms@verge.net.au>
Willy Tarreaucee013e2020-06-05 11:40:38 +02005 * Copyright 2020 Willy Tarreau <w@1wt.eu>
Simon Horman0d16a402015-01-30 11:22:58 +09006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
14#include <stdlib.h>
15
Willy Tarreaucee013e2020-06-05 11:40:38 +020016#include <haproxy/action-t.h>
17#include <haproxy/api.h>
18#include <haproxy/check.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020019#include <haproxy/errors.h>
Willy Tarreaucee013e2020-06-05 11:40:38 +020020#include <haproxy/list.h>
21#include <haproxy/mailers.h>
22#include <haproxy/pool.h>
Willy Tarreaucee013e2020-06-05 11:40:38 +020023#include <haproxy/proxy-t.h>
24#include <haproxy/server-t.h>
25#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020026#include <haproxy/tcpcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020027#include <haproxy/thread.h>
28#include <haproxy/time.h>
29#include <haproxy/tools.h>
Willy Tarreaucee013e2020-06-05 11:40:38 +020030
Simon Horman0d16a402015-01-30 11:22:58 +090031
32struct mailers *mailers = NULL;
Willy Tarreaucee013e2020-06-05 11:40:38 +020033
34DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
35
36/****************************** Email alerts ******************************/
37/* NOTE: It may be pertinent to use an applet to handle email alerts */
38/* instead of a tcp-check ruleset */
39/**************************************************************************/
40void email_alert_free(struct email_alert *alert)
41{
42 struct tcpcheck_rule *rule, *back;
43
44 if (!alert)
45 return;
46
47 if (alert->rules.list) {
48 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +020049 LIST_DELETE(&rule->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +020050 free_tcpcheck(rule, 1);
51 }
52 free_tcpcheck_vars(&alert->rules.preset_vars);
Willy Tarreau61cfdf42021-02-20 10:46:51 +010053 ha_free(&alert->rules.list);
Willy Tarreaucee013e2020-06-05 11:40:38 +020054 }
55 pool_free(pool_head_email_alert, alert);
56}
57
Willy Tarreau144f84a2021-03-02 16:09:26 +010058static struct task *process_email_alert(struct task *t, void *context, unsigned int state)
Willy Tarreaucee013e2020-06-05 11:40:38 +020059{
60 struct check *check = context;
61 struct email_alertq *q;
62 struct email_alert *alert;
63
64 q = container_of(check, typeof(*q), check);
65
66 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
67 while (1) {
68 if (!(check->state & CHK_ST_ENABLED)) {
69 if (LIST_ISEMPTY(&q->email_alerts)) {
70 /* All alerts processed, queue the task */
71 t->expire = TICK_ETERNITY;
72 task_queue(t);
73 goto end;
74 }
75
76 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
Willy Tarreau2b718102021-04-21 07:32:39 +020077 LIST_DELETE(&alert->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +020078 t->expire = now_ms;
79 check->tcpcheck_rules = &alert->rules;
80 check->status = HCHK_STATUS_INI;
81 check->state |= CHK_ST_ENABLED;
82 }
83
84 process_chk(t, context, state);
85 if (check->state & CHK_ST_INPROGRESS)
86 break;
87
88 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
89 email_alert_free(alert);
90 check->tcpcheck_rules = NULL;
91 check->server = NULL;
92 check->state &= ~CHK_ST_ENABLED;
93 }
94 end:
95 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
96 return t;
97}
98
99/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
100 *
101 * The function returns 1 in success case, otherwise, it returns 0 and err is
102 * filled.
103 */
104int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
105{
106 struct mailer *mailer;
107 struct email_alertq *queues;
108 const char *err_str;
109 int i = 0;
110
111 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
112 memprintf(err, "out of memory while allocating mailer alerts queues");
113 goto fail_no_queue;
114 }
115
116 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
117 struct email_alertq *q = &queues[i];
118 struct check *check = &q->check;
119 struct task *t;
120
121 LIST_INIT(&q->email_alerts);
122 HA_SPIN_INIT(&q->lock);
123 check->inter = mls->timeout.mail;
124 check->rise = DEF_AGENT_RISETIME;
125 check->proxy = p;
126 check->fall = DEF_AGENT_FALLTIME;
127 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
128 memprintf(err, "%s", err_str);
129 goto error;
130 }
131
132 check->xprt = mailer->xprt;
133 check->addr = mailer->addr;
134 check->port = get_host_port(&mailer->addr);
135
136 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
137 memprintf(err, "out of memory while allocating mailer alerts task");
138 goto error;
139 }
140
141 check->task = t;
142 t->process = process_email_alert;
143 t->context = check;
144
145 /* check this in one ms */
146 t->expire = TICK_ETERNITY;
147 check->start = now;
148 task_queue(t);
149 }
150
151 mls->users++;
152 free(p->email_alert.mailers.name);
153 p->email_alert.mailers.m = mls;
154 p->email_alert.queues = queues;
155 return 0;
156
157 error:
158 for (i = 0; i < mls->count; i++) {
159 struct email_alertq *q = &queues[i];
160 struct check *check = &q->check;
161
162 free_check(check);
163 }
164 free(queues);
165 fail_no_queue:
166 return 1;
167}
168
169static int enqueue_one_email_alert(struct proxy *p, struct server *s,
170 struct email_alertq *q, const char *msg)
171{
172 struct email_alert *alert;
173 struct tcpcheck_rule *tcpcheck;
174 struct check *check = &q->check;
175
176 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
177 goto error;
178 LIST_INIT(&alert->list);
179 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
180 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
181 if (!alert->rules.list)
182 goto error;
183 LIST_INIT(alert->rules.list);
184 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
185 alert->srv = s;
186
Willy Tarreau3ab0a0b2021-03-22 21:07:52 +0100187 if ((tcpcheck = pool_zalloc(pool_head_tcpcheck_rule)) == NULL)
Willy Tarreaucee013e2020-06-05 11:40:38 +0200188 goto error;
Willy Tarreaucee013e2020-06-05 11:40:38 +0200189 tcpcheck->action = TCPCHK_ACT_CONNECT;
190 tcpcheck->comment = NULL;
191
Willy Tarreau2b718102021-04-21 07:32:39 +0200192 LIST_APPEND(alert->rules.list, &tcpcheck->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +0200193
194 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
195 goto error;
196
197 {
198 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
199 if (!add_tcpcheck_send_strs(&alert->rules, strs))
200 goto error;
201 }
202
203 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
204 goto error;
205
206 {
207 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
208 if (!add_tcpcheck_send_strs(&alert->rules, strs))
209 goto error;
210 }
211
212 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
213 goto error;
214
215 {
216 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
217 if (!add_tcpcheck_send_strs(&alert->rules, strs))
218 goto error;
219 }
220
221 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
222 goto error;
223
224 {
225 const char * const strs[2] = { "DATA\r\n" };
226 if (!add_tcpcheck_send_strs(&alert->rules, strs))
227 goto error;
228 }
229
230 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
231 goto error;
232
233 {
234 struct tm tm;
235 char datestr[48];
236 const char * const strs[18] = {
237 "From: ", p->email_alert.from, "\r\n",
238 "To: ", p->email_alert.to, "\r\n",
239 "Date: ", datestr, "\r\n",
240 "Subject: [HAproxy Alert] ", msg, "\r\n",
241 "\r\n",
242 msg, "\r\n",
243 "\r\n",
244 ".\r\n",
245 NULL
246 };
247
248 get_localtime(date.tv_sec, &tm);
249
250 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
251 goto error;
252 }
253
254 if (!add_tcpcheck_send_strs(&alert->rules, strs))
255 goto error;
256 }
257
258 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
259 goto error;
260
261 {
262 const char * const strs[2] = { "QUIT\r\n" };
263 if (!add_tcpcheck_send_strs(&alert->rules, strs))
264 goto error;
265 }
266
267 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
268 goto error;
269
270 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
271 task_wakeup(check->task, TASK_WOKEN_MSG);
Willy Tarreau2b718102021-04-21 07:32:39 +0200272 LIST_APPEND(&q->email_alerts, &alert->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +0200273 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
274 return 1;
275
276error:
277 email_alert_free(alert);
278 return 0;
279}
280
281static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
282{
283 int i;
284 struct mailer *mailer;
285
286 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
287 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
288 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
289 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
290 return;
291 }
292 }
293
294 return;
295}
296
297/*
298 * Send email alert if configured.
299 */
300void send_email_alert(struct server *s, int level, const char *format, ...)
301{
302 va_list argp;
303 char buf[1024];
304 int len;
305 struct proxy *p = s->proxy;
306
307 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
308 return;
309
310 va_start(argp, format);
311 len = vsnprintf(buf, sizeof(buf), format, argp);
312 va_end(argp);
313
314 if (len < 0 || len >= sizeof(buf)) {
315 ha_alert("Email alert [%s] could not format message\n", p->id);
316 return;
317 }
318
319 enqueue_email_alert(p, s, buf);
320}