mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-05 22:56:57 +02:00
This puts an end to the occasional confusion between the "now" date that is internal, monotonic and not synchronized with the system's date, and "date" which is the system's date and not necessarily monotonic. Variable "now" was removed and replaced with a 64-bit integer "now_ns" which is a counter of nanoseconds. It wraps every 585 years, so if all goes well (i.e. if humanity does not need haproxy anymore in 500 years), it will just never wrap. This implies that now_ns is never nul and that the zero value can reliably be used as "not set yet" for a timestamp if needed. This will also simplify date checks where it becomes possible again to do "date1<date2". All occurrences of "tv_to_ns(&now)" were simply replaced by "now_ns". Due to the intricacies between now, global_now and now_offset, all 3 had to be turned to nanoseconds at once. It's not a problem since all of them were solely used in 3 functions in clock.c, but they make the patch look bigger than it really is. The clock_update_local_date() and clock_update_global_date() functions are now much simpler as there's no need anymore to perform conversions nor to round the timeval up or down. The wrapping continues to happen by presetting the internal offset in the short future so that the 32-bit now_ms continues to wrap 20 seconds after boot. The start_time used to calculate uptime can still be turned to nanoseconds now. One interrogation concerns global_now_ms which is used only for the freq counters. It's unclear whether there's more value in using two variables that need to be synchronized sequentially like today or to just use global_now_ns divided by 1 million. Both approaches will work equally well on modern systems, the difference might come from smaller ones. Better not change anyhting for now. One benefit of the new approach is that we now have an internal date with a resolution of the nanosecond and the precision of the microsecond, which can be useful to extend some measurements given that timestamps also have this resolution.
322 lines
8.0 KiB
C
322 lines
8.0 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;
|
|
|
|
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 (!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);
|
|
}
|