| /* |
| * 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); |
| } |