blob: 16ee60a2873cdf717a65755a1964b2e793edd00d [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>
19#include <haproxy/list.h>
20#include <haproxy/mailers.h>
21#include <haproxy/pool.h>
22#include <haproxy/tools.h>
23#include <haproxy/time.h>
24#include <haproxy/thread.h>
25#include <haproxy/log.h>
26#include <haproxy/proxy-t.h>
27#include <haproxy/server-t.h>
28#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020029#include <haproxy/tcpcheck.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) {
49 LIST_DEL(&rule->list);
50 free_tcpcheck(rule, 1);
51 }
52 free_tcpcheck_vars(&alert->rules.preset_vars);
53 free(alert->rules.list);
54 alert->rules.list = NULL;
55 }
56 pool_free(pool_head_email_alert, alert);
57}
58
59static struct task *process_email_alert(struct task *t, void *context, unsigned short state)
60{
61 struct check *check = context;
62 struct email_alertq *q;
63 struct email_alert *alert;
64
65 q = container_of(check, typeof(*q), check);
66
67 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
68 while (1) {
69 if (!(check->state & CHK_ST_ENABLED)) {
70 if (LIST_ISEMPTY(&q->email_alerts)) {
71 /* All alerts processed, queue the task */
72 t->expire = TICK_ETERNITY;
73 task_queue(t);
74 goto end;
75 }
76
77 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
78 LIST_DEL(&alert->list);
79 t->expire = now_ms;
80 check->tcpcheck_rules = &alert->rules;
81 check->status = HCHK_STATUS_INI;
82 check->state |= CHK_ST_ENABLED;
83 }
84
85 process_chk(t, context, state);
86 if (check->state & CHK_ST_INPROGRESS)
87 break;
88
89 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
90 email_alert_free(alert);
91 check->tcpcheck_rules = NULL;
92 check->server = NULL;
93 check->state &= ~CHK_ST_ENABLED;
94 }
95 end:
96 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
97 return t;
98}
99
100/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
101 *
102 * The function returns 1 in success case, otherwise, it returns 0 and err is
103 * filled.
104 */
105int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
106{
107 struct mailer *mailer;
108 struct email_alertq *queues;
109 const char *err_str;
110 int i = 0;
111
112 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
113 memprintf(err, "out of memory while allocating mailer alerts queues");
114 goto fail_no_queue;
115 }
116
117 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
118 struct email_alertq *q = &queues[i];
119 struct check *check = &q->check;
120 struct task *t;
121
122 LIST_INIT(&q->email_alerts);
123 HA_SPIN_INIT(&q->lock);
124 check->inter = mls->timeout.mail;
125 check->rise = DEF_AGENT_RISETIME;
126 check->proxy = p;
127 check->fall = DEF_AGENT_FALLTIME;
128 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
129 memprintf(err, "%s", err_str);
130 goto error;
131 }
132
133 check->xprt = mailer->xprt;
134 check->addr = mailer->addr;
135 check->port = get_host_port(&mailer->addr);
136
137 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
138 memprintf(err, "out of memory while allocating mailer alerts task");
139 goto error;
140 }
141
142 check->task = t;
143 t->process = process_email_alert;
144 t->context = check;
145
146 /* check this in one ms */
147 t->expire = TICK_ETERNITY;
148 check->start = now;
149 task_queue(t);
150 }
151
152 mls->users++;
153 free(p->email_alert.mailers.name);
154 p->email_alert.mailers.m = mls;
155 p->email_alert.queues = queues;
156 return 0;
157
158 error:
159 for (i = 0; i < mls->count; i++) {
160 struct email_alertq *q = &queues[i];
161 struct check *check = &q->check;
162
163 free_check(check);
164 }
165 free(queues);
166 fail_no_queue:
167 return 1;
168}
169
170static int enqueue_one_email_alert(struct proxy *p, struct server *s,
171 struct email_alertq *q, const char *msg)
172{
173 struct email_alert *alert;
174 struct tcpcheck_rule *tcpcheck;
175 struct check *check = &q->check;
176
177 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
178 goto error;
179 LIST_INIT(&alert->list);
180 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
181 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
182 if (!alert->rules.list)
183 goto error;
184 LIST_INIT(alert->rules.list);
185 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
186 alert->srv = s;
187
188 if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
189 goto error;
190 memset(tcpcheck, 0, sizeof(*tcpcheck));
191 tcpcheck->action = TCPCHK_ACT_CONNECT;
192 tcpcheck->comment = NULL;
193
194 LIST_ADDQ(alert->rules.list, &tcpcheck->list);
195
196 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
197 goto error;
198
199 {
200 const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
201 if (!add_tcpcheck_send_strs(&alert->rules, strs))
202 goto error;
203 }
204
205 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
206 goto error;
207
208 {
209 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
210 if (!add_tcpcheck_send_strs(&alert->rules, strs))
211 goto error;
212 }
213
214 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
215 goto error;
216
217 {
218 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
219 if (!add_tcpcheck_send_strs(&alert->rules, strs))
220 goto error;
221 }
222
223 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
224 goto error;
225
226 {
227 const char * const strs[2] = { "DATA\r\n" };
228 if (!add_tcpcheck_send_strs(&alert->rules, strs))
229 goto error;
230 }
231
232 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
233 goto error;
234
235 {
236 struct tm tm;
237 char datestr[48];
238 const char * const strs[18] = {
239 "From: ", p->email_alert.from, "\r\n",
240 "To: ", p->email_alert.to, "\r\n",
241 "Date: ", datestr, "\r\n",
242 "Subject: [HAproxy Alert] ", msg, "\r\n",
243 "\r\n",
244 msg, "\r\n",
245 "\r\n",
246 ".\r\n",
247 NULL
248 };
249
250 get_localtime(date.tv_sec, &tm);
251
252 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
253 goto error;
254 }
255
256 if (!add_tcpcheck_send_strs(&alert->rules, strs))
257 goto error;
258 }
259
260 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
261 goto error;
262
263 {
264 const char * const strs[2] = { "QUIT\r\n" };
265 if (!add_tcpcheck_send_strs(&alert->rules, strs))
266 goto error;
267 }
268
269 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
270 goto error;
271
272 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
273 task_wakeup(check->task, TASK_WOKEN_MSG);
274 LIST_ADDQ(&q->email_alerts, &alert->list);
275 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
276 return 1;
277
278error:
279 email_alert_free(alert);
280 return 0;
281}
282
283static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
284{
285 int i;
286 struct mailer *mailer;
287
288 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
289 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
290 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
291 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
292 return;
293 }
294 }
295
296 return;
297}
298
299/*
300 * Send email alert if configured.
301 */
302void send_email_alert(struct server *s, int level, const char *format, ...)
303{
304 va_list argp;
305 char buf[1024];
306 int len;
307 struct proxy *p = s->proxy;
308
309 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
310 return;
311
312 va_start(argp, format);
313 len = vsnprintf(buf, sizeof(buf), format, argp);
314 va_end(argp);
315
316 if (len < 0 || len >= sizeof(buf)) {
317 ha_alert("Email alert [%s] could not format message\n", p->id);
318 return;
319 }
320
321 enqueue_email_alert(p, s, buf);
322}