blob: c09e73c3507746832e6fcf957216a269e027245d [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
Aurelien DARRAGON5bed48f2023-04-21 17:32:46 +020034/* Set to 1 to disable email sending through checks even if the
35 * mailers are configured to do so. (e.g.: disable from lua)
36 */
37int send_email_disabled = 0;
38
Willy Tarreaucee013e2020-06-05 11:40:38 +020039DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
40
41/****************************** Email alerts ******************************/
42/* NOTE: It may be pertinent to use an applet to handle email alerts */
43/* instead of a tcp-check ruleset */
44/**************************************************************************/
45void email_alert_free(struct email_alert *alert)
46{
47 struct tcpcheck_rule *rule, *back;
48
49 if (!alert)
50 return;
51
52 if (alert->rules.list) {
53 list_for_each_entry_safe(rule, back, alert->rules.list, list) {
Willy Tarreau2b718102021-04-21 07:32:39 +020054 LIST_DELETE(&rule->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +020055 free_tcpcheck(rule, 1);
56 }
57 free_tcpcheck_vars(&alert->rules.preset_vars);
Willy Tarreau61cfdf42021-02-20 10:46:51 +010058 ha_free(&alert->rules.list);
Willy Tarreaucee013e2020-06-05 11:40:38 +020059 }
60 pool_free(pool_head_email_alert, alert);
61}
62
Willy Tarreau144f84a2021-03-02 16:09:26 +010063static struct task *process_email_alert(struct task *t, void *context, unsigned int state)
Willy Tarreaucee013e2020-06-05 11:40:38 +020064{
65 struct check *check = context;
66 struct email_alertq *q;
67 struct email_alert *alert;
68
69 q = container_of(check, typeof(*q), check);
70
71 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
72 while (1) {
73 if (!(check->state & CHK_ST_ENABLED)) {
74 if (LIST_ISEMPTY(&q->email_alerts)) {
75 /* All alerts processed, queue the task */
76 t->expire = TICK_ETERNITY;
77 task_queue(t);
78 goto end;
79 }
80
81 alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
Willy Tarreau2b718102021-04-21 07:32:39 +020082 LIST_DELETE(&alert->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +020083 t->expire = now_ms;
84 check->tcpcheck_rules = &alert->rules;
85 check->status = HCHK_STATUS_INI;
86 check->state |= CHK_ST_ENABLED;
87 }
88
89 process_chk(t, context, state);
90 if (check->state & CHK_ST_INPROGRESS)
91 break;
92
93 alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
94 email_alert_free(alert);
95 check->tcpcheck_rules = NULL;
96 check->server = NULL;
97 check->state &= ~CHK_ST_ENABLED;
98 }
99 end:
100 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
101 return t;
102}
103
104/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
105 *
106 * The function returns 1 in success case, otherwise, it returns 0 and err is
107 * filled.
108 */
109int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
110{
111 struct mailer *mailer;
112 struct email_alertq *queues;
113 const char *err_str;
114 int i = 0;
115
116 if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
117 memprintf(err, "out of memory while allocating mailer alerts queues");
118 goto fail_no_queue;
119 }
120
121 for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
122 struct email_alertq *q = &queues[i];
123 struct check *check = &q->check;
124 struct task *t;
125
126 LIST_INIT(&q->email_alerts);
127 HA_SPIN_INIT(&q->lock);
Christopher Faulet58e35012022-06-08 09:17:14 +0200128 check->obj_type = OBJ_TYPE_CHECK;
Willy Tarreaucee013e2020-06-05 11:40:38 +0200129 check->inter = mls->timeout.mail;
130 check->rise = DEF_AGENT_RISETIME;
131 check->proxy = p;
132 check->fall = DEF_AGENT_FALLTIME;
133 if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
134 memprintf(err, "%s", err_str);
135 goto error;
136 }
137
138 check->xprt = mailer->xprt;
139 check->addr = mailer->addr;
140 check->port = get_host_port(&mailer->addr);
141
Willy Tarreaubeeabf52021-10-01 18:23:30 +0200142 if ((t = task_new_anywhere()) == NULL) {
Willy Tarreaucee013e2020-06-05 11:40:38 +0200143 memprintf(err, "out of memory while allocating mailer alerts task");
144 goto error;
145 }
146
147 check->task = t;
148 t->process = process_email_alert;
149 t->context = check;
150
151 /* check this in one ms */
152 t->expire = TICK_ETERNITY;
Willy Tarreau69530f52023-04-28 09:16:15 +0200153 check->start = now_ns;
Willy Tarreaucee013e2020-06-05 11:40:38 +0200154 task_queue(t);
155 }
156
157 mls->users++;
158 free(p->email_alert.mailers.name);
159 p->email_alert.mailers.m = mls;
160 p->email_alert.queues = queues;
161 return 0;
162
163 error:
164 for (i = 0; i < mls->count; i++) {
165 struct email_alertq *q = &queues[i];
166 struct check *check = &q->check;
167
168 free_check(check);
169 }
170 free(queues);
171 fail_no_queue:
172 return 1;
173}
174
175static int enqueue_one_email_alert(struct proxy *p, struct server *s,
176 struct email_alertq *q, const char *msg)
177{
178 struct email_alert *alert;
179 struct tcpcheck_rule *tcpcheck;
180 struct check *check = &q->check;
181
182 if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
183 goto error;
184 LIST_INIT(&alert->list);
185 alert->rules.flags = TCPCHK_RULES_TCP_CHK;
186 alert->rules.list = calloc(1, sizeof(*alert->rules.list));
187 if (!alert->rules.list)
188 goto error;
189 LIST_INIT(alert->rules.list);
190 LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
191 alert->srv = s;
192
Willy Tarreau3ab0a0b2021-03-22 21:07:52 +0100193 if ((tcpcheck = pool_zalloc(pool_head_tcpcheck_rule)) == NULL)
Willy Tarreaucee013e2020-06-05 11:40:38 +0200194 goto error;
Willy Tarreaucee013e2020-06-05 11:40:38 +0200195 tcpcheck->action = TCPCHK_ACT_CONNECT;
196 tcpcheck->comment = NULL;
197
Willy Tarreau2b718102021-04-21 07:32:39 +0200198 LIST_APPEND(alert->rules.list, &tcpcheck->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +0200199
200 if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
201 goto error;
202
203 {
Lukas Tribus1a16e4e2022-02-17 15:40:51 +0100204 const char * const strs[4] = { "HELO ", p->email_alert.myhostname, "\r\n" };
Willy Tarreaucee013e2020-06-05 11:40:38 +0200205 if (!add_tcpcheck_send_strs(&alert->rules, strs))
206 goto error;
207 }
208
209 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
210 goto error;
211
212 {
213 const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
214 if (!add_tcpcheck_send_strs(&alert->rules, strs))
215 goto error;
216 }
217
218 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
219 goto error;
220
221 {
222 const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
223 if (!add_tcpcheck_send_strs(&alert->rules, strs))
224 goto error;
225 }
226
227 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
228 goto error;
229
230 {
231 const char * const strs[2] = { "DATA\r\n" };
232 if (!add_tcpcheck_send_strs(&alert->rules, strs))
233 goto error;
234 }
235
236 if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
237 goto error;
238
239 {
240 struct tm tm;
241 char datestr[48];
242 const char * const strs[18] = {
243 "From: ", p->email_alert.from, "\r\n",
244 "To: ", p->email_alert.to, "\r\n",
245 "Date: ", datestr, "\r\n",
Willy Tarreau64975cf2021-05-09 06:45:16 +0200246 "Subject: [HAProxy Alert] ", msg, "\r\n",
Willy Tarreaucee013e2020-06-05 11:40:38 +0200247 "\r\n",
248 msg, "\r\n",
249 "\r\n",
250 ".\r\n",
251 NULL
252 };
253
254 get_localtime(date.tv_sec, &tm);
255
256 if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
257 goto error;
258 }
259
260 if (!add_tcpcheck_send_strs(&alert->rules, strs))
261 goto error;
262 }
263
264 if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
265 goto error;
266
267 {
268 const char * const strs[2] = { "QUIT\r\n" };
269 if (!add_tcpcheck_send_strs(&alert->rules, strs))
270 goto error;
271 }
272
273 if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
274 goto error;
275
276 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
277 task_wakeup(check->task, TASK_WOKEN_MSG);
Willy Tarreau2b718102021-04-21 07:32:39 +0200278 LIST_APPEND(&q->email_alerts, &alert->list);
Willy Tarreaucee013e2020-06-05 11:40:38 +0200279 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
280 return 1;
281
282error:
283 email_alert_free(alert);
284 return 0;
285}
286
287static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
288{
289 int i;
290 struct mailer *mailer;
291
292 for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
293 i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
294 if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
295 ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
296 return;
297 }
298 }
299
300 return;
301}
302
303/*
304 * Send email alert if configured.
305 */
306void send_email_alert(struct server *s, int level, const char *format, ...)
307{
308 va_list argp;
309 char buf[1024];
310 int len;
311 struct proxy *p = s->proxy;
312
Aurelien DARRAGON5bed48f2023-04-21 17:32:46 +0200313 if (send_email_disabled)
314 return;
315
Willy Tarreaucee013e2020-06-05 11:40:38 +0200316 if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
317 return;
318
319 va_start(argp, format);
320 len = vsnprintf(buf, sizeof(buf), format, argp);
321 va_end(argp);
322
323 if (len < 0 || len >= sizeof(buf)) {
324 ha_alert("Email alert [%s] could not format message\n", p->id);
325 return;
326 }
327
328 enqueue_email_alert(p, s, buf);
329}