2015-01-31

Signed-off-by: Simon Horman <horms@verge.net.au>

---

include/proto/checks.h | 2 +

include/types/checks.h | 2 +-

include/types/proxy.h | 18 ++-

src/cfgparse.c | 26 ++--

src/checks.c | 321 +++++++++++++++++++++++++++++++++++++++++++++++++

src/server.c | 1 +

6 files changed, 356 insertions(+), 14 deletions(-)

diff --git a/include/proto/checks.h b/include/proto/checks.h

index 24dec79..b4faed0 100644

--- a/include/proto/checks.h

+++ b/include/proto/checks.h

@@ -47,6 +47,8 @@ static inline void health_adjust(struct server *s, short status)

const char *init_check(struct check *check, int type);

void free_check(struct check *check);

+void send_email_alert(struct server *s, const char *format, ...)

+ __attribute__ ((format(printf, 2, 3)));

#endif /* _PROTO_CHECKS_H */

/*

diff --git a/include/types/checks.h b/include/types/checks.h

index 8162a06..4b35d30 100644

--- a/include/types/checks.h

+++ b/include/types/checks.h

@@ -181,7 +181,7 @@ struct check {

char **envp; /* the environment to use if running a process-based check */

struct pid_list *curpid; /* entry in pid_list used for current process-based test, or -1 if not in test */

struct protocol *proto; /* server address protocol for health checks */

- struct sockaddr_storage addr; /* the address to check, if different from <addr> */

+ struct sockaddr_storage addr; /* the address to check */

};

struct check_status {

diff --git a/include/types/proxy.h b/include/types/proxy.h

index 72d1024..230b804 100644

--- a/include/types/proxy.h

+++ b/include/types/proxy.h

@@ -208,6 +208,19 @@ struct error_snapshot {

char buf[BUFSIZE]; /* copy of the beginning of the message */

};

+struct email_alert {

+ struct list list;

+ struct list tcpcheck_rules;

+};

+

+struct email_alertq {

+ struct list email_alerts;

+ struct check check; /* Email alerts are implemented using existing check

+ * code even though they are not checks. This structure

+ * is as a parameter to the check code.

+ * Each check corresponds to a mailer */

+};

+

struct proxy {

enum obj_type obj_type; /* object type == OBJ_TYPE_PROXY */

enum pr_state state; /* proxy state, one of PR_* */

@@ -386,9 +399,10 @@ struct proxy {

struct mailers *m; /* Mailer to send email alerts via */

char *name;

} mailers;

- char *from; /* Address to send email allerts from */

- char *to; /* Address(es) to send email allerts to */

+ char *from; /* Address to send email alerts from */

+ char *to; /* Address(es) to send email alerts to */

char *myhostname; /* Identity to use in HELO command sent to mailer */

+ struct email_alertq *queues; /* per-mailer alerts queues */

} email_alert;

};

diff --git a/src/cfgparse.c b/src/cfgparse.c

index de94074..3af0449 100644

--- a/src/cfgparse.c

+++ b/src/cfgparse.c

@@ -2019,8 +2019,8 @@ int cfg_parse_mailers(const char *file, int linenum, char **args, int kwm)

}

proto = protocol_by_family(sk->ss_family);

- if (!proto || !proto->connect) {

- Alert("parsing [%s:%d] : '%s %s' : connect() not supported for this address family.\n",

+ if (!proto || !proto->connect || proto->sock_prot != IPPROTO_TCP) {

+ Alert("parsing [%s:%d] : '%s %s' : TCP not supported for this address family.\n",

file, linenum, args[0], args[1]);

err_code |= ERR_ALERT | ERR_FATAL;

goto out;

@@ -6607,15 +6607,19 @@ int check_config_validity()

}

}

- if (

- (curproxy->email_alert.mailers.name || curproxy->email_alert.from || curproxy->email_alert.myhostname || curproxy->email_alert.to) &&

- !(curproxy->email_alert.mailers.name && curproxy->email_alert.from && curproxy->email_alert.to)) {

- Warning("config : 'email-alert' will be ignored for %s '%s' (the presence any of "

- "'email-alert from', 'email-alert mailer', 'email-alert hostname' or 'email-alert to' requrires each of"

- "'email-alert from', 'email-alert mailer' and 'email-alert to' to be present).\n",

- proxy_type_str(curproxy), curproxy->id);

- err_code |= ERR_WARN;

- free_email_alert(curproxy);

+ if ((curproxy->email_alert.mailers.name || curproxy->email_alert.from ||

+ curproxy->email_alert.myhostname || curproxy->email_alert.to)) {

+ if (!(curproxy->email_alert.mailers.name && curproxy->email_alert.from && curproxy->email_alert.to)) {

+ Warning("config : 'email-alert' will be ignored for %s '%s' (the presence any of "

+ "'email-alert from', 'email-alert mailer', 'email-alert hostname' or 'email-alert to' "

+ "requrires each of 'email-alert from', 'email-alert mailer' and 'email-alert' "

+ "to be present).\n",

+ proxy_type_str(curproxy), curproxy->id);

+ err_code |= ERR_WARN;

+ free_email_alert(curproxy);

+ }

+ if (!curproxy->email_alert.myhostname)

+ curproxy->email_alert.myhostname = hostname;

}

if (curproxy->check_command) {

diff --git a/src/checks.c b/src/checks.c

index 0f99d47..ea167de 100644

--- a/src/checks.c

+++ b/src/checks.c

@@ -16,6 +16,7 @@

#include <errno.h>

#include <fcntl.h>

#include <signal.h>

+#include <stdarg.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

@@ -36,6 +37,7 @@

#include <common/time.h>

#include <types/global.h>

+#include <types/mailers.h>

#ifdef USE_OPENSSL

#include <types/ssl_sock.h>

@@ -315,6 +317,7 @@ static void set_server_check_status(struct check *check, short status, const cha

Warning("%s.\n", trash.str);

send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.str);

+ send_email_alert(s, "%s", trash.str);

}

}

@@ -2814,6 +2817,324 @@ void free_check(struct check *check)

free(check->conn);

}

