blob: b508f8579b9a112a6089b4023489859b4e84f41a [file] [log] [blame]
Willy Tarreaubaaee002006-06-26 02:48:02 +02001/*
2 * Health-checks functions.
3 *
Willy Tarreau26c25062009-03-08 09:38:41 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreaubaaee002006-06-26 02:48:02 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
Willy Tarreaub8816082008-01-18 12:18:15 +010014#include <assert.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020015#include <ctype.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020016#include <errno.h>
17#include <fcntl.h>
Willy Tarreau9b39dc52014-07-08 00:54:10 +020018#include <signal.h>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090019#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020020#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020021#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020022#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020023#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020024#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010025#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020026#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040027#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090028#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020029#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020030#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020031#include <arpa/inet.h>
32
Willy Tarreau122eba92020-06-04 10:15:32 +020033#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020034#include <haproxy/api.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020035#include <haproxy/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +020036#include <haproxy/check.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020037#include <haproxy/chunk.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020038#include <haproxy/dns.h>
Willy Tarreau2741c8c2020-06-02 11:28:02 +020039#include <haproxy/istbuf.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020040#include <haproxy/list.h>
Willy Tarreaucee013e2020-06-05 11:40:38 +020041#include <haproxy/mailers.h>
Willy Tarreaua55c4542020-06-04 22:59:39 +020042#include <haproxy/queue.h>
Willy Tarreau7cd8b6e2020-06-02 17:32:26 +020043#include <haproxy/regex.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020044#include <haproxy/tools.h>
Willy Tarreau92b4f132020-06-01 11:05:15 +020045#include <haproxy/time.h>
Willy Tarreau3f567e42020-05-28 15:29:19 +020046#include <haproxy/thread.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020047#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020048#include <haproxy/http_htx.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020049#include <haproxy/h1.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020050#include <haproxy/htx.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020051#include <haproxy/log.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020052#include <haproxy/proxy.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020053#include <haproxy/server.h>
Willy Tarreau3727a8a2020-06-04 17:37:26 +020054#include <haproxy/signal.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020055#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020056#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020057#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020058#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020059#include <haproxy/tcpcheck.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020060#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020061
Willy Tarreauf268ee82020-06-04 17:05:57 +020062#include <haproxy/global.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020063
Willy Tarreauaa74c4e2020-06-04 10:19:23 +020064#include <haproxy/arg.h>
Willy Tarreau0f6ffd62020-06-03 19:33:00 +020065#include <haproxy/fd.h>
Willy Tarreaufc8f6a82020-06-03 19:20:59 +020066#include <haproxy/port_range.h>
Willy Tarreaufc774542020-06-04 17:31:04 +020067#include <haproxy/proto_tcp.h>
Willy Tarreau2dd7c352020-06-03 15:26:55 +020068#include <haproxy/protocol.h>
Willy Tarreau832ce652020-06-04 08:36:05 +020069#include <haproxy/proto_udp.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020070#include <haproxy/sample.h>
Olivier Houchard9130a962017-10-17 17:33:43 +020071
Christopher Faulet61cc8522020-04-20 14:54:42 +020072static int wake_srv_chk(struct conn_stream *cs);
73struct data_cb check_conn_cb = {
74 .wake = wake_srv_chk,
75 .name = "CHCK",
76};
Christopher Fauletd7e63962020-04-17 20:15:59 +020077
Christopher Faulet5d503fc2020-03-30 20:34:34 +020078
Gaetan Rivet05d692d2020-02-14 17:42:54 +010079/* Dummy frontend used to create all checks sessions. */
Willy Tarreau51cd5952020-06-05 12:25:38 +020080struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020081
Christopher Faulet61cc8522020-04-20 14:54:42 +020082/**************************************************************************/
83/************************ Handle check results ****************************/
84/**************************************************************************/
85struct check_status {
86 short result; /* one of SRV_CHK_* */
87 char *info; /* human readable short info */
88 char *desc; /* long description */
89};
90
91struct analyze_status {
92 char *desc; /* description */
93 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
94};
95
Simon Horman63a4a822012-03-19 07:24:41 +090096static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010097 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
98 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +020099 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200100
Willy Tarreau23964182014-05-20 20:56:30 +0200101 /* Below we have finished checks */
102 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100103 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100104
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100105 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200106
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100107 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
108 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
109 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200110
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100111 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
112 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
113 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200114
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100115 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
116 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200117
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200118 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200119
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100120 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
121 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
122 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900123
124 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
125 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200126 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200127};
128
Simon Horman63a4a822012-03-19 07:24:41 +0900129static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100130 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
131
132 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
133 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
134
135 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
136 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
137 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
138 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
139
140 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
141 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
142 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
143};
144
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100145/* checks if <err> is a real error for errno or one that can be ignored, and
146 * return 0 for these ones or <err> for real ones.
147 */
148static inline int unclean_errno(int err)
149{
150 if (err == EAGAIN || err == EINPROGRESS ||
151 err == EISCONN || err == EALREADY)
152 return 0;
153 return err;
154}
155
Christopher Faulet61cc8522020-04-20 14:54:42 +0200156/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200157const char *get_check_status_description(short check_status) {
158
159 const char *desc;
160
161 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200162 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200163 else
164 desc = NULL;
165
166 if (desc && *desc)
167 return desc;
168 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200169 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200170}
171
Christopher Faulet61cc8522020-04-20 14:54:42 +0200172/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200173const char *get_check_status_info(short check_status) {
174
175 const char *info;
176
177 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200178 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200179 else
180 info = NULL;
181
182 if (info && *info)
183 return info;
184 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200185 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200186}
187
Christopher Faulet61cc8522020-04-20 14:54:42 +0200188/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100189const char *get_analyze_status(short analyze_status) {
190
191 const char *desc;
192
193 if (analyze_status < HANA_STATUS_SIZE)
194 desc = analyze_statuses[analyze_status].desc;
195 else
196 desc = NULL;
197
198 if (desc && *desc)
199 return desc;
200 else
201 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
202}
203
Christopher Faulet61cc8522020-04-20 14:54:42 +0200204/* Sets check->status, update check->duration and fill check->result with an
205 * adequate CHK_RES_* value. The new check->health is computed based on the
206 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200207 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200208 * Shows information in logs about failed health check if server is UP or
209 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200210 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200211void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100212{
Simon Horman4a741432013-02-23 15:35:38 +0900213 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200214 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200215 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900216
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200217 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100218 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900219 check->desc[0] = '\0';
220 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200221 return;
222 }
223
Simon Horman4a741432013-02-23 15:35:38 +0900224 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200225 return;
226
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200227 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900228 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
229 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200230 } else
Simon Horman4a741432013-02-23 15:35:38 +0900231 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200232
Simon Horman4a741432013-02-23 15:35:38 +0900233 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200234 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900235 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200236
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100237 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900238 check->duration = -1;
239 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200240 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900241 check->duration = tv_ms_elapsed(&check->start, &now);
242 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200243 }
244
Willy Tarreau23964182014-05-20 20:56:30 +0200245 /* no change is expected if no state change occurred */
246 if (check->result == CHK_RES_NEUTRAL)
247 return;
248
Olivier Houchard0923fa42019-01-11 18:43:04 +0100249 /* If the check was really just sending a mail, it won't have an
250 * associated server, so we're done now.
251 */
252 if (!s)
253 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200254 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200255
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200256 switch (check->result) {
257 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200258 /* Failure to connect to the agent as a secondary check should not
259 * cause the server to be marked down.
260 */
261 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900262 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200263 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100264 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200265 report = 1;
266 check->health--;
267 if (check->health < check->rise)
268 check->health = 0;
269 }
270 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200271
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200272 case CHK_RES_PASSED:
273 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
274 if ((check->health < check->rise + check->fall - 1) &&
275 (check->result == CHK_RES_PASSED || check->health > 0)) {
276 report = 1;
277 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200278
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200279 if (check->health >= check->rise)
280 check->health = check->rise + check->fall - 1; /* OK now */
281 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200282
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200283 /* clear consecutive_errors if observing is enabled */
284 if (s->onerror)
285 s->consecutive_errors = 0;
286 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100287
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200288 default:
289 break;
290 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200291
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200292 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
293 (status != prev_status || report)) {
294 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200295 "%s check for %sserver %s/%s %s%s",
296 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200297 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100298 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100299 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200300 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200301
Emeric Brun5a133512017-10-19 14:42:30 +0200302 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200303
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100304 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200305 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
306 (check->health >= check->rise) ? check->fall : check->rise,
307 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200308
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200309 ha_warning("%s.\n", trash.area);
310 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
311 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200312 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200313}
314
Willy Tarreau4eec5472014-05-20 22:32:27 +0200315/* Marks the check <check>'s server down if the current check is already failed
316 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200317 */
Willy Tarreau4eec5472014-05-20 22:32:27 +0200318static void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200319{
Simon Horman4a741432013-02-23 15:35:38 +0900320 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900321
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200322 /* The agent secondary check should only cause a server to be marked
323 * as down if check->status is HCHK_STATUS_L7STS, which indicates
324 * that the agent returned "fail", "stopped" or "down".
325 * The implication here is that failure to connect to the agent
326 * as a secondary check should not cause the server to be marked
327 * down. */
328 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
329 return;
330
Willy Tarreau4eec5472014-05-20 22:32:27 +0200331 if (check->health > 0)
332 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100333
Willy Tarreau4eec5472014-05-20 22:32:27 +0200334 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200335 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200336}
337
Willy Tarreauaf549582014-05-16 17:37:50 +0200338/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200339 * it isn't in maintenance, it is not tracking a down server and other checks
340 * comply. The rule is simple : by default, a server is up, unless any of the
341 * following conditions is true :
342 * - health check failed (check->health < rise)
343 * - agent check failed (agent->health < rise)
344 * - the server tracks a down server (track && track->state == STOPPED)
345 * Note that if the server has a slowstart, it will switch to STARTING instead
346 * of RUNNING. Also, only the health checks support the nolb mode, so the
347 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200348 */
Willy Tarreau3e048382014-05-21 10:30:54 +0200349static void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200350{
Simon Horman4a741432013-02-23 15:35:38 +0900351 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100352
Emeric Brun52a91d32017-08-31 14:41:55 +0200353 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200354 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100355
Emeric Brun52a91d32017-08-31 14:41:55 +0200356 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200357 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100358
Willy Tarreau3e048382014-05-21 10:30:54 +0200359 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
360 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100361
Willy Tarreau3e048382014-05-21 10:30:54 +0200362 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
363 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200364
Emeric Brun52a91d32017-08-31 14:41:55 +0200365 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200366 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100367
Emeric Brun5a133512017-10-19 14:42:30 +0200368 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100369}
370
Willy Tarreaudb58b792014-05-21 13:57:23 +0200371/* Marks the check <check> as valid and tries to set its server into stopping mode
372 * if it was running or starting, and provided it isn't in maintenance and other
373 * checks comply. The conditions for the server to be marked in stopping mode are
374 * the same as for it to be turned up. Also, only the health checks support the
375 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200376 */
Willy Tarreaudb58b792014-05-21 13:57:23 +0200377static void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200378{
Simon Horman4a741432013-02-23 15:35:38 +0900379 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100380
Emeric Brun52a91d32017-08-31 14:41:55 +0200381 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200382 return;
383
Willy Tarreaudb58b792014-05-21 13:57:23 +0200384 if (check->state & CHK_ST_AGENT)
385 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100386
Emeric Brun52a91d32017-08-31 14:41:55 +0200387 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200388 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100389
Willy Tarreaudb58b792014-05-21 13:57:23 +0200390 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
391 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100392
Willy Tarreaudb58b792014-05-21 13:57:23 +0200393 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
394 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100395
Willy Tarreaub26881a2017-12-23 11:16:49 +0100396 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100397}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200398
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100399/* note: use health_adjust() only, which first checks that the observe mode is
400 * enabled.
401 */
402void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100403{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100404 int failed;
405 int expire;
406
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100407 if (s->observe >= HANA_OBS_SIZE)
408 return;
409
Willy Tarreaubb956662013-01-24 00:37:39 +0100410 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100411 return;
412
413 switch (analyze_statuses[status].lr[s->observe - 1]) {
414 case 1:
415 failed = 1;
416 break;
417
418 case 2:
419 failed = 0;
420 break;
421
422 default:
423 return;
424 }
425
426 if (!failed) {
427 /* good: clear consecutive_errors */
428 s->consecutive_errors = 0;
429 return;
430 }
431
Olivier Houchard7059c552019-03-08 18:49:32 +0100432 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100433
434 if (s->consecutive_errors < s->consecutive_errors_limit)
435 return;
436
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100437 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
438 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100439
440 switch (s->onerror) {
441 case HANA_ONERR_FASTINTER:
442 /* force fastinter - nothing to do here as all modes force it */
443 break;
444
445 case HANA_ONERR_SUDDTH:
446 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900447 if (s->check.health > s->check.rise)
448 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100449
450 /* no break - fall through */
451
452 case HANA_ONERR_FAILCHK:
453 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200454 set_server_check_status(&s->check, HCHK_STATUS_HANA,
455 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200456 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100457 break;
458
459 case HANA_ONERR_MARKDWN:
460 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900461 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200462 set_server_check_status(&s->check, HCHK_STATUS_HANA,
463 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200464 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100465 break;
466
467 default:
468 /* write a warning? */
469 break;
470 }
471
472 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100473 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100474
Simon Horman66183002013-02-23 10:16:43 +0900475 if (s->check.fastinter) {
476 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300477 if (s->check.task->expire > expire) {
Willy Tarreau5b3a2022012-09-28 15:01:02 +0200478 s->check.task->expire = expire;
Sergiy Prykhodko1d57e502013-09-21 12:05:00 +0300479 /* requeue check task with new expire */
480 task_queue(s->check.task);
481 }
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100482 }
Willy Tarreauef781042010-01-27 11:53:01 +0100483}
484
Christopher Faulet61cc8522020-04-20 14:54:42 +0200485/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100486 * closed, keep errno intact as it is supposed to contain the valid error code.
487 * If no error is reported, check the socket's error queue using getsockopt().
488 * Warning, this must be done only once when returning from poll, and never
489 * after an I/O error was attempted, otherwise the error queue might contain
490 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
491 * socket. Returns non-zero if an error was reported, zero if everything is
492 * clean (including a properly closed socket).
493 */
494static int retrieve_errno_from_socket(struct connection *conn)
495{
496 int skerr;
497 socklen_t lskerr = sizeof(skerr);
498
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100499 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100500 return 1;
501
Willy Tarreau3c728722014-01-23 13:50:42 +0100502 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100503 return 0;
504
Willy Tarreau585744b2017-08-24 14:31:19 +0200505 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100506 errno = skerr;
507
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100508 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100509
510 if (!errno) {
511 /* we could not retrieve an error, that does not mean there is
512 * none. Just don't change anything and only report the prior
513 * error if any.
514 */
515 if (conn->flags & CO_FL_ERROR)
516 return 1;
517 else
518 return 0;
519 }
520
521 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
522 return 1;
523}
524
Christopher Faulet61cc8522020-04-20 14:54:42 +0200525/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100526 * and adjust the server status accordingly. It may make use of <errno_bck>
527 * if non-null when the caller is absolutely certain of its validity (eg:
528 * checked just after a syscall). If the caller doesn't have a valid errno,
529 * it can pass zero, and retrieve_errno_from_socket() will be called to try
530 * to extract errno from the socket. If no error is reported, it will consider
531 * the <expired> flag. This is intended to be used when a connection error was
532 * reported in conn->flags or when a timeout was reported in <expired>. The
533 * function takes care of not updating a server status which was already set.
534 * All situations where at least one of <expired> or CO_FL_ERROR are set
535 * produce a status.
536 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200537void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100538{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200539 struct conn_stream *cs = check->cs;
540 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100541 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200542 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200543 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100544
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100545 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100546 return;
547
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100548 errno = unclean_errno(errno_bck);
549 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100550 retrieve_errno_from_socket(conn);
551
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200552 if (conn && !(conn->flags & CO_FL_ERROR) &&
553 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554 return;
555
556 /* we'll try to build a meaningful error message depending on the
557 * context of the error possibly present in conn->err_code, and the
558 * socket error possibly collected above. This is useful to know the
559 * exact step of the L6 layer (eg: SSL handshake).
560 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200561 chk = get_trash_chunk();
562
Christopher Faulet799f3a42020-04-07 12:06:14 +0200563 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200564 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200565 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200566 if (!step)
567 chunk_printf(chk, " at initial connection step of tcp-check");
568 else {
569 chunk_printf(chk, " at step %d of tcp-check", step);
570 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200571 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
572 if (check->current_step->connect.port)
573 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200574 else
575 chunk_appendf(chk, " (connect)");
576 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200577 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
578 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100579
580 switch (expect->type) {
581 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200582 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100583 break;
584 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200585 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100586 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200587 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200588 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100589 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200590 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100591 chunk_appendf(chk, " (expect binary regex)");
592 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200593 case TCPCHK_EXPECT_STRING_LF:
594 chunk_appendf(chk, " (expect log-format string)");
595 break;
596 case TCPCHK_EXPECT_BINARY_LF:
597 chunk_appendf(chk, " (expect log-format binary)");
598 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200599 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200600 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200601 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200602 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200603 chunk_appendf(chk, " (expect HTTP status regex)");
604 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200605 case TCPCHK_EXPECT_HTTP_HEADER:
606 chunk_appendf(chk, " (expect HTTP header pattern)");
607 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200608 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200609 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200610 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200611 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200612 chunk_appendf(chk, " (expect HTTP body regex)");
613 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200614 case TCPCHK_EXPECT_HTTP_BODY_LF:
615 chunk_appendf(chk, " (expect log-format HTTP body)");
616 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200617 case TCPCHK_EXPECT_CUSTOM:
618 chunk_appendf(chk, " (expect custom function)");
619 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100620 case TCPCHK_EXPECT_UNDEF:
621 chunk_appendf(chk, " (undefined expect!)");
622 break;
623 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200624 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200625 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200626 chunk_appendf(chk, " (send)");
627 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200628
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200629 if (check->current_step && check->current_step->comment)
630 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200631 }
632 }
633
Willy Tarreau00149122017-10-04 18:05:01 +0200634 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100635 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200636 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
637 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100638 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200639 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
640 chk->area);
641 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100642 }
643 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100644 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200645 chunk_printf(&trash, "%s%s", strerror(errno),
646 chk->area);
647 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100648 }
649 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200650 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100651 }
652 }
653
Willy Tarreau00149122017-10-04 18:05:01 +0200654 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200655 /* NOTE: this is reported after <fall> tries */
656 chunk_printf(chk, "No port available for the TCP connection");
657 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
658 }
659
Willy Tarreau00149122017-10-04 18:05:01 +0200660 if (!conn) {
661 /* connection allocation error before the connection was established */
662 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
663 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100664 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100665 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200666 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100667 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
668 else if (expired)
669 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200670
671 /*
672 * might be due to a server IP change.
673 * Let's trigger a DNS resolution if none are currently running.
674 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100675 if (check->server)
676 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200677
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100678 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100679 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100680 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200681 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100682 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
683 else if (expired)
684 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
685 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200686 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100687 /* I/O error after connection was established and before we could diagnose */
688 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
689 }
690 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200691 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
692
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100693 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200694 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
695 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200696 tout = check->current_step->expect.tout_status;
697 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100698 }
699
700 return;
701}
702
Simon Horman98637e52014-06-20 12:30:16 +0900703
Christopher Faulet61cc8522020-04-20 14:54:42 +0200704/* Builds the server state header used by HTTP health-checks */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200705int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +0900706{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200707 int sv_state;
708 int ratio;
709 char addr[46];
710 char port[6];
711 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
712 "UP %d/%d", "UP",
713 "NOLB %d/%d", "NOLB",
714 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +0900715
Christopher Faulet61cc8522020-04-20 14:54:42 +0200716 if (!(s->check.state & CHK_ST_ENABLED))
717 sv_state = 6;
718 else if (s->cur_state != SRV_ST_STOPPED) {
719 if (s->check.health == s->check.rise + s->check.fall - 1)
720 sv_state = 3; /* UP */
721 else
722 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +0900723
Christopher Faulet61cc8522020-04-20 14:54:42 +0200724 if (s->cur_state == SRV_ST_STOPPING)
725 sv_state += 2;
726 } else {
727 if (s->check.health)
728 sv_state = 1; /* going up */
729 else
730 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +0900731 }
Willy Tarreaub7b24782016-06-21 15:32:29 +0200732
Christopher Faulet61cc8522020-04-20 14:54:42 +0200733 chunk_appendf(buf, srv_hlt_st[sv_state],
734 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
735 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +0200736
Christopher Faulet61cc8522020-04-20 14:54:42 +0200737 addr_to_str(&s->addr, addr, sizeof(addr));
738 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
739 snprintf(port, sizeof(port), "%u", s->svc_port);
740 else
741 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +0200742
Christopher Faulet61cc8522020-04-20 14:54:42 +0200743 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
744 addr, port, s->proxy->id, s->id,
745 global.node,
746 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
747 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
748 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
749 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +0100750
Christopher Faulet61cc8522020-04-20 14:54:42 +0200751 if ((s->cur_state == SRV_ST_STARTING) &&
752 now.tv_sec < s->last_change + s->slowstart &&
753 now.tv_sec >= s->last_change) {
754 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
755 chunk_appendf(buf, "; throttle=%d%%", ratio);
756 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200757
Christopher Faulet61cc8522020-04-20 14:54:42 +0200758 return b_data(buf);
759}
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200760
Willy Tarreau51cd5952020-06-05 12:25:38 +0200761/**************************************************************************/
762/************** Health-checks based on an external process ****************/
763/**************************************************************************/
764static struct list pid_list = LIST_HEAD_INIT(pid_list);
765static struct pool_head *pool_head_pid_list;
766__decl_spinlock(pid_list_lock);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200767
Willy Tarreau51cd5952020-06-05 12:25:38 +0200768struct extcheck_env {
769 char *name; /* environment variable name */
770 int vmaxlen; /* value maximum length, used to determine the required memory allocation */
771};
Simon Horman98637e52014-06-20 12:30:16 +0900772
Willy Tarreau51cd5952020-06-05 12:25:38 +0200773/* environment variables memory requirement for different types of data */
774#define EXTCHK_SIZE_EVAL_INIT 0 /* size determined during the init phase,
775 * such environment variables are not updatable. */
776#define EXTCHK_SIZE_ULONG 20 /* max string length for an unsigned long value */
777#define EXTCHK_SIZE_UINT 11 /* max string length for an unsigned int value */
778#define EXTCHK_SIZE_ADDR INET6_ADDRSTRLEN+1 /* max string length for an address */
Simon Horman98637e52014-06-20 12:30:16 +0900779
Willy Tarreau51cd5952020-06-05 12:25:38 +0200780/* external checks environment variables */
781enum {
782 EXTCHK_PATH = 0,
Simon Horman98637e52014-06-20 12:30:16 +0900783
Willy Tarreau51cd5952020-06-05 12:25:38 +0200784 /* Proxy specific environment variables */
785 EXTCHK_HAPROXY_PROXY_NAME, /* the backend name */
786 EXTCHK_HAPROXY_PROXY_ID, /* the backend id */
787 EXTCHK_HAPROXY_PROXY_ADDR, /* the first bind address if available (or empty) */
788 EXTCHK_HAPROXY_PROXY_PORT, /* the first bind port if available (or empty) */
Simon Horman98637e52014-06-20 12:30:16 +0900789
Willy Tarreau51cd5952020-06-05 12:25:38 +0200790 /* Server specific environment variables */
791 EXTCHK_HAPROXY_SERVER_NAME, /* the server name */
792 EXTCHK_HAPROXY_SERVER_ID, /* the server id */
793 EXTCHK_HAPROXY_SERVER_ADDR, /* the server address */
794 EXTCHK_HAPROXY_SERVER_PORT, /* the server port if available (or empty) */
795 EXTCHK_HAPROXY_SERVER_MAXCONN, /* the server max connections */
796 EXTCHK_HAPROXY_SERVER_CURCONN, /* the current number of connections on the server */
Simon Horman98637e52014-06-20 12:30:16 +0900797
Willy Tarreau51cd5952020-06-05 12:25:38 +0200798 EXTCHK_SIZE
799};
Simon Horman98637e52014-06-20 12:30:16 +0900800
Willy Tarreau51cd5952020-06-05 12:25:38 +0200801const struct extcheck_env extcheck_envs[EXTCHK_SIZE] = {
802 [EXTCHK_PATH] = { "PATH", EXTCHK_SIZE_EVAL_INIT },
803 [EXTCHK_HAPROXY_PROXY_NAME] = { "HAPROXY_PROXY_NAME", EXTCHK_SIZE_EVAL_INIT },
804 [EXTCHK_HAPROXY_PROXY_ID] = { "HAPROXY_PROXY_ID", EXTCHK_SIZE_EVAL_INIT },
805 [EXTCHK_HAPROXY_PROXY_ADDR] = { "HAPROXY_PROXY_ADDR", EXTCHK_SIZE_EVAL_INIT },
806 [EXTCHK_HAPROXY_PROXY_PORT] = { "HAPROXY_PROXY_PORT", EXTCHK_SIZE_EVAL_INIT },
807 [EXTCHK_HAPROXY_SERVER_NAME] = { "HAPROXY_SERVER_NAME", EXTCHK_SIZE_EVAL_INIT },
808 [EXTCHK_HAPROXY_SERVER_ID] = { "HAPROXY_SERVER_ID", EXTCHK_SIZE_EVAL_INIT },
809 [EXTCHK_HAPROXY_SERVER_ADDR] = { "HAPROXY_SERVER_ADDR", EXTCHK_SIZE_ADDR },
810 [EXTCHK_HAPROXY_SERVER_PORT] = { "HAPROXY_SERVER_PORT", EXTCHK_SIZE_UINT },
811 [EXTCHK_HAPROXY_SERVER_MAXCONN] = { "HAPROXY_SERVER_MAXCONN", EXTCHK_SIZE_EVAL_INIT },
812 [EXTCHK_HAPROXY_SERVER_CURCONN] = { "HAPROXY_SERVER_CURCONN", EXTCHK_SIZE_ULONG },
813};
Simon Horman98637e52014-06-20 12:30:16 +0900814
Willy Tarreau51cd5952020-06-05 12:25:38 +0200815void block_sigchld(void)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200816{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200817 sigset_t set;
818 sigemptyset(&set);
819 sigaddset(&set, SIGCHLD);
820 assert(ha_sigmask(SIG_BLOCK, &set, NULL) == 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200821}
Simon Horman98637e52014-06-20 12:30:16 +0900822
Willy Tarreau51cd5952020-06-05 12:25:38 +0200823void unblock_sigchld(void)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200824{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200825 sigset_t set;
826 sigemptyset(&set);
827 sigaddset(&set, SIGCHLD);
828 assert(ha_sigmask(SIG_UNBLOCK, &set, NULL) == 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200829}
Simon Horman98637e52014-06-20 12:30:16 +0900830
Willy Tarreau51cd5952020-06-05 12:25:38 +0200831static struct pid_list *pid_list_add(pid_t pid, struct task *t)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200832{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200833 struct pid_list *elem;
834 struct check *check = t->context;
Simon Horman98637e52014-06-20 12:30:16 +0900835
Willy Tarreau51cd5952020-06-05 12:25:38 +0200836 elem = pool_alloc(pool_head_pid_list);
837 if (!elem)
838 return NULL;
839 elem->pid = pid;
840 elem->t = t;
841 elem->exited = 0;
842 check->curpid = elem;
843 LIST_INIT(&elem->list);
Simon Horman98637e52014-06-20 12:30:16 +0900844
Willy Tarreau51cd5952020-06-05 12:25:38 +0200845 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
846 LIST_ADD(&pid_list, &elem->list);
847 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Simon Horman98637e52014-06-20 12:30:16 +0900848
Willy Tarreau51cd5952020-06-05 12:25:38 +0200849 return elem;
850}
Simon Horman98637e52014-06-20 12:30:16 +0900851
Willy Tarreau51cd5952020-06-05 12:25:38 +0200852static void pid_list_del(struct pid_list *elem)
853{
854 struct check *check;
Simon Horman98637e52014-06-20 12:30:16 +0900855
Willy Tarreau51cd5952020-06-05 12:25:38 +0200856 if (!elem)
857 return;
Simon Horman98637e52014-06-20 12:30:16 +0900858
Willy Tarreau51cd5952020-06-05 12:25:38 +0200859 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
860 LIST_DEL(&elem->list);
861 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Willy Tarreau62ac84f2017-11-05 10:11:13 +0100862
Willy Tarreau51cd5952020-06-05 12:25:38 +0200863 if (!elem->exited)
864 kill(elem->pid, SIGTERM);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200865
Willy Tarreau51cd5952020-06-05 12:25:38 +0200866 check = elem->t->context;
867 check->curpid = NULL;
868 pool_free(pool_head_pid_list, elem);
Simon Horman98637e52014-06-20 12:30:16 +0900869}
870
Willy Tarreau51cd5952020-06-05 12:25:38 +0200871/* Called from inside SIGCHLD handler, SIGCHLD is blocked */
872static void pid_list_expire(pid_t pid, int status)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200873{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200874 struct pid_list *elem;
Willy Tarreau1ae1b7b2012-09-28 15:28:30 +0200875
Willy Tarreau51cd5952020-06-05 12:25:38 +0200876 HA_SPIN_LOCK(PID_LIST_LOCK, &pid_list_lock);
877 list_for_each_entry(elem, &pid_list, list) {
878 if (elem->pid == pid) {
879 elem->t->expire = now_ms;
880 elem->status = status;
881 elem->exited = 1;
882 task_wakeup(elem->t, TASK_WOKEN_IO);
883 break;
884 }
Christopher Faulet61cc8522020-04-20 14:54:42 +0200885 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200886 HA_SPIN_UNLOCK(PID_LIST_LOCK, &pid_list_lock);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200887}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200888
Willy Tarreau51cd5952020-06-05 12:25:38 +0200889static void sigchld_handler(struct sig_handler *sh)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200890{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200891 pid_t pid;
892 int status;
Gaetan Rivet05d692d2020-02-14 17:42:54 +0100893
Willy Tarreau51cd5952020-06-05 12:25:38 +0200894 while ((pid = waitpid(0, &status, WNOHANG)) > 0)
895 pid_list_expire(pid, status);
896}
Gaetan Rivet05d692d2020-02-14 17:42:54 +0100897
Willy Tarreau51cd5952020-06-05 12:25:38 +0200898static int init_pid_list(void)
899{
900 if (pool_head_pid_list != NULL)
901 /* Nothing to do */
902 return 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200903
Willy Tarreau51cd5952020-06-05 12:25:38 +0200904 if (!signal_register_fct(SIGCHLD, sigchld_handler, SIGCHLD)) {
905 ha_alert("Failed to set signal handler for external health checks: %s. Aborting.\n",
906 strerror(errno));
907 return 1;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200908 }
909
Willy Tarreau51cd5952020-06-05 12:25:38 +0200910 pool_head_pid_list = create_pool("pid_list", sizeof(struct pid_list), MEM_F_SHARED);
911 if (pool_head_pid_list == NULL) {
912 ha_alert("Failed to allocate memory pool for external health checks: %s. Aborting.\n",
913 strerror(errno));
914 return 1;
Willy Tarreaubaaee002006-06-26 02:48:02 +0200915 }
Willy Tarreaubaaee002006-06-26 02:48:02 +0200916
Willy Tarreau51cd5952020-06-05 12:25:38 +0200917 return 0;
918}
919
920/* helper macro to set an environment variable and jump to a specific label on failure. */
921#define EXTCHK_SETENV(check, envidx, value, fail) { if (extchk_setenv(check, envidx, value)) goto fail; }
Simon Horman98637e52014-06-20 12:30:16 +0900922
Willy Tarreau51cd5952020-06-05 12:25:38 +0200923/*
924 * helper function to allocate enough memory to store an environment variable.
925 * It will also check that the environment variable is updatable, and silently
926 * fail if not.
927 */
928static int extchk_setenv(struct check *check, int idx, const char *value)
929{
930 int len, ret;
931 char *envname;
932 int vmaxlen;
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200933
Willy Tarreau51cd5952020-06-05 12:25:38 +0200934 if (idx < 0 || idx >= EXTCHK_SIZE) {
935 ha_alert("Illegal environment variable index %d. Aborting.\n", idx);
936 return 1;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200937 }
Simon Horman98637e52014-06-20 12:30:16 +0900938
Willy Tarreau51cd5952020-06-05 12:25:38 +0200939 envname = extcheck_envs[idx].name;
940 vmaxlen = extcheck_envs[idx].vmaxlen;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +0200941
Willy Tarreau51cd5952020-06-05 12:25:38 +0200942 /* Check if the environment variable is already set, and silently reject
943 * the update if this one is not updatable. */
944 if ((vmaxlen == EXTCHK_SIZE_EVAL_INIT) && (check->envp[idx]))
945 return 0;
Willy Tarreau6dd4ac82019-09-03 18:55:02 +0200946
Willy Tarreau51cd5952020-06-05 12:25:38 +0200947 /* Instead of sending NOT_USED, sending an empty value is preferable */
948 if (strcmp(value, "NOT_USED") == 0) {
949 value = "";
Simon Horman5c942422013-11-25 10:46:32 +0900950 }
951
Willy Tarreau51cd5952020-06-05 12:25:38 +0200952 len = strlen(envname) + 1;
953 if (vmaxlen == EXTCHK_SIZE_EVAL_INIT)
954 len += strlen(value);
955 else
956 len += vmaxlen;
Simon Horman5c942422013-11-25 10:46:32 +0900957
Willy Tarreau51cd5952020-06-05 12:25:38 +0200958 if (!check->envp[idx])
959 check->envp[idx] = malloc(len + 1);
Willy Tarreau1746eec2014-04-25 10:46:47 +0200960
Willy Tarreau51cd5952020-06-05 12:25:38 +0200961 if (!check->envp[idx]) {
962 ha_alert("Failed to allocate memory for the environment variable '%s'. Aborting.\n", envname);
963 return 1;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200964 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200965 ret = snprintf(check->envp[idx], len + 1, "%s=%s", envname, value);
966 if (ret < 0) {
967 ha_alert("Failed to store the environment variable '%s'. Reason : %s. Aborting.\n", envname, strerror(errno));
968 return 1;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200969 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200970 else if (ret > len) {
971 ha_alert("Environment variable '%s' was truncated. Aborting.\n", envname);
972 return 1;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200973 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200974 return 0;
Simon Horman5c942422013-11-25 10:46:32 +0900975}
976
Willy Tarreau51cd5952020-06-05 12:25:38 +0200977static int prepare_external_check(struct check *check)
Willy Tarreau865c5142016-12-21 20:04:48 +0100978{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200979 struct server *s = check->server;
Willy Tarreau51cd5952020-06-05 12:25:38 +0200980 struct proxy *px = s->proxy;
981 struct listener *listener = NULL, *l;
982 int i;
983 const char *path = px->check_path ? px->check_path : DEF_CHECK_PATH;
984 char buf[256];
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +0200985
Willy Tarreau51cd5952020-06-05 12:25:38 +0200986 list_for_each_entry(l, &px->conf.listeners, by_fe)
987 /* Use the first INET, INET6 or UNIX listener */
988 if (l->addr.ss_family == AF_INET ||
989 l->addr.ss_family == AF_INET6 ||
990 l->addr.ss_family == AF_UNIX) {
991 listener = l;
992 break;
993 }
Willy Tarreaue7b73482013-11-21 11:50:50 +0100994
Willy Tarreau51cd5952020-06-05 12:25:38 +0200995 check->curpid = NULL;
996 check->envp = calloc((EXTCHK_SIZE + 1), sizeof(char *));
997 if (!check->envp) {
998 ha_alert("Failed to allocate memory for environment variables. Aborting\n");
999 goto err;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001000 }
Willy Tarreau15f39102013-12-11 20:41:18 +01001001
Willy Tarreau51cd5952020-06-05 12:25:38 +02001002 check->argv = calloc(6, sizeof(char *));
1003 if (!check->argv) {
1004 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1005 goto err;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001006 }
1007
Willy Tarreau51cd5952020-06-05 12:25:38 +02001008 check->argv[0] = px->check_command;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001009
Willy Tarreau51cd5952020-06-05 12:25:38 +02001010 if (!listener) {
1011 check->argv[1] = strdup("NOT_USED");
1012 check->argv[2] = strdup("NOT_USED");
Christopher Faulet61cc8522020-04-20 14:54:42 +02001013 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001014 else if (listener->addr.ss_family == AF_INET ||
1015 listener->addr.ss_family == AF_INET6) {
1016 addr_to_str(&listener->addr, buf, sizeof(buf));
1017 check->argv[1] = strdup(buf);
1018 port_to_str(&listener->addr, buf, sizeof(buf));
1019 check->argv[2] = strdup(buf);
1020 }
1021 else if (listener->addr.ss_family == AF_UNIX) {
1022 const struct sockaddr_un *un;
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001023
Willy Tarreau51cd5952020-06-05 12:25:38 +02001024 un = (struct sockaddr_un *)&listener->addr;
1025 check->argv[1] = strdup(un->sun_path);
1026 check->argv[2] = strdup("NOT_USED");
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +02001027 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001028 else {
1029 ha_alert("Starting [%s:%s] check: unsupported address family.\n", px->id, s->id);
1030 goto err;
Christopher Faulet931ae5b2020-04-28 10:31:53 +02001031 }
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +02001032
Willy Tarreau51cd5952020-06-05 12:25:38 +02001033 if (!check->argv[1] || !check->argv[2]) {
1034 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1035 goto err;
Christopher Faulet206368d2020-04-03 14:51:06 +02001036 }
1037
Willy Tarreau51cd5952020-06-05 12:25:38 +02001038 check->argv[3] = calloc(EXTCHK_SIZE_ADDR, sizeof(*check->argv[3]));
1039 check->argv[4] = calloc(EXTCHK_SIZE_UINT, sizeof(*check->argv[4]));
1040 if (!check->argv[3] || !check->argv[4]) {
1041 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1042 goto err;
Christopher Faulet206368d2020-04-03 14:51:06 +02001043 }
1044
Willy Tarreau51cd5952020-06-05 12:25:38 +02001045 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
1046 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1047 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
Christopher Faulet206368d2020-04-03 14:51:06 +02001048
Willy Tarreau51cd5952020-06-05 12:25:38 +02001049 for (i = 0; i < 5; i++) {
1050 if (!check->argv[i]) {
1051 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1052 goto err;
1053 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001054 }
Christopher Faulet206368d2020-04-03 14:51:06 +02001055
Willy Tarreau51cd5952020-06-05 12:25:38 +02001056 EXTCHK_SETENV(check, EXTCHK_PATH, path, err);
1057 /* Add proxy environment variables */
1058 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_NAME, px->id, err);
1059 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ID, ultoa_r(px->uuid, buf, sizeof(buf)), err);
1060 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_ADDR, check->argv[1], err);
1061 EXTCHK_SETENV(check, EXTCHK_HAPROXY_PROXY_PORT, check->argv[2], err);
1062 /* Add server environment variables */
1063 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_NAME, s->id, err);
1064 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ID, ultoa_r(s->puid, buf, sizeof(buf)), err);
1065 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3], err);
1066 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4], err);
1067 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_MAXCONN, ultoa_r(s->maxconn, buf, sizeof(buf)), err);
1068 EXTCHK_SETENV(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)), err);
Christopher Faulet206368d2020-04-03 14:51:06 +02001069
Willy Tarreau51cd5952020-06-05 12:25:38 +02001070 /* Ensure that we don't leave any hole in check->envp */
1071 for (i = 0; i < EXTCHK_SIZE; i++)
1072 if (!check->envp[i])
1073 EXTCHK_SETENV(check, i, "", err);
Christopher Faulet206368d2020-04-03 14:51:06 +02001074
Willy Tarreau51cd5952020-06-05 12:25:38 +02001075 return 1;
1076err:
1077 if (check->envp) {
1078 for (i = 0; i < EXTCHK_SIZE; i++)
1079 free(check->envp[i]);
1080 free(check->envp);
1081 check->envp = NULL;
Christopher Faulet206368d2020-04-03 14:51:06 +02001082 }
1083
Willy Tarreau51cd5952020-06-05 12:25:38 +02001084 if (check->argv) {
1085 for (i = 1; i < 5; i++)
1086 free(check->argv[i]);
1087 free(check->argv);
1088 check->argv = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001089 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001090 return 0;
Christopher Faulet206368d2020-04-03 14:51:06 +02001091}
1092
Willy Tarreau51cd5952020-06-05 12:25:38 +02001093/*
1094 * establish a server health-check that makes use of a process.
1095 *
1096 * It can return one of :
1097 * - SF_ERR_NONE if everything's OK
1098 * - SF_ERR_RESOURCE if a system resource is lacking (eg: fd limits, ports, ...)
1099 * Additionally, in the case of SF_ERR_RESOURCE, an emergency log will be emitted.
1100 *
1101 * Blocks and then unblocks SIGCHLD
Christopher Faulet61cc8522020-04-20 14:54:42 +02001102 */
Willy Tarreau51cd5952020-06-05 12:25:38 +02001103static int connect_proc_chk(struct task *t)
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001104{
Willy Tarreau51cd5952020-06-05 12:25:38 +02001105 char buf[256];
1106 struct check *check = t->context;
1107 struct server *s = check->server;
1108 struct proxy *px = s->proxy;
1109 int status;
1110 pid_t pid;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001111
Willy Tarreau51cd5952020-06-05 12:25:38 +02001112 status = SF_ERR_RESOURCE;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001113
Willy Tarreau51cd5952020-06-05 12:25:38 +02001114 block_sigchld();
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001115
Willy Tarreau51cd5952020-06-05 12:25:38 +02001116 pid = fork();
1117 if (pid < 0) {
1118 ha_alert("Failed to fork process for external health check%s: %s. Aborting.\n",
1119 (global.tune.options & GTUNE_INSECURE_FORK) ?
1120 "" : " (likely caused by missing 'insecure-fork-wanted')",
1121 strerror(errno));
1122 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
1123 goto out;
Christopher Faulet2edcd4c2020-04-28 10:39:50 +02001124 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001125 if (pid == 0) {
1126 /* Child */
1127 extern char **environ;
1128 struct rlimit limit;
1129 int fd;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001130
Willy Tarreau51cd5952020-06-05 12:25:38 +02001131 /* close all FDs. Keep stdin/stdout/stderr in verbose mode */
1132 fd = (global.mode & (MODE_QUIET|MODE_VERBOSE)) == MODE_QUIET ? 0 : 3;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001133
Willy Tarreau51cd5952020-06-05 12:25:38 +02001134 my_closefrom(fd);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001135
Willy Tarreau51cd5952020-06-05 12:25:38 +02001136 /* restore the initial FD limits */
1137 limit.rlim_cur = rlim_fd_cur_at_boot;
1138 limit.rlim_max = rlim_fd_max_at_boot;
1139 if (setrlimit(RLIMIT_NOFILE, &limit) == -1) {
1140 getrlimit(RLIMIT_NOFILE, &limit);
1141 ha_warning("External check: failed to restore initial FD limits (cur=%u max=%u), using cur=%u max=%u\n",
1142 rlim_fd_cur_at_boot, rlim_fd_max_at_boot,
1143 (unsigned int)limit.rlim_cur, (unsigned int)limit.rlim_max);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001144 }
Christopher Faulet574e7bd2020-05-06 15:38:58 +02001145
Willy Tarreau51cd5952020-06-05 12:25:38 +02001146 environ = check->envp;
Christopher Faulet574e7bd2020-05-06 15:38:58 +02001147
Willy Tarreau51cd5952020-06-05 12:25:38 +02001148 /* Update some environment variables and command args: curconn, server addr and server port */
1149 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_CURCONN, ultoa_r(s->cur_sess, buf, sizeof(buf)));
Christopher Faulet574e7bd2020-05-06 15:38:58 +02001150
Willy Tarreau51cd5952020-06-05 12:25:38 +02001151 addr_to_str(&s->addr, check->argv[3], EXTCHK_SIZE_ADDR);
1152 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_ADDR, check->argv[3]);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001153
Willy Tarreau51cd5952020-06-05 12:25:38 +02001154 *check->argv[4] = 0;
1155 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
1156 snprintf(check->argv[4], EXTCHK_SIZE_UINT, "%u", s->svc_port);
1157 extchk_setenv(check, EXTCHK_HAPROXY_SERVER_PORT, check->argv[4]);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001158
Willy Tarreau51cd5952020-06-05 12:25:38 +02001159 haproxy_unblock_signals();
1160 execvp(px->check_command, check->argv);
1161 ha_alert("Failed to exec process for external health check: %s. Aborting.\n",
1162 strerror(errno));
1163 exit(-1);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001164 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001165
Willy Tarreau51cd5952020-06-05 12:25:38 +02001166 /* Parent */
1167 if (check->result == CHK_RES_UNKNOWN) {
1168 if (pid_list_add(pid, t) != NULL) {
1169 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet6d471212020-04-22 11:09:25 +02001170
Willy Tarreau51cd5952020-06-05 12:25:38 +02001171 if (px->timeout.check && px->timeout.connect) {
1172 int t_con = tick_add(now_ms, px->timeout.connect);
1173 t->expire = tick_first(t->expire, t_con);
1174 }
1175 status = SF_ERR_NONE;
Christopher Faulet815516d2020-04-21 13:02:14 +02001176 goto out;
1177 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001178 else {
1179 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
1180 }
1181 kill(pid, SIGTERM); /* process creation error */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001182 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001183 else
1184 set_server_check_status(check, HCHK_STATUS_SOCKERR, strerror(errno));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001185
Willy Tarreau51cd5952020-06-05 12:25:38 +02001186out:
1187 unblock_sigchld();
1188 return status;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001189}
1190
Willy Tarreau51cd5952020-06-05 12:25:38 +02001191/*
1192 * manages a server health-check that uses an external process. Returns
1193 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
1194 *
1195 * Please do NOT place any return statement in this function and only leave
1196 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +02001197 */
Willy Tarreau51cd5952020-06-05 12:25:38 +02001198static struct task *process_chk_proc(struct task *t, void *context, unsigned short state)
Christopher Faulet1997eca2020-04-03 23:13:50 +02001199{
Willy Tarreau51cd5952020-06-05 12:25:38 +02001200 struct check *check = context;
1201 struct server *s = check->server;
1202 int rv;
1203 int ret;
1204 int expired = tick_is_expired(t->expire, now_ms);
Christopher Faulet1997eca2020-04-03 23:13:50 +02001205
Willy Tarreau51cd5952020-06-05 12:25:38 +02001206 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
1207 if (!(check->state & CHK_ST_INPROGRESS)) {
1208 /* no check currently running */
1209 if (!expired) /* woke up too early */
1210 goto out_unlock;
Christopher Faulet1997eca2020-04-03 23:13:50 +02001211
Willy Tarreau51cd5952020-06-05 12:25:38 +02001212 /* we don't send any health-checks when the proxy is
1213 * stopped, the server should not be checked or the check
1214 * is disabled.
Christopher Faulet61cc8522020-04-20 14:54:42 +02001215 */
Willy Tarreau51cd5952020-06-05 12:25:38 +02001216 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
1217 s->proxy->state == PR_STSTOPPED)
1218 goto reschedule;
Christopher Faulet1941bab2020-05-05 07:55:50 +02001219
Willy Tarreau51cd5952020-06-05 12:25:38 +02001220 /* we'll initiate a new check */
1221 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet1941bab2020-05-05 07:55:50 +02001222
Willy Tarreau51cd5952020-06-05 12:25:38 +02001223 check->state |= CHK_ST_INPROGRESS;
Christopher Faulet1997eca2020-04-03 23:13:50 +02001224
Willy Tarreau51cd5952020-06-05 12:25:38 +02001225 ret = connect_proc_chk(t);
1226 if (ret == SF_ERR_NONE) {
1227 /* the process was forked, we allow up to min(inter,
1228 * timeout.connect) for it to report its status, but
1229 * only when timeout.check is set as it may be to short
1230 * for a full check otherwise.
1231 */
1232 t->expire = tick_add(now_ms, MS_TO_TICKS(check->inter));
Christopher Faulet1997eca2020-04-03 23:13:50 +02001233
Willy Tarreau51cd5952020-06-05 12:25:38 +02001234 if (s->proxy->timeout.check && s->proxy->timeout.connect) {
1235 int t_con = tick_add(now_ms, s->proxy->timeout.connect);
1236 t->expire = tick_first(t->expire, t_con);
1237 }
1238 task_set_affinity(t, tid_bit);
1239 goto reschedule;
1240 }
Christopher Faulet1997eca2020-04-03 23:13:50 +02001241
Willy Tarreau51cd5952020-06-05 12:25:38 +02001242 /* here, we failed to start the check */
Christopher Fauletf2b3be52020-04-02 18:07:37 +02001243
Willy Tarreau51cd5952020-06-05 12:25:38 +02001244 check->state &= ~CHK_ST_INPROGRESS;
1245 check_notify_failure(check);
Christopher Faulet267b01b2020-04-04 10:27:09 +02001246
Willy Tarreau51cd5952020-06-05 12:25:38 +02001247 /* we allow up to min(inter, timeout.connect) for a connection
1248 * to establish but only when timeout.check is set
1249 * as it may be to short for a full check otherwise
1250 */
1251 while (tick_is_expired(t->expire, now_ms)) {
1252 int t_con;
Christopher Faulet267b01b2020-04-04 10:27:09 +02001253
Willy Tarreau51cd5952020-06-05 12:25:38 +02001254 t_con = tick_add(t->expire, s->proxy->timeout.connect);
1255 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet267b01b2020-04-04 10:27:09 +02001256
Willy Tarreau51cd5952020-06-05 12:25:38 +02001257 if (s->proxy->timeout.check)
1258 t->expire = tick_first(t->expire, t_con);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001259 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001260 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001261 else {
1262 /* there was a test running.
1263 * First, let's check whether there was an uncaught error,
1264 * which can happen on connect timeout or error.
1265 */
1266 if (check->result == CHK_RES_UNKNOWN) {
1267 /* good connection is enough for pure TCP check */
1268 struct pid_list *elem = check->curpid;
1269 int status = HCHK_STATUS_UNKNOWN;
Christopher Faulet39708192020-05-05 10:47:36 +02001270
Willy Tarreau51cd5952020-06-05 12:25:38 +02001271 if (elem->exited) {
1272 status = elem->status; /* Save in case the process exits between use below */
1273 if (!WIFEXITED(status))
1274 check->code = -1;
1275 else
1276 check->code = WEXITSTATUS(status);
1277 if (!WIFEXITED(status) || WEXITSTATUS(status))
1278 status = HCHK_STATUS_PROCERR;
1279 else
1280 status = HCHK_STATUS_PROCOK;
1281 } else if (expired) {
1282 status = HCHK_STATUS_PROCTOUT;
1283 ha_warning("kill %d\n", (int)elem->pid);
1284 kill(elem->pid, SIGTERM);
Christopher Faulet0d6909b2020-05-05 15:50:37 +02001285 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001286 set_server_check_status(check, status, NULL);
Christopher Faulet39708192020-05-05 10:47:36 +02001287 }
Christopher Faulet083eff32020-05-07 15:41:39 +02001288
Willy Tarreau51cd5952020-06-05 12:25:38 +02001289 if (check->result == CHK_RES_FAILED) {
1290 /* a failure or timeout detected */
1291 check_notify_failure(check);
Christopher Faulet39708192020-05-05 10:47:36 +02001292 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001293 else if (check->result == CHK_RES_CONDPASS) {
1294 /* check is OK but asks for stopping mode */
1295 check_notify_stopping(check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001296 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001297 else if (check->result == CHK_RES_PASSED) {
1298 /* a success was detected */
1299 check_notify_success(check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001300 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001301 task_set_affinity(t, 1);
1302 check->state &= ~CHK_ST_INPROGRESS;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001303
Willy Tarreau51cd5952020-06-05 12:25:38 +02001304 pid_list_del(check->curpid);
Christopher Fauletaaab0832020-05-05 15:54:22 +02001305
Willy Tarreau51cd5952020-06-05 12:25:38 +02001306 rv = 0;
1307 if (global.spread_checks > 0) {
1308 rv = srv_getinter(check) * global.spread_checks / 100;
1309 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001310 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001311 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001312 }
1313
Willy Tarreau51cd5952020-06-05 12:25:38 +02001314 reschedule:
1315 while (tick_is_expired(t->expire, now_ms))
1316 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001317
Willy Tarreau51cd5952020-06-05 12:25:38 +02001318 out_unlock:
1319 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
1320 return t;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001321}
1322
Willy Tarreau51cd5952020-06-05 12:25:38 +02001323
1324/**************************************************************************/
1325/***************** Health-checks based on connections *********************/
1326/**************************************************************************/
1327/* This function is used only for server health-checks. It handles connection
1328 * status updates including errors. If necessary, it wakes the check task up.
1329 * It returns 0 on normal cases, <0 if at least one close() has happened on the
1330 * connection (eg: reconnect). It relies on tcpcheck_main().
Christopher Faulet61cc8522020-04-20 14:54:42 +02001331 */
Willy Tarreau51cd5952020-06-05 12:25:38 +02001332static int wake_srv_chk(struct conn_stream *cs)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001333{
Willy Tarreau51cd5952020-06-05 12:25:38 +02001334 struct connection *conn = cs->conn;
1335 struct check *check = cs->data;
1336 struct email_alertq *q = container_of(check, typeof(*q), check);
1337 int ret = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001338
Willy Tarreau51cd5952020-06-05 12:25:38 +02001339 if (check->server)
1340 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
1341 else
1342 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001343
Willy Tarreau51cd5952020-06-05 12:25:38 +02001344 /* we may have to make progress on the TCP checks */
1345 ret = tcpcheck_main(check);
Christopher Fauletaaab0832020-05-05 15:54:22 +02001346
Willy Tarreau51cd5952020-06-05 12:25:38 +02001347 cs = check->cs;
1348 conn = cs->conn;
Christopher Fauletaaab0832020-05-05 15:54:22 +02001349
Willy Tarreau51cd5952020-06-05 12:25:38 +02001350 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
1351 /* We may get error reports bypassing the I/O handlers, typically
1352 * the case when sending a pure TCP check which fails, then the I/O
1353 * handlers above are not called. This is completely handled by the
1354 * main processing task so let's simply wake it up. If we get here,
1355 * we expect errno to still be valid.
1356 */
1357 chk_report_conn_err(check, errno, 0);
1358 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001359 }
1360
Willy Tarreau51cd5952020-06-05 12:25:38 +02001361 if (check->result != CHK_RES_UNKNOWN) {
1362 /* Check complete or aborted. If connection not yet closed do it
1363 * now and wake the check task up to be sure the result is
1364 * handled ASAP. */
1365 conn_sock_drain(conn);
1366 cs_close(cs);
1367 ret = -1;
1368 /* We may have been scheduled to run, and the
1369 * I/O handler expects to have a cs, so remove
1370 * the tasklet
1371 */
1372 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1373 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001374 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001375
Willy Tarreau51cd5952020-06-05 12:25:38 +02001376 if (check->server)
1377 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
1378 else
1379 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001380
Willy Tarreau51cd5952020-06-05 12:25:38 +02001381 /* if a connection got replaced, we must absolutely prevent the connection
1382 * handler from touching its fd, and perform the FD polling updates ourselves
1383 */
1384 if (ret < 0)
1385 conn_cond_update_polling(conn);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001386
Christopher Faulet61cc8522020-04-20 14:54:42 +02001387 return ret;
1388}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001389
Willy Tarreau51cd5952020-06-05 12:25:38 +02001390/* This function checks if any I/O is wanted, and if so, attempts to do so */
1391static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001392{
Willy Tarreau51cd5952020-06-05 12:25:38 +02001393 struct check *check = ctx;
1394 struct conn_stream *cs = check->cs;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001395
Willy Tarreau51cd5952020-06-05 12:25:38 +02001396 wake_srv_chk(cs);
1397 return NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001398}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02001399
Willy Tarreau51cd5952020-06-05 12:25:38 +02001400/* manages a server health-check that uses a connection. Returns
1401 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Faulet61cc8522020-04-20 14:54:42 +02001402 *
1403 * Please do NOT place any return statement in this function and only leave
Willy Tarreau51cd5952020-06-05 12:25:38 +02001404 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +02001405 */
Willy Tarreau51cd5952020-06-05 12:25:38 +02001406static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001407{
Willy Tarreau51cd5952020-06-05 12:25:38 +02001408 struct check *check = context;
1409 struct proxy *proxy = check->proxy;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001410 struct conn_stream *cs = check->cs;
1411 struct connection *conn = cs_conn(cs);
Willy Tarreau51cd5952020-06-05 12:25:38 +02001412 int rv;
1413 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaudeccd112018-06-14 18:38:55 +02001414
Willy Tarreau51cd5952020-06-05 12:25:38 +02001415 if (check->server)
1416 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
1417 if (!(check->state & CHK_ST_INPROGRESS)) {
1418 /* no check currently running */
1419 if (!expired) /* woke up too early */
1420 goto out_unlock;
Willy Tarreauabca5b62013-12-06 14:19:25 +01001421
Willy Tarreau51cd5952020-06-05 12:25:38 +02001422 /* we don't send any health-checks when the proxy is
1423 * stopped, the server should not be checked or the check
1424 * is disabled.
1425 */
1426 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
1427 proxy->state == PR_STSTOPPED)
1428 goto reschedule;
Christopher Faulet404f9192020-04-09 23:13:54 +02001429
Willy Tarreau51cd5952020-06-05 12:25:38 +02001430 /* we'll initiate a new check */
1431 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet404f9192020-04-09 23:13:54 +02001432
Willy Tarreau51cd5952020-06-05 12:25:38 +02001433 check->state |= CHK_ST_INPROGRESS;
1434 b_reset(&check->bi);
1435 b_reset(&check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001436
Willy Tarreau51cd5952020-06-05 12:25:38 +02001437 task_set_affinity(t, tid_bit);
1438
1439 check->current_step = NULL;
1440 tcpcheck_main(check);
1441 goto out_unlock;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001442 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001443 else {
1444 /* there was a test running.
1445 * First, let's check whether there was an uncaught error,
1446 * which can happen on connect timeout or error.
1447 */
1448 if (check->result == CHK_RES_UNKNOWN) {
1449 if ((conn->flags & CO_FL_ERROR) || cs->flags & CS_FL_ERROR || expired) {
1450 chk_report_conn_err(check, 0, expired);
1451 }
1452 else
1453 goto out_unlock; /* timeout not reached, wait again */
Christopher Faulet61cc8522020-04-20 14:54:42 +02001454 }
Christopher Faulet404f9192020-04-09 23:13:54 +02001455
Willy Tarreau51cd5952020-06-05 12:25:38 +02001456 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001457
Willy Tarreau51cd5952020-06-05 12:25:38 +02001458 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001459
Willy Tarreau51cd5952020-06-05 12:25:38 +02001460 if (conn && conn->xprt) {
1461 /* The check was aborted and the connection was not yet closed.
1462 * This can happen upon timeout, or when an external event such
1463 * as a failed response coupled with "observe layer7" caused the
1464 * server state to be suddenly changed.
1465 */
1466 conn_sock_drain(conn);
1467 cs_close(cs);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001468 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001469
Willy Tarreau51cd5952020-06-05 12:25:38 +02001470 if (cs) {
1471 if (check->wait_list.events)
1472 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
1473 /* We may have been scheduled to run, and the
1474 * I/O handler expects to have a cs, so remove
1475 * the tasklet
1476 */
1477 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
1478 cs_destroy(cs);
1479 cs = check->cs = NULL;
1480 conn = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001481 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001482
1483 if (check->sess != NULL) {
1484 vars_prune(&check->vars, check->sess, NULL);
1485 session_free(check->sess);
1486 check->sess = NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +02001487 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001488
1489 if (check->server) {
1490 if (check->result == CHK_RES_FAILED) {
1491 /* a failure or timeout detected */
1492 check_notify_failure(check);
1493 }
1494 else if (check->result == CHK_RES_CONDPASS) {
1495 /* check is OK but asks for stopping mode */
1496 check_notify_stopping(check);
1497 }
1498 else if (check->result == CHK_RES_PASSED) {
1499 /* a success was detected */
1500 check_notify_success(check);
1501 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001502 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001503 task_set_affinity(t, MAX_THREADS_MASK);
1504 check->state &= ~CHK_ST_INPROGRESS;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001505
Willy Tarreau51cd5952020-06-05 12:25:38 +02001506 if (check->server) {
1507 rv = 0;
1508 if (global.spread_checks > 0) {
1509 rv = srv_getinter(check) * global.spread_checks / 100;
1510 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001511 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001512 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +02001513 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001514 }
Willy Tarreau51cd5952020-06-05 12:25:38 +02001515
1516 reschedule:
1517 while (tick_is_expired(t->expire, now_ms))
1518 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
1519 out_unlock:
1520 if (check->server)
1521 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
1522 return t;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001523}
1524
Willy Tarreau51cd5952020-06-05 12:25:38 +02001525
Christopher Faulet61cc8522020-04-20 14:54:42 +02001526/**************************************************************************/
1527/************************** Init/deinit checks ****************************/
1528/**************************************************************************/
Willy Tarreaucee013e2020-06-05 11:40:38 +02001529const char *init_check(struct check *check, int type)
Christopher Faulet61cc8522020-04-20 14:54:42 +02001530{
1531 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001532
Christopher Faulet61cc8522020-04-20 14:54:42 +02001533 b_reset(&check->bi); check->bi.size = global.tune.chksize;
1534 b_reset(&check->bo); check->bo.size = global.tune.chksize;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001535
Christopher Faulet61cc8522020-04-20 14:54:42 +02001536 check->bi.area = calloc(check->bi.size, sizeof(char));
1537 check->bo.area = calloc(check->bo.size, sizeof(char));
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001538
Christopher Faulet61cc8522020-04-20 14:54:42 +02001539 if (!check->bi.area || !check->bo.area)
1540 return "out of memory while allocating check buffer";
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001541
Christopher Faulet61cc8522020-04-20 14:54:42 +02001542 check->wait_list.tasklet = tasklet_new();
1543 if (!check->wait_list.tasklet)
1544 return "out of memory while allocating check tasklet";
1545 check->wait_list.events = 0;
1546 check->wait_list.tasklet->process = event_srv_chk_io;
1547 check->wait_list.tasklet->context = check;
1548 return NULL;
1549}
1550
1551void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001552{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001553 task_destroy(check->task);
1554 if (check->wait_list.tasklet)
1555 tasklet_free(check->wait_list.tasklet);
1556
1557 free(check->bi.area);
1558 free(check->bo.area);
1559 if (check->cs) {
1560 free(check->cs->conn);
1561 check->cs->conn = NULL;
1562 cs_free(check->cs);
1563 check->cs = NULL;
1564 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001565}
1566
Christopher Faulet61cc8522020-04-20 14:54:42 +02001567/* manages a server health-check. Returns the time the task accepts to wait, or
1568 * TIME_ETERNITY for infinity.
1569 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001570struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001571{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001572 struct check *check = context;
1573
1574 if (check->type == PR_O2_EXT_CHK)
1575 return process_chk_proc(t, context, state);
1576 return process_chk_conn(t, context, state);
1577
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001578}
1579
Christopher Faulet61cc8522020-04-20 14:54:42 +02001580
1581static int start_check_task(struct check *check, int mininter,
1582 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001583{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001584 struct task *t;
1585 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001586
Christopher Faulet61cc8522020-04-20 14:54:42 +02001587 if (check->type == PR_O2_EXT_CHK)
1588 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001589
Christopher Faulet61cc8522020-04-20 14:54:42 +02001590 /* task for the check */
1591 if ((t = task_new(thread_mask)) == NULL) {
1592 ha_alert("Starting [%s:%s] check: out of memory.\n",
1593 check->server->proxy->id, check->server->id);
1594 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001595 }
1596
Christopher Faulet61cc8522020-04-20 14:54:42 +02001597 check->task = t;
1598 t->process = process_chk;
1599 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001600
Christopher Faulet61cc8522020-04-20 14:54:42 +02001601 if (mininter < srv_getinter(check))
1602 mininter = srv_getinter(check);
1603
1604 if (global.max_spread_checks && mininter > global.max_spread_checks)
1605 mininter = global.max_spread_checks;
1606
1607 /* check this every ms */
1608 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1609 check->start = now;
1610 task_queue(t);
1611
1612 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001613}
1614
Christopher Faulet61cc8522020-04-20 14:54:42 +02001615/* updates the server's weight during a warmup stage. Once the final weight is
1616 * reached, the task automatically stops. Note that any server status change
1617 * must have updated s->last_change accordingly.
1618 */
1619static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001620{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001621 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001622
Christopher Faulet61cc8522020-04-20 14:54:42 +02001623 /* by default, plan on stopping the task */
1624 t->expire = TICK_ETERNITY;
1625 if ((s->next_admin & SRV_ADMF_MAINT) ||
1626 (s->next_state != SRV_ST_STARTING))
1627 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001628
Christopher Faulet61cc8522020-04-20 14:54:42 +02001629 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001630
Christopher Faulet61cc8522020-04-20 14:54:42 +02001631 /* recalculate the weights and update the state */
1632 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001633
Christopher Faulet61cc8522020-04-20 14:54:42 +02001634 /* probably that we can refill this server with a bit more connections */
1635 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001636
Christopher Faulet61cc8522020-04-20 14:54:42 +02001637 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001638
Christopher Faulet61cc8522020-04-20 14:54:42 +02001639 /* get back there in 1 second or 1/20th of the slowstart interval,
1640 * whichever is greater, resulting in small 5% steps.
1641 */
1642 if (s->next_state == SRV_ST_STARTING)
1643 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1644 return t;
1645}
1646
1647/*
1648 * Start health-check.
1649 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1650 */
1651static int start_checks()
1652{
1653
1654 struct proxy *px;
1655 struct server *s;
1656 struct task *t;
1657 int nbcheck=0, mininter=0, srvpos=0;
1658
1659 /* 0- init the dummy frontend used to create all checks sessions */
1660 init_new_proxy(&checks_fe);
1661 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1662 checks_fe.mode = PR_MODE_TCP;
1663 checks_fe.maxconn = 0;
1664 checks_fe.conn_retries = CONN_RETRIES;
1665 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1666 checks_fe.timeout.client = TICK_ETERNITY;
1667
1668 /* 1- count the checkers to run simultaneously.
1669 * We also determine the minimum interval among all of those which
1670 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1671 * will be used to spread their start-up date. Those which have
1672 * a shorter interval will start independently and will not dictate
1673 * too short an interval for all others.
1674 */
1675 for (px = proxies_list; px; px = px->next) {
1676 for (s = px->srv; s; s = s->next) {
1677 if (s->slowstart) {
1678 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1679 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1680 return ERR_ALERT | ERR_FATAL;
1681 }
1682 /* We need a warmup task that will be called when the server
1683 * state switches from down to up.
1684 */
1685 s->warmup = t;
1686 t->process = server_warmup;
1687 t->context = s;
1688 /* server can be in this state only because of */
1689 if (s->next_state == SRV_ST_STARTING)
1690 task_schedule(s->warmup, tick_add(now_ms, MS_TO_TICKS(MAX(1000, (now.tv_sec - s->last_change)) / 20)));
Christopher Faulet5c288742020-03-31 08:15:58 +02001691 }
1692
Christopher Faulet61cc8522020-04-20 14:54:42 +02001693 if (s->check.state & CHK_ST_CONFIGURED) {
1694 nbcheck++;
1695 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1696 (!mininter || mininter > srv_getinter(&s->check)))
1697 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001698 }
1699
Christopher Faulet61cc8522020-04-20 14:54:42 +02001700 if (s->agent.state & CHK_ST_CONFIGURED) {
1701 nbcheck++;
1702 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1703 (!mininter || mininter > srv_getinter(&s->agent)))
1704 mininter = srv_getinter(&s->agent);
1705 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001706 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001707 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001708
Christopher Faulet61cc8522020-04-20 14:54:42 +02001709 if (!nbcheck)
1710 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001711
Christopher Faulet61cc8522020-04-20 14:54:42 +02001712 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001713
Christopher Faulet61cc8522020-04-20 14:54:42 +02001714 /*
1715 * 2- start them as far as possible from each others. For this, we will
1716 * start them after their interval set to the min interval divided by
1717 * the number of servers, weighted by the server's position in the list.
1718 */
1719 for (px = proxies_list; px; px = px->next) {
1720 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1721 if (init_pid_list()) {
1722 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1723 return ERR_ALERT | ERR_FATAL;
1724 }
1725 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001726
Christopher Faulet61cc8522020-04-20 14:54:42 +02001727 for (s = px->srv; s; s = s->next) {
1728 /* A task for the main check */
1729 if (s->check.state & CHK_ST_CONFIGURED) {
1730 if (s->check.type == PR_O2_EXT_CHK) {
1731 if (!prepare_external_check(&s->check))
1732 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001733 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001734 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1735 return ERR_ALERT | ERR_FATAL;
1736 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001737 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001738
Christopher Faulet61cc8522020-04-20 14:54:42 +02001739 /* A task for a auxiliary agent check */
1740 if (s->agent.state & CHK_ST_CONFIGURED) {
1741 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1742 return ERR_ALERT | ERR_FATAL;
1743 }
1744 srvpos++;
1745 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001746 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001747 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001748 return 0;
1749}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001750
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001751
Christopher Faulet61cc8522020-04-20 14:54:42 +02001752/*
1753 * Return value:
1754 * the port to be used for the health check
1755 * 0 in case no port could be found for the check
1756 */
1757static int srv_check_healthcheck_port(struct check *chk)
1758{
1759 int i = 0;
1760 struct server *srv = NULL;
1761
1762 srv = chk->server;
1763
1764 /* by default, we use the health check port ocnfigured */
1765 if (chk->port > 0)
1766 return chk->port;
1767
1768 /* try to get the port from check_core.addr if check.port not set */
1769 i = get_host_port(&chk->addr);
1770 if (i > 0)
1771 return i;
1772
1773 /* try to get the port from server address */
1774 /* prevent MAPPORTS from working at this point, since checks could
1775 * not be performed in such case (MAPPORTS impose a relative ports
1776 * based on live traffic)
1777 */
1778 if (srv->flags & SRV_F_MAPPORTS)
1779 return 0;
1780
1781 i = srv->svc_port; /* by default */
1782 if (i > 0)
1783 return i;
1784
1785 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001786}
1787
Christopher Faulet61cc8522020-04-20 14:54:42 +02001788/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1789 * if an error occurred.
1790 */
1791static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001792{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001793 const char *err;
1794 struct tcpcheck_rule *r;
1795 int ret = 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001796
Christopher Faulet61cc8522020-04-20 14:54:42 +02001797 if (!srv->do_check)
1798 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001799
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001800
Christopher Faulet61cc8522020-04-20 14:54:42 +02001801 /* If neither a port nor an addr was specified and no check transport
1802 * layer is forced, then the transport layer used by the checks is the
1803 * same as for the production traffic. Otherwise we use raw_sock by
1804 * default, unless one is specified.
1805 */
1806 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1807 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1808 srv->check.use_ssl = srv->use_ssl;
1809 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001810 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001811 else if (srv->check.use_ssl == 1)
1812 srv->check.xprt = xprt_get(XPRT_SSL);
1813 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001814 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001815 else if (srv->check.use_ssl == 1)
1816 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001817
Christopher Faulet12882cf2020-04-23 15:50:18 +02001818 /* Inherit the mux protocol from the server if not already defined for
1819 * the check
1820 */
1821 if (srv->mux_proto && !srv->check.mux_proto)
1822 srv->check.mux_proto = srv->mux_proto;
1823
Christopher Faulet61cc8522020-04-20 14:54:42 +02001824 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001825
Christopher Faulet61cc8522020-04-20 14:54:42 +02001826 /* We need at least a service port, a check port or the first tcp-check
1827 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1828 */
1829 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1830 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1831 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001832
Christopher Faulet61cc8522020-04-20 14:54:42 +02001833 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1834 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1835 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1836 ret |= ERR_ALERT | ERR_ABORT;
1837 goto out;
1838 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001839
Christopher Faulet61cc8522020-04-20 14:54:42 +02001840 /* search the first action (connect / send / expect) in the list */
1841 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1842 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1843 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1844 "nor tcp_check rule 'connect' with port information.\n",
1845 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1846 ret |= ERR_ALERT | ERR_ABORT;
1847 goto out;
1848 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001849
Christopher Faulet61cc8522020-04-20 14:54:42 +02001850 /* scan the tcp-check ruleset to ensure a port has been configured */
1851 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1852 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1853 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1854 "and a tcp_check rule 'connect' with no port information.\n",
1855 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1856 ret |= ERR_ALERT | ERR_ABORT;
1857 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001858 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001859 }
1860
Christopher Faulet61cc8522020-04-20 14:54:42 +02001861 init:
1862 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1863 struct tcpcheck_ruleset *rs = NULL;
1864 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1865 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001866
Christopher Faulet61cc8522020-04-20 14:54:42 +02001867 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1868 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001869
Christopher Faulet61cc8522020-04-20 14:54:42 +02001870 rs = find_tcpcheck_ruleset("*tcp-check");
1871 if (!rs) {
1872 rs = create_tcpcheck_ruleset("*tcp-check");
1873 if (rs == NULL) {
1874 ha_alert("config: %s '%s': out of memory.\n",
1875 proxy_type_str(srv->proxy), srv->proxy->id);
1876 ret |= ERR_ALERT | ERR_FATAL;
1877 goto out;
1878 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001879 }
1880
Christopher Faulet61cc8522020-04-20 14:54:42 +02001881 free_tcpcheck_vars(&rules->preset_vars);
1882 rules->list = &rs->rules;
1883 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001884 }
1885
Christopher Faulet61cc8522020-04-20 14:54:42 +02001886 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1887 if (err) {
1888 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1889 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1890 ret |= ERR_ALERT | ERR_ABORT;
1891 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001892 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001893 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1894 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001895
Christopher Faulet61cc8522020-04-20 14:54:42 +02001896 out:
1897 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001898}
1899
Christopher Faulet61cc8522020-04-20 14:54:42 +02001900/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1901 * if an error occurred.
1902 */
1903static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001904{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001905 struct tcpcheck_rule *chk;
1906 const char *err;
1907 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001908
Christopher Faulet61cc8522020-04-20 14:54:42 +02001909 if (!srv->do_agent)
1910 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001911
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001912 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001913 * implicit one is inserted before all others.
1914 */
1915 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1916 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1917 chk = calloc(1, sizeof(*chk));
1918 if (!chk) {
1919 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1920 " to agent-check for server '%s' (out of memory).\n",
1921 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1922 ret |= ERR_ALERT | ERR_FATAL;
1923 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001924 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001925 chk->action = TCPCHK_ACT_CONNECT;
1926 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1927 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001928 }
1929
Christopher Faulete5870d82020-04-15 11:32:03 +02001930
Christopher Faulet61cc8522020-04-20 14:54:42 +02001931 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1932 if (err) {
1933 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1934 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1935 ret |= ERR_ALERT | ERR_ABORT;
1936 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001937 }
1938
Christopher Faulet61cc8522020-04-20 14:54:42 +02001939 if (!srv->agent.inter)
1940 srv->agent.inter = srv->check.inter;
1941
1942 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1943 global.maxsock++;
1944
1945 out:
1946 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001947}
1948
Christopher Faulet61cc8522020-04-20 14:54:42 +02001949static void deinit_srv_check(struct server *srv)
1950{
1951 if (srv->check.state & CHK_ST_CONFIGURED)
1952 free_check(&srv->check);
1953 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1954 srv->do_check = 0;
1955}
Christopher Faulete5870d82020-04-15 11:32:03 +02001956
Christopher Faulet61cc8522020-04-20 14:54:42 +02001957
1958static void deinit_srv_agent_check(struct server *srv)
1959{
1960 if (srv->agent.tcpcheck_rules) {
1961 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1962 free(srv->agent.tcpcheck_rules);
1963 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001964 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001965
Christopher Faulet61cc8522020-04-20 14:54:42 +02001966 if (srv->agent.state & CHK_ST_CONFIGURED)
1967 free_check(&srv->agent);
1968
1969 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1970 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001971}
1972
Willy Tarreaucee013e2020-06-05 11:40:38 +02001973REGISTER_POST_SERVER_CHECK(init_srv_check);
1974REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001975REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001976
Willy Tarreaucee013e2020-06-05 11:40:38 +02001977REGISTER_SERVER_DEINIT(deinit_srv_check);
1978REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001979
Christopher Faulet61cc8522020-04-20 14:54:42 +02001980
1981/**************************************************************************/
1982/************************** Check sample fetches **************************/
1983/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001984
Christopher Faulet61cc8522020-04-20 14:54:42 +02001985static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001986 { /* END */ },
1987}};
1988
1989INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1990
1991
1992/**************************************************************************/
1993/************************ Check's parsing functions ***********************/
1994/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001995/* Parses the "http-check" proxy keyword */
1996static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1997 struct proxy *defpx, const char *file, int line,
1998 char **errmsg)
1999{
Christopher Faulete5870d82020-04-15 11:32:03 +02002000 struct tcpcheck_ruleset *rs = NULL;
2001 struct tcpcheck_rule *chk = NULL;
2002 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02002003
2004 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
2005 ret = 1;
2006
2007 cur_arg = 1;
2008 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
2009 /* enable a graceful server shutdown on an HTTP 404 response */
2010 curpx->options |= PR_O_DISABLE404;
2011 if (too_many_args(1, args, errmsg, NULL))
2012 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02002013 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02002014 }
2015 else if (strcmp(args[cur_arg], "send-state") == 0) {
2016 /* enable emission of the apparent state of a server in HTTP checks */
2017 curpx->options2 |= PR_O2_CHK_SNDST;
2018 if (too_many_args(1, args, errmsg, NULL))
2019 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02002020 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02002021 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02002022
Christopher Faulete5870d82020-04-15 11:32:03 +02002023 /* Deduce the ruleset name from the proxy info */
2024 chunk_printf(&trash, "*http-check-%s_%s-%d",
2025 ((curpx == defpx) ? "defaults" : curpx->id),
2026 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02002027
Christopher Faulet61cc8522020-04-20 14:54:42 +02002028 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002029 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002030 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002031 if (rs == NULL) {
2032 memprintf(errmsg, "out of memory.\n");
2033 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02002034 }
2035 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02002036
Christopher Faulete5870d82020-04-15 11:32:03 +02002037 index = 0;
2038 if (!LIST_ISEMPTY(&rs->rules)) {
2039 chk = LIST_PREV(&rs->rules, typeof(chk), list);
2040 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
2041 index = chk->index + 1;
2042 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02002043
Christopher Faulete5870d82020-04-15 11:32:03 +02002044 if (strcmp(args[cur_arg], "connect") == 0)
2045 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
2046 else if (strcmp(args[cur_arg], "send") == 0)
2047 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
2048 else if (strcmp(args[cur_arg], "expect") == 0)
2049 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
2050 file, line, errmsg);
2051 else if (strcmp(args[cur_arg], "comment") == 0)
2052 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
2053 else {
2054 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02002055
Christopher Faulete5870d82020-04-15 11:32:03 +02002056 if (!kw) {
2057 action_kw_tcp_check_build_list(&trash);
2058 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
2059 " 'send', 'expect'%s%s. but got '%s'",
2060 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
2061 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02002062 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002063 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
2064 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02002065
Christopher Faulete5870d82020-04-15 11:32:03 +02002066 if (!chk) {
2067 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
2068 goto error;
2069 }
2070 ret = (*errmsg != NULL); /* Handle warning */
2071
2072 chk->index = index;
2073 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
2074 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
2075 /* Use this ruleset if the proxy already has http-check enabled */
2076 curpx->tcpcheck_rules.list = &rs->rules;
2077 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
2078 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
2079 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
2080 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02002081 goto error;
2082 }
2083 }
2084 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02002085 /* mark this ruleset as unused for now */
2086 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
2087 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02002088 }
2089
Christopher Faulete5870d82020-04-15 11:32:03 +02002090 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02002091 return ret;
2092
2093 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02002094 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002095 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02002096 return -1;
2097}
2098
Christopher Faulete9111b62020-04-09 18:12:08 +02002099/* Parses the "external-check" proxy keyword */
2100static int proxy_parse_extcheck(char **args, int section, struct proxy *curpx,
2101 struct proxy *defpx, const char *file, int line,
2102 char **errmsg)
2103{
2104 int cur_arg, ret = 0;
2105
2106 cur_arg = 1;
2107 if (!*(args[cur_arg])) {
2108 memprintf(errmsg, "missing argument after '%s'.\n", args[0]);
2109 goto error;
2110 }
2111
2112 if (strcmp(args[cur_arg], "command") == 0) {
2113 if (too_many_args(2, args, errmsg, NULL))
2114 goto error;
2115 if (!*(args[cur_arg+1])) {
2116 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
2117 goto error;
2118 }
2119 free(curpx->check_command);
2120 curpx->check_command = strdup(args[cur_arg+1]);
2121 }
2122 else if (strcmp(args[cur_arg], "path") == 0) {
2123 if (too_many_args(2, args, errmsg, NULL))
2124 goto error;
2125 if (!*(args[cur_arg+1])) {
2126 memprintf(errmsg, "missing argument after '%s'.", args[cur_arg]);
2127 goto error;
2128 }
2129 free(curpx->check_path);
2130 curpx->check_path = strdup(args[cur_arg+1]);
2131 }
2132 else {
2133 memprintf(errmsg, "'%s' only supports 'command' and 'path'. but got '%s'.",
2134 args[0], args[1]);
2135 goto error;
2136 }
2137
2138 ret = (*errmsg != NULL); /* Handle warning */
2139 return ret;
2140
2141error:
2142 return -1;
2143}
Christopher Faulet33f05df2020-04-01 11:08:50 +02002144
Christopher Faulet430e4802020-04-09 15:28:16 +02002145/* Parses the "option tcp-check" proxy keyword */
2146int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2147 const char *file, int line)
2148{
Christopher Faulet404f9192020-04-09 23:13:54 +02002149 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02002150 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2151 int err_code = 0;
2152
2153 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2154 err_code |= ERR_WARN;
2155
2156 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2157 goto out;
2158
Christopher Faulet404f9192020-04-09 23:13:54 +02002159 curpx->options2 &= ~PR_O2_CHK_ANY;
2160 curpx->options2 |= PR_O2_TCPCHK_CHK;
2161
Christopher Fauletd7e63962020-04-17 20:15:59 +02002162 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02002163 /* If a tcp-check rulesset is already set, do nothing */
2164 if (rules->list)
2165 goto out;
2166
2167 /* If a tcp-check ruleset is waiting to be used for the current proxy,
2168 * get it.
2169 */
2170 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
2171 goto curpx_ruleset;
2172
2173 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
2174 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002175 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02002176 if (rs)
2177 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02002178 }
2179
Christopher Faulet404f9192020-04-09 23:13:54 +02002180 curpx_ruleset:
2181 /* Deduce the ruleset name from the proxy info */
2182 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
2183 ((curpx == defpx) ? "defaults" : curpx->id),
2184 curpx->conf.file, curpx->conf.line);
2185
Christopher Faulet61cc8522020-04-20 14:54:42 +02002186 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02002187 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002188 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02002189 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02002190 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2191 goto error;
2192 }
Christopher Faulet430e4802020-04-09 15:28:16 +02002193 }
2194
Christopher Faulet404f9192020-04-09 23:13:54 +02002195 ruleset_found:
2196 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02002197 rules->list = &rs->rules;
Christopher Fauletd7e63962020-04-17 20:15:59 +02002198 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02002199
2200 out:
2201 return err_code;
2202
2203 error:
2204 err_code |= ERR_ALERT | ERR_FATAL;
2205 goto out;
2206}
Christopher Faulet33f05df2020-04-01 11:08:50 +02002207
2208/* Parses the "option redis-check" proxy keyword */
2209int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2210 const char *file, int line)
2211{
2212 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
2213 static char *redis_res = "+PONG\r\n";
2214
2215 struct tcpcheck_ruleset *rs = NULL;
2216 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2217 struct tcpcheck_rule *chk;
2218 char *errmsg = NULL;
2219 int err_code = 0;
2220
2221 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2222 err_code |= ERR_WARN;
2223
2224 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2225 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02002226
2227 curpx->options2 &= ~PR_O2_CHK_ANY;
2228 curpx->options2 |= PR_O2_TCPCHK_CHK;
2229
2230 free_tcpcheck_vars(&rules->preset_vars);
2231 rules->list = NULL;
2232 rules->flags = 0;
2233
Christopher Faulet61cc8522020-04-20 14:54:42 +02002234 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02002235 if (rs)
2236 goto ruleset_found;
2237
Christopher Faulet61cc8522020-04-20 14:54:42 +02002238 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02002239 if (rs == NULL) {
2240 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2241 goto error;
2242 }
2243
2244 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
2245 1, curpx, &rs->rules, file, line, &errmsg);
2246 if (!chk) {
2247 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2248 goto error;
2249 }
2250 chk->index = 0;
2251 LIST_ADDQ(&rs->rules, &chk->list);
2252
2253 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
2254 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02002255 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02002256 "on-success", "Redis server is ok",
2257 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002258 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02002259 if (!chk) {
2260 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2261 goto error;
2262 }
2263 chk->index = 1;
2264 LIST_ADDQ(&rs->rules, &chk->list);
2265
Christopher Faulet33f05df2020-04-01 11:08:50 +02002266 ruleset_found:
2267 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002268 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02002269
2270 out:
2271 free(errmsg);
2272 return err_code;
2273
2274 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002275 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02002276 err_code |= ERR_ALERT | ERR_FATAL;
2277 goto out;
2278}
2279
Christopher Faulet811f78c2020-04-01 11:10:27 +02002280
2281/* Parses the "option ssl-hello-chk" proxy keyword */
2282int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2283 const char *file, int line)
2284{
2285 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
2286 * ssl-hello-chk option to ensure that the remote server speaks SSL.
2287 *
2288 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
2289 */
2290 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05002291 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02002292 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
2293 "0079" /* ContentLength : 0x79 bytes after this one */
2294 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
2295 "000075" /* HandshakeLength : 0x75 bytes after this one */
2296 "0300" /* Hello Version : 0x0300 = v3 */
2297 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
2298 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
2299 "00" /* Session ID length : empty (no session ID) */
2300 "004E" /* Cipher Suite Length : 78 bytes after this one */
2301 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
2302 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
2303 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
2304 "000D" "000E" "000F" "0010" /* various bit lengths, */
2305 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
2306 "0015" "0016" "0017" "0018"
2307 "0019" "001A" "001B" "002F"
2308 "0030" "0031" "0032" "0033"
2309 "0034" "0035" "0036" "0037"
2310 "0038" "0039" "003A"
2311 "01" /* Compression Length : 0x01 = 1 byte for types */
2312 "00" /* Compression Type : 0x00 = NULL compression */
2313 };
2314
2315 struct tcpcheck_ruleset *rs = NULL;
2316 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2317 struct tcpcheck_rule *chk;
2318 char *errmsg = NULL;
2319 int err_code = 0;
2320
2321 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2322 err_code |= ERR_WARN;
2323
2324 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2325 goto out;
2326
Christopher Faulet811f78c2020-04-01 11:10:27 +02002327 curpx->options2 &= ~PR_O2_CHK_ANY;
2328 curpx->options2 |= PR_O2_TCPCHK_CHK;
2329
2330 free_tcpcheck_vars(&rules->preset_vars);
2331 rules->list = NULL;
2332 rules->flags = 0;
2333
Christopher Faulet61cc8522020-04-20 14:54:42 +02002334 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02002335 if (rs)
2336 goto ruleset_found;
2337
Christopher Faulet61cc8522020-04-20 14:54:42 +02002338 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02002339 if (rs == NULL) {
2340 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2341 goto error;
2342 }
2343
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002344 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02002345 1, curpx, &rs->rules, file, line, &errmsg);
2346 if (!chk) {
2347 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2348 goto error;
2349 }
2350 chk->index = 0;
2351 LIST_ADDQ(&rs->rules, &chk->list);
2352
2353 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02002354 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02002355 "error-status", "L6RSP", "tout-status", "L6TOUT",
2356 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002357 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02002358 if (!chk) {
2359 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2360 goto error;
2361 }
2362 chk->index = 1;
2363 LIST_ADDQ(&rs->rules, &chk->list);
2364
Christopher Faulet811f78c2020-04-01 11:10:27 +02002365 ruleset_found:
2366 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002367 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02002368
2369 out:
2370 free(errmsg);
2371 return err_code;
2372
2373 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002374 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02002375 err_code |= ERR_ALERT | ERR_FATAL;
2376 goto out;
2377}
2378
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002379/* Parses the "option smtpchk" proxy keyword */
2380int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2381 const char *file, int line)
2382{
2383 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
2384
2385 struct tcpcheck_ruleset *rs = NULL;
2386 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2387 struct tcpcheck_rule *chk;
2388 struct tcpcheck_var *var = NULL;
2389 char *cmd = NULL, *errmsg = NULL;
2390 int err_code = 0;
2391
2392 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2393 err_code |= ERR_WARN;
2394
2395 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
2396 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002397
2398 curpx->options2 &= ~PR_O2_CHK_ANY;
2399 curpx->options2 |= PR_O2_TCPCHK_CHK;
2400
2401 free_tcpcheck_vars(&rules->preset_vars);
2402 rules->list = NULL;
2403 rules->flags = 0;
2404
2405 cur_arg += 2;
2406 if (*args[cur_arg] && *args[cur_arg+1] &&
2407 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
2408 cmd = calloc(strlen(args[cur_arg]) + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
2409 if (cmd)
2410 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
2411 }
2412 else {
2413 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
2414 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
2415 cmd = strdup("HELO localhost");
2416 }
2417
Christopher Fauletb61caf42020-04-21 10:57:42 +02002418 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002419 if (cmd == NULL || var == NULL) {
2420 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2421 goto error;
2422 }
2423 var->data.type = SMP_T_STR;
2424 var->data.u.str.area = cmd;
2425 var->data.u.str.data = strlen(cmd);
2426 LIST_INIT(&var->list);
2427 LIST_ADDQ(&rules->preset_vars, &var->list);
2428 cmd = NULL;
2429 var = NULL;
2430
Christopher Faulet61cc8522020-04-20 14:54:42 +02002431 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002432 if (rs)
2433 goto ruleset_found;
2434
Christopher Faulet61cc8522020-04-20 14:54:42 +02002435 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002436 if (rs == NULL) {
2437 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2438 goto error;
2439 }
2440
2441 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2442 1, curpx, &rs->rules, file, line, &errmsg);
2443 if (!chk) {
2444 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2445 goto error;
2446 }
2447 chk->index = 0;
2448 LIST_ADDQ(&rs->rules, &chk->list);
2449
2450 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
2451 "min-recv", "4",
2452 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002453 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002454 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002455 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002456 if (!chk) {
2457 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2458 goto error;
2459 }
2460 chk->index = 1;
2461 LIST_ADDQ(&rs->rules, &chk->list);
2462
2463 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
2464 "min-recv", "4",
2465 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02002466 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
2467 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002468 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002469 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002470 if (!chk) {
2471 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2472 goto error;
2473 }
2474 chk->index = 2;
2475 LIST_ADDQ(&rs->rules, &chk->list);
2476
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002477 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002478 1, curpx, &rs->rules, file, line, &errmsg);
2479 if (!chk) {
2480 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2481 goto error;
2482 }
2483 chk->index = 3;
2484 LIST_ADDQ(&rs->rules, &chk->list);
2485
2486 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
2487 "min-recv", "4",
2488 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02002489 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
2490 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
2491 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002492 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002493 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002494 if (!chk) {
2495 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2496 goto error;
2497 }
2498 chk->index = 4;
2499 LIST_ADDQ(&rs->rules, &chk->list);
2500
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002501 ruleset_found:
2502 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002503 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002504
2505 out:
2506 free(errmsg);
2507 return err_code;
2508
2509 error:
2510 free(cmd);
2511 free(var);
2512 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002513 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02002514 err_code |= ERR_ALERT | ERR_FATAL;
2515 goto out;
2516}
Christopher Faulet811f78c2020-04-01 11:10:27 +02002517
Christopher Fauletce355072020-04-02 11:44:39 +02002518/* Parses the "option pgsql-check" proxy keyword */
2519int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2520 const char *file, int line)
2521{
2522 static char pgsql_req[] = {
2523 "%[var(check.plen),htonl,hex]" /* The packet length*/
2524 "00030000" /* the version 3.0 */
2525 "7573657200" /* "user" key */
2526 "%[var(check.username),hex]00" /* the username */
2527 "00"
2528 };
2529
2530 struct tcpcheck_ruleset *rs = NULL;
2531 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2532 struct tcpcheck_rule *chk;
2533 struct tcpcheck_var *var = NULL;
2534 char *user = NULL, *errmsg = NULL;
2535 size_t packetlen = 0;
2536 int err_code = 0;
2537
2538 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2539 err_code |= ERR_WARN;
2540
2541 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
2542 goto out;
2543
Christopher Fauletce355072020-04-02 11:44:39 +02002544 curpx->options2 &= ~PR_O2_CHK_ANY;
2545 curpx->options2 |= PR_O2_TCPCHK_CHK;
2546
2547 free_tcpcheck_vars(&rules->preset_vars);
2548 rules->list = NULL;
2549 rules->flags = 0;
2550
2551 cur_arg += 2;
2552 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2553 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
2554 file, line, args[0], args[1]);
2555 goto error;
2556 }
2557 if (strcmp(args[cur_arg], "user") == 0) {
2558 packetlen = 15 + strlen(args[cur_arg+1]);
2559 user = strdup(args[cur_arg+1]);
2560
Christopher Fauletb61caf42020-04-21 10:57:42 +02002561 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02002562 if (user == NULL || var == NULL) {
2563 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2564 goto error;
2565 }
2566 var->data.type = SMP_T_STR;
2567 var->data.u.str.area = user;
2568 var->data.u.str.data = strlen(user);
2569 LIST_INIT(&var->list);
2570 LIST_ADDQ(&rules->preset_vars, &var->list);
2571 user = NULL;
2572 var = NULL;
2573
Christopher Fauletb61caf42020-04-21 10:57:42 +02002574 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02002575 if (var == NULL) {
2576 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2577 goto error;
2578 }
2579 var->data.type = SMP_T_SINT;
2580 var->data.u.sint = packetlen;
2581 LIST_INIT(&var->list);
2582 LIST_ADDQ(&rules->preset_vars, &var->list);
2583 var = NULL;
2584 }
2585 else {
2586 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
2587 file, line, args[0], args[1]);
2588 goto error;
2589 }
2590
Christopher Faulet61cc8522020-04-20 14:54:42 +02002591 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002592 if (rs)
2593 goto ruleset_found;
2594
Christopher Faulet61cc8522020-04-20 14:54:42 +02002595 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002596 if (rs == NULL) {
2597 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2598 goto error;
2599 }
2600
2601 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2602 1, curpx, &rs->rules, file, line, &errmsg);
2603 if (!chk) {
2604 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2605 goto error;
2606 }
2607 chk->index = 0;
2608 LIST_ADDQ(&rs->rules, &chk->list);
2609
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002610 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002611 1, curpx, &rs->rules, file, line, &errmsg);
2612 if (!chk) {
2613 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2614 goto error;
2615 }
2616 chk->index = 1;
2617 LIST_ADDQ(&rs->rules, &chk->list);
2618
2619 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2620 "min-recv", "5",
2621 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002622 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002623 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002624 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002625 if (!chk) {
2626 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2627 goto error;
2628 }
2629 chk->index = 2;
2630 LIST_ADDQ(&rs->rules, &chk->list);
2631
Christopher Fauletb841c742020-04-27 18:29:49 +02002632 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^52000000(08|0A|0C)000000(00|02|03|04|05|06)",
Christopher Fauletce355072020-04-02 11:44:39 +02002633 "min-recv", "9",
2634 "error-status", "L7STS",
2635 "on-success", "PostgreSQL server is ok",
2636 "on-error", "PostgreSQL unknown error",
2637 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002638 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002639 if (!chk) {
2640 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2641 goto error;
2642 }
2643 chk->index = 3;
2644 LIST_ADDQ(&rs->rules, &chk->list);
2645
Christopher Fauletce355072020-04-02 11:44:39 +02002646 ruleset_found:
2647 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002648 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002649
2650 out:
2651 free(errmsg);
2652 return err_code;
2653
2654 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002655 free(user);
2656 free(var);
2657 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002658 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002659 err_code |= ERR_ALERT | ERR_FATAL;
2660 goto out;
2661}
2662
2663
2664/* Parses the "option mysql-check" proxy keyword */
2665int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2666 const char *file, int line)
2667{
2668 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2669 * const char mysql40_client_auth_pkt[] = {
2670 * "\x0e\x00\x00" // packet length
2671 * "\x01" // packet number
2672 * "\x00\x00" // client capabilities
2673 * "\x00\x00\x01" // max packet
2674 * "haproxy\x00" // username (null terminated string)
2675 * "\x00" // filler (always 0x00)
2676 * "\x01\x00\x00" // packet length
2677 * "\x00" // packet number
2678 * "\x01" // COM_QUIT command
2679 * };
2680 */
2681 static char mysql40_rsname[] = "*mysql40-check";
2682 static char mysql40_req[] = {
2683 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2684 "0080" /* client capabilities */
2685 "000001" /* max packet */
2686 "%[var(check.username),hex]00" /* the username */
2687 "00" /* filler (always 0x00) */
2688 "010000" /* packet length*/
2689 "00" /* sequence ID */
2690 "01" /* COM_QUIT command */
2691 };
2692
2693 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2694 * const char mysql41_client_auth_pkt[] = {
2695 * "\x0e\x00\x00\" // packet length
2696 * "\x01" // packet number
2697 * "\x00\x00\x00\x00" // client capabilities
2698 * "\x00\x00\x00\x01" // max packet
2699 * "\x21" // character set (UTF-8)
2700 * char[23] // All zeroes
2701 * "haproxy\x00" // username (null terminated string)
2702 * "\x00" // filler (always 0x00)
2703 * "\x01\x00\x00" // packet length
2704 * "\x00" // packet number
2705 * "\x01" // COM_QUIT command
2706 * };
2707 */
2708 static char mysql41_rsname[] = "*mysql41-check";
2709 static char mysql41_req[] = {
2710 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2711 "00820000" /* client capabilities */
2712 "00800001" /* max packet */
2713 "21" /* character set (UTF-8) */
2714 "000000000000000000000000" /* 23 bytes, al zeroes */
2715 "0000000000000000000000"
2716 "%[var(check.username),hex]00" /* the username */
2717 "00" /* filler (always 0x00) */
2718 "010000" /* packet length*/
2719 "00" /* sequence ID */
2720 "01" /* COM_QUIT command */
2721 };
2722
2723 struct tcpcheck_ruleset *rs = NULL;
2724 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2725 struct tcpcheck_rule *chk;
2726 struct tcpcheck_var *var = NULL;
2727 char *mysql_rsname = "*mysql-check";
2728 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2729 int index = 0, err_code = 0;
2730
2731 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2732 err_code |= ERR_WARN;
2733
2734 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2735 goto out;
2736
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002737 curpx->options2 &= ~PR_O2_CHK_ANY;
2738 curpx->options2 |= PR_O2_TCPCHK_CHK;
2739
2740 free_tcpcheck_vars(&rules->preset_vars);
2741 rules->list = NULL;
2742 rules->flags = 0;
2743
2744 cur_arg += 2;
2745 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002746 int packetlen, userlen;
2747
2748 if (strcmp(args[cur_arg], "user") != 0) {
2749 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2750 file, line, args[0], args[1], args[cur_arg]);
2751 goto error;
2752 }
2753
2754 if (*(args[cur_arg+1]) == 0) {
2755 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2756 file, line, args[0], args[1], args[cur_arg]);
2757 goto error;
2758 }
2759
2760 hdr = calloc(4, sizeof(*hdr));
2761 user = strdup(args[cur_arg+1]);
2762 userlen = strlen(args[cur_arg+1]);
2763
2764 if (hdr == NULL || user == NULL) {
2765 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2766 goto error;
2767 }
2768
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002769 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002770 packetlen = userlen + 7 + 27;
2771 mysql_req = mysql41_req;
2772 mysql_rsname = mysql41_rsname;
2773 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002774 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002775 packetlen = userlen + 7;
2776 mysql_req = mysql40_req;
2777 mysql_rsname = mysql40_rsname;
2778 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002779 else {
2780 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2781 file, line, args[cur_arg], args[cur_arg+2]);
2782 goto error;
2783 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002784
2785 hdr[0] = (unsigned char)(packetlen & 0xff);
2786 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2787 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2788 hdr[3] = 1;
2789
Christopher Fauletb61caf42020-04-21 10:57:42 +02002790 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002791 if (var == NULL) {
2792 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2793 goto error;
2794 }
2795 var->data.type = SMP_T_STR;
2796 var->data.u.str.area = hdr;
2797 var->data.u.str.data = 4;
2798 LIST_INIT(&var->list);
2799 LIST_ADDQ(&rules->preset_vars, &var->list);
2800 hdr = NULL;
2801 var = NULL;
2802
Christopher Fauletb61caf42020-04-21 10:57:42 +02002803 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002804 if (var == NULL) {
2805 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2806 goto error;
2807 }
2808 var->data.type = SMP_T_STR;
2809 var->data.u.str.area = user;
2810 var->data.u.str.data = strlen(user);
2811 LIST_INIT(&var->list);
2812 LIST_ADDQ(&rules->preset_vars, &var->list);
2813 user = NULL;
2814 var = NULL;
2815 }
2816
Christopher Faulet61cc8522020-04-20 14:54:42 +02002817 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002818 if (rs)
2819 goto ruleset_found;
2820
Christopher Faulet61cc8522020-04-20 14:54:42 +02002821 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002822 if (rs == NULL) {
2823 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2824 goto error;
2825 }
2826
2827 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2828 1, curpx, &rs->rules, file, line, &errmsg);
2829 if (!chk) {
2830 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2831 goto error;
2832 }
2833 chk->index = index++;
2834 LIST_ADDQ(&rs->rules, &chk->list);
2835
2836 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002837 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002838 1, curpx, &rs->rules, file, line, &errmsg);
2839 if (!chk) {
2840 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2841 goto error;
2842 }
2843 chk->index = index++;
2844 LIST_ADDQ(&rs->rules, &chk->list);
2845 }
2846
2847 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002848 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002849 if (!chk) {
2850 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2851 goto error;
2852 }
2853 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2854 chk->index = index++;
2855 LIST_ADDQ(&rs->rules, &chk->list);
2856
2857 if (mysql_req) {
2858 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002859 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002860 if (!chk) {
2861 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2862 goto error;
2863 }
2864 chk->expect.custom = tcpcheck_mysql_expect_ok;
2865 chk->index = index++;
2866 LIST_ADDQ(&rs->rules, &chk->list);
2867 }
2868
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002869 ruleset_found:
2870 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002871 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002872
2873 out:
2874 free(errmsg);
2875 return err_code;
2876
2877 error:
2878 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002879 free(user);
2880 free(var);
2881 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002882 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002883 err_code |= ERR_ALERT | ERR_FATAL;
2884 goto out;
2885}
2886
Christopher Faulet1997eca2020-04-03 23:13:50 +02002887int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2888 const char *file, int line)
2889{
2890 static char *ldap_req = "300C020101600702010304008000";
2891
2892 struct tcpcheck_ruleset *rs = NULL;
2893 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2894 struct tcpcheck_rule *chk;
2895 char *errmsg = NULL;
2896 int err_code = 0;
2897
2898 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2899 err_code |= ERR_WARN;
2900
2901 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2902 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002903
2904 curpx->options2 &= ~PR_O2_CHK_ANY;
2905 curpx->options2 |= PR_O2_TCPCHK_CHK;
2906
2907 free_tcpcheck_vars(&rules->preset_vars);
2908 rules->list = NULL;
2909 rules->flags = 0;
2910
Christopher Faulet61cc8522020-04-20 14:54:42 +02002911 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002912 if (rs)
2913 goto ruleset_found;
2914
Christopher Faulet61cc8522020-04-20 14:54:42 +02002915 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002916 if (rs == NULL) {
2917 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2918 goto error;
2919 }
2920
2921 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2922 1, curpx, &rs->rules, file, line, &errmsg);
2923 if (!chk) {
2924 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2925 goto error;
2926 }
2927 chk->index = 0;
2928 LIST_ADDQ(&rs->rules, &chk->list);
2929
2930 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2931 "min-recv", "14",
2932 "on-error", "Not LDAPv3 protocol",
2933 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002934 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002935 if (!chk) {
2936 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2937 goto error;
2938 }
2939 chk->index = 1;
2940 LIST_ADDQ(&rs->rules, &chk->list);
2941
2942 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002943 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002944 if (!chk) {
2945 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2946 goto error;
2947 }
2948 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2949 chk->index = 2;
2950 LIST_ADDQ(&rs->rules, &chk->list);
2951
Christopher Faulet1997eca2020-04-03 23:13:50 +02002952 ruleset_found:
2953 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02002954 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002955
2956 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002957 free(errmsg);
2958 return err_code;
2959
2960 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002961 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002962 err_code |= ERR_ALERT | ERR_FATAL;
2963 goto out;
2964}
2965
2966int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2967 const char *file, int line)
2968{
2969 struct tcpcheck_ruleset *rs = NULL;
2970 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2971 struct tcpcheck_rule *chk;
2972 char *spop_req = NULL;
2973 char *errmsg = NULL;
2974 int spop_len = 0, err_code = 0;
2975
2976 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2977 err_code |= ERR_WARN;
2978
2979 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2980 goto out;
2981
Christopher Faulet267b01b2020-04-04 10:27:09 +02002982 curpx->options2 &= ~PR_O2_CHK_ANY;
2983 curpx->options2 |= PR_O2_TCPCHK_CHK;
2984
2985 free_tcpcheck_vars(&rules->preset_vars);
2986 rules->list = NULL;
2987 rules->flags = 0;
2988
2989
Christopher Faulet61cc8522020-04-20 14:54:42 +02002990 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002991 if (rs)
2992 goto ruleset_found;
2993
Christopher Faulet61cc8522020-04-20 14:54:42 +02002994 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002995 if (rs == NULL) {
2996 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2997 goto error;
2998 }
2999
3000 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
3001 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
3002 goto error;
3003 }
3004 chunk_reset(&trash);
3005 dump_binary(&trash, spop_req, spop_len);
3006 trash.area[trash.data] = '\0';
3007
3008 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
3009 1, curpx, &rs->rules, file, line, &errmsg);
3010 if (!chk) {
3011 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
3012 goto error;
3013 }
3014 chk->index = 0;
3015 LIST_ADDQ(&rs->rules, &chk->list);
3016
3017 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02003018 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02003019 if (!chk) {
3020 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
3021 goto error;
3022 }
3023 chk->expect.custom = tcpcheck_spop_expect_agenthello;
3024 chk->index = 1;
3025 LIST_ADDQ(&rs->rules, &chk->list);
3026
Christopher Faulet267b01b2020-04-04 10:27:09 +02003027 ruleset_found:
3028 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02003029 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02003030
3031 out:
3032 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02003033 free(errmsg);
3034 return err_code;
3035
3036 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003037 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02003038 err_code |= ERR_ALERT | ERR_FATAL;
3039 goto out;
3040}
Christopher Fauletce355072020-04-02 11:44:39 +02003041
Christopher Faulete5870d82020-04-15 11:32:03 +02003042
3043struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
3044{
3045 struct tcpcheck_rule *chk = NULL;
3046 struct tcpcheck_http_hdr *hdr = NULL;
3047 char *meth = NULL, *uri = NULL, *vsn = NULL;
3048 char *hdrs, *body;
3049
3050 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
3051 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
3052 if (hdrs == body)
3053 hdrs = NULL;
3054 if (hdrs) {
3055 *hdrs = '\0';
3056 hdrs +=2;
3057 }
3058 if (body) {
3059 *body = '\0';
3060 body += 4;
3061 }
3062 if (hdrs || body) {
3063 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
3064 " Please, consider to use 'http-check send' directive instead.");
3065 }
3066
3067 chk = calloc(1, sizeof(*chk));
3068 if (!chk) {
3069 memprintf(errmsg, "out of memory");
3070 goto error;
3071 }
3072 chk->action = TCPCHK_ACT_SEND;
3073 chk->send.type = TCPCHK_SEND_HTTP;
3074 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
3075 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
3076 LIST_INIT(&chk->send.http.hdrs);
3077
3078 /* Copy the method, uri and version */
3079 if (*args[cur_arg]) {
3080 if (!*args[cur_arg+1])
3081 uri = args[cur_arg];
3082 else
3083 meth = args[cur_arg];
3084 }
3085 if (*args[cur_arg+1])
3086 uri = args[cur_arg+1];
3087 if (*args[cur_arg+2])
3088 vsn = args[cur_arg+2];
3089
3090 if (meth) {
3091 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
3092 chk->send.http.meth.str.area = strdup(meth);
3093 chk->send.http.meth.str.data = strlen(meth);
3094 if (!chk->send.http.meth.str.area) {
3095 memprintf(errmsg, "out of memory");
3096 goto error;
3097 }
3098 }
3099 if (uri) {
3100 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02003101 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02003102 memprintf(errmsg, "out of memory");
3103 goto error;
3104 }
3105 }
3106 if (vsn) {
3107 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02003108 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02003109 memprintf(errmsg, "out of memory");
3110 goto error;
3111 }
3112 }
3113
3114 /* Copy the header */
3115 if (hdrs) {
3116 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
3117 struct h1m h1m;
3118 int i, ret;
3119
3120 /* Build and parse the request */
3121 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
3122
3123 h1m.flags = H1_MF_HDRS_ONLY;
3124 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
3125 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
3126 &h1m, NULL);
3127 if (ret <= 0) {
3128 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
3129 goto error;
3130 }
3131
Christopher Fauletb61caf42020-04-21 10:57:42 +02003132 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02003133 hdr = calloc(1, sizeof(*hdr));
3134 if (!hdr) {
3135 memprintf(errmsg, "out of memory");
3136 goto error;
3137 }
3138 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02003139 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02003140 if (!hdr->name.ptr) {
3141 memprintf(errmsg, "out of memory");
3142 goto error;
3143 }
3144
Christopher Fauletb61caf42020-04-21 10:57:42 +02003145 ist0(tmp_hdrs[i].v);
3146 if (!parse_logformat_string(istptr(tmp_hdrs[i].v), px, &hdr->value, 0, SMP_VAL_BE_CHK_RUL, errmsg))
Christopher Faulete5870d82020-04-15 11:32:03 +02003147 goto error;
3148 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
3149 }
3150 }
3151
3152 /* Copy the body */
3153 if (body) {
3154 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02003155 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02003156 memprintf(errmsg, "out of memory");
3157 goto error;
3158 }
3159 }
3160
3161 return chk;
3162
3163 error:
3164 free_tcpcheck_http_hdr(hdr);
3165 free_tcpcheck(chk, 0);
3166 return NULL;
3167}
3168
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003169int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
3170 const char *file, int line)
3171{
Christopher Faulete5870d82020-04-15 11:32:03 +02003172 struct tcpcheck_ruleset *rs = NULL;
3173 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
3174 struct tcpcheck_rule *chk;
3175 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003176 int err_code = 0;
3177
3178 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
3179 err_code |= ERR_WARN;
3180
3181 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
3182 goto out;
3183
Christopher Faulete5870d82020-04-15 11:32:03 +02003184 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
3185 if (!chk) {
3186 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
3187 goto error;
3188 }
3189 if (errmsg) {
3190 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
3191 err_code |= ERR_WARN;
3192 free(errmsg);
3193 errmsg = NULL;
3194 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003195
Christopher Faulete5870d82020-04-15 11:32:03 +02003196 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003197 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02003198 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003199
Christopher Faulete5870d82020-04-15 11:32:03 +02003200 free_tcpcheck_vars(&rules->preset_vars);
3201 rules->list = NULL;
3202 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003203
Christopher Faulete5870d82020-04-15 11:32:03 +02003204 /* Deduce the ruleset name from the proxy info */
3205 chunk_printf(&trash, "*http-check-%s_%s-%d",
3206 ((curpx == defpx) ? "defaults" : curpx->id),
3207 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003208
Christopher Faulet61cc8522020-04-20 14:54:42 +02003209 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02003210 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02003211 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02003212 if (rs == NULL) {
3213 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
3214 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003215 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003216 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003217
Christopher Faulete5870d82020-04-15 11:32:03 +02003218 rules->list = &rs->rules;
3219 rules->flags |= TCPCHK_RULES_HTTP_CHK;
3220 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
3221 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
3222 rules->list = NULL;
3223 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003224 }
Christopher Faulete5870d82020-04-15 11:32:03 +02003225
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003226 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02003227 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003228 return err_code;
3229
3230 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02003231 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02003232 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02003233 err_code |= ERR_ALERT | ERR_FATAL;
3234 goto out;
3235}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003236
Christopher Faulet6f557912020-04-09 15:58:50 +02003237int proxy_parse_external_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
3238 const char *file, int line)
3239{
3240 int err_code = 0;
3241
Christopher Faulet6f557912020-04-09 15:58:50 +02003242 curpx->options2 &= ~PR_O2_CHK_ANY;
3243 curpx->options2 |= PR_O2_EXT_CHK;
3244 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
3245 goto out;
3246
3247 out:
3248 return err_code;
3249}
3250
Christopher Fauletce8111e2020-04-06 15:04:11 +02003251/* Parse the "addr" server keyword */
3252static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3253 char **errmsg)
3254{
3255 struct sockaddr_storage *sk;
3256 struct protocol *proto;
3257 int port1, port2, err_code = 0;
3258
3259
3260 if (!*args[*cur_arg+1]) {
3261 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
3262 goto error;
3263 }
3264
3265 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, errmsg, NULL, NULL, 1);
3266 if (!sk) {
3267 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
3268 goto error;
3269 }
3270
3271 proto = protocol_by_family(sk->ss_family);
3272 if (!proto || !proto->connect) {
3273 memprintf(errmsg, "'%s %s' : connect() not supported for this address family.",
3274 args[*cur_arg], args[*cur_arg+1]);
3275 goto error;
3276 }
3277
3278 if (port1 != port2) {
3279 memprintf(errmsg, "'%s' : port ranges and offsets are not allowed in '%s'.",
3280 args[*cur_arg], args[*cur_arg+1]);
3281 goto error;
3282 }
3283
3284 srv->check.addr = srv->agent.addr = *sk;
3285 srv->flags |= SRV_F_CHECKADDR;
3286 srv->flags |= SRV_F_AGENTADDR;
3287
3288 out:
3289 return err_code;
3290
3291 error:
3292 err_code |= ERR_ALERT | ERR_FATAL;
3293 goto out;
3294}
3295
3296
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003297/* Parse the "agent-addr" server keyword */
3298static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3299 char **errmsg)
3300{
3301 int err_code = 0;
3302
3303 if (!*(args[*cur_arg+1])) {
3304 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
3305 goto error;
3306 }
3307 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
3308 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
3309 goto error;
3310 }
3311
3312 out:
3313 return err_code;
3314
3315 error:
3316 err_code |= ERR_ALERT | ERR_FATAL;
3317 goto out;
3318}
3319
3320/* Parse the "agent-check" server keyword */
3321static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3322 char **errmsg)
3323{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003324 struct tcpcheck_ruleset *rs = NULL;
3325 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
3326 struct tcpcheck_rule *chk;
3327 int err_code = 0;
3328
3329 if (srv->do_agent)
3330 goto out;
3331
3332 if (!rules) {
3333 rules = calloc(1, sizeof(*rules));
3334 if (!rules) {
3335 memprintf(errmsg, "out of memory.");
3336 goto error;
3337 }
3338 LIST_INIT(&rules->preset_vars);
3339 srv->agent.tcpcheck_rules = rules;
3340 }
3341 rules->list = NULL;
3342 rules->flags = 0;
3343
Christopher Faulet61cc8522020-04-20 14:54:42 +02003344 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003345 if (rs)
3346 goto ruleset_found;
3347
Christopher Faulet61cc8522020-04-20 14:54:42 +02003348 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003349 if (rs == NULL) {
3350 memprintf(errmsg, "out of memory.");
3351 goto error;
3352 }
3353
Christopher Fauletb50b3e62020-05-05 18:43:43 +02003354 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003355 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
3356 if (!chk) {
3357 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
3358 goto error;
3359 }
3360 chk->index = 0;
3361 LIST_ADDQ(&rs->rules, &chk->list);
3362
3363 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02003364 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
3365 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003366 if (!chk) {
3367 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
3368 goto error;
3369 }
3370 chk->expect.custom = tcpcheck_agent_expect_reply;
3371 chk->index = 1;
3372 LIST_ADDQ(&rs->rules, &chk->list);
3373
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003374 ruleset_found:
3375 rules->list = &rs->rules;
Christopher Faulet404f9192020-04-09 23:13:54 +02003376 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003377 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003378
3379 out:
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003380 return 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003381
3382 error:
3383 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02003384 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003385 err_code |= ERR_ALERT | ERR_FATAL;
3386 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003387}
3388
3389/* Parse the "agent-inter" server keyword */
3390static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3391 char **errmsg)
3392{
3393 const char *err = NULL;
3394 unsigned int delay;
3395 int err_code = 0;
3396
3397 if (!*(args[*cur_arg+1])) {
3398 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3399 goto error;
3400 }
3401
3402 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3403 if (err == PARSE_TIME_OVER) {
3404 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3405 args[*cur_arg+1], args[*cur_arg], srv->id);
3406 goto error;
3407 }
3408 else if (err == PARSE_TIME_UNDER) {
3409 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3410 args[*cur_arg+1], args[*cur_arg], srv->id);
3411 goto error;
3412 }
3413 else if (err) {
3414 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3415 *err, srv->id);
3416 goto error;
3417 }
3418 if (delay <= 0) {
3419 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3420 delay, args[*cur_arg], srv->id);
3421 goto error;
3422 }
3423 srv->agent.inter = delay;
3424
3425 out:
3426 return err_code;
3427
3428 error:
3429 err_code |= ERR_ALERT | ERR_FATAL;
3430 goto out;
3431}
3432
3433/* Parse the "agent-port" server keyword */
3434static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3435 char **errmsg)
3436{
3437 int err_code = 0;
3438
3439 if (!*(args[*cur_arg+1])) {
3440 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3441 goto error;
3442 }
3443
3444 global.maxsock++;
3445 srv->agent.port = atol(args[*cur_arg+1]);
3446
3447 out:
3448 return err_code;
3449
3450 error:
3451 err_code |= ERR_ALERT | ERR_FATAL;
3452 goto out;
3453}
3454
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003455int set_srv_agent_send(struct server *srv, const char *send)
3456{
3457 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
3458 struct tcpcheck_var *var = NULL;
3459 char *str;
3460
3461 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02003462 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003463 if (str == NULL || var == NULL)
3464 goto error;
3465
3466 free_tcpcheck_vars(&rules->preset_vars);
3467
3468 var->data.type = SMP_T_STR;
3469 var->data.u.str.area = str;
3470 var->data.u.str.data = strlen(str);
3471 LIST_INIT(&var->list);
3472 LIST_ADDQ(&rules->preset_vars, &var->list);
3473
3474 return 1;
3475
3476 error:
3477 free(str);
3478 free(var);
3479 return 0;
3480}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003481
3482/* Parse the "agent-send" server keyword */
3483static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3484 char **errmsg)
3485{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003486 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003487 int err_code = 0;
3488
3489 if (!*(args[*cur_arg+1])) {
3490 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
3491 goto error;
3492 }
3493
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003494 if (!rules) {
3495 rules = calloc(1, sizeof(*rules));
3496 if (!rules) {
3497 memprintf(errmsg, "out of memory.");
3498 goto error;
3499 }
3500 LIST_INIT(&rules->preset_vars);
3501 srv->agent.tcpcheck_rules = rules;
3502 }
3503
3504 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003505 memprintf(errmsg, "out of memory.");
3506 goto error;
3507 }
3508
3509 out:
3510 return err_code;
3511
3512 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003513 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003514 err_code |= ERR_ALERT | ERR_FATAL;
3515 goto out;
3516}
3517
3518/* Parse the "no-agent-send" server keyword */
3519static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3520 char **errmsg)
3521{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02003522 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003523 return 0;
3524}
3525
Christopher Fauletce8111e2020-04-06 15:04:11 +02003526/* Parse the "check" server keyword */
3527static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3528 char **errmsg)
3529{
3530 srv->do_check = 1;
3531 return 0;
3532}
3533
3534/* Parse the "check-send-proxy" server keyword */
3535static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3536 char **errmsg)
3537{
3538 srv->check.send_proxy = 1;
3539 return 0;
3540}
3541
3542/* Parse the "check-via-socks4" server keyword */
3543static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3544 char **errmsg)
3545{
3546 srv->check.via_socks4 = 1;
3547 return 0;
3548}
3549
3550/* Parse the "no-check" server keyword */
3551static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3552 char **errmsg)
3553{
3554 deinit_srv_check(srv);
3555 return 0;
3556}
3557
3558/* Parse the "no-check-send-proxy" server keyword */
3559static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3560 char **errmsg)
3561{
3562 srv->check.send_proxy = 0;
3563 return 0;
3564}
3565
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003566/* parse the "check-proto" server keyword */
3567static int srv_parse_check_proto(char **args, int *cur_arg,
3568 struct proxy *px, struct server *newsrv, char **err)
3569{
3570 int err_code = 0;
3571
3572 if (!*args[*cur_arg + 1]) {
3573 memprintf(err, "'%s' : missing value", args[*cur_arg]);
3574 goto error;
3575 }
3576 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
3577 if (!newsrv->check.mux_proto) {
3578 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
3579 goto error;
3580 }
3581
3582 out:
3583 return err_code;
3584
3585 error:
3586 err_code |= ERR_ALERT | ERR_FATAL;
3587 goto out;
3588}
3589
3590
Christopher Fauletce8111e2020-04-06 15:04:11 +02003591/* Parse the "rise" server keyword */
3592static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3593 char **errmsg)
3594{
3595 int err_code = 0;
3596
3597 if (!*args[*cur_arg + 1]) {
3598 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3599 goto error;
3600 }
3601
3602 srv->check.rise = atol(args[*cur_arg+1]);
3603 if (srv->check.rise <= 0) {
3604 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3605 goto error;
3606 }
3607
3608 if (srv->check.health)
3609 srv->check.health = srv->check.rise;
3610
3611 out:
3612 return err_code;
3613
3614 error:
3615 deinit_srv_agent_check(srv);
3616 err_code |= ERR_ALERT | ERR_FATAL;
3617 goto out;
3618 return 0;
3619}
3620
3621/* Parse the "fall" server keyword */
3622static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3623 char **errmsg)
3624{
3625 int err_code = 0;
3626
3627 if (!*args[*cur_arg + 1]) {
3628 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3629 goto error;
3630 }
3631
3632 srv->check.fall = atol(args[*cur_arg+1]);
3633 if (srv->check.fall <= 0) {
3634 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3635 goto error;
3636 }
3637
3638 out:
3639 return err_code;
3640
3641 error:
3642 deinit_srv_agent_check(srv);
3643 err_code |= ERR_ALERT | ERR_FATAL;
3644 goto out;
3645 return 0;
3646}
3647
3648/* Parse the "inter" server keyword */
3649static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3650 char **errmsg)
3651{
3652 const char *err = NULL;
3653 unsigned int delay;
3654 int err_code = 0;
3655
3656 if (!*(args[*cur_arg+1])) {
3657 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3658 goto error;
3659 }
3660
3661 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3662 if (err == PARSE_TIME_OVER) {
3663 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3664 args[*cur_arg+1], args[*cur_arg], srv->id);
3665 goto error;
3666 }
3667 else if (err == PARSE_TIME_UNDER) {
3668 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3669 args[*cur_arg+1], args[*cur_arg], srv->id);
3670 goto error;
3671 }
3672 else if (err) {
3673 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3674 *err, srv->id);
3675 goto error;
3676 }
3677 if (delay <= 0) {
3678 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3679 delay, args[*cur_arg], srv->id);
3680 goto error;
3681 }
3682 srv->check.inter = delay;
3683
3684 out:
3685 return err_code;
3686
3687 error:
3688 err_code |= ERR_ALERT | ERR_FATAL;
3689 goto out;
3690}
3691
3692
3693/* Parse the "fastinter" server keyword */
3694static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3695 char **errmsg)
3696{
3697 const char *err = NULL;
3698 unsigned int delay;
3699 int err_code = 0;
3700
3701 if (!*(args[*cur_arg+1])) {
3702 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3703 goto error;
3704 }
3705
3706 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3707 if (err == PARSE_TIME_OVER) {
3708 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3709 args[*cur_arg+1], args[*cur_arg], srv->id);
3710 goto error;
3711 }
3712 else if (err == PARSE_TIME_UNDER) {
3713 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3714 args[*cur_arg+1], args[*cur_arg], srv->id);
3715 goto error;
3716 }
3717 else if (err) {
3718 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3719 *err, srv->id);
3720 goto error;
3721 }
3722 if (delay <= 0) {
3723 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3724 delay, args[*cur_arg], srv->id);
3725 goto error;
3726 }
3727 srv->check.fastinter = delay;
3728
3729 out:
3730 return err_code;
3731
3732 error:
3733 err_code |= ERR_ALERT | ERR_FATAL;
3734 goto out;
3735}
3736
3737
3738/* Parse the "downinter" server keyword */
3739static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3740 char **errmsg)
3741{
3742 const char *err = NULL;
3743 unsigned int delay;
3744 int err_code = 0;
3745
3746 if (!*(args[*cur_arg+1])) {
3747 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3748 goto error;
3749 }
3750
3751 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3752 if (err == PARSE_TIME_OVER) {
3753 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3754 args[*cur_arg+1], args[*cur_arg], srv->id);
3755 goto error;
3756 }
3757 else if (err == PARSE_TIME_UNDER) {
3758 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3759 args[*cur_arg+1], args[*cur_arg], srv->id);
3760 goto error;
3761 }
3762 else if (err) {
3763 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3764 *err, srv->id);
3765 goto error;
3766 }
3767 if (delay <= 0) {
3768 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3769 delay, args[*cur_arg], srv->id);
3770 goto error;
3771 }
3772 srv->check.downinter = delay;
3773
3774 out:
3775 return err_code;
3776
3777 error:
3778 err_code |= ERR_ALERT | ERR_FATAL;
3779 goto out;
3780}
3781
3782/* Parse the "port" server keyword */
3783static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3784 char **errmsg)
3785{
3786 int err_code = 0;
3787
3788 if (!*(args[*cur_arg+1])) {
3789 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3790 goto error;
3791 }
3792
3793 global.maxsock++;
3794 srv->check.port = atol(args[*cur_arg+1]);
3795 srv->flags |= SRV_F_CHECKPORT;
3796
3797 out:
3798 return err_code;
3799
3800 error:
3801 err_code |= ERR_ALERT | ERR_FATAL;
3802 goto out;
3803}
3804
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003805static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003806 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
3807 { CFG_LISTEN, "external-check", proxy_parse_extcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003808 { 0, NULL, NULL },
3809}};
3810
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003811static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003812 { "addr", srv_parse_addr, 1, 1 }, /* IP address to send health to or to probe from agent-check */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003813 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3814 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3815 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3816 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3817 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003818 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003819 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003820 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3821 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003822 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003823 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3824 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3825 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3826 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3827 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3828 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3829 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3830 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003831 { NULL, NULL, 0 },
3832}};
3833
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003834INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003835INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003836
Willy Tarreaubd741542010-03-16 18:46:54 +01003837/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003838 * Local variables:
3839 * c-indent-level: 8
3840 * c-basic-offset: 8
3841 * End:
3842 */