mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-01-06 20:29:48 +00:00
5bed48fec8
Exposing a new hlua function, available from body or init contexts, that forcefully disables the sending of email alerts even if the mailers are defined in haproxy configuration. This will help for sending email directly from lua. (prevent legacy email sending from intefering with lua)
330 lines
8.2 KiB
C
330 lines
8.2 KiB
C
/*
|
|
* Mailer management.
|
|
*
|
|
* Copyright 2015 Horms Solutions Ltd, Simon Horman <horms@verge.net.au>
|
|
* Copyright 2020 Willy Tarreau <w@1wt.eu>
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <haproxy/action-t.h>
|
|
#include <haproxy/api.h>
|
|
#include <haproxy/check.h>
|
|
#include <haproxy/errors.h>
|
|
#include <haproxy/list.h>
|
|
#include <haproxy/mailers.h>
|
|
#include <haproxy/pool.h>
|
|
#include <haproxy/proxy-t.h>
|
|
#include <haproxy/server-t.h>
|
|
#include <haproxy/task.h>
|
|
#include <haproxy/tcpcheck.h>
|
|
#include <haproxy/thread.h>
|
|
#include <haproxy/time.h>
|
|
#include <haproxy/tools.h>
|
|
|
|
|
|
struct mailers *mailers = NULL;
|
|
|
|
/* Set to 1 to disable email sending through checks even if the
|
|
* mailers are configured to do so. (e.g.: disable from lua)
|
|
*/
|
|
int send_email_disabled = 0;
|
|
|
|
DECLARE_STATIC_POOL(pool_head_email_alert, "email_alert", sizeof(struct email_alert));
|
|
|
|
/****************************** Email alerts ******************************/
|
|
/* NOTE: It may be pertinent to use an applet to handle email alerts */
|
|
/* instead of a tcp-check ruleset */
|
|
/**************************************************************************/
|
|
void email_alert_free(struct email_alert *alert)
|
|
{
|
|
struct tcpcheck_rule *rule, *back;
|
|
|
|
if (!alert)
|
|
return;
|
|
|
|
if (alert->rules.list) {
|
|
list_for_each_entry_safe(rule, back, alert->rules.list, list) {
|
|
LIST_DELETE(&rule->list);
|
|
free_tcpcheck(rule, 1);
|
|
}
|
|
free_tcpcheck_vars(&alert->rules.preset_vars);
|
|
ha_free(&alert->rules.list);
|
|
}
|
|
pool_free(pool_head_email_alert, alert);
|
|
}
|
|
|
|
static struct task *process_email_alert(struct task *t, void *context, unsigned int state)
|
|
{
|
|
struct check *check = context;
|
|
struct email_alertq *q;
|
|
struct email_alert *alert;
|
|
|
|
q = container_of(check, typeof(*q), check);
|
|
|
|
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
while (1) {
|
|
if (!(check->state & CHK_ST_ENABLED)) {
|
|
if (LIST_ISEMPTY(&q->email_alerts)) {
|
|
/* All alerts processed, queue the task */
|
|
t->expire = TICK_ETERNITY;
|
|
task_queue(t);
|
|
goto end;
|
|
}
|
|
|
|
alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
|
|
LIST_DELETE(&alert->list);
|
|
t->expire = now_ms;
|
|
check->tcpcheck_rules = &alert->rules;
|
|
check->status = HCHK_STATUS_INI;
|
|
check->state |= CHK_ST_ENABLED;
|
|
}
|
|
|
|
process_chk(t, context, state);
|
|
if (check->state & CHK_ST_INPROGRESS)
|
|
break;
|
|
|
|
alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
|
|
email_alert_free(alert);
|
|
check->tcpcheck_rules = NULL;
|
|
check->server = NULL;
|
|
check->state &= ~CHK_ST_ENABLED;
|
|
}
|
|
end:
|
|
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
return t;
|
|
}
|
|
|
|
/* Initializes mailer alerts for the proxy <p> using <mls> parameters.
|
|
*
|
|
* The function returns 1 in success case, otherwise, it returns 0 and err is
|
|
* filled.
|
|
*/
|
|
int init_email_alert(struct mailers *mls, struct proxy *p, char **err)
|
|
{
|
|
struct mailer *mailer;
|
|
struct email_alertq *queues;
|
|
const char *err_str;
|
|
int i = 0;
|
|
|
|
if ((queues = calloc(mls->count, sizeof(*queues))) == NULL) {
|
|
memprintf(err, "out of memory while allocating mailer alerts queues");
|
|
goto fail_no_queue;
|
|
}
|
|
|
|
for (mailer = mls->mailer_list; mailer; i++, mailer = mailer->next) {
|
|
struct email_alertq *q = &queues[i];
|
|
struct check *check = &q->check;
|
|
struct task *t;
|
|
|
|
LIST_INIT(&q->email_alerts);
|
|
HA_SPIN_INIT(&q->lock);
|
|
check->obj_type = OBJ_TYPE_CHECK;
|
|
check->inter = mls->timeout.mail;
|
|
check->rise = DEF_AGENT_RISETIME;
|
|
check->proxy = p;
|
|
check->fall = DEF_AGENT_FALLTIME;
|
|
if ((err_str = init_check(check, PR_O2_TCPCHK_CHK))) {
|
|
memprintf(err, "%s", err_str);
|
|
goto error;
|
|
}
|
|
|
|
check->xprt = mailer->xprt;
|
|
check->addr = mailer->addr;
|
|
check->port = get_host_port(&mailer->addr);
|
|
|
|
if ((t = task_new_anywhere()) == NULL) {
|
|
memprintf(err, "out of memory while allocating mailer alerts task");
|
|
goto error;
|
|
}
|
|
|
|
check->task = t;
|
|
t->process = process_email_alert;
|
|
t->context = check;
|
|
|
|
/* check this in one ms */
|
|
t->expire = TICK_ETERNITY;
|
|
check->start = now_ns;
|
|
task_queue(t);
|
|
}
|
|
|
|
mls->users++;
|
|
free(p->email_alert.mailers.name);
|
|
p->email_alert.mailers.m = mls;
|
|
p->email_alert.queues = queues;
|
|
return 0;
|
|
|
|
error:
|
|
for (i = 0; i < mls->count; i++) {
|
|
struct email_alertq *q = &queues[i];
|
|
struct check *check = &q->check;
|
|
|
|
free_check(check);
|
|
}
|
|
free(queues);
|
|
fail_no_queue:
|
|
return 1;
|
|
}
|
|
|
|
static int enqueue_one_email_alert(struct proxy *p, struct server *s,
|
|
struct email_alertq *q, const char *msg)
|
|
{
|
|
struct email_alert *alert;
|
|
struct tcpcheck_rule *tcpcheck;
|
|
struct check *check = &q->check;
|
|
|
|
if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
|
|
goto error;
|
|
LIST_INIT(&alert->list);
|
|
alert->rules.flags = TCPCHK_RULES_TCP_CHK;
|
|
alert->rules.list = calloc(1, sizeof(*alert->rules.list));
|
|
if (!alert->rules.list)
|
|
goto error;
|
|
LIST_INIT(alert->rules.list);
|
|
LIST_INIT(&alert->rules.preset_vars); /* unused for email alerts */
|
|
alert->srv = s;
|
|
|
|
if ((tcpcheck = pool_zalloc(pool_head_tcpcheck_rule)) == NULL)
|
|
goto error;
|
|
tcpcheck->action = TCPCHK_ACT_CONNECT;
|
|
tcpcheck->comment = NULL;
|
|
|
|
LIST_APPEND(alert->rules.list, &tcpcheck->list);
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[4] = { "HELO ", p->email_alert.myhostname, "\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[2] = { "DATA\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
|
|
goto error;
|
|
|
|
{
|
|
struct tm tm;
|
|
char datestr[48];
|
|
const char * const strs[18] = {
|
|
"From: ", p->email_alert.from, "\r\n",
|
|
"To: ", p->email_alert.to, "\r\n",
|
|
"Date: ", datestr, "\r\n",
|
|
"Subject: [HAProxy Alert] ", msg, "\r\n",
|
|
"\r\n",
|
|
msg, "\r\n",
|
|
"\r\n",
|
|
".\r\n",
|
|
NULL
|
|
};
|
|
|
|
get_localtime(date.tv_sec, &tm);
|
|
|
|
if (strftime(datestr, sizeof(datestr), "%a, %d %b %Y %T %z (%Z)", &tm) == 0) {
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
|
|
goto error;
|
|
|
|
{
|
|
const char * const strs[2] = { "QUIT\r\n" };
|
|
if (!add_tcpcheck_send_strs(&alert->rules, strs))
|
|
goto error;
|
|
}
|
|
|
|
if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
|
|
goto error;
|
|
|
|
HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
task_wakeup(check->task, TASK_WOKEN_MSG);
|
|
LIST_APPEND(&q->email_alerts, &alert->list);
|
|
HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
|
|
return 1;
|
|
|
|
error:
|
|
email_alert_free(alert);
|
|
return 0;
|
|
}
|
|
|
|
static void enqueue_email_alert(struct proxy *p, struct server *s, const char *msg)
|
|
{
|
|
int i;
|
|
struct mailer *mailer;
|
|
|
|
for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;
|
|
i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {
|
|
if (!enqueue_one_email_alert(p, s, &p->email_alert.queues[i], msg)) {
|
|
ha_alert("Email alert [%s] could not be enqueued: out of memory\n", p->id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Send email alert if configured.
|
|
*/
|
|
void send_email_alert(struct server *s, int level, const char *format, ...)
|
|
{
|
|
va_list argp;
|
|
char buf[1024];
|
|
int len;
|
|
struct proxy *p = s->proxy;
|
|
|
|
if (send_email_disabled)
|
|
return;
|
|
|
|
if (!p->email_alert.mailers.m || level > p->email_alert.level || format == NULL)
|
|
return;
|
|
|
|
va_start(argp, format);
|
|
len = vsnprintf(buf, sizeof(buf), format, argp);
|
|
va_end(argp);
|
|
|
|
if (len < 0 || len >= sizeof(buf)) {
|
|
ha_alert("Email alert [%s] could not format message\n", p->id);
|
|
return;
|
|
}
|
|
|
|
enqueue_email_alert(p, s, buf);
|
|
}
|