+void email_alert_free(struct email_alert *alert)

+{

+ struct tcpcheck_rule *rule, *back;

+

+ if (!alert)

+ return;

+

+ list_for_each_entry_safe(rule, back, &alert->tcpcheck_rules, list)

+ free(rule);

+ free(alert);

+}

+

+static struct task *process_email_alert(struct task *t)

+{

+ struct check *check = t->context;

+ struct email_alertq *q;

+

+ q = container_of(check, typeof(*q), check);

+

+ if (!(check->state & CHK_ST_ENABLED)) {

+ if (LIST_ISEMPTY(&q->email_alerts)) {

+ /* All alerts processed, delete check */

+ task_delete(t);

+ task_free(t);

+ check->task = NULL;

+ return NULL;

+ } else {

+ struct email_alert *alert;

+

+ alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);

+ check->tcpcheck_rules = &alert->tcpcheck_rules;

+ LIST_DEL(&alert->list);

+

+ check->state |= CHK_ST_ENABLED;

+ }

+

+ }

+

+ process_chk(t);

+

+ if (!(check->state & CHK_ST_INPROGRESS) && check->tcpcheck_rules) {

+ struct email_alert *alert;

+

+ alert = container_of(check->tcpcheck_rules, typeof(*alert), tcpcheck_rules);

+ email_alert_free(alert);

+

+ check->tcpcheck_rules = NULL;

+ check->state &= ~CHK_ST_ENABLED;

+ }

+ return t;

+}

+

+static int init_email_alert_checks(struct server *s)

+{

+ int i;

+ struct mailer *mailer;

+ const char *err_str;

+ struct proxy *p = s->proxy;

+

+ if (p->email_alert.queues)

+ /* Already initialised, nothing to do */

+ return 1;

+

+ p->email_alert.queues = calloc(p->email_alert.mailers.m->count, sizeof *p->email_alert.queues);

+ if (!p->email_alert.queues) {

+ err_str = "out of memory while allocating checks array";

+ goto error_alert;

+ }

+

+ for (i = 0, mailer = p->email_alert.mailers.m->mailer_list;

+ i < p->email_alert.mailers.m->count; i++, mailer = mailer->next) {

+ struct email_alertq *q = &p->email_alert.queues;

+ struct check *check = &q->check;

+

+

+ LIST_INIT(&q->email_alerts);

+

+ check->inter = DEF_CHKINTR; /* XXX: Would like to Skip to the next alert, if any, ASAP.

+ * But need enough time so that timeouts don't occur

+ * during tcp check procssing. For now just us an arbitrary default. */

+ check->rise = DEF_AGENT_RISETIME;

+ check->fall = DEF_AGENT_FALLTIME;

+ err_str = init_check(check, PR_O2_TCPCHK_CHK);

+ if (err_str) {

+ goto error_free;

+ }

+

+ check->xprt = mailer->xprt;

+ if (!get_host_port(&mailer->addr))

+ /* Default to submission port */

+ check->port = 587;

+ check->proto = mailer->proto;

+ check->addr = mailer->addr;

+ check->server = s;

+ }

+

+ return 1;

+

+error_free:

+ while (i-- > 1)

+ task_free(p->email_alert.queues.check.task);

+ free(p->email_alert.queues);

+ p->email_alert.queues = NULL;

+error_alert:

+ Alert("Email alert [%s] could not be initialised: %s\n", p->id, err_str);

+ return 0;

+}

+

+

+static int add_tcpcheck_expect_str(struct list *list, const char *str)

