diff options
Diffstat (limited to '')
-rw-r--r-- | src/mailers.c | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/src/mailers.c b/src/mailers.c new file mode 100644 index 0000000..05d5313 --- /dev/null +++ b/src/mailers.c @@ -0,0 +1,321 @@ +/* + * 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; + +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; + 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 (!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); +} |