+{

+ struct tcpcheck_rule *tcpcheck;

+

+ tcpcheck = calloc(1, sizeof *tcpcheck);

+ if (!tcpcheck)

+ return 0;

+

+ tcpcheck->action = TCPCHK_ACT_EXPECT;

+ tcpcheck->string = strdup(str);

+ if (!tcpcheck->string) {

+ free(tcpcheck);

+ return 0;

+ }

+

+ LIST_ADDQ(list, &tcpcheck->list);

+ return 1;

+}

+

+static int add_tcpcheck_send_strs(struct list *list, const char * const *strs)

+{

+ struct tcpcheck_rule *tcpcheck;

+ int i;

+

+ tcpcheck = calloc(1, sizeof *tcpcheck);

+ if (!tcpcheck)

+ return 0;

+

+ tcpcheck->action = TCPCHK_ACT_SEND;

+

+ tcpcheck->string_len = 0;

+ for (i = 0; strs; i++)

+ tcpcheck->string_len += strlen(strs);

+

+ tcpcheck->string = malloc(tcpcheck->string_len + 1);

+ if (!tcpcheck->string) {

+ free(tcpcheck);

+ return 0;

+ }

+ tcpcheck->string[0] = '\0';

+

+ for (i = 0; strs; i++)

+ strcat(tcpcheck->string, strs);

+

+ LIST_ADDQ(list, &tcpcheck->list);

+ return 1;

+}

+

+static int enqueue_one_email_alert(struct email_alertq *q, const char *msg)

+{

+ struct email_alert *alert = NULL;

+ struct tcpcheck_rule *tcpcheck;

+ struct check *check = &q->check;

+ struct proxy *p = check->server->proxy;

+

+ alert = calloc(1, sizeof *alert);

+ if (!alert) {

+ goto error;

+ }

+ LIST_INIT(&alert->tcpcheck_rules);

+

+ tcpcheck = calloc(1, sizeof *tcpcheck);

+ if (!tcpcheck)

+ goto error;

+ tcpcheck->action = TCPCHK_ACT_CONNECT;

+ LIST_ADDQ(&alert->tcpcheck_rules, &tcpcheck->list);

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "220 "))

+ goto error;

+

+ {

+ const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };

+ if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))

+ goto error;

+ }

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))

+ goto error;

+

+ {

+ const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };

+ if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))

+ goto error;

+ }

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))

+ goto error;

+

+ {

+ const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };

+ if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))

+ goto error;

+ }

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))

+ goto error;

+

+ {

+ const char * const strs[2] = { "DATA\r\n" };

+ if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))

+ goto error;

+ }

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "354 "))

+ goto error;

+

+ {

+ struct tm tm;

+ char datestr[48];

+ const char * const strs[18] = {

+ "From: ", p->email_alert.from, "\n",

+ "To: ", p->email_alert.to, "\n",

+ "Date: ", datestr, "\n",

+ "Subject: [HAproxy Alert] ", msg, "\n",

+ "\n",

+ msg, "\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->tcpcheck_rules, strs))

+ goto error;

+ }

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))

+ goto error;

+

+ {

+ const char * const strs[2] = { "QUIT\r\n" };

+ if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))

+ goto error;

+ }

+

+ if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "221 "))

+ goto error;

+

+ if (!check->task) {

+ struct task *t;

+

+ if ((t = task_new()) == NULL)

+ goto error;

+

+ check->task = t;

+ t->process = process_email_alert;

+ t->context = check;

+

+ /* check this in one ms */

+ t->expire = tick_add(now_ms, MS_TO_TICKS(1));

+ check->start = now;

+ task_queue(t);

+ }

+

+ LIST_ADDQ(&q->email_alerts, &alert->list);

+

+ return 1;

+

+error:

+ email_alert_free(alert);

+ return 0;

+}

+

+static void enqueue_email_alert(struct proxy *p, 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->email_alert.queues, msg)) {

+ 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, const char *format, ...)

+{

+ va_list argp;

+ char buf[1024];

+ int len;

+ struct proxy *p = s->proxy;

+

+ if (!p->email_alert.mailers.m || format == NULL || !init_email_alert_checks(s))

+ return;

+

+ va_start(argp, format);

+ len = vsnprintf(buf, sizeof(buf), format, argp);

+ va_end(argp);

+

+ if (len < 0) {

+ Alert("Email alert [%s] could format message\n", p->id);

+ return;

+ }

+

+ enqueue_email_alert(p, buf);

+}

+

/*

* Local variables:

diff --git a/src/server.c b/src/server.c

index 9a3bf23..6acd332 100644

--- a/src/server.c

+++ b/src/server.c

@@ -255,6 +255,7 @@ void srv_set_stopped(struct server *s, const char *reason)

"%sServer %s/%s is DOWN", s->flags & SRV_F_BACKUP ? "Backup " : "",

s->proxy->id, s->id);

+ send_email_alert(s, "%s.", trash.str);

srv_append_status(&trash, s, reason, xferred, 0);

Warning("%s.\n", trash.str);

--

2.1.4

Show more