blob: 57c9958e770641d046557f80b3d7a456481fba06 [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>
Simon Horman0ba0e4a2015-01-30 11:23:00 +090018#include <stdarg.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020019#include <stdio.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020020#include <stdlib.h>
Willy Tarreau2dd0d472006-06-29 17:53:05 +020021#include <string.h>
Krzysztof Oledzkib304dc72007-10-14 23:40:01 +020022#include <time.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020023#include <unistd.h>
Willy Tarreau9f6dc722019-03-01 11:15:10 +010024#include <sys/resource.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020025#include <sys/socket.h>
Dmitry Sivachenkocaf58982009-08-24 15:11:06 +040026#include <sys/types.h>
Simon Horman98637e52014-06-20 12:30:16 +090027#include <sys/wait.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020028#include <netinet/in.h>
Willy Tarreau1274bc42009-07-15 07:16:31 +020029#include <netinet/tcp.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020030#include <arpa/inet.h>
31
Willy Tarreau122eba92020-06-04 10:15:32 +020032#include <haproxy/action.h>
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020033#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020034#include <haproxy/arg.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 Tarreau7c18b542020-06-11 09:23:02 +020038#include <haproxy/dgram.h>
Willy Tarreaueb92deb2020-06-04 10:53:16 +020039#include <haproxy/dns.h>
Christopher Fauletb51e0372020-11-25 13:47:00 +010040#include <haproxy/dynbuf-t.h>
Willy Tarreaubcc67332020-06-05 15:31:31 +020041#include <haproxy/extcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020042#include <haproxy/fd.h>
43#include <haproxy/global.h>
44#include <haproxy/h1.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020045#include <haproxy/http.h>
Willy Tarreau87735332020-06-04 09:08:41 +020046#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020047#include <haproxy/htx.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020048#include <haproxy/istbuf.h>
49#include <haproxy/list.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020050#include <haproxy/log.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020051#include <haproxy/mailers.h>
52#include <haproxy/port_range.h>
53#include <haproxy/proto_tcp.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020054#include <haproxy/protocol.h>
Willy Tarreaua264d962020-06-04 22:29:18 +020055#include <haproxy/proxy.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020056#include <haproxy/queue.h>
57#include <haproxy/regex.h>
58#include <haproxy/sample.h>
Willy Tarreau1e56f922020-06-04 23:20:13 +020059#include <haproxy/server.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020060#include <haproxy/ssl_sock.h>
Willy Tarreau2eec9b52020-06-04 19:58:55 +020061#include <haproxy/stats-t.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020062#include <haproxy/stream_interface.h>
Willy Tarreaucea0e1b2020-06-04 17:25:40 +020063#include <haproxy/task.h>
Willy Tarreau51cd5952020-06-05 12:25:38 +020064#include <haproxy/tcpcheck.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020065#include <haproxy/thread.h>
66#include <haproxy/time.h>
67#include <haproxy/tools.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020068#include <haproxy/vars.h>
Willy Tarreaubaaee002006-06-26 02:48:02 +020069
Olivier Houchard9130a962017-10-17 17:33:43 +020070
Christopher Faulet61cc8522020-04-20 14:54:42 +020071static int wake_srv_chk(struct conn_stream *cs);
72struct data_cb check_conn_cb = {
73 .wake = wake_srv_chk,
74 .name = "CHCK",
75};
Christopher Fauletd7e63962020-04-17 20:15:59 +020076
Christopher Faulet5d503fc2020-03-30 20:34:34 +020077
Gaetan Rivet05d692d2020-02-14 17:42:54 +010078/* Dummy frontend used to create all checks sessions. */
Willy Tarreau51cd5952020-06-05 12:25:38 +020079struct proxy checks_fe;
Christopher Faulet31dff9b2017-10-23 15:45:20 +020080
Christopher Faulet61cc8522020-04-20 14:54:42 +020081/**************************************************************************/
82/************************ Handle check results ****************************/
83/**************************************************************************/
84struct check_status {
85 short result; /* one of SRV_CHK_* */
86 char *info; /* human readable short info */
87 char *desc; /* long description */
88};
89
90struct analyze_status {
91 char *desc; /* description */
92 unsigned char lr[HANA_OBS_SIZE]; /* result for l4/l7: 0 = ignore, 1 - error, 2 - OK */
93};
94
Simon Horman63a4a822012-03-19 07:24:41 +090095static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +010096 [HCHK_STATUS_UNKNOWN] = { CHK_RES_UNKNOWN, "UNK", "Unknown" },
97 [HCHK_STATUS_INI] = { CHK_RES_UNKNOWN, "INI", "Initializing" },
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +020098 [HCHK_STATUS_START] = { /* SPECIAL STATUS*/ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020099
Willy Tarreau23964182014-05-20 20:56:30 +0200100 /* Below we have finished checks */
101 [HCHK_STATUS_CHECKED] = { CHK_RES_NEUTRAL, "CHECKED", "No status change" },
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100102 [HCHK_STATUS_HANA] = { CHK_RES_FAILED, "HANA", "Health analyze" },
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100103
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100104 [HCHK_STATUS_SOCKERR] = { CHK_RES_FAILED, "SOCKERR", "Socket error" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200105
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100106 [HCHK_STATUS_L4OK] = { CHK_RES_PASSED, "L4OK", "Layer4 check passed" },
107 [HCHK_STATUS_L4TOUT] = { CHK_RES_FAILED, "L4TOUT", "Layer4 timeout" },
108 [HCHK_STATUS_L4CON] = { CHK_RES_FAILED, "L4CON", "Layer4 connection problem" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200109
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100110 [HCHK_STATUS_L6OK] = { CHK_RES_PASSED, "L6OK", "Layer6 check passed" },
111 [HCHK_STATUS_L6TOUT] = { CHK_RES_FAILED, "L6TOUT", "Layer6 timeout" },
112 [HCHK_STATUS_L6RSP] = { CHK_RES_FAILED, "L6RSP", "Layer6 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200113
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100114 [HCHK_STATUS_L7TOUT] = { CHK_RES_FAILED, "L7TOUT", "Layer7 timeout" },
115 [HCHK_STATUS_L7RSP] = { CHK_RES_FAILED, "L7RSP", "Layer7 invalid response" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200116
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200117 [HCHK_STATUS_L57DATA] = { /* DUMMY STATUS */ },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200118
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100119 [HCHK_STATUS_L7OKD] = { CHK_RES_PASSED, "L7OK", "Layer7 check passed" },
120 [HCHK_STATUS_L7OKCD] = { CHK_RES_CONDPASS, "L7OKC", "Layer7 check conditionally passed" },
121 [HCHK_STATUS_L7STS] = { CHK_RES_FAILED, "L7STS", "Layer7 wrong status" },
Simon Horman98637e52014-06-20 12:30:16 +0900122
123 [HCHK_STATUS_PROCERR] = { CHK_RES_FAILED, "PROCERR", "External check error" },
124 [HCHK_STATUS_PROCTOUT] = { CHK_RES_FAILED, "PROCTOUT", "External check timeout" },
Cyril Bonté77010d82014-08-07 01:55:37 +0200125 [HCHK_STATUS_PROCOK] = { CHK_RES_PASSED, "PROCOK", "External check passed" },
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200126};
127
Simon Horman63a4a822012-03-19 07:24:41 +0900128static const struct analyze_status analyze_statuses[HANA_STATUS_SIZE] = { /* 0: ignore, 1: error, 2: OK */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100129 [HANA_STATUS_UNKNOWN] = { "Unknown", { 0, 0 }},
130
131 [HANA_STATUS_L4_OK] = { "L4 successful connection", { 2, 0 }},
132 [HANA_STATUS_L4_ERR] = { "L4 unsuccessful connection", { 1, 1 }},
133
134 [HANA_STATUS_HTTP_OK] = { "Correct http response", { 0, 2 }},
135 [HANA_STATUS_HTTP_STS] = { "Wrong http response", { 0, 1 }},
136 [HANA_STATUS_HTTP_HDRRSP] = { "Invalid http response (headers)", { 0, 1 }},
137 [HANA_STATUS_HTTP_RSP] = { "Invalid http response", { 0, 1 }},
138
139 [HANA_STATUS_HTTP_READ_ERROR] = { "Read error (http)", { 0, 1 }},
140 [HANA_STATUS_HTTP_READ_TIMEOUT] = { "Read timeout (http)", { 0, 1 }},
141 [HANA_STATUS_HTTP_BROKEN_PIPE] = { "Close from server (http)", { 0, 1 }},
142};
143
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100144/* checks if <err> is a real error for errno or one that can be ignored, and
145 * return 0 for these ones or <err> for real ones.
146 */
147static inline int unclean_errno(int err)
148{
149 if (err == EAGAIN || err == EINPROGRESS ||
150 err == EISCONN || err == EALREADY)
151 return 0;
152 return err;
153}
154
Christopher Faulet61cc8522020-04-20 14:54:42 +0200155/* Converts check_status code to description */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200156const char *get_check_status_description(short check_status) {
157
158 const char *desc;
159
160 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200161 desc = check_statuses[check_status].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200162 else
163 desc = NULL;
164
165 if (desc && *desc)
166 return desc;
167 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200168 return check_statuses[HCHK_STATUS_UNKNOWN].desc;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200169}
170
Christopher Faulet61cc8522020-04-20 14:54:42 +0200171/* Converts check_status code to short info */
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200172const char *get_check_status_info(short check_status) {
173
174 const char *info;
175
176 if (check_status < HCHK_STATUS_SIZE)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200177 info = check_statuses[check_status].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200178 else
179 info = NULL;
180
181 if (info && *info)
182 return info;
183 else
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200184 return check_statuses[HCHK_STATUS_UNKNOWN].info;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200185}
186
Christopher Faulet61cc8522020-04-20 14:54:42 +0200187/* Convert analyze_status to description */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100188const char *get_analyze_status(short analyze_status) {
189
190 const char *desc;
191
192 if (analyze_status < HANA_STATUS_SIZE)
193 desc = analyze_statuses[analyze_status].desc;
194 else
195 desc = NULL;
196
197 if (desc && *desc)
198 return desc;
199 else
200 return analyze_statuses[HANA_STATUS_UNKNOWN].desc;
201}
202
Christopher Faulet61cc8522020-04-20 14:54:42 +0200203/* Sets check->status, update check->duration and fill check->result with an
204 * adequate CHK_RES_* value. The new check->health is computed based on the
205 * result.
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200206 *
Christopher Faulet61cc8522020-04-20 14:54:42 +0200207 * Shows information in logs about failed health check if server is UP or
208 * succeeded health checks if server is DOWN.
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200209 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200210void set_server_check_status(struct check *check, short status, const char *desc)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100211{
Simon Horman4a741432013-02-23 15:35:38 +0900212 struct server *s = check->server;
Willy Tarreaubef1b322014-05-13 21:01:39 +0200213 short prev_status = check->status;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200214 int report = 0;
Simon Horman4a741432013-02-23 15:35:38 +0900215
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200216 if (status == HCHK_STATUS_START) {
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100217 check->result = CHK_RES_UNKNOWN; /* no result yet */
Simon Horman4a741432013-02-23 15:35:38 +0900218 check->desc[0] = '\0';
219 check->start = now;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200220 return;
221 }
222
Simon Horman4a741432013-02-23 15:35:38 +0900223 if (!check->status)
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200224 return;
225
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200226 if (desc && *desc) {
Simon Horman4a741432013-02-23 15:35:38 +0900227 strncpy(check->desc, desc, HCHK_DESC_LEN-1);
228 check->desc[HCHK_DESC_LEN-1] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200229 } else
Simon Horman4a741432013-02-23 15:35:38 +0900230 check->desc[0] = '\0';
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +0200231
Simon Horman4a741432013-02-23 15:35:38 +0900232 check->status = status;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200233 if (check_statuses[status].result)
Simon Horman4a741432013-02-23 15:35:38 +0900234 check->result = check_statuses[status].result;
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200235
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100236 if (status == HCHK_STATUS_HANA)
Simon Horman4a741432013-02-23 15:35:38 +0900237 check->duration = -1;
238 else if (!tv_iszero(&check->start)) {
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200239 /* set_server_check_status() may be called more than once */
Simon Horman4a741432013-02-23 15:35:38 +0900240 check->duration = tv_ms_elapsed(&check->start, &now);
241 tv_zero(&check->start);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200242 }
243
Willy Tarreau23964182014-05-20 20:56:30 +0200244 /* no change is expected if no state change occurred */
245 if (check->result == CHK_RES_NEUTRAL)
246 return;
247
Olivier Houchard0923fa42019-01-11 18:43:04 +0100248 /* If the check was really just sending a mail, it won't have an
249 * associated server, so we're done now.
250 */
251 if (!s)
252 return;
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200253 report = 0;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200254
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200255 switch (check->result) {
256 case CHK_RES_FAILED:
Willy Tarreau12634e12014-05-23 11:32:36 +0200257 /* Failure to connect to the agent as a secondary check should not
258 * cause the server to be marked down.
259 */
260 if ((!(check->state & CHK_ST_AGENT) ||
Simon Hormaneaabd522015-02-26 11:26:17 +0900261 (check->status >= HCHK_STATUS_L57DATA)) &&
Christopher Fauletb119a792018-05-02 12:12:45 +0200262 (check->health > 0)) {
Olivier Houchard7059c552019-03-08 18:49:32 +0100263 _HA_ATOMIC_ADD(&s->counters.failed_checks, 1);
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200264 report = 1;
265 check->health--;
266 if (check->health < check->rise)
267 check->health = 0;
268 }
269 break;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200270
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200271 case CHK_RES_PASSED:
272 case CHK_RES_CONDPASS: /* "condpass" cannot make the first step but it OK after a "passed" */
273 if ((check->health < check->rise + check->fall - 1) &&
274 (check->result == CHK_RES_PASSED || check->health > 0)) {
275 report = 1;
276 check->health++;
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200277
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200278 if (check->health >= check->rise)
279 check->health = check->rise + check->fall - 1; /* OK now */
280 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200281
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200282 /* clear consecutive_errors if observing is enabled */
283 if (s->onerror)
284 s->consecutive_errors = 0;
285 break;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100286
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200287 default:
288 break;
289 }
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200290
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200291 if (s->proxy->options2 & PR_O2_LOGHCHKS &&
292 (status != prev_status || report)) {
293 chunk_printf(&trash,
Willy Tarreau12634e12014-05-23 11:32:36 +0200294 "%s check for %sserver %s/%s %s%s",
295 (check->state & CHK_ST_AGENT) ? "Agent" : "Health",
Willy Tarreauc93cd162014-05-13 15:54:22 +0200296 s->flags & SRV_F_BACKUP ? "backup " : "",
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100297 s->proxy->id, s->id,
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100298 (check->result == CHK_RES_CONDPASS) ? "conditionally ":"",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200299 (check->result >= CHK_RES_PASSED) ? "succeeded" : "failed");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200300
Emeric Brun5a133512017-10-19 14:42:30 +0200301 srv_append_status(&trash, s, check, -1, 0);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200302
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100303 chunk_appendf(&trash, ", status: %d/%d %s",
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200304 (check->health >= check->rise) ? check->health - check->rise + 1 : check->health,
305 (check->health >= check->rise) ? check->fall : check->rise,
306 (check->health >= check->rise) ? (s->uweight ? "UP" : "DRAIN") : "DOWN");
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200307
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200308 ha_warning("%s.\n", trash.area);
309 send_log(s->proxy, LOG_NOTICE, "%s.\n", trash.area);
310 send_email_alert(s, LOG_INFO, "%s", trash.area);
Krzysztof Piotr Oledzki213014e2009-09-27 15:50:02 +0200311 }
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200312}
313
Willy Tarreau4eec5472014-05-20 22:32:27 +0200314/* Marks the check <check>'s server down if the current check is already failed
315 * and the server is not down yet nor in maintenance.
Willy Tarreaubaaee002006-06-26 02:48:02 +0200316 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200317void check_notify_failure(struct check *check)
Willy Tarreaubaaee002006-06-26 02:48:02 +0200318{
Simon Horman4a741432013-02-23 15:35:38 +0900319 struct server *s = check->server;
Simon Hormane0d1bfb2011-06-21 14:34:58 +0900320
Willy Tarreau7b1d47c2014-05-20 14:55:13 +0200321 /* The agent secondary check should only cause a server to be marked
322 * as down if check->status is HCHK_STATUS_L7STS, which indicates
323 * that the agent returned "fail", "stopped" or "down".
324 * The implication here is that failure to connect to the agent
325 * as a secondary check should not cause the server to be marked
326 * down. */
327 if ((check->state & CHK_ST_AGENT) && check->status != HCHK_STATUS_L7STS)
328 return;
329
Willy Tarreau4eec5472014-05-20 22:32:27 +0200330 if (check->health > 0)
331 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100332
Willy Tarreau4eec5472014-05-20 22:32:27 +0200333 /* We only report a reason for the check if we did not do so previously */
Emeric Brun5a133512017-10-19 14:42:30 +0200334 srv_set_stopped(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Willy Tarreaubaaee002006-06-26 02:48:02 +0200335}
336
Willy Tarreauaf549582014-05-16 17:37:50 +0200337/* Marks the check <check> as valid and tries to set its server up, provided
Willy Tarreau3e048382014-05-21 10:30:54 +0200338 * it isn't in maintenance, it is not tracking a down server and other checks
339 * comply. The rule is simple : by default, a server is up, unless any of the
340 * following conditions is true :
341 * - health check failed (check->health < rise)
342 * - agent check failed (agent->health < rise)
343 * - the server tracks a down server (track && track->state == STOPPED)
344 * Note that if the server has a slowstart, it will switch to STARTING instead
345 * of RUNNING. Also, only the health checks support the nolb mode, so the
346 * agent's success may not take the server out of this mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200347 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200348void check_notify_success(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200349{
Simon Horman4a741432013-02-23 15:35:38 +0900350 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100351
Emeric Brun52a91d32017-08-31 14:41:55 +0200352 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200353 return;
Cyril Bontécd19e512010-01-31 22:34:03 +0100354
Emeric Brun52a91d32017-08-31 14:41:55 +0200355 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreauaf549582014-05-16 17:37:50 +0200356 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100357
Willy Tarreau3e048382014-05-21 10:30:54 +0200358 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
359 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100360
Willy Tarreau3e048382014-05-21 10:30:54 +0200361 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
362 return;
Willy Tarreauaf549582014-05-16 17:37:50 +0200363
Emeric Brun52a91d32017-08-31 14:41:55 +0200364 if ((check->state & CHK_ST_AGENT) && s->next_state == SRV_ST_STOPPING)
Willy Tarreau3e048382014-05-21 10:30:54 +0200365 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100366
Emeric Brun5a133512017-10-19 14:42:30 +0200367 srv_set_running(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100368}
369
Willy Tarreaudb58b792014-05-21 13:57:23 +0200370/* Marks the check <check> as valid and tries to set its server into stopping mode
371 * if it was running or starting, and provided it isn't in maintenance and other
372 * checks comply. The conditions for the server to be marked in stopping mode are
373 * the same as for it to be turned up. Also, only the health checks support the
374 * nolb mode.
Willy Tarreauaf549582014-05-16 17:37:50 +0200375 */
Willy Tarreaubcc67332020-06-05 15:31:31 +0200376void check_notify_stopping(struct check *check)
Willy Tarreauaf549582014-05-16 17:37:50 +0200377{
Simon Horman4a741432013-02-23 15:35:38 +0900378 struct server *s = check->server;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100379
Emeric Brun52a91d32017-08-31 14:41:55 +0200380 if (s->next_admin & SRV_ADMF_MAINT)
Willy Tarreauaf549582014-05-16 17:37:50 +0200381 return;
382
Willy Tarreaudb58b792014-05-21 13:57:23 +0200383 if (check->state & CHK_ST_AGENT)
384 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100385
Emeric Brun52a91d32017-08-31 14:41:55 +0200386 if (s->track && s->track->next_state == SRV_ST_STOPPED)
Willy Tarreaudb58b792014-05-21 13:57:23 +0200387 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100388
Willy Tarreaudb58b792014-05-21 13:57:23 +0200389 if ((s->check.state & CHK_ST_ENABLED) && (s->check.health < s->check.rise))
390 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100391
Willy Tarreaudb58b792014-05-21 13:57:23 +0200392 if ((s->agent.state & CHK_ST_ENABLED) && (s->agent.health < s->agent.rise))
393 return;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100394
Willy Tarreaub26881a2017-12-23 11:16:49 +0100395 srv_set_stopping(s, NULL, (!s->track && !(s->proxy->options2 & PR_O2_LOGHCHKS)) ? check : NULL);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100396}
Willy Tarreaubaaee002006-06-26 02:48:02 +0200397
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100398/* note: use health_adjust() only, which first checks that the observe mode is
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100399 * enabled. This will take the server lock if needed.
Willy Tarreau9fe7aae2013-12-31 23:47:37 +0100400 */
401void __health_adjust(struct server *s, short status)
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100402{
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100403 int failed;
404 int expire;
405
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100406 if (s->observe >= HANA_OBS_SIZE)
407 return;
408
Willy Tarreaubb956662013-01-24 00:37:39 +0100409 if (status >= HANA_STATUS_SIZE || !analyze_statuses[status].desc)
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100410 return;
411
412 switch (analyze_statuses[status].lr[s->observe - 1]) {
413 case 1:
414 failed = 1;
415 break;
416
417 case 2:
418 failed = 0;
419 break;
420
421 default:
422 return;
423 }
424
425 if (!failed) {
426 /* good: clear consecutive_errors */
427 s->consecutive_errors = 0;
428 return;
429 }
430
Olivier Houchard7059c552019-03-08 18:49:32 +0100431 _HA_ATOMIC_ADD(&s->consecutive_errors, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100432
433 if (s->consecutive_errors < s->consecutive_errors_limit)
434 return;
435
Willy Tarreau19d14ef2012-10-29 16:51:55 +0100436 chunk_printf(&trash, "Detected %d consecutive errors, last one was: %s",
437 s->consecutive_errors, get_analyze_status(status));
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100438
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100439 if (s->check.fastinter)
440 expire = tick_add(now_ms, MS_TO_TICKS(s->check.fastinter));
441 else
442 expire = TICK_ETERNITY;
443
444 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
445
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100446 switch (s->onerror) {
447 case HANA_ONERR_FASTINTER:
448 /* force fastinter - nothing to do here as all modes force it */
449 break;
450
451 case HANA_ONERR_SUDDTH:
452 /* simulate a pre-fatal failed health check */
Simon Horman58c32972013-11-25 10:46:38 +0900453 if (s->check.health > s->check.rise)
454 s->check.health = s->check.rise + 1;
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100455
Tim Duesterhus588b3142020-05-29 14:35:51 +0200456 /* fall through */
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100457
458 case HANA_ONERR_FAILCHK:
459 /* simulate a failed health check */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200460 set_server_check_status(&s->check, HCHK_STATUS_HANA,
461 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200462 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100463 break;
464
465 case HANA_ONERR_MARKDWN:
466 /* mark server down */
Simon Horman58c32972013-11-25 10:46:38 +0900467 s->check.health = s->check.rise;
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200468 set_server_check_status(&s->check, HCHK_STATUS_HANA,
469 trash.area);
Willy Tarreau4eec5472014-05-20 22:32:27 +0200470 check_notify_failure(&s->check);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100471 break;
472
473 default:
474 /* write a warning? */
475 break;
476 }
477
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100478 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
479
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100480 s->consecutive_errors = 0;
Olivier Houchard7059c552019-03-08 18:49:32 +0100481 _HA_ATOMIC_ADD(&s->counters.failed_hana, 1);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100482
Christopher Fauletfed5db82021-05-07 11:45:26 +0200483 if (tick_isset(expire) && tick_is_lt(expire, s->check.task->expire)) {
Willy Tarreau93da4dc2021-02-17 15:20:19 +0100484 /* requeue check task with new expire */
485 task_schedule(s->check.task, expire);
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100486 }
Willy Tarreauef781042010-01-27 11:53:01 +0100487}
488
Christopher Faulet61cc8522020-04-20 14:54:42 +0200489/* Checks the connection. If an error has already been reported or the socket is
Willy Tarreau20a18342013-12-05 00:31:46 +0100490 * closed, keep errno intact as it is supposed to contain the valid error code.
491 * If no error is reported, check the socket's error queue using getsockopt().
492 * Warning, this must be done only once when returning from poll, and never
493 * after an I/O error was attempted, otherwise the error queue might contain
494 * inconsistent errors. If an error is detected, the CO_FL_ERROR is set on the
495 * socket. Returns non-zero if an error was reported, zero if everything is
496 * clean (including a properly closed socket).
497 */
498static int retrieve_errno_from_socket(struct connection *conn)
499{
500 int skerr;
501 socklen_t lskerr = sizeof(skerr);
502
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100503 if (conn->flags & CO_FL_ERROR && (unclean_errno(errno) || !conn->ctrl))
Willy Tarreau20a18342013-12-05 00:31:46 +0100504 return 1;
505
Willy Tarreau3c728722014-01-23 13:50:42 +0100506 if (!conn_ctrl_ready(conn))
Willy Tarreau20a18342013-12-05 00:31:46 +0100507 return 0;
508
Willy Tarreau585744b2017-08-24 14:31:19 +0200509 if (getsockopt(conn->handle.fd, SOL_SOCKET, SO_ERROR, &skerr, &lskerr) == 0)
Willy Tarreau20a18342013-12-05 00:31:46 +0100510 errno = skerr;
511
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100512 errno = unclean_errno(errno);
Willy Tarreau20a18342013-12-05 00:31:46 +0100513
514 if (!errno) {
515 /* we could not retrieve an error, that does not mean there is
516 * none. Just don't change anything and only report the prior
517 * error if any.
518 */
519 if (conn->flags & CO_FL_ERROR)
520 return 1;
521 else
522 return 0;
523 }
524
525 conn->flags |= CO_FL_ERROR | CO_FL_SOCK_WR_SH | CO_FL_SOCK_RD_SH;
526 return 1;
527}
528
Christopher Faulet61cc8522020-04-20 14:54:42 +0200529/* Tries to collect as much information as possible on the connection status,
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100530 * and adjust the server status accordingly. It may make use of <errno_bck>
531 * if non-null when the caller is absolutely certain of its validity (eg:
532 * checked just after a syscall). If the caller doesn't have a valid errno,
533 * it can pass zero, and retrieve_errno_from_socket() will be called to try
534 * to extract errno from the socket. If no error is reported, it will consider
535 * the <expired> flag. This is intended to be used when a connection error was
536 * reported in conn->flags or when a timeout was reported in <expired>. The
537 * function takes care of not updating a server status which was already set.
538 * All situations where at least one of <expired> or CO_FL_ERROR are set
539 * produce a status.
540 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200541void chk_report_conn_err(struct check *check, int errno_bck, int expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100542{
Olivier Houchard9aaf7782017-09-13 18:30:23 +0200543 struct conn_stream *cs = check->cs;
544 struct connection *conn = cs_conn(cs);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100545 const char *err_msg;
Willy Tarreau83061a82018-07-13 11:56:34 +0200546 struct buffer *chk;
Willy Tarreau213c6782014-10-02 14:51:02 +0200547 int step;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100548
Willy Tarreau6aaa1b82013-12-11 17:09:34 +0100549 if (check->result != CHK_RES_UNKNOWN)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100550 return;
551
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100552 errno = unclean_errno(errno_bck);
553 if (conn && errno)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100554 retrieve_errno_from_socket(conn);
555
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200556 if (conn && !(conn->flags & CO_FL_ERROR) &&
557 !(cs->flags & CS_FL_ERROR) && !expired)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100558 return;
559
560 /* we'll try to build a meaningful error message depending on the
561 * context of the error possibly present in conn->err_code, and the
562 * socket error possibly collected above. This is useful to know the
563 * exact step of the L6 layer (eg: SSL handshake).
564 */
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200565 chk = get_trash_chunk();
566
Christopher Faulet799f3a42020-04-07 12:06:14 +0200567 if (check->type == PR_O2_TCPCHK_CHK &&
Christopher Fauletd7e63962020-04-17 20:15:59 +0200568 (check->tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200569 step = tcpcheck_get_step_id(check, NULL);
Willy Tarreau213c6782014-10-02 14:51:02 +0200570 if (!step)
571 chunk_printf(chk, " at initial connection step of tcp-check");
572 else {
573 chunk_printf(chk, " at step %d of tcp-check", step);
574 /* we were looking for a string */
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200575 if (check->current_step && check->current_step->action == TCPCHK_ACT_CONNECT) {
576 if (check->current_step->connect.port)
577 chunk_appendf(chk, " (connect port %d)" ,check->current_step->connect.port);
Willy Tarreau213c6782014-10-02 14:51:02 +0200578 else
579 chunk_appendf(chk, " (connect)");
580 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200581 else if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT) {
582 struct tcpcheck_expect *expect = &check->current_step->expect;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100583
584 switch (expect->type) {
585 case TCPCHK_EXPECT_STRING:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200586 chunk_appendf(chk, " (expect string '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100587 break;
588 case TCPCHK_EXPECT_BINARY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200589 chunk_appendf(chk, " (expect binary '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Gaetan Rivetb616add2020-02-07 15:37:17 +0100590 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200591 case TCPCHK_EXPECT_STRING_REGEX:
Willy Tarreau213c6782014-10-02 14:51:02 +0200592 chunk_appendf(chk, " (expect regex)");
Gaetan Rivetb616add2020-02-07 15:37:17 +0100593 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200594 case TCPCHK_EXPECT_BINARY_REGEX:
Gaetan Rivetefab6c62020-02-07 15:37:17 +0100595 chunk_appendf(chk, " (expect binary regex)");
596 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200597 case TCPCHK_EXPECT_STRING_LF:
598 chunk_appendf(chk, " (expect log-format string)");
599 break;
600 case TCPCHK_EXPECT_BINARY_LF:
601 chunk_appendf(chk, " (expect log-format binary)");
602 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200603 case TCPCHK_EXPECT_HTTP_STATUS:
Christopher Faulet8021a5f2020-04-24 13:53:12 +0200604 chunk_appendf(chk, " (expect HTTP status codes)");
Christopher Faulete5870d82020-04-15 11:32:03 +0200605 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200606 case TCPCHK_EXPECT_HTTP_STATUS_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200607 chunk_appendf(chk, " (expect HTTP status regex)");
608 break;
Christopher Faulet39708192020-05-05 10:47:36 +0200609 case TCPCHK_EXPECT_HTTP_HEADER:
610 chunk_appendf(chk, " (expect HTTP header pattern)");
611 break;
Christopher Faulete5870d82020-04-15 11:32:03 +0200612 case TCPCHK_EXPECT_HTTP_BODY:
Christopher Fauletb61caf42020-04-21 10:57:42 +0200613 chunk_appendf(chk, " (expect HTTP body content '%.*s')", (unsigned int)istlen(expect->data), istptr(expect->data));
Christopher Faulete5870d82020-04-15 11:32:03 +0200614 break;
Christopher Faulet67a23452020-05-05 18:10:01 +0200615 case TCPCHK_EXPECT_HTTP_BODY_REGEX:
Christopher Faulete5870d82020-04-15 11:32:03 +0200616 chunk_appendf(chk, " (expect HTTP body regex)");
617 break;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200618 case TCPCHK_EXPECT_HTTP_BODY_LF:
619 chunk_appendf(chk, " (expect log-format HTTP body)");
620 break;
Christopher Faulet9e6ed152020-04-03 15:24:06 +0200621 case TCPCHK_EXPECT_CUSTOM:
622 chunk_appendf(chk, " (expect custom function)");
623 break;
Gaetan Rivetb616add2020-02-07 15:37:17 +0100624 case TCPCHK_EXPECT_UNDEF:
625 chunk_appendf(chk, " (undefined expect!)");
626 break;
627 }
Willy Tarreau213c6782014-10-02 14:51:02 +0200628 }
Christopher Fauletb2c2e0f2020-03-30 11:05:10 +0200629 else if (check->current_step && check->current_step->action == TCPCHK_ACT_SEND) {
Willy Tarreau213c6782014-10-02 14:51:02 +0200630 chunk_appendf(chk, " (send)");
631 }
Baptiste Assmann22b09d22015-05-01 08:03:04 +0200632
Christopher Faulet6f2a5e42020-04-01 13:11:41 +0200633 if (check->current_step && check->current_step->comment)
634 chunk_appendf(chk, " comment: '%s'", check->current_step->comment);
Baptiste Assmann5ecb77f2013-10-06 23:24:13 +0200635 }
636 }
637
Willy Tarreau00149122017-10-04 18:05:01 +0200638 if (conn && conn->err_code) {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100639 if (unclean_errno(errno))
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200640 chunk_printf(&trash, "%s (%s)%s", conn_err_code_str(conn), strerror(errno),
641 chk->area);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100642 else
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200643 chunk_printf(&trash, "%s%s", conn_err_code_str(conn),
644 chk->area);
645 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100646 }
647 else {
Willy Tarreauc8dc20a2019-12-27 12:03:27 +0100648 if (unclean_errno(errno)) {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200649 chunk_printf(&trash, "%s%s", strerror(errno),
650 chk->area);
651 err_msg = trash.area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100652 }
653 else {
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200654 err_msg = chk->area;
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100655 }
656 }
657
Willy Tarreau00149122017-10-04 18:05:01 +0200658 if (check->state & CHK_ST_PORT_MISS) {
Baptiste Assmann95db2bc2016-06-13 14:15:41 +0200659 /* NOTE: this is reported after <fall> tries */
660 chunk_printf(chk, "No port available for the TCP connection");
661 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
662 }
663
Christopher Faulet5e293762020-10-26 11:10:49 +0100664 if (!conn || !conn->ctrl) {
665 /* error before any connection attempt (connection allocation error or no control layer) */
Willy Tarreau00149122017-10-04 18:05:01 +0200666 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
667 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100668 else if (conn->flags & CO_FL_WAIT_L4_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100669 /* L4 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200670 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100671 set_server_check_status(check, HCHK_STATUS_L4CON, err_msg);
672 else if (expired)
673 set_server_check_status(check, HCHK_STATUS_L4TOUT, err_msg);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200674
675 /*
676 * might be due to a server IP change.
677 * Let's trigger a DNS resolution if none are currently running.
678 */
Olivier Houchard0923fa42019-01-11 18:43:04 +0100679 if (check->server)
680 dns_trigger_resolution(check->server->dns_requester);
Baptiste Assmanna68ca962015-04-14 01:15:08 +0200681
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100682 }
Willy Tarreauc192b0a2020-01-23 09:11:58 +0100683 else if (conn->flags & CO_FL_WAIT_L6_CONN) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100684 /* L6 not established (yet) */
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200685 if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100686 set_server_check_status(check, HCHK_STATUS_L6RSP, err_msg);
687 else if (expired)
688 set_server_check_status(check, HCHK_STATUS_L6TOUT, err_msg);
689 }
Willy Tarreau4ff3b892017-10-16 15:17:17 +0200690 else if (conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR) {
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100691 /* I/O error after connection was established and before we could diagnose */
692 set_server_check_status(check, HCHK_STATUS_SOCKERR, err_msg);
693 }
694 else if (expired) {
Christopher Fauletcf80f2f2020-04-01 11:04:52 +0200695 enum healthcheck_status tout = HCHK_STATUS_L7TOUT;
696
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100697 /* connection established but expired check */
Christopher Faulet1941bab2020-05-05 07:55:50 +0200698 if (check->current_step && check->current_step->action == TCPCHK_ACT_EXPECT &&
699 check->current_step->expect.tout_status != HCHK_STATUS_UNKNOWN)
Christopher Faulet811f78c2020-04-01 11:10:27 +0200700 tout = check->current_step->expect.tout_status;
701 set_server_check_status(check, tout, err_msg);
Willy Tarreau25e2ab52013-12-04 11:17:05 +0100702 }
703
704 return;
705}
706
Simon Horman98637e52014-06-20 12:30:16 +0900707
Christopher Faulet61cc8522020-04-20 14:54:42 +0200708/* Builds the server state header used by HTTP health-checks */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200709int httpchk_build_status_header(struct server *s, struct buffer *buf)
Simon Horman98637e52014-06-20 12:30:16 +0900710{
Christopher Faulet61cc8522020-04-20 14:54:42 +0200711 int sv_state;
712 int ratio;
713 char addr[46];
714 char port[6];
715 const char *srv_hlt_st[7] = { "DOWN", "DOWN %d/%d",
716 "UP %d/%d", "UP",
717 "NOLB %d/%d", "NOLB",
718 "no check" };
Simon Horman98637e52014-06-20 12:30:16 +0900719
Christopher Faulet61cc8522020-04-20 14:54:42 +0200720 if (!(s->check.state & CHK_ST_ENABLED))
721 sv_state = 6;
722 else if (s->cur_state != SRV_ST_STOPPED) {
723 if (s->check.health == s->check.rise + s->check.fall - 1)
724 sv_state = 3; /* UP */
725 else
726 sv_state = 2; /* going down */
Simon Horman98637e52014-06-20 12:30:16 +0900727
Christopher Faulet61cc8522020-04-20 14:54:42 +0200728 if (s->cur_state == SRV_ST_STOPPING)
729 sv_state += 2;
730 } else {
731 if (s->check.health)
732 sv_state = 1; /* going up */
733 else
734 sv_state = 0; /* DOWN */
Simon Horman98637e52014-06-20 12:30:16 +0900735 }
Willy Tarreaub7b24782016-06-21 15:32:29 +0200736
Christopher Faulet61cc8522020-04-20 14:54:42 +0200737 chunk_appendf(buf, srv_hlt_st[sv_state],
738 (s->cur_state != SRV_ST_STOPPED) ? (s->check.health - s->check.rise + 1) : (s->check.health),
739 (s->cur_state != SRV_ST_STOPPED) ? (s->check.fall) : (s->check.rise));
Willy Tarreaub7b24782016-06-21 15:32:29 +0200740
Christopher Faulet61cc8522020-04-20 14:54:42 +0200741 addr_to_str(&s->addr, addr, sizeof(addr));
742 if (s->addr.ss_family == AF_INET || s->addr.ss_family == AF_INET6)
743 snprintf(port, sizeof(port), "%u", s->svc_port);
744 else
745 *port = 0;
Willy Tarreaub7b24782016-06-21 15:32:29 +0200746
Christopher Faulet61cc8522020-04-20 14:54:42 +0200747 chunk_appendf(buf, "; address=%s; port=%s; name=%s/%s; node=%s; weight=%d/%d; scur=%d/%d; qcur=%d",
748 addr, port, s->proxy->id, s->id,
749 global.node,
750 (s->cur_eweight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
751 (s->proxy->lbprm.tot_weight * s->proxy->lbprm.wmult + s->proxy->lbprm.wdiv - 1) / s->proxy->lbprm.wdiv,
752 s->cur_sess, s->proxy->beconn - s->proxy->nbpend,
753 s->nbpend);
Willy Tarreau9f6dc722019-03-01 11:15:10 +0100754
Christopher Faulet61cc8522020-04-20 14:54:42 +0200755 if ((s->cur_state == SRV_ST_STARTING) &&
756 now.tv_sec < s->last_change + s->slowstart &&
757 now.tv_sec >= s->last_change) {
758 ratio = MAX(1, 100 * (now.tv_sec - s->last_change) / s->slowstart);
759 chunk_appendf(buf, "; throttle=%d%%", ratio);
760 }
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200761
Christopher Faulet61cc8522020-04-20 14:54:42 +0200762 return b_data(buf);
763}
Christopher Fauletaaae9a02020-04-26 09:50:31 +0200764
Willy Tarreau51cd5952020-06-05 12:25:38 +0200765/**************************************************************************/
Willy Tarreau51cd5952020-06-05 12:25:38 +0200766/***************** Health-checks based on connections *********************/
767/**************************************************************************/
768/* This function is used only for server health-checks. It handles connection
769 * status updates including errors. If necessary, it wakes the check task up.
770 * It returns 0 on normal cases, <0 if at least one close() has happened on the
771 * connection (eg: reconnect). It relies on tcpcheck_main().
Christopher Faulet61cc8522020-04-20 14:54:42 +0200772 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200773static int wake_srv_chk(struct conn_stream *cs)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200774{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200775 struct connection *conn = cs->conn;
776 struct check *check = cs->data;
777 struct email_alertq *q = container_of(check, typeof(*q), check);
778 int ret = 0;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200779
Willy Tarreau51cd5952020-06-05 12:25:38 +0200780 if (check->server)
781 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
782 else
783 HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200784
Willy Tarreau51cd5952020-06-05 12:25:38 +0200785 /* we may have to make progress on the TCP checks */
786 ret = tcpcheck_main(check);
Christopher Fauletaaab0832020-05-05 15:54:22 +0200787
Willy Tarreau51cd5952020-06-05 12:25:38 +0200788 cs = check->cs;
789 conn = cs->conn;
Christopher Fauletaaab0832020-05-05 15:54:22 +0200790
Willy Tarreau51cd5952020-06-05 12:25:38 +0200791 if (unlikely(conn->flags & CO_FL_ERROR || cs->flags & CS_FL_ERROR)) {
792 /* We may get error reports bypassing the I/O handlers, typically
793 * the case when sending a pure TCP check which fails, then the I/O
794 * handlers above are not called. This is completely handled by the
795 * main processing task so let's simply wake it up. If we get here,
796 * we expect errno to still be valid.
797 */
798 chk_report_conn_err(check, errno, 0);
799 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200800 }
801
Christopher Faulet444b7b12021-01-18 15:47:03 +0100802 if (check->result != CHK_RES_UNKNOWN || ret == -1) {
Willy Tarreau51cd5952020-06-05 12:25:38 +0200803 /* Check complete or aborted. If connection not yet closed do it
804 * now and wake the check task up to be sure the result is
805 * handled ASAP. */
806 conn_sock_drain(conn);
807 cs_close(cs);
808 ret = -1;
Christopher Faulet444b7b12021-01-18 15:47:03 +0100809
810 if (check->wait_list.events)
811 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
812
Willy Tarreau51cd5952020-06-05 12:25:38 +0200813 /* We may have been scheduled to run, and the
814 * I/O handler expects to have a cs, so remove
815 * the tasklet
816 */
817 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
818 task_wakeup(check->task, TASK_WOKEN_IO);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200819 }
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200820
Willy Tarreau51cd5952020-06-05 12:25:38 +0200821 if (check->server)
822 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
823 else
824 HA_SPIN_UNLOCK(EMAIL_ALERTS_LOCK, &q->lock);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200825
Willy Tarreau51cd5952020-06-05 12:25:38 +0200826 /* if a connection got replaced, we must absolutely prevent the connection
827 * handler from touching its fd, and perform the FD polling updates ourselves
828 */
829 if (ret < 0)
830 conn_cond_update_polling(conn);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200831
Christopher Faulet61cc8522020-04-20 14:54:42 +0200832 return ret;
833}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200834
Willy Tarreau51cd5952020-06-05 12:25:38 +0200835/* This function checks if any I/O is wanted, and if so, attempts to do so */
836static struct task *event_srv_chk_io(struct task *t, void *ctx, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200837{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200838 struct check *check = ctx;
839 struct conn_stream *cs = check->cs;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200840
Willy Tarreau51cd5952020-06-05 12:25:38 +0200841 wake_srv_chk(cs);
842 return NULL;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200843}
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +0200844
Willy Tarreau51cd5952020-06-05 12:25:38 +0200845/* manages a server health-check that uses a connection. Returns
846 * the time the task accepts to wait, or TIME_ETERNITY for infinity.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200847 *
848 * Please do NOT place any return statement in this function and only leave
Willy Tarreau51cd5952020-06-05 12:25:38 +0200849 * via the out_unlock label.
Christopher Faulet61cc8522020-04-20 14:54:42 +0200850 */
Willy Tarreau51cd5952020-06-05 12:25:38 +0200851static struct task *process_chk_conn(struct task *t, void *context, unsigned short state)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200852{
Willy Tarreau51cd5952020-06-05 12:25:38 +0200853 struct check *check = context;
854 struct proxy *proxy = check->proxy;
Christopher Faulet67da4812021-05-06 16:01:18 +0200855 struct conn_stream *cs;
856 struct connection *conn;
Willy Tarreau51cd5952020-06-05 12:25:38 +0200857 int rv;
858 int expired = tick_is_expired(t->expire, now_ms);
Willy Tarreaudeccd112018-06-14 18:38:55 +0200859
Willy Tarreau51cd5952020-06-05 12:25:38 +0200860 if (check->server)
861 HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock);
Christopher Faulet67da4812021-05-06 16:01:18 +0200862
Willy Tarreau51cd5952020-06-05 12:25:38 +0200863 if (!(check->state & CHK_ST_INPROGRESS)) {
864 /* no check currently running */
865 if (!expired) /* woke up too early */
866 goto out_unlock;
Willy Tarreauabca5b62013-12-06 14:19:25 +0100867
Willy Tarreau51cd5952020-06-05 12:25:38 +0200868 /* we don't send any health-checks when the proxy is
869 * stopped, the server should not be checked or the check
870 * is disabled.
871 */
872 if (((check->state & (CHK_ST_ENABLED | CHK_ST_PAUSED)) != CHK_ST_ENABLED) ||
Willy Tarreauc3914d42020-09-24 08:39:22 +0200873 proxy->disabled)
Willy Tarreau51cd5952020-06-05 12:25:38 +0200874 goto reschedule;
Christopher Faulet404f9192020-04-09 23:13:54 +0200875
Willy Tarreau51cd5952020-06-05 12:25:38 +0200876 /* we'll initiate a new check */
877 set_server_check_status(check, HCHK_STATUS_START, NULL);
Christopher Faulet404f9192020-04-09 23:13:54 +0200878
Willy Tarreau51cd5952020-06-05 12:25:38 +0200879 check->state |= CHK_ST_INPROGRESS;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200880
Willy Tarreau51cd5952020-06-05 12:25:38 +0200881 task_set_affinity(t, tid_bit);
882
883 check->current_step = NULL;
884 tcpcheck_main(check);
Christopher Faulet67da4812021-05-06 16:01:18 +0200885 expired = 0;
Christopher Faulet61cc8522020-04-20 14:54:42 +0200886 }
Christopher Faulet67da4812021-05-06 16:01:18 +0200887
888 cs = check->cs;
889 conn = cs_conn(cs);
890
891 /* there was a test running.
892 * First, let's check whether there was an uncaught error,
893 * which can happen on connect timeout or error.
894 */
895 if (check->result == CHK_RES_UNKNOWN) {
896 if ((conn && ((conn->flags & CO_FL_ERROR) || (cs->flags & CS_FL_ERROR))) || expired) {
897 chk_report_conn_err(check, 0, expired);
898 }
899 else {
900 if (check->state & CHK_ST_CLOSE_CONN) {
901 cs_destroy(cs);
902 cs = NULL;
903 conn = NULL;
904 check->cs = NULL;
905 check->state &= ~CHK_ST_CLOSE_CONN;
906 tcpcheck_main(check);
Christopher Faulet444b7b12021-01-18 15:47:03 +0100907 }
Christopher Faulet67da4812021-05-06 16:01:18 +0200908 if (check->result == CHK_RES_UNKNOWN)
909 goto out_unlock; /* timeout not reached, wait again */
Christopher Faulet61cc8522020-04-20 14:54:42 +0200910 }
Christopher Faulet67da4812021-05-06 16:01:18 +0200911 }
Christopher Faulet404f9192020-04-09 23:13:54 +0200912
Christopher Faulet67da4812021-05-06 16:01:18 +0200913 /* check complete or aborted */
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200914
Christopher Faulet67da4812021-05-06 16:01:18 +0200915 check->current_step = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200916
Christopher Faulet67da4812021-05-06 16:01:18 +0200917 if (conn && conn->xprt) {
918 /* The check was aborted and the connection was not yet closed.
919 * This can happen upon timeout, or when an external event such
920 * as a failed response coupled with "observe layer7" caused the
921 * server state to be suddenly changed.
922 */
923 conn_sock_drain(conn);
924 cs_close(cs);
925 }
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200926
Christopher Faulet67da4812021-05-06 16:01:18 +0200927 if (cs) {
928 if (check->wait_list.events)
929 cs->conn->mux->unsubscribe(cs, check->wait_list.events, &check->wait_list);
930 /* We may have been scheduled to run, and the
931 * I/O handler expects to have a cs, so remove
932 * the tasklet
933 */
934 tasklet_remove_from_tasklet_list(check->wait_list.tasklet);
935 cs_destroy(cs);
936 cs = check->cs = NULL;
937 conn = NULL;
938 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200939
Christopher Faulet67da4812021-05-06 16:01:18 +0200940 if (check->sess != NULL) {
941 vars_prune(&check->vars, check->sess, NULL);
942 session_free(check->sess);
943 check->sess = NULL;
944 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200945
Christopher Faulet67da4812021-05-06 16:01:18 +0200946 if (check->server) {
947 if (check->result == CHK_RES_FAILED) {
948 /* a failure or timeout detected */
949 check_notify_failure(check);
950 }
951 else if (check->result == CHK_RES_CONDPASS) {
952 /* check is OK but asks for stopping mode */
953 check_notify_stopping(check);
Christopher Faulet61cc8522020-04-20 14:54:42 +0200954 }
Christopher Faulet67da4812021-05-06 16:01:18 +0200955 else if (check->result == CHK_RES_PASSED) {
956 /* a success was detected */
957 check_notify_success(check);
958 }
959 }
960 task_set_affinity(t, MAX_THREADS_MASK);
961 check_release_buf(check, &check->bi);
962 check_release_buf(check, &check->bo);
963 check->state &= ~(CHK_ST_INPROGRESS|CHK_ST_IN_ALLOC|CHK_ST_OUT_ALLOC);
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200964
Christopher Faulet67da4812021-05-06 16:01:18 +0200965 if (check->server) {
966 rv = 0;
967 if (global.spread_checks > 0) {
968 rv = srv_getinter(check) * global.spread_checks / 100;
969 rv -= (int) (2 * rv * (ha_random32() / 4294967295.0));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200970 }
Christopher Faulet67da4812021-05-06 16:01:18 +0200971 t->expire = tick_add(now_ms, MS_TO_TICKS(srv_getinter(check) + rv));
Christopher Faulet61cc8522020-04-20 14:54:42 +0200972 }
Willy Tarreau51cd5952020-06-05 12:25:38 +0200973
974 reschedule:
975 while (tick_is_expired(t->expire, now_ms))
976 t->expire = tick_add(t->expire, MS_TO_TICKS(check->inter));
977 out_unlock:
978 if (check->server)
979 HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock);
980 return t;
Christopher Fauletba3c68f2020-04-01 16:27:05 +0200981}
982
Willy Tarreau51cd5952020-06-05 12:25:38 +0200983
Christopher Faulet61cc8522020-04-20 14:54:42 +0200984/**************************************************************************/
985/************************** Init/deinit checks ****************************/
986/**************************************************************************/
Christopher Fauletb51e0372020-11-25 13:47:00 +0100987/*
988 * Tries to grab a buffer and to re-enables processing on check <target>. The
989 * check flags are used to figure what buffer was requested. It returns 1 if the
990 * allocation succeeds, in which case the I/O tasklet is woken up, or 0 if it's
991 * impossible to wake up and we prefer to be woken up later.
992 */
993int check_buf_available(void *target)
Christopher Faulet61cc8522020-04-20 14:54:42 +0200994{
Christopher Fauletb51e0372020-11-25 13:47:00 +0100995 struct check *check = target;
996
997 if ((check->state & CHK_ST_IN_ALLOC) && b_alloc_margin(&check->bi, 0)) {
998 check->state &= ~CHK_ST_IN_ALLOC;
999 tasklet_wakeup(check->wait_list.tasklet);
1000 return 1;
1001 }
1002 if ((check->state & CHK_ST_OUT_ALLOC) && b_alloc_margin(&check->bo, 0)) {
1003 check->state &= ~CHK_ST_OUT_ALLOC;
1004 tasklet_wakeup(check->wait_list.tasklet);
1005 return 1;
1006 }
1007
1008 return 0;
1009}
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001010
Christopher Fauletb51e0372020-11-25 13:47:00 +01001011/*
1012 * Allocate a buffer. If if fails, it adds the check in buffer wait queue.
1013 */
1014struct buffer *check_get_buf(struct check *check, struct buffer *bptr)
1015{
1016 struct buffer *buf = NULL;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001017
Willy Tarreau954827a2021-02-20 11:49:49 +01001018 if (likely(!LIST_ADDED(&check->buf_wait.list)) &&
Christopher Fauletb51e0372020-11-25 13:47:00 +01001019 unlikely((buf = b_alloc_margin(bptr, 0)) == NULL)) {
1020 check->buf_wait.target = check;
1021 check->buf_wait.wakeup_cb = check_buf_available;
Willy Tarreau954827a2021-02-20 11:49:49 +01001022 LIST_ADDQ(&ti->buffer_wq, &check->buf_wait.list);
Christopher Fauletb51e0372020-11-25 13:47:00 +01001023 }
1024 return buf;
1025}
1026
1027/*
1028 * Release a buffer, if any, and try to wake up entities waiting in the buffer
1029 * wait queue.
1030 */
1031void check_release_buf(struct check *check, struct buffer *bptr)
1032{
1033 if (bptr->size) {
1034 b_free(bptr);
Willy Tarreau132b3a42021-02-20 12:02:46 +01001035 offer_buffers(check->buf_wait.target, 1);
Christopher Fauletb51e0372020-11-25 13:47:00 +01001036 }
1037}
1038
1039const char *init_check(struct check *check, int type)
1040{
1041 check->type = type;
Christopher Fauletba3c68f2020-04-01 16:27:05 +02001042
Christopher Fauletb51e0372020-11-25 13:47:00 +01001043 check->bi = BUF_NULL;
1044 check->bo = BUF_NULL;
Willy Tarreau954827a2021-02-20 11:49:49 +01001045 LIST_INIT(&check->buf_wait.list);
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001046
Christopher Faulet61cc8522020-04-20 14:54:42 +02001047 check->wait_list.tasklet = tasklet_new();
1048 if (!check->wait_list.tasklet)
1049 return "out of memory while allocating check tasklet";
1050 check->wait_list.events = 0;
1051 check->wait_list.tasklet->process = event_srv_chk_io;
1052 check->wait_list.tasklet->context = check;
1053 return NULL;
1054}
1055
1056void free_check(struct check *check)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001057{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001058 task_destroy(check->task);
1059 if (check->wait_list.tasklet)
1060 tasklet_free(check->wait_list.tasklet);
1061
Christopher Fauletb51e0372020-11-25 13:47:00 +01001062 check_release_buf(check, &check->bi);
1063 check_release_buf(check, &check->bo);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001064 if (check->cs) {
1065 free(check->cs->conn);
1066 check->cs->conn = NULL;
1067 cs_free(check->cs);
1068 check->cs = NULL;
1069 }
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001070}
1071
Christopher Faulet61cc8522020-04-20 14:54:42 +02001072/* manages a server health-check. Returns the time the task accepts to wait, or
1073 * TIME_ETERNITY for infinity.
1074 */
Willy Tarreaucee013e2020-06-05 11:40:38 +02001075struct task *process_chk(struct task *t, void *context, unsigned short state)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001076{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001077 struct check *check = context;
1078
1079 if (check->type == PR_O2_EXT_CHK)
1080 return process_chk_proc(t, context, state);
1081 return process_chk_conn(t, context, state);
1082
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001083}
1084
Christopher Faulet61cc8522020-04-20 14:54:42 +02001085
1086static int start_check_task(struct check *check, int mininter,
1087 int nbcheck, int srvpos)
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001088{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001089 struct task *t;
1090 unsigned long thread_mask = MAX_THREADS_MASK;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001091
Christopher Faulet61cc8522020-04-20 14:54:42 +02001092 if (check->type == PR_O2_EXT_CHK)
1093 thread_mask = 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001094
Christopher Faulet61cc8522020-04-20 14:54:42 +02001095 /* task for the check */
1096 if ((t = task_new(thread_mask)) == NULL) {
1097 ha_alert("Starting [%s:%s] check: out of memory.\n",
1098 check->server->proxy->id, check->server->id);
1099 return 0;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001100 }
1101
Christopher Faulet61cc8522020-04-20 14:54:42 +02001102 check->task = t;
1103 t->process = process_chk;
1104 t->context = check;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001105
Christopher Faulet61cc8522020-04-20 14:54:42 +02001106 if (mininter < srv_getinter(check))
1107 mininter = srv_getinter(check);
1108
1109 if (global.max_spread_checks && mininter > global.max_spread_checks)
1110 mininter = global.max_spread_checks;
1111
1112 /* check this every ms */
1113 t->expire = tick_add(now_ms, MS_TO_TICKS(mininter * srvpos / nbcheck));
1114 check->start = now;
1115 task_queue(t);
1116
1117 return 1;
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001118}
1119
Christopher Faulet61cc8522020-04-20 14:54:42 +02001120/* updates the server's weight during a warmup stage. Once the final weight is
1121 * reached, the task automatically stops. Note that any server status change
1122 * must have updated s->last_change accordingly.
1123 */
1124static struct task *server_warmup(struct task *t, void *context, unsigned short state)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001125{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001126 struct server *s = context;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001127
Christopher Faulet61cc8522020-04-20 14:54:42 +02001128 /* by default, plan on stopping the task */
1129 t->expire = TICK_ETERNITY;
1130 if ((s->next_admin & SRV_ADMF_MAINT) ||
1131 (s->next_state != SRV_ST_STARTING))
1132 return t;
Christopher Faulete5870d82020-04-15 11:32:03 +02001133
Christopher Faulet61cc8522020-04-20 14:54:42 +02001134 HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001135
Christopher Faulet61cc8522020-04-20 14:54:42 +02001136 /* recalculate the weights and update the state */
1137 server_recalc_eweight(s, 1);
Christopher Faulet5c288742020-03-31 08:15:58 +02001138
Christopher Faulet61cc8522020-04-20 14:54:42 +02001139 /* probably that we can refill this server with a bit more connections */
1140 pendconn_grab_from_px(s);
Christopher Faulet5c288742020-03-31 08:15:58 +02001141
Christopher Faulet61cc8522020-04-20 14:54:42 +02001142 HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
Christopher Faulet5c288742020-03-31 08:15:58 +02001143
Christopher Faulet61cc8522020-04-20 14:54:42 +02001144 /* get back there in 1 second or 1/20th of the slowstart interval,
1145 * whichever is greater, resulting in small 5% steps.
1146 */
1147 if (s->next_state == SRV_ST_STARTING)
1148 t->expire = tick_add(now_ms, MS_TO_TICKS(MAX(1000, s->slowstart / 20)));
1149 return t;
1150}
1151
1152/*
1153 * Start health-check.
1154 * Returns 0 if OK, ERR_FATAL on error, and prints the error in this case.
1155 */
1156static int start_checks()
1157{
1158
1159 struct proxy *px;
1160 struct server *s;
1161 struct task *t;
1162 int nbcheck=0, mininter=0, srvpos=0;
1163
1164 /* 0- init the dummy frontend used to create all checks sessions */
1165 init_new_proxy(&checks_fe);
Christopher Faulet644cf332021-04-16 10:49:07 +02001166 checks_fe.id = strdup("CHECKS-FE");
Christopher Faulet61cc8522020-04-20 14:54:42 +02001167 checks_fe.cap = PR_CAP_FE | PR_CAP_BE;
1168 checks_fe.mode = PR_MODE_TCP;
1169 checks_fe.maxconn = 0;
1170 checks_fe.conn_retries = CONN_RETRIES;
1171 checks_fe.options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
1172 checks_fe.timeout.client = TICK_ETERNITY;
1173
1174 /* 1- count the checkers to run simultaneously.
1175 * We also determine the minimum interval among all of those which
1176 * have an interval larger than SRV_CHK_INTER_THRES. This interval
1177 * will be used to spread their start-up date. Those which have
1178 * a shorter interval will start independently and will not dictate
1179 * too short an interval for all others.
1180 */
1181 for (px = proxies_list; px; px = px->next) {
1182 for (s = px->srv; s; s = s->next) {
1183 if (s->slowstart) {
1184 if ((t = task_new(MAX_THREADS_MASK)) == NULL) {
1185 ha_alert("Starting [%s:%s] check: out of memory.\n", px->id, s->id);
1186 return ERR_ALERT | ERR_FATAL;
1187 }
1188 /* We need a warmup task that will be called when the server
1189 * state switches from down to up.
1190 */
1191 s->warmup = t;
1192 t->process = server_warmup;
1193 t->context = s;
1194 /* server can be in this state only because of */
1195 if (s->next_state == SRV_ST_STARTING)
1196 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 +02001197 }
1198
Christopher Faulet61cc8522020-04-20 14:54:42 +02001199 if (s->check.state & CHK_ST_CONFIGURED) {
1200 nbcheck++;
1201 if ((srv_getinter(&s->check) >= SRV_CHK_INTER_THRES) &&
1202 (!mininter || mininter > srv_getinter(&s->check)))
1203 mininter = srv_getinter(&s->check);
Christopher Faulet5c288742020-03-31 08:15:58 +02001204 }
1205
Christopher Faulet61cc8522020-04-20 14:54:42 +02001206 if (s->agent.state & CHK_ST_CONFIGURED) {
1207 nbcheck++;
1208 if ((srv_getinter(&s->agent) >= SRV_CHK_INTER_THRES) &&
1209 (!mininter || mininter > srv_getinter(&s->agent)))
1210 mininter = srv_getinter(&s->agent);
1211 }
Christopher Faulet5c288742020-03-31 08:15:58 +02001212 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001213 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001214
Christopher Faulet61cc8522020-04-20 14:54:42 +02001215 if (!nbcheck)
1216 return 0;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001217
Christopher Faulet61cc8522020-04-20 14:54:42 +02001218 srand((unsigned)time(NULL));
Christopher Fauletb7d30092020-03-30 15:19:03 +02001219
Christopher Faulet61cc8522020-04-20 14:54:42 +02001220 /*
1221 * 2- start them as far as possible from each others. For this, we will
1222 * start them after their interval set to the min interval divided by
1223 * the number of servers, weighted by the server's position in the list.
1224 */
1225 for (px = proxies_list; px; px = px->next) {
1226 if ((px->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
1227 if (init_pid_list()) {
1228 ha_alert("Starting [%s] check: out of memory.\n", px->id);
1229 return ERR_ALERT | ERR_FATAL;
1230 }
1231 }
Christopher Fauletb7d30092020-03-30 15:19:03 +02001232
Christopher Faulet61cc8522020-04-20 14:54:42 +02001233 for (s = px->srv; s; s = s->next) {
1234 /* A task for the main check */
1235 if (s->check.state & CHK_ST_CONFIGURED) {
1236 if (s->check.type == PR_O2_EXT_CHK) {
1237 if (!prepare_external_check(&s->check))
1238 return ERR_ALERT | ERR_FATAL;
Christopher Fauletb7d30092020-03-30 15:19:03 +02001239 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001240 if (!start_check_task(&s->check, mininter, nbcheck, srvpos))
1241 return ERR_ALERT | ERR_FATAL;
1242 srvpos++;
Christopher Faulet98572322020-03-30 13:16:44 +02001243 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001244
Christopher Faulet61cc8522020-04-20 14:54:42 +02001245 /* A task for a auxiliary agent check */
1246 if (s->agent.state & CHK_ST_CONFIGURED) {
1247 if (!start_check_task(&s->agent, mininter, nbcheck, srvpos)) {
1248 return ERR_ALERT | ERR_FATAL;
1249 }
1250 srvpos++;
1251 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001252 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001253 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001254 return 0;
1255}
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001256
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001257
Christopher Faulet61cc8522020-04-20 14:54:42 +02001258/*
1259 * Return value:
1260 * the port to be used for the health check
1261 * 0 in case no port could be found for the check
1262 */
1263static int srv_check_healthcheck_port(struct check *chk)
1264{
1265 int i = 0;
1266 struct server *srv = NULL;
1267
1268 srv = chk->server;
1269
1270 /* by default, we use the health check port ocnfigured */
1271 if (chk->port > 0)
1272 return chk->port;
1273
1274 /* try to get the port from check_core.addr if check.port not set */
1275 i = get_host_port(&chk->addr);
1276 if (i > 0)
1277 return i;
1278
1279 /* try to get the port from server address */
1280 /* prevent MAPPORTS from working at this point, since checks could
1281 * not be performed in such case (MAPPORTS impose a relative ports
1282 * based on live traffic)
1283 */
1284 if (srv->flags & SRV_F_MAPPORTS)
1285 return 0;
1286
1287 i = srv->svc_port; /* by default */
1288 if (i > 0)
1289 return i;
1290
1291 return 0;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001292}
1293
Christopher Faulet61cc8522020-04-20 14:54:42 +02001294/* Initializes an health-check attached to the server <srv>. Non-zero is returned
1295 * if an error occurred.
1296 */
1297static int init_srv_check(struct server *srv)
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001298{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001299 const char *err;
1300 struct tcpcheck_rule *r;
1301 int ret = 0;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001302 int check_type;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001303
Christopher Faulet0a5e7132021-01-12 17:29:45 +01001304 if (!srv->do_check || !(srv->proxy->cap & PR_CAP_BE))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001305 goto out;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001306
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001307 check_type = srv->check.tcpcheck_rules->flags & TCPCHK_RULES_PROTO_CHK;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001308
Christopher Faulet61cc8522020-04-20 14:54:42 +02001309 /* If neither a port nor an addr was specified and no check transport
1310 * layer is forced, then the transport layer used by the checks is the
1311 * same as for the production traffic. Otherwise we use raw_sock by
1312 * default, unless one is specified.
1313 */
1314 if (!srv->check.port && !is_addr(&srv->check.addr)) {
1315 if (!srv->check.use_ssl && srv->use_ssl != -1) {
1316 srv->check.use_ssl = srv->use_ssl;
1317 srv->check.xprt = srv->xprt;
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001318 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001319 else if (srv->check.use_ssl == 1)
1320 srv->check.xprt = xprt_get(XPRT_SSL);
1321 srv->check.send_proxy |= (srv->pp_opts);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001322 }
Christopher Faulet66163ec2020-05-20 22:36:24 +02001323 else if (srv->check.use_ssl == 1)
1324 srv->check.xprt = xprt_get(XPRT_SSL);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001325
Christopher Faulet12882cf2020-04-23 15:50:18 +02001326 /* Inherit the mux protocol from the server if not already defined for
1327 * the check
1328 */
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001329 if (srv->mux_proto && !srv->check.mux_proto &&
1330 ((srv->mux_proto->mode == PROTO_MODE_HTTP && check_type == TCPCHK_RULES_HTTP_CHK) ||
1331 (srv->mux_proto->mode == PROTO_MODE_TCP && check_type != TCPCHK_RULES_HTTP_CHK))) {
Christopher Faulet12882cf2020-04-23 15:50:18 +02001332 srv->check.mux_proto = srv->mux_proto;
Amaury Denoyellea940bcb2020-11-13 12:34:56 +01001333 }
Amaury Denoyelle92c8ac12020-11-13 12:34:57 +01001334 /* test that check proto is valid if explicitly defined */
1335 else if (srv->check.mux_proto &&
1336 ((srv->check.mux_proto->mode == PROTO_MODE_HTTP && check_type != TCPCHK_RULES_HTTP_CHK) ||
1337 (srv->check.mux_proto->mode == PROTO_MODE_TCP && check_type == TCPCHK_RULES_HTTP_CHK))) {
1338 ha_alert("config: %s '%s': server '%s' uses an incompatible MUX protocol for the selected check type\n",
1339 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1340 ret |= ERR_ALERT | ERR_FATAL;
1341 goto out;
1342 }
Christopher Faulet12882cf2020-04-23 15:50:18 +02001343
Christopher Faulet61cc8522020-04-20 14:54:42 +02001344 /* validate <srv> server health-check settings */
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001345
Christopher Faulet61cc8522020-04-20 14:54:42 +02001346 /* We need at least a service port, a check port or the first tcp-check
1347 * rule must be a 'connect' one when checking an IPv4/IPv6 server.
1348 */
1349 if ((srv_check_healthcheck_port(&srv->check) != 0) ||
1350 (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
1351 goto init;
Christopher Fauletf50f4e92020-03-30 19:52:29 +02001352
Christopher Faulet61cc8522020-04-20 14:54:42 +02001353 if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
1354 ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
1355 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1356 ret |= ERR_ALERT | ERR_ABORT;
1357 goto out;
1358 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001359
Christopher Faulet61cc8522020-04-20 14:54:42 +02001360 /* search the first action (connect / send / expect) in the list */
1361 r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
1362 if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
1363 ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
1364 "nor tcp_check rule 'connect' with port information.\n",
1365 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1366 ret |= ERR_ALERT | ERR_ABORT;
1367 goto out;
1368 }
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001369
Christopher Faulet61cc8522020-04-20 14:54:42 +02001370 /* scan the tcp-check ruleset to ensure a port has been configured */
1371 list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
1372 if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
1373 ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
1374 "and a tcp_check rule 'connect' with no port information.\n",
1375 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1376 ret |= ERR_ALERT | ERR_ABORT;
1377 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001378 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001379 }
1380
Christopher Faulet61cc8522020-04-20 14:54:42 +02001381 init:
1382 if (!(srv->proxy->options2 & PR_O2_CHK_ANY)) {
1383 struct tcpcheck_ruleset *rs = NULL;
1384 struct tcpcheck_rules *rules = &srv->proxy->tcpcheck_rules;
1385 //char *errmsg = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001386
Christopher Faulet61cc8522020-04-20 14:54:42 +02001387 srv->proxy->options2 &= ~PR_O2_CHK_ANY;
1388 srv->proxy->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulete5870d82020-04-15 11:32:03 +02001389
Christopher Faulet61cc8522020-04-20 14:54:42 +02001390 rs = find_tcpcheck_ruleset("*tcp-check");
1391 if (!rs) {
1392 rs = create_tcpcheck_ruleset("*tcp-check");
1393 if (rs == NULL) {
1394 ha_alert("config: %s '%s': out of memory.\n",
1395 proxy_type_str(srv->proxy), srv->proxy->id);
1396 ret |= ERR_ALERT | ERR_FATAL;
1397 goto out;
1398 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001399 }
1400
Christopher Faulet61cc8522020-04-20 14:54:42 +02001401 free_tcpcheck_vars(&rules->preset_vars);
1402 rules->list = &rs->rules;
1403 rules->flags = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001404 }
1405
Christopher Faulet61cc8522020-04-20 14:54:42 +02001406 err = init_check(&srv->check, srv->proxy->options2 & PR_O2_CHK_ANY);
1407 if (err) {
1408 ha_alert("config: %s '%s': unable to init check for server '%s' (%s).\n",
1409 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1410 ret |= ERR_ALERT | ERR_ABORT;
1411 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001412 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001413 srv->check.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED;
1414 global.maxsock++;
Christopher Faulete5870d82020-04-15 11:32:03 +02001415
Christopher Faulet61cc8522020-04-20 14:54:42 +02001416 out:
1417 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001418}
1419
Christopher Faulet61cc8522020-04-20 14:54:42 +02001420/* Initializes an agent-check attached to the server <srv>. Non-zero is returned
1421 * if an error occurred.
1422 */
1423static int init_srv_agent_check(struct server *srv)
Christopher Faulete5870d82020-04-15 11:32:03 +02001424{
Christopher Faulet61cc8522020-04-20 14:54:42 +02001425 struct tcpcheck_rule *chk;
1426 const char *err;
1427 int ret = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001428
Christopher Faulet0a5e7132021-01-12 17:29:45 +01001429 if (!srv->do_agent || !(srv->proxy->cap & PR_CAP_BE))
Christopher Faulet61cc8522020-04-20 14:54:42 +02001430 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001431
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001432 /* If there is no connect rule preceding all send / expect rules, an
Christopher Faulet61cc8522020-04-20 14:54:42 +02001433 * implicit one is inserted before all others.
1434 */
1435 chk = get_first_tcpcheck_rule(srv->agent.tcpcheck_rules);
1436 if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
1437 chk = calloc(1, sizeof(*chk));
1438 if (!chk) {
1439 ha_alert("config : %s '%s': unable to add implicit tcp-check connect rule"
1440 " to agent-check for server '%s' (out of memory).\n",
1441 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
1442 ret |= ERR_ALERT | ERR_FATAL;
1443 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001444 }
Christopher Faulet61cc8522020-04-20 14:54:42 +02001445 chk->action = TCPCHK_ACT_CONNECT;
1446 chk->connect.options = (TCPCHK_OPT_DEFAULT_CONNECT|TCPCHK_OPT_IMPLICIT);
1447 LIST_ADD(srv->agent.tcpcheck_rules->list, &chk->list);
Christopher Faulete5870d82020-04-15 11:32:03 +02001448 }
1449
Christopher Faulete5870d82020-04-15 11:32:03 +02001450
Christopher Faulet61cc8522020-04-20 14:54:42 +02001451 err = init_check(&srv->agent, PR_O2_TCPCHK_CHK);
1452 if (err) {
1453 ha_alert("config: %s '%s': unable to init agent-check for server '%s' (%s).\n",
1454 proxy_type_str(srv->proxy), srv->proxy->id, srv->id, err);
1455 ret |= ERR_ALERT | ERR_ABORT;
1456 goto out;
Christopher Faulete5870d82020-04-15 11:32:03 +02001457 }
1458
Christopher Faulet61cc8522020-04-20 14:54:42 +02001459 if (!srv->agent.inter)
1460 srv->agent.inter = srv->check.inter;
1461
1462 srv->agent.state |= CHK_ST_CONFIGURED | CHK_ST_ENABLED | CHK_ST_AGENT;
1463 global.maxsock++;
1464
1465 out:
1466 return ret;
Christopher Faulete5870d82020-04-15 11:32:03 +02001467}
1468
Christopher Faulet61cc8522020-04-20 14:54:42 +02001469static void deinit_srv_check(struct server *srv)
1470{
1471 if (srv->check.state & CHK_ST_CONFIGURED)
1472 free_check(&srv->check);
1473 srv->check.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED;
1474 srv->do_check = 0;
1475}
Christopher Faulete5870d82020-04-15 11:32:03 +02001476
Christopher Faulet61cc8522020-04-20 14:54:42 +02001477
1478static void deinit_srv_agent_check(struct server *srv)
1479{
1480 if (srv->agent.tcpcheck_rules) {
1481 free_tcpcheck_vars(&srv->agent.tcpcheck_rules->preset_vars);
1482 free(srv->agent.tcpcheck_rules);
1483 srv->agent.tcpcheck_rules = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001484 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001485
Christopher Faulet61cc8522020-04-20 14:54:42 +02001486 if (srv->agent.state & CHK_ST_CONFIGURED)
1487 free_check(&srv->agent);
1488
1489 srv->agent.state &= ~CHK_ST_CONFIGURED & ~CHK_ST_ENABLED & ~CHK_ST_AGENT;
1490 srv->do_agent = 0;
Christopher Faulete5870d82020-04-15 11:32:03 +02001491}
1492
Willy Tarreaucee013e2020-06-05 11:40:38 +02001493REGISTER_POST_SERVER_CHECK(init_srv_check);
1494REGISTER_POST_SERVER_CHECK(init_srv_agent_check);
Willy Tarreaucee013e2020-06-05 11:40:38 +02001495REGISTER_POST_CHECK(start_checks);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001496
Willy Tarreaucee013e2020-06-05 11:40:38 +02001497REGISTER_SERVER_DEINIT(deinit_srv_check);
1498REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001499
Christopher Faulet61cc8522020-04-20 14:54:42 +02001500
1501/**************************************************************************/
1502/************************** Check sample fetches **************************/
1503/**************************************************************************/
Christopher Fauletfd6c2292020-03-25 18:20:15 +01001504
Christopher Faulet61cc8522020-04-20 14:54:42 +02001505static struct sample_fetch_kw_list smp_kws = {ILH, {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001506 { /* END */ },
1507}};
1508
1509INITCALL1(STG_REGISTER, sample_register_fetches, &smp_kws);
1510
1511
1512/**************************************************************************/
1513/************************ Check's parsing functions ***********************/
1514/**************************************************************************/
Christopher Faulet51b129f2020-04-09 15:54:18 +02001515/* Parses the "http-check" proxy keyword */
1516static int proxy_parse_httpcheck(char **args, int section, struct proxy *curpx,
1517 struct proxy *defpx, const char *file, int line,
1518 char **errmsg)
1519{
Christopher Faulete5870d82020-04-15 11:32:03 +02001520 struct tcpcheck_ruleset *rs = NULL;
1521 struct tcpcheck_rule *chk = NULL;
1522 int index, cur_arg, ret = 0;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001523
1524 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[0], NULL))
1525 ret = 1;
1526
1527 cur_arg = 1;
1528 if (strcmp(args[cur_arg], "disable-on-404") == 0) {
1529 /* enable a graceful server shutdown on an HTTP 404 response */
1530 curpx->options |= PR_O_DISABLE404;
1531 if (too_many_args(1, args, errmsg, NULL))
1532 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001533 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001534 }
1535 else if (strcmp(args[cur_arg], "send-state") == 0) {
1536 /* enable emission of the apparent state of a server in HTTP checks */
1537 curpx->options2 |= PR_O2_CHK_SNDST;
1538 if (too_many_args(1, args, errmsg, NULL))
1539 goto error;
Christopher Faulete5870d82020-04-15 11:32:03 +02001540 goto out;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001541 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001542
Christopher Faulete5870d82020-04-15 11:32:03 +02001543 /* Deduce the ruleset name from the proxy info */
1544 chunk_printf(&trash, "*http-check-%s_%s-%d",
1545 ((curpx == defpx) ? "defaults" : curpx->id),
1546 curpx->conf.file, curpx->conf.line);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001547
Christopher Faulet61cc8522020-04-20 14:54:42 +02001548 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001549 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001550 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02001551 if (rs == NULL) {
1552 memprintf(errmsg, "out of memory.\n");
1553 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001554 }
1555 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001556
Christopher Faulete5870d82020-04-15 11:32:03 +02001557 index = 0;
1558 if (!LIST_ISEMPTY(&rs->rules)) {
1559 chk = LIST_PREV(&rs->rules, typeof(chk), list);
1560 if (chk->action != TCPCHK_ACT_SEND || !(chk->send.http.flags & TCPCHK_SND_HTTP_FROM_OPT))
1561 index = chk->index + 1;
Christopher Faulet37e583d2021-03-12 12:00:14 +01001562 chk = NULL;
Christopher Faulete5870d82020-04-15 11:32:03 +02001563 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001564
Christopher Faulete5870d82020-04-15 11:32:03 +02001565 if (strcmp(args[cur_arg], "connect") == 0)
1566 chk = parse_tcpcheck_connect(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1567 else if (strcmp(args[cur_arg], "send") == 0)
1568 chk = parse_tcpcheck_send_http(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1569 else if (strcmp(args[cur_arg], "expect") == 0)
1570 chk = parse_tcpcheck_expect(args, cur_arg, curpx, &rs->rules, TCPCHK_RULES_HTTP_CHK,
1571 file, line, errmsg);
1572 else if (strcmp(args[cur_arg], "comment") == 0)
1573 chk = parse_tcpcheck_comment(args, cur_arg, curpx, &rs->rules, file, line, errmsg);
1574 else {
1575 struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001576
Christopher Faulete5870d82020-04-15 11:32:03 +02001577 if (!kw) {
1578 action_kw_tcp_check_build_list(&trash);
1579 memprintf(errmsg, "'%s' only supports 'disable-on-404', 'send-state', 'comment', 'connect',"
1580 " 'send', 'expect'%s%s. but got '%s'",
1581 args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
1582 goto error;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001583 }
Christopher Faulete5870d82020-04-15 11:32:03 +02001584 chk = parse_tcpcheck_action(args, cur_arg, curpx, &rs->rules, kw, file, line, errmsg);
1585 }
Christopher Faulet51b129f2020-04-09 15:54:18 +02001586
Christopher Faulete5870d82020-04-15 11:32:03 +02001587 if (!chk) {
1588 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1589 goto error;
1590 }
1591 ret = (*errmsg != NULL); /* Handle warning */
1592
1593 chk->index = index;
1594 if ((curpx->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK &&
1595 (curpx->tcpcheck_rules.flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_HTTP_CHK) {
1596 /* Use this ruleset if the proxy already has http-check enabled */
1597 curpx->tcpcheck_rules.list = &rs->rules;
1598 curpx->tcpcheck_rules.flags &= ~TCPCHK_RULES_UNUSED_HTTP_RS;
1599 if (!tcpcheck_add_http_rule(chk, &curpx->tcpcheck_rules, errmsg)) {
1600 memprintf(errmsg, "'%s %s' : %s.", args[0], args[1], *errmsg);
1601 curpx->tcpcheck_rules.list = NULL;
Christopher Faulet51b129f2020-04-09 15:54:18 +02001602 goto error;
1603 }
1604 }
1605 else {
Christopher Faulete5870d82020-04-15 11:32:03 +02001606 /* mark this ruleset as unused for now */
1607 curpx->tcpcheck_rules.flags |= TCPCHK_RULES_UNUSED_HTTP_RS;
1608 LIST_ADDQ(&rs->rules, &chk->list);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001609 }
1610
Christopher Faulete5870d82020-04-15 11:32:03 +02001611 out:
Christopher Faulet51b129f2020-04-09 15:54:18 +02001612 return ret;
1613
1614 error:
Christopher Faulete5870d82020-04-15 11:32:03 +02001615 free_tcpcheck(chk, 0);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001616 free_tcpcheck_ruleset(rs);
Christopher Faulet51b129f2020-04-09 15:54:18 +02001617 return -1;
1618}
1619
Christopher Faulet430e4802020-04-09 15:28:16 +02001620/* Parses the "option tcp-check" proxy keyword */
1621int proxy_parse_tcp_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1622 const char *file, int line)
1623{
Christopher Faulet404f9192020-04-09 23:13:54 +02001624 struct tcpcheck_ruleset *rs = NULL;
Christopher Faulet430e4802020-04-09 15:28:16 +02001625 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1626 int err_code = 0;
1627
1628 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1629 err_code |= ERR_WARN;
1630
1631 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1632 goto out;
1633
Christopher Faulet404f9192020-04-09 23:13:54 +02001634 curpx->options2 &= ~PR_O2_CHK_ANY;
1635 curpx->options2 |= PR_O2_TCPCHK_CHK;
1636
Christopher Fauletd7e63962020-04-17 20:15:59 +02001637 if ((rules->flags & TCPCHK_RULES_PROTO_CHK) == TCPCHK_RULES_TCP_CHK) {
Christopher Faulet404f9192020-04-09 23:13:54 +02001638 /* If a tcp-check rulesset is already set, do nothing */
1639 if (rules->list)
1640 goto out;
1641
1642 /* If a tcp-check ruleset is waiting to be used for the current proxy,
1643 * get it.
1644 */
1645 if (rules->flags & TCPCHK_RULES_UNUSED_TCP_RS)
1646 goto curpx_ruleset;
1647
1648 /* Otherwise, try to get the tcp-check ruleset of the default proxy */
1649 chunk_printf(&trash, "*tcp-check-defaults_%s-%d", defpx->conf.file, defpx->conf.line);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001650 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001651 if (rs)
1652 goto ruleset_found;
Christopher Faulet430e4802020-04-09 15:28:16 +02001653 }
1654
Christopher Faulet404f9192020-04-09 23:13:54 +02001655 curpx_ruleset:
1656 /* Deduce the ruleset name from the proxy info */
1657 chunk_printf(&trash, "*tcp-check-%s_%s-%d",
1658 ((curpx == defpx) ? "defaults" : curpx->id),
1659 curpx->conf.file, curpx->conf.line);
1660
Christopher Faulet61cc8522020-04-20 14:54:42 +02001661 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001662 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02001663 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulet404f9192020-04-09 23:13:54 +02001664 if (rs == NULL) {
Christopher Faulet430e4802020-04-09 15:28:16 +02001665 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1666 goto error;
1667 }
Christopher Faulet430e4802020-04-09 15:28:16 +02001668 }
1669
Christopher Faulet404f9192020-04-09 23:13:54 +02001670 ruleset_found:
1671 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet404f9192020-04-09 23:13:54 +02001672 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001673 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Fauletd7e63962020-04-17 20:15:59 +02001674 rules->flags |= TCPCHK_RULES_TCP_CHK;
Christopher Faulet430e4802020-04-09 15:28:16 +02001675
1676 out:
1677 return err_code;
1678
1679 error:
1680 err_code |= ERR_ALERT | ERR_FATAL;
1681 goto out;
1682}
Christopher Faulet33f05df2020-04-01 11:08:50 +02001683
1684/* Parses the "option redis-check" proxy keyword */
1685int proxy_parse_redis_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1686 const char *file, int line)
1687{
1688 static char *redis_req = "*1\r\n$4\r\nPING\r\n";
1689 static char *redis_res = "+PONG\r\n";
1690
1691 struct tcpcheck_ruleset *rs = NULL;
1692 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1693 struct tcpcheck_rule *chk;
1694 char *errmsg = NULL;
1695 int err_code = 0;
1696
1697 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1698 err_code |= ERR_WARN;
1699
1700 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1701 goto out;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001702
1703 curpx->options2 &= ~PR_O2_CHK_ANY;
1704 curpx->options2 |= PR_O2_TCPCHK_CHK;
1705
1706 free_tcpcheck_vars(&rules->preset_vars);
1707 rules->list = NULL;
1708 rules->flags = 0;
1709
Christopher Faulet61cc8522020-04-20 14:54:42 +02001710 rs = find_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001711 if (rs)
1712 goto ruleset_found;
1713
Christopher Faulet61cc8522020-04-20 14:54:42 +02001714 rs = create_tcpcheck_ruleset("*redis-check");
Christopher Faulet33f05df2020-04-01 11:08:50 +02001715 if (rs == NULL) {
1716 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1717 goto error;
1718 }
1719
1720 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send", redis_req, ""},
1721 1, curpx, &rs->rules, file, line, &errmsg);
1722 if (!chk) {
1723 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1724 goto error;
1725 }
1726 chk->index = 0;
1727 LIST_ADDQ(&rs->rules, &chk->list);
1728
1729 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "string", redis_res,
1730 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001731 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Faulet33f05df2020-04-01 11:08:50 +02001732 "on-success", "Redis server is ok",
1733 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001734 1, curpx, &rs->rules, TCPCHK_RULES_REDIS_CHK, file, line, &errmsg);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001735 if (!chk) {
1736 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1737 goto error;
1738 }
1739 chk->index = 1;
1740 LIST_ADDQ(&rs->rules, &chk->list);
1741
Christopher Faulet33f05df2020-04-01 11:08:50 +02001742 ruleset_found:
1743 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001744 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001745 rules->flags |= TCPCHK_RULES_REDIS_CHK;
Christopher Faulet33f05df2020-04-01 11:08:50 +02001746
1747 out:
1748 free(errmsg);
1749 return err_code;
1750
1751 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001752 free_tcpcheck_ruleset(rs);
Christopher Faulet33f05df2020-04-01 11:08:50 +02001753 err_code |= ERR_ALERT | ERR_FATAL;
1754 goto out;
1755}
1756
Christopher Faulet811f78c2020-04-01 11:10:27 +02001757
1758/* Parses the "option ssl-hello-chk" proxy keyword */
1759int proxy_parse_ssl_hello_chk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1760 const char *file, int line)
1761{
1762 /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the
1763 * ssl-hello-chk option to ensure that the remote server speaks SSL.
1764 *
1765 * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.
1766 */
1767 static char sslv3_client_hello[] = {
Ilya Shipitsinc02a23f2020-05-06 00:53:22 +05001768 "16" /* ContentType : 0x16 = Handshake */
Christopher Faulet811f78c2020-04-01 11:10:27 +02001769 "0300" /* ProtocolVersion : 0x0300 = SSLv3 */
1770 "0079" /* ContentLength : 0x79 bytes after this one */
1771 "01" /* HanshakeType : 0x01 = CLIENT HELLO */
1772 "000075" /* HandshakeLength : 0x75 bytes after this one */
1773 "0300" /* Hello Version : 0x0300 = v3 */
1774 "%[date(),htonl,hex]" /* Unix GMT Time (s) : filled with <now> (@0x0B) */
1775 "%[str(HAPROXYSSLCHK\nHAPROXYSSLCHK\n),hex]" /* Random : must be exactly 28 bytes */
1776 "00" /* Session ID length : empty (no session ID) */
1777 "004E" /* Cipher Suite Length : 78 bytes after this one */
1778 "0001" "0002" "0003" "0004" /* 39 most common ciphers : */
1779 "0005" "0006" "0007" "0008" /* 0x01...0x1B, 0x2F...0x3A */
1780 "0009" "000A" "000B" "000C" /* This covers RSA/DH, */
1781 "000D" "000E" "000F" "0010" /* various bit lengths, */
1782 "0011" "0012" "0013" "0014" /* SHA1/MD5, DES/3DES/AES... */
1783 "0015" "0016" "0017" "0018"
1784 "0019" "001A" "001B" "002F"
1785 "0030" "0031" "0032" "0033"
1786 "0034" "0035" "0036" "0037"
1787 "0038" "0039" "003A"
1788 "01" /* Compression Length : 0x01 = 1 byte for types */
1789 "00" /* Compression Type : 0x00 = NULL compression */
1790 };
1791
1792 struct tcpcheck_ruleset *rs = NULL;
1793 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1794 struct tcpcheck_rule *chk;
1795 char *errmsg = NULL;
1796 int err_code = 0;
1797
1798 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1799 err_code |= ERR_WARN;
1800
1801 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
1802 goto out;
1803
Christopher Faulet811f78c2020-04-01 11:10:27 +02001804 curpx->options2 &= ~PR_O2_CHK_ANY;
1805 curpx->options2 |= PR_O2_TCPCHK_CHK;
1806
1807 free_tcpcheck_vars(&rules->preset_vars);
1808 rules->list = NULL;
1809 rules->flags = 0;
1810
Christopher Faulet61cc8522020-04-20 14:54:42 +02001811 rs = find_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001812 if (rs)
1813 goto ruleset_found;
1814
Christopher Faulet61cc8522020-04-20 14:54:42 +02001815 rs = create_tcpcheck_ruleset("*ssl-hello-check");
Christopher Faulet811f78c2020-04-01 11:10:27 +02001816 if (rs == NULL) {
1817 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1818 goto error;
1819 }
1820
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001821 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", sslv3_client_hello, ""},
Christopher Faulet811f78c2020-04-01 11:10:27 +02001822 1, curpx, &rs->rules, file, line, &errmsg);
1823 if (!chk) {
1824 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1825 goto error;
1826 }
1827 chk->index = 0;
1828 LIST_ADDQ(&rs->rules, &chk->list);
1829
1830 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^1[56]",
Christopher Fauletec07e382020-04-07 14:56:26 +02001831 "min-recv", "5", "ok-status", "L6OK",
Christopher Faulet811f78c2020-04-01 11:10:27 +02001832 "error-status", "L6RSP", "tout-status", "L6TOUT",
1833 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001834 1, curpx, &rs->rules, TCPCHK_RULES_SSL3_CHK, file, line, &errmsg);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001835 if (!chk) {
1836 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1837 goto error;
1838 }
1839 chk->index = 1;
1840 LIST_ADDQ(&rs->rules, &chk->list);
1841
Christopher Faulet811f78c2020-04-01 11:10:27 +02001842 ruleset_found:
1843 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001844 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001845 rules->flags |= TCPCHK_RULES_SSL3_CHK;
Christopher Faulet811f78c2020-04-01 11:10:27 +02001846
1847 out:
1848 free(errmsg);
1849 return err_code;
1850
1851 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02001852 free_tcpcheck_ruleset(rs);
Christopher Faulet811f78c2020-04-01 11:10:27 +02001853 err_code |= ERR_ALERT | ERR_FATAL;
1854 goto out;
1855}
1856
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001857/* Parses the "option smtpchk" proxy keyword */
1858int proxy_parse_smtpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
1859 const char *file, int line)
1860{
1861 static char *smtp_req = "%[var(check.smtp_cmd)]\r\n";
1862
1863 struct tcpcheck_ruleset *rs = NULL;
1864 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
1865 struct tcpcheck_rule *chk;
1866 struct tcpcheck_var *var = NULL;
1867 char *cmd = NULL, *errmsg = NULL;
1868 int err_code = 0;
1869
1870 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
1871 err_code |= ERR_WARN;
1872
1873 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
1874 goto out;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001875
1876 curpx->options2 &= ~PR_O2_CHK_ANY;
1877 curpx->options2 |= PR_O2_TCPCHK_CHK;
1878
1879 free_tcpcheck_vars(&rules->preset_vars);
1880 rules->list = NULL;
1881 rules->flags = 0;
1882
1883 cur_arg += 2;
1884 if (*args[cur_arg] && *args[cur_arg+1] &&
1885 (strcmp(args[cur_arg], "EHLO") == 0 || strcmp(args[cur_arg], "HELO") == 0)) {
Tim Duesterhus2867b402020-06-12 15:58:48 +02001886 /* <EHLO|HELO> + space (1) + <host> + null byte (1) */
1887 cmd = calloc(strlen(args[cur_arg]) + 1 + strlen(args[cur_arg+1]) + 1, sizeof(*cmd));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001888 if (cmd)
1889 sprintf(cmd, "%s %s", args[cur_arg], args[cur_arg+1]);
1890 }
1891 else {
1892 /* this just hits the default for now, but you could potentially expand it to allow for other stuff
1893 though, it's unlikely you'd want to send anything other than an EHLO or HELO */
1894 cmd = strdup("HELO localhost");
1895 }
1896
Christopher Fauletb61caf42020-04-21 10:57:42 +02001897 var = create_tcpcheck_var(ist("check.smtp_cmd"));
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001898 if (cmd == NULL || var == NULL) {
1899 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1900 goto error;
1901 }
1902 var->data.type = SMP_T_STR;
1903 var->data.u.str.area = cmd;
1904 var->data.u.str.data = strlen(cmd);
1905 LIST_INIT(&var->list);
1906 LIST_ADDQ(&rules->preset_vars, &var->list);
1907 cmd = NULL;
1908 var = NULL;
1909
Christopher Faulet61cc8522020-04-20 14:54:42 +02001910 rs = find_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001911 if (rs)
1912 goto ruleset_found;
1913
Christopher Faulet61cc8522020-04-20 14:54:42 +02001914 rs = create_tcpcheck_ruleset("*smtp-check");
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001915 if (rs == NULL) {
1916 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
1917 goto error;
1918 }
1919
1920 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
1921 1, curpx, &rs->rules, file, line, &errmsg);
1922 if (!chk) {
1923 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1924 goto error;
1925 }
1926 chk->index = 0;
1927 LIST_ADDQ(&rs->rules, &chk->list);
1928
1929 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^[0-9]{3}[ \r]",
1930 "min-recv", "4",
1931 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02001932 "on-error", "%[res.payload(0,0),cut_crlf]",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001933 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001934 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001935 if (!chk) {
1936 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1937 goto error;
1938 }
1939 chk->index = 1;
1940 LIST_ADDQ(&rs->rules, &chk->list);
1941
1942 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[ \r]",
1943 "min-recv", "4",
1944 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001945 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1946 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001947 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001948 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001949 if (!chk) {
1950 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1951 goto error;
1952 }
1953 chk->index = 2;
1954 LIST_ADDQ(&rs->rules, &chk->list);
1955
Christopher Fauletb50b3e62020-05-05 18:43:43 +02001956 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", smtp_req, ""},
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001957 1, curpx, &rs->rules, file, line, &errmsg);
1958 if (!chk) {
1959 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1960 goto error;
1961 }
1962 chk->index = 3;
1963 LIST_ADDQ(&rs->rules, &chk->list);
1964
1965 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rstring", "^2[0-9]{2}[- \r]",
1966 "min-recv", "4",
1967 "error-status", "L7STS",
Christopher Faulete596d182020-05-05 17:46:34 +02001968 "on-error", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1969 "on-success", "%[res.payload(4,0),ltrim(' '),cut_crlf]",
1970 "status-code", "res.payload(0,3)",
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001971 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02001972 1, curpx, &rs->rules, TCPCHK_RULES_SMTP_CHK, file, line, &errmsg);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001973 if (!chk) {
1974 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
1975 goto error;
1976 }
1977 chk->index = 4;
1978 LIST_ADDQ(&rs->rules, &chk->list);
1979
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001980 ruleset_found:
1981 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01001982 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02001983 rules->flags |= TCPCHK_RULES_SMTP_CHK;
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001984
1985 out:
1986 free(errmsg);
1987 return err_code;
1988
1989 error:
1990 free(cmd);
1991 free(var);
1992 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02001993 free_tcpcheck_ruleset(rs);
Christopher Fauletfbcc77c2020-04-01 20:54:05 +02001994 err_code |= ERR_ALERT | ERR_FATAL;
1995 goto out;
1996}
Christopher Faulet811f78c2020-04-01 11:10:27 +02001997
Christopher Fauletce355072020-04-02 11:44:39 +02001998/* Parses the "option pgsql-check" proxy keyword */
1999int proxy_parse_pgsql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2000 const char *file, int line)
2001{
2002 static char pgsql_req[] = {
2003 "%[var(check.plen),htonl,hex]" /* The packet length*/
2004 "00030000" /* the version 3.0 */
2005 "7573657200" /* "user" key */
2006 "%[var(check.username),hex]00" /* the username */
2007 "00"
2008 };
2009
2010 struct tcpcheck_ruleset *rs = NULL;
2011 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2012 struct tcpcheck_rule *chk;
2013 struct tcpcheck_var *var = NULL;
2014 char *user = NULL, *errmsg = NULL;
2015 size_t packetlen = 0;
2016 int err_code = 0;
2017
2018 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2019 err_code |= ERR_WARN;
2020
2021 if (alertif_too_many_args_idx(2, 1, file, line, args, &err_code))
2022 goto out;
2023
Christopher Fauletce355072020-04-02 11:44:39 +02002024 curpx->options2 &= ~PR_O2_CHK_ANY;
2025 curpx->options2 |= PR_O2_TCPCHK_CHK;
2026
2027 free_tcpcheck_vars(&rules->preset_vars);
2028 rules->list = NULL;
2029 rules->flags = 0;
2030
2031 cur_arg += 2;
2032 if (!*args[cur_arg] || !*args[cur_arg+1]) {
2033 ha_alert("parsing [%s:%d] : '%s %s' expects 'user <username>' as argument.\n",
2034 file, line, args[0], args[1]);
2035 goto error;
2036 }
2037 if (strcmp(args[cur_arg], "user") == 0) {
2038 packetlen = 15 + strlen(args[cur_arg+1]);
2039 user = strdup(args[cur_arg+1]);
2040
Christopher Fauletb61caf42020-04-21 10:57:42 +02002041 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletce355072020-04-02 11:44:39 +02002042 if (user == NULL || var == NULL) {
2043 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2044 goto error;
2045 }
2046 var->data.type = SMP_T_STR;
2047 var->data.u.str.area = user;
2048 var->data.u.str.data = strlen(user);
2049 LIST_INIT(&var->list);
2050 LIST_ADDQ(&rules->preset_vars, &var->list);
2051 user = NULL;
2052 var = NULL;
2053
Christopher Fauletb61caf42020-04-21 10:57:42 +02002054 var = create_tcpcheck_var(ist("check.plen"));
Christopher Fauletce355072020-04-02 11:44:39 +02002055 if (var == NULL) {
2056 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2057 goto error;
2058 }
2059 var->data.type = SMP_T_SINT;
2060 var->data.u.sint = packetlen;
2061 LIST_INIT(&var->list);
2062 LIST_ADDQ(&rules->preset_vars, &var->list);
2063 var = NULL;
2064 }
2065 else {
2066 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user'.\n",
2067 file, line, args[0], args[1]);
2068 goto error;
2069 }
2070
Christopher Faulet61cc8522020-04-20 14:54:42 +02002071 rs = find_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002072 if (rs)
2073 goto ruleset_found;
2074
Christopher Faulet61cc8522020-04-20 14:54:42 +02002075 rs = create_tcpcheck_ruleset("*pgsql-check");
Christopher Fauletce355072020-04-02 11:44:39 +02002076 if (rs == NULL) {
2077 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2078 goto error;
2079 }
2080
2081 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2082 1, curpx, &rs->rules, file, line, &errmsg);
2083 if (!chk) {
2084 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2085 goto error;
2086 }
2087 chk->index = 0;
2088 LIST_ADDQ(&rs->rules, &chk->list);
2089
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002090 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", pgsql_req, ""},
Christopher Fauletce355072020-04-02 11:44:39 +02002091 1, curpx, &rs->rules, file, line, &errmsg);
2092 if (!chk) {
2093 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2094 goto error;
2095 }
2096 chk->index = 1;
2097 LIST_ADDQ(&rs->rules, &chk->list);
2098
2099 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "!rstring", "^E",
2100 "min-recv", "5",
2101 "error-status", "L7RSP",
Christopher Faulete596d182020-05-05 17:46:34 +02002102 "on-error", "%[res.payload(6,0)]",
Christopher Fauletce355072020-04-02 11:44:39 +02002103 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002104 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002105 if (!chk) {
2106 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2107 goto error;
2108 }
2109 chk->index = 2;
2110 LIST_ADDQ(&rs->rules, &chk->list);
2111
Christopher Fauletb841c742020-04-27 18:29:49 +02002112 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 +02002113 "min-recv", "9",
2114 "error-status", "L7STS",
2115 "on-success", "PostgreSQL server is ok",
2116 "on-error", "PostgreSQL unknown error",
2117 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002118 1, curpx, &rs->rules, TCPCHK_RULES_PGSQL_CHK, file, line, &errmsg);
Christopher Fauletce355072020-04-02 11:44:39 +02002119 if (!chk) {
2120 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2121 goto error;
2122 }
2123 chk->index = 3;
2124 LIST_ADDQ(&rs->rules, &chk->list);
2125
Christopher Fauletce355072020-04-02 11:44:39 +02002126 ruleset_found:
2127 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002128 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002129 rules->flags |= TCPCHK_RULES_PGSQL_CHK;
Christopher Fauletce355072020-04-02 11:44:39 +02002130
2131 out:
2132 free(errmsg);
2133 return err_code;
2134
2135 error:
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002136 free(user);
2137 free(var);
2138 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002139 free_tcpcheck_ruleset(rs);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002140 err_code |= ERR_ALERT | ERR_FATAL;
2141 goto out;
2142}
2143
2144
2145/* Parses the "option mysql-check" proxy keyword */
2146int proxy_parse_mysql_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2147 const char *file, int line)
2148{
2149 /* This is an example of a MySQL >=4.0 client Authentication packet kindly provided by Cyril Bonte.
2150 * const char mysql40_client_auth_pkt[] = {
2151 * "\x0e\x00\x00" // packet length
2152 * "\x01" // packet number
2153 * "\x00\x00" // client capabilities
2154 * "\x00\x00\x01" // max packet
2155 * "haproxy\x00" // username (null terminated string)
2156 * "\x00" // filler (always 0x00)
2157 * "\x01\x00\x00" // packet length
2158 * "\x00" // packet number
2159 * "\x01" // COM_QUIT command
2160 * };
2161 */
2162 static char mysql40_rsname[] = "*mysql40-check";
2163 static char mysql40_req[] = {
2164 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2165 "0080" /* client capabilities */
2166 "000001" /* max packet */
2167 "%[var(check.username),hex]00" /* the username */
2168 "00" /* filler (always 0x00) */
2169 "010000" /* packet length*/
2170 "00" /* sequence ID */
2171 "01" /* COM_QUIT command */
2172 };
2173
2174 /* This is an example of a MySQL >=4.1 client Authentication packet provided by Nenad Merdanovic.
2175 * const char mysql41_client_auth_pkt[] = {
2176 * "\x0e\x00\x00\" // packet length
2177 * "\x01" // packet number
2178 * "\x00\x00\x00\x00" // client capabilities
2179 * "\x00\x00\x00\x01" // max packet
2180 * "\x21" // character set (UTF-8)
2181 * char[23] // All zeroes
2182 * "haproxy\x00" // username (null terminated string)
2183 * "\x00" // filler (always 0x00)
2184 * "\x01\x00\x00" // packet length
2185 * "\x00" // packet number
2186 * "\x01" // COM_QUIT command
2187 * };
2188 */
2189 static char mysql41_rsname[] = "*mysql41-check";
2190 static char mysql41_req[] = {
2191 "%[var(check.header),hex]" /* 3 bytes for the packet length and 1 byte for the sequence ID */
2192 "00820000" /* client capabilities */
2193 "00800001" /* max packet */
2194 "21" /* character set (UTF-8) */
2195 "000000000000000000000000" /* 23 bytes, al zeroes */
2196 "0000000000000000000000"
2197 "%[var(check.username),hex]00" /* the username */
2198 "00" /* filler (always 0x00) */
2199 "010000" /* packet length*/
2200 "00" /* sequence ID */
2201 "01" /* COM_QUIT command */
2202 };
2203
2204 struct tcpcheck_ruleset *rs = NULL;
2205 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2206 struct tcpcheck_rule *chk;
2207 struct tcpcheck_var *var = NULL;
2208 char *mysql_rsname = "*mysql-check";
2209 char *mysql_req = NULL, *hdr = NULL, *user = NULL, *errmsg = NULL;
2210 int index = 0, err_code = 0;
2211
2212 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2213 err_code |= ERR_WARN;
2214
2215 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2216 goto out;
2217
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002218 curpx->options2 &= ~PR_O2_CHK_ANY;
2219 curpx->options2 |= PR_O2_TCPCHK_CHK;
2220
2221 free_tcpcheck_vars(&rules->preset_vars);
2222 rules->list = NULL;
2223 rules->flags = 0;
2224
2225 cur_arg += 2;
2226 if (*args[cur_arg]) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002227 int packetlen, userlen;
2228
2229 if (strcmp(args[cur_arg], "user") != 0) {
2230 ha_alert("parsing [%s:%d] : '%s %s' only supports optional values: 'user' (got '%s').\n",
2231 file, line, args[0], args[1], args[cur_arg]);
2232 goto error;
2233 }
2234
2235 if (*(args[cur_arg+1]) == 0) {
2236 ha_alert("parsing [%s:%d] : '%s %s %s' expects <username> as argument.\n",
2237 file, line, args[0], args[1], args[cur_arg]);
2238 goto error;
2239 }
2240
2241 hdr = calloc(4, sizeof(*hdr));
2242 user = strdup(args[cur_arg+1]);
2243 userlen = strlen(args[cur_arg+1]);
2244
2245 if (hdr == NULL || user == NULL) {
2246 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2247 goto error;
2248 }
2249
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002250 if (!*args[cur_arg+2] || strcmp(args[cur_arg+2], "post-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002251 packetlen = userlen + 7 + 27;
2252 mysql_req = mysql41_req;
2253 mysql_rsname = mysql41_rsname;
2254 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002255 else if (strcmp(args[cur_arg+2], "pre-41") == 0) {
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002256 packetlen = userlen + 7;
2257 mysql_req = mysql40_req;
2258 mysql_rsname = mysql40_rsname;
2259 }
Christopher Faulet62f79fe2020-05-18 18:13:03 +02002260 else {
2261 ha_alert("parsing [%s:%d] : keyword '%s' only supports 'post-41' and 'pre-41' (got '%s').\n",
2262 file, line, args[cur_arg], args[cur_arg+2]);
2263 goto error;
2264 }
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002265
2266 hdr[0] = (unsigned char)(packetlen & 0xff);
2267 hdr[1] = (unsigned char)((packetlen >> 8) & 0xff);
2268 hdr[2] = (unsigned char)((packetlen >> 16) & 0xff);
2269 hdr[3] = 1;
2270
Christopher Fauletb61caf42020-04-21 10:57:42 +02002271 var = create_tcpcheck_var(ist("check.header"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002272 if (var == NULL) {
2273 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2274 goto error;
2275 }
2276 var->data.type = SMP_T_STR;
2277 var->data.u.str.area = hdr;
2278 var->data.u.str.data = 4;
2279 LIST_INIT(&var->list);
2280 LIST_ADDQ(&rules->preset_vars, &var->list);
2281 hdr = NULL;
2282 var = NULL;
2283
Christopher Fauletb61caf42020-04-21 10:57:42 +02002284 var = create_tcpcheck_var(ist("check.username"));
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002285 if (var == NULL) {
2286 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2287 goto error;
2288 }
2289 var->data.type = SMP_T_STR;
2290 var->data.u.str.area = user;
2291 var->data.u.str.data = strlen(user);
2292 LIST_INIT(&var->list);
2293 LIST_ADDQ(&rules->preset_vars, &var->list);
2294 user = NULL;
2295 var = NULL;
2296 }
2297
Christopher Faulet61cc8522020-04-20 14:54:42 +02002298 rs = find_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002299 if (rs)
2300 goto ruleset_found;
2301
Christopher Faulet61cc8522020-04-20 14:54:42 +02002302 rs = create_tcpcheck_ruleset(mysql_rsname);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002303 if (rs == NULL) {
2304 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2305 goto error;
2306 }
2307
2308 chk = parse_tcpcheck_connect((char *[]){"tcp-check", "connect", "default", "linger", ""},
2309 1, curpx, &rs->rules, file, line, &errmsg);
2310 if (!chk) {
2311 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2312 goto error;
2313 }
2314 chk->index = index++;
2315 LIST_ADDQ(&rs->rules, &chk->list);
2316
2317 if (mysql_req) {
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002318 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary-lf", mysql_req, ""},
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002319 1, curpx, &rs->rules, file, line, &errmsg);
2320 if (!chk) {
2321 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2322 goto error;
2323 }
2324 chk->index = index++;
2325 LIST_ADDQ(&rs->rules, &chk->list);
2326 }
2327
2328 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002329 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002330 if (!chk) {
2331 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2332 goto error;
2333 }
2334 chk->expect.custom = tcpcheck_mysql_expect_iniths;
2335 chk->index = index++;
2336 LIST_ADDQ(&rs->rules, &chk->list);
2337
2338 if (mysql_req) {
2339 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002340 1, curpx, &rs->rules, TCPCHK_RULES_MYSQL_CHK, file, line, &errmsg);
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002341 if (!chk) {
2342 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2343 goto error;
2344 }
2345 chk->expect.custom = tcpcheck_mysql_expect_ok;
2346 chk->index = index++;
2347 LIST_ADDQ(&rs->rules, &chk->list);
2348 }
2349
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002350 ruleset_found:
2351 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002352 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002353 rules->flags |= TCPCHK_RULES_MYSQL_CHK;
Christopher Fauletf2b3be52020-04-02 18:07:37 +02002354
2355 out:
2356 free(errmsg);
2357 return err_code;
2358
2359 error:
2360 free(hdr);
Christopher Fauletce355072020-04-02 11:44:39 +02002361 free(user);
2362 free(var);
2363 free_tcpcheck_vars(&rules->preset_vars);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002364 free_tcpcheck_ruleset(rs);
Christopher Fauletce355072020-04-02 11:44:39 +02002365 err_code |= ERR_ALERT | ERR_FATAL;
2366 goto out;
2367}
2368
Christopher Faulet1997eca2020-04-03 23:13:50 +02002369int proxy_parse_ldap_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2370 const char *file, int line)
2371{
2372 static char *ldap_req = "300C020101600702010304008000";
2373
2374 struct tcpcheck_ruleset *rs = NULL;
2375 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2376 struct tcpcheck_rule *chk;
2377 char *errmsg = NULL;
2378 int err_code = 0;
2379
2380 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2381 err_code |= ERR_WARN;
2382
2383 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2384 goto out;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002385
2386 curpx->options2 &= ~PR_O2_CHK_ANY;
2387 curpx->options2 |= PR_O2_TCPCHK_CHK;
2388
2389 free_tcpcheck_vars(&rules->preset_vars);
2390 rules->list = NULL;
2391 rules->flags = 0;
2392
Christopher Faulet61cc8522020-04-20 14:54:42 +02002393 rs = find_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002394 if (rs)
2395 goto ruleset_found;
2396
Christopher Faulet61cc8522020-04-20 14:54:42 +02002397 rs = create_tcpcheck_ruleset("*ldap-check");
Christopher Faulet1997eca2020-04-03 23:13:50 +02002398 if (rs == NULL) {
2399 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2400 goto error;
2401 }
2402
2403 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", ldap_req, ""},
2404 1, curpx, &rs->rules, file, line, &errmsg);
2405 if (!chk) {
2406 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2407 goto error;
2408 }
2409 chk->index = 0;
2410 LIST_ADDQ(&rs->rules, &chk->list);
2411
2412 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "rbinary", "^30",
2413 "min-recv", "14",
2414 "on-error", "Not LDAPv3 protocol",
2415 ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002416 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002417 if (!chk) {
2418 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2419 goto error;
2420 }
2421 chk->index = 1;
2422 LIST_ADDQ(&rs->rules, &chk->list);
2423
2424 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002425 1, curpx, &rs->rules, TCPCHK_RULES_LDAP_CHK, file, line, &errmsg);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002426 if (!chk) {
2427 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2428 goto error;
2429 }
2430 chk->expect.custom = tcpcheck_ldap_expect_bindrsp;
2431 chk->index = 2;
2432 LIST_ADDQ(&rs->rules, &chk->list);
2433
Christopher Faulet1997eca2020-04-03 23:13:50 +02002434 ruleset_found:
2435 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002436 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002437 rules->flags |= TCPCHK_RULES_LDAP_CHK;
Christopher Faulet1997eca2020-04-03 23:13:50 +02002438
2439 out:
Christopher Faulet267b01b2020-04-04 10:27:09 +02002440 free(errmsg);
2441 return err_code;
2442
2443 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002444 free_tcpcheck_ruleset(rs);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002445 err_code |= ERR_ALERT | ERR_FATAL;
2446 goto out;
2447}
2448
2449int proxy_parse_spop_check_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2450 const char *file, int line)
2451{
2452 struct tcpcheck_ruleset *rs = NULL;
2453 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2454 struct tcpcheck_rule *chk;
2455 char *spop_req = NULL;
2456 char *errmsg = NULL;
2457 int spop_len = 0, err_code = 0;
2458
2459 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2460 err_code |= ERR_WARN;
2461
2462 if (alertif_too_many_args_idx(0, 1, file, line, args, &err_code))
2463 goto out;
2464
Christopher Faulet267b01b2020-04-04 10:27:09 +02002465 curpx->options2 &= ~PR_O2_CHK_ANY;
2466 curpx->options2 |= PR_O2_TCPCHK_CHK;
2467
2468 free_tcpcheck_vars(&rules->preset_vars);
2469 rules->list = NULL;
2470 rules->flags = 0;
2471
2472
Christopher Faulet61cc8522020-04-20 14:54:42 +02002473 rs = find_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002474 if (rs)
2475 goto ruleset_found;
2476
Christopher Faulet61cc8522020-04-20 14:54:42 +02002477 rs = create_tcpcheck_ruleset("*spop-check");
Christopher Faulet267b01b2020-04-04 10:27:09 +02002478 if (rs == NULL) {
2479 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2480 goto error;
2481 }
2482
2483 if (spoe_prepare_healthcheck_request(&spop_req, &spop_len) == -1) {
2484 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2485 goto error;
2486 }
2487 chunk_reset(&trash);
2488 dump_binary(&trash, spop_req, spop_len);
2489 trash.area[trash.data] = '\0';
2490
2491 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-binary", b_head(&trash), ""},
2492 1, curpx, &rs->rules, file, line, &errmsg);
2493 if (!chk) {
2494 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2495 goto error;
2496 }
2497 chk->index = 0;
2498 LIST_ADDQ(&rs->rules, &chk->list);
2499
2500 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", "min-recv", "4", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002501 1, curpx, &rs->rules, TCPCHK_RULES_SPOP_CHK, file, line, &errmsg);
Christopher Faulet267b01b2020-04-04 10:27:09 +02002502 if (!chk) {
2503 ha_alert("parsing [%s:%d] : %s\n", file, line, errmsg);
2504 goto error;
2505 }
2506 chk->expect.custom = tcpcheck_spop_expect_agenthello;
2507 chk->index = 1;
2508 LIST_ADDQ(&rs->rules, &chk->list);
2509
Christopher Faulet267b01b2020-04-04 10:27:09 +02002510 ruleset_found:
2511 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002512 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002513 rules->flags |= TCPCHK_RULES_SPOP_CHK;
Christopher Faulet267b01b2020-04-04 10:27:09 +02002514
2515 out:
2516 free(spop_req);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002517 free(errmsg);
2518 return err_code;
2519
2520 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002521 free_tcpcheck_ruleset(rs);
Christopher Faulet1997eca2020-04-03 23:13:50 +02002522 err_code |= ERR_ALERT | ERR_FATAL;
2523 goto out;
2524}
Christopher Fauletce355072020-04-02 11:44:39 +02002525
Christopher Faulete5870d82020-04-15 11:32:03 +02002526
2527struct tcpcheck_rule *proxy_parse_httpchk_req(char **args, int cur_arg, struct proxy *px, char **errmsg)
2528{
2529 struct tcpcheck_rule *chk = NULL;
2530 struct tcpcheck_http_hdr *hdr = NULL;
2531 char *meth = NULL, *uri = NULL, *vsn = NULL;
2532 char *hdrs, *body;
2533
2534 hdrs = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n") : NULL);
2535 body = (*args[cur_arg+2] ? strstr(args[cur_arg+2], "\r\n\r\n") : NULL);
2536 if (hdrs == body)
2537 hdrs = NULL;
2538 if (hdrs) {
2539 *hdrs = '\0';
2540 hdrs +=2;
2541 }
2542 if (body) {
2543 *body = '\0';
2544 body += 4;
2545 }
2546 if (hdrs || body) {
2547 memprintf(errmsg, "hiding headers or body at the end of the version string is deprecated."
2548 " Please, consider to use 'http-check send' directive instead.");
2549 }
2550
2551 chk = calloc(1, sizeof(*chk));
2552 if (!chk) {
2553 memprintf(errmsg, "out of memory");
2554 goto error;
2555 }
2556 chk->action = TCPCHK_ACT_SEND;
2557 chk->send.type = TCPCHK_SEND_HTTP;
2558 chk->send.http.flags |= TCPCHK_SND_HTTP_FROM_OPT;
2559 chk->send.http.meth.meth = HTTP_METH_OPTIONS;
2560 LIST_INIT(&chk->send.http.hdrs);
2561
2562 /* Copy the method, uri and version */
2563 if (*args[cur_arg]) {
2564 if (!*args[cur_arg+1])
2565 uri = args[cur_arg];
2566 else
2567 meth = args[cur_arg];
2568 }
2569 if (*args[cur_arg+1])
2570 uri = args[cur_arg+1];
2571 if (*args[cur_arg+2])
2572 vsn = args[cur_arg+2];
2573
2574 if (meth) {
2575 chk->send.http.meth.meth = find_http_meth(meth, strlen(meth));
2576 chk->send.http.meth.str.area = strdup(meth);
2577 chk->send.http.meth.str.data = strlen(meth);
2578 if (!chk->send.http.meth.str.area) {
2579 memprintf(errmsg, "out of memory");
2580 goto error;
2581 }
2582 }
2583 if (uri) {
2584 chk->send.http.uri = ist2(strdup(uri), strlen(uri));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002585 if (!isttest(chk->send.http.uri)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002586 memprintf(errmsg, "out of memory");
2587 goto error;
2588 }
2589 }
2590 if (vsn) {
2591 chk->send.http.vsn = ist2(strdup(vsn), strlen(vsn));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002592 if (!isttest(chk->send.http.vsn)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002593 memprintf(errmsg, "out of memory");
2594 goto error;
2595 }
2596 }
2597
2598 /* Copy the header */
2599 if (hdrs) {
2600 struct http_hdr tmp_hdrs[global.tune.max_http_hdr];
2601 struct h1m h1m;
2602 int i, ret;
2603
2604 /* Build and parse the request */
2605 chunk_printf(&trash, "%s\r\n\r\n", hdrs);
2606
2607 h1m.flags = H1_MF_HDRS_ONLY;
2608 ret = h1_headers_to_hdr_list(b_orig(&trash), b_tail(&trash),
2609 tmp_hdrs, sizeof(tmp_hdrs)/sizeof(tmp_hdrs[0]),
2610 &h1m, NULL);
2611 if (ret <= 0) {
2612 memprintf(errmsg, "unable to parse the request '%s'.", b_orig(&trash));
2613 goto error;
2614 }
2615
Christopher Fauletb61caf42020-04-21 10:57:42 +02002616 for (i = 0; istlen(tmp_hdrs[i].n); i++) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002617 hdr = calloc(1, sizeof(*hdr));
2618 if (!hdr) {
2619 memprintf(errmsg, "out of memory");
2620 goto error;
2621 }
2622 LIST_INIT(&hdr->value);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002623 hdr->name = istdup(tmp_hdrs[i].n);
Christopher Faulete5870d82020-04-15 11:32:03 +02002624 if (!hdr->name.ptr) {
2625 memprintf(errmsg, "out of memory");
2626 goto error;
2627 }
2628
Christopher Fauletb61caf42020-04-21 10:57:42 +02002629 ist0(tmp_hdrs[i].v);
2630 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 +02002631 goto error;
2632 LIST_ADDQ(&chk->send.http.hdrs, &hdr->list);
2633 }
2634 }
2635
2636 /* Copy the body */
2637 if (body) {
2638 chk->send.http.body = ist2(strdup(body), strlen(body));
Christopher Fauletb61caf42020-04-21 10:57:42 +02002639 if (!isttest(chk->send.http.body)) {
Christopher Faulete5870d82020-04-15 11:32:03 +02002640 memprintf(errmsg, "out of memory");
2641 goto error;
2642 }
2643 }
2644
2645 return chk;
2646
2647 error:
2648 free_tcpcheck_http_hdr(hdr);
2649 free_tcpcheck(chk, 0);
2650 return NULL;
2651}
2652
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002653int proxy_parse_httpchk_opt(char **args, int cur_arg, struct proxy *curpx, struct proxy *defpx,
2654 const char *file, int line)
2655{
Christopher Faulete5870d82020-04-15 11:32:03 +02002656 struct tcpcheck_ruleset *rs = NULL;
2657 struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
2658 struct tcpcheck_rule *chk;
2659 char *errmsg = NULL;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002660 int err_code = 0;
2661
2662 if (warnifnotcap(curpx, PR_CAP_BE, file, line, args[cur_arg+1], NULL))
2663 err_code |= ERR_WARN;
2664
2665 if (alertif_too_many_args_idx(3, 1, file, line, args, &err_code))
2666 goto out;
2667
Christopher Faulete5870d82020-04-15 11:32:03 +02002668 chk = proxy_parse_httpchk_req(args, cur_arg+2, curpx, &errmsg);
2669 if (!chk) {
2670 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2671 goto error;
2672 }
2673 if (errmsg) {
2674 ha_warning("parsing [%s:%d]: '%s %s' : %s\n", file, line, args[0], args[1], errmsg);
2675 err_code |= ERR_WARN;
2676 free(errmsg);
2677 errmsg = NULL;
2678 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002679
Christopher Faulete5870d82020-04-15 11:32:03 +02002680 no_request:
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002681 curpx->options2 &= ~PR_O2_CHK_ANY;
Christopher Faulete5870d82020-04-15 11:32:03 +02002682 curpx->options2 |= PR_O2_TCPCHK_CHK;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002683
Christopher Faulete5870d82020-04-15 11:32:03 +02002684 free_tcpcheck_vars(&rules->preset_vars);
2685 rules->list = NULL;
2686 rules->flags |= TCPCHK_SND_HTTP_FROM_OPT;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002687
Christopher Faulete5870d82020-04-15 11:32:03 +02002688 /* Deduce the ruleset name from the proxy info */
2689 chunk_printf(&trash, "*http-check-%s_%s-%d",
2690 ((curpx == defpx) ? "defaults" : curpx->id),
2691 curpx->conf.file, curpx->conf.line);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002692
Christopher Faulet61cc8522020-04-20 14:54:42 +02002693 rs = find_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002694 if (rs == NULL) {
Christopher Faulet61cc8522020-04-20 14:54:42 +02002695 rs = create_tcpcheck_ruleset(b_orig(&trash));
Christopher Faulete5870d82020-04-15 11:32:03 +02002696 if (rs == NULL) {
2697 ha_alert("parsing [%s:%d] : out of memory.\n", file, line);
2698 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002699 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002700 }
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002701
Christopher Faulete5870d82020-04-15 11:32:03 +02002702 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002703 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulete5870d82020-04-15 11:32:03 +02002704 rules->flags |= TCPCHK_RULES_HTTP_CHK;
2705 if (!tcpcheck_add_http_rule(chk, rules, &errmsg)) {
2706 ha_alert("parsing [%s:%d] : '%s %s' : %s.\n", file, line, args[0], args[1], errmsg);
2707 rules->list = NULL;
2708 goto error;
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002709 }
Christopher Faulete5870d82020-04-15 11:32:03 +02002710
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002711 out:
Christopher Faulete5870d82020-04-15 11:32:03 +02002712 free(errmsg);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002713 return err_code;
2714
2715 error:
Christopher Faulet61cc8522020-04-20 14:54:42 +02002716 free_tcpcheck_ruleset(rs);
Christopher Faulete5870d82020-04-15 11:32:03 +02002717 free_tcpcheck(chk, 0);
Christopher Faulet6c2a7432020-04-09 14:48:48 +02002718 err_code |= ERR_ALERT | ERR_FATAL;
2719 goto out;
2720}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002721
Christopher Fauletce8111e2020-04-06 15:04:11 +02002722/* Parse the "addr" server keyword */
2723static int srv_parse_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2724 char **errmsg)
2725{
2726 struct sockaddr_storage *sk;
Christopher Fauletce8111e2020-04-06 15:04:11 +02002727 int port1, port2, err_code = 0;
2728
2729
2730 if (!*args[*cur_arg+1]) {
2731 memprintf(errmsg, "'%s' expects <ipv4|ipv6> as argument.", args[*cur_arg]);
2732 goto error;
2733 }
2734
Willy Tarreau65ec4e32020-09-16 19:17:08 +02002735 sk = str2sa_range(args[*cur_arg+1], NULL, &port1, &port2, NULL, NULL, errmsg, NULL, NULL,
2736 PA_O_RESOLVE | PA_O_PORT_OK | PA_O_STREAM | PA_O_CONNECT);
Christopher Fauletce8111e2020-04-06 15:04:11 +02002737 if (!sk) {
2738 memprintf(errmsg, "'%s' : %s", args[*cur_arg], *errmsg);
2739 goto error;
2740 }
2741
Christopher Fauletce8111e2020-04-06 15:04:11 +02002742 srv->check.addr = srv->agent.addr = *sk;
2743 srv->flags |= SRV_F_CHECKADDR;
2744 srv->flags |= SRV_F_AGENTADDR;
2745
2746 out:
2747 return err_code;
2748
2749 error:
2750 err_code |= ERR_ALERT | ERR_FATAL;
2751 goto out;
2752}
2753
2754
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002755/* Parse the "agent-addr" server keyword */
2756static int srv_parse_agent_addr(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2757 char **errmsg)
2758{
2759 int err_code = 0;
2760
2761 if (!*(args[*cur_arg+1])) {
2762 memprintf(errmsg, "'%s' expects an address as argument.", args[*cur_arg]);
2763 goto error;
2764 }
2765 if(str2ip(args[*cur_arg+1], &srv->agent.addr) == NULL) {
2766 memprintf(errmsg, "parsing agent-addr failed. Check if '%s' is correct address.", args[*cur_arg+1]);
2767 goto error;
2768 }
2769
2770 out:
2771 return err_code;
2772
2773 error:
2774 err_code |= ERR_ALERT | ERR_FATAL;
2775 goto out;
2776}
2777
2778/* Parse the "agent-check" server keyword */
2779static int srv_parse_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2780 char **errmsg)
2781{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002782 struct tcpcheck_ruleset *rs = NULL;
2783 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2784 struct tcpcheck_rule *chk;
2785 int err_code = 0;
2786
2787 if (srv->do_agent)
2788 goto out;
2789
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002790 if (!(curpx->cap & PR_CAP_BE)) {
2791 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2792 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2793 return ERR_WARN;
2794 }
2795
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002796 if (!rules) {
2797 rules = calloc(1, sizeof(*rules));
2798 if (!rules) {
2799 memprintf(errmsg, "out of memory.");
2800 goto error;
2801 }
2802 LIST_INIT(&rules->preset_vars);
2803 srv->agent.tcpcheck_rules = rules;
2804 }
2805 rules->list = NULL;
2806 rules->flags = 0;
2807
Christopher Faulet61cc8522020-04-20 14:54:42 +02002808 rs = find_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002809 if (rs)
2810 goto ruleset_found;
2811
Christopher Faulet61cc8522020-04-20 14:54:42 +02002812 rs = create_tcpcheck_ruleset("*agent-check");
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002813 if (rs == NULL) {
2814 memprintf(errmsg, "out of memory.");
2815 goto error;
2816 }
2817
Christopher Fauletb50b3e62020-05-05 18:43:43 +02002818 chk = parse_tcpcheck_send((char *[]){"tcp-check", "send-lf", "%[var(check.agent_string)]", ""},
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002819 1, curpx, &rs->rules, srv->conf.file, srv->conf.line, errmsg);
2820 if (!chk) {
2821 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2822 goto error;
2823 }
2824 chk->index = 0;
2825 LIST_ADDQ(&rs->rules, &chk->list);
2826
2827 chk = parse_tcpcheck_expect((char *[]){"tcp-check", "expect", "custom", ""},
Christopher Faulete5870d82020-04-15 11:32:03 +02002828 1, curpx, &rs->rules, TCPCHK_RULES_AGENT_CHK,
2829 srv->conf.file, srv->conf.line, errmsg);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002830 if (!chk) {
2831 memprintf(errmsg, "'%s': %s", args[*cur_arg], *errmsg);
2832 goto error;
2833 }
2834 chk->expect.custom = tcpcheck_agent_expect_reply;
2835 chk->index = 1;
2836 LIST_ADDQ(&rs->rules, &chk->list);
2837
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002838 ruleset_found:
2839 rules->list = &rs->rules;
Christopher Fauletb9492cf2020-11-25 16:43:12 +01002840 rules->flags &= ~(TCPCHK_RULES_PROTO_CHK|TCPCHK_RULES_UNUSED_RS);
Christopher Faulet404f9192020-04-09 23:13:54 +02002841 rules->flags |= TCPCHK_RULES_AGENT_CHK;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002842 srv->do_agent = 1;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002843
2844 out:
Dirkjan Bussink8b37d8d2021-06-18 19:57:49 +00002845 return err_code;
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002846
2847 error:
2848 deinit_srv_agent_check(srv);
Christopher Faulet61cc8522020-04-20 14:54:42 +02002849 free_tcpcheck_ruleset(rs);
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002850 err_code |= ERR_ALERT | ERR_FATAL;
2851 goto out;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002852}
2853
2854/* Parse the "agent-inter" server keyword */
2855static int srv_parse_agent_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2856 char **errmsg)
2857{
2858 const char *err = NULL;
2859 unsigned int delay;
2860 int err_code = 0;
2861
2862 if (!*(args[*cur_arg+1])) {
2863 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
2864 goto error;
2865 }
2866
2867 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
2868 if (err == PARSE_TIME_OVER) {
2869 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
2870 args[*cur_arg+1], args[*cur_arg], srv->id);
2871 goto error;
2872 }
2873 else if (err == PARSE_TIME_UNDER) {
2874 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
2875 args[*cur_arg+1], args[*cur_arg], srv->id);
2876 goto error;
2877 }
2878 else if (err) {
2879 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
2880 *err, srv->id);
2881 goto error;
2882 }
2883 if (delay <= 0) {
2884 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
2885 delay, args[*cur_arg], srv->id);
2886 goto error;
2887 }
2888 srv->agent.inter = delay;
2889
2890 out:
2891 return err_code;
2892
2893 error:
2894 err_code |= ERR_ALERT | ERR_FATAL;
2895 goto out;
2896}
2897
2898/* Parse the "agent-port" server keyword */
2899static int srv_parse_agent_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2900 char **errmsg)
2901{
2902 int err_code = 0;
2903
2904 if (!*(args[*cur_arg+1])) {
2905 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
2906 goto error;
2907 }
2908
2909 global.maxsock++;
2910 srv->agent.port = atol(args[*cur_arg+1]);
2911
2912 out:
2913 return err_code;
2914
2915 error:
2916 err_code |= ERR_ALERT | ERR_FATAL;
2917 goto out;
2918}
2919
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002920int set_srv_agent_send(struct server *srv, const char *send)
2921{
2922 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
2923 struct tcpcheck_var *var = NULL;
2924 char *str;
2925
2926 str = strdup(send);
Christopher Fauletb61caf42020-04-21 10:57:42 +02002927 var = create_tcpcheck_var(ist("check.agent_string"));
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002928 if (str == NULL || var == NULL)
2929 goto error;
2930
2931 free_tcpcheck_vars(&rules->preset_vars);
2932
2933 var->data.type = SMP_T_STR;
2934 var->data.u.str.area = str;
2935 var->data.u.str.data = strlen(str);
2936 LIST_INIT(&var->list);
2937 LIST_ADDQ(&rules->preset_vars, &var->list);
2938
2939 return 1;
2940
2941 error:
2942 free(str);
2943 free(var);
2944 return 0;
2945}
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002946
2947/* Parse the "agent-send" server keyword */
2948static int srv_parse_agent_send(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2949 char **errmsg)
2950{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002951 struct tcpcheck_rules *rules = srv->agent.tcpcheck_rules;
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002952 int err_code = 0;
2953
2954 if (!*(args[*cur_arg+1])) {
2955 memprintf(errmsg, "'%s' expects a string as argument.", args[*cur_arg]);
2956 goto error;
2957 }
2958
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002959 if (!rules) {
2960 rules = calloc(1, sizeof(*rules));
2961 if (!rules) {
2962 memprintf(errmsg, "out of memory.");
2963 goto error;
2964 }
2965 LIST_INIT(&rules->preset_vars);
2966 srv->agent.tcpcheck_rules = rules;
2967 }
2968
2969 if (!set_srv_agent_send(srv, args[*cur_arg+1])) {
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002970 memprintf(errmsg, "out of memory.");
2971 goto error;
2972 }
2973
2974 out:
2975 return err_code;
2976
2977 error:
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002978 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002979 err_code |= ERR_ALERT | ERR_FATAL;
2980 goto out;
2981}
2982
2983/* Parse the "no-agent-send" server keyword */
2984static int srv_parse_no_agent_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2985 char **errmsg)
2986{
Christopher Faulet0ae3d1d2020-04-06 17:54:24 +02002987 deinit_srv_agent_check(srv);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02002988 return 0;
2989}
2990
Christopher Fauletce8111e2020-04-06 15:04:11 +02002991/* Parse the "check" server keyword */
2992static int srv_parse_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
2993 char **errmsg)
2994{
Christopher Faulet0a5e7132021-01-12 17:29:45 +01002995 if (!(curpx->cap & PR_CAP_BE)) {
2996 memprintf(errmsg, "'%s' ignored because %s '%s' has no backend capability",
2997 args[*cur_arg], proxy_type_str(curpx), curpx->id);
2998 return ERR_WARN;
2999 }
3000
Christopher Fauletce8111e2020-04-06 15:04:11 +02003001 srv->do_check = 1;
3002 return 0;
3003}
3004
3005/* Parse the "check-send-proxy" server keyword */
3006static int srv_parse_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3007 char **errmsg)
3008{
3009 srv->check.send_proxy = 1;
3010 return 0;
3011}
3012
3013/* Parse the "check-via-socks4" server keyword */
3014static int srv_parse_check_via_socks4(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3015 char **errmsg)
3016{
3017 srv->check.via_socks4 = 1;
3018 return 0;
3019}
3020
3021/* Parse the "no-check" server keyword */
3022static int srv_parse_no_check(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3023 char **errmsg)
3024{
3025 deinit_srv_check(srv);
3026 return 0;
3027}
3028
3029/* Parse the "no-check-send-proxy" server keyword */
3030static int srv_parse_no_check_send_proxy(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3031 char **errmsg)
3032{
3033 srv->check.send_proxy = 0;
3034 return 0;
3035}
3036
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003037/* parse the "check-proto" server keyword */
3038static int srv_parse_check_proto(char **args, int *cur_arg,
3039 struct proxy *px, struct server *newsrv, char **err)
3040{
3041 int err_code = 0;
3042
3043 if (!*args[*cur_arg + 1]) {
3044 memprintf(err, "'%s' : missing value", args[*cur_arg]);
3045 goto error;
3046 }
3047 newsrv->check.mux_proto = get_mux_proto(ist2(args[*cur_arg + 1], strlen(args[*cur_arg + 1])));
3048 if (!newsrv->check.mux_proto) {
3049 memprintf(err, "'%s' : unknown MUX protocol '%s'", args[*cur_arg], args[*cur_arg+1]);
3050 goto error;
3051 }
3052
3053 out:
3054 return err_code;
3055
3056 error:
3057 err_code |= ERR_ALERT | ERR_FATAL;
3058 goto out;
3059}
3060
3061
Christopher Fauletce8111e2020-04-06 15:04:11 +02003062/* Parse the "rise" server keyword */
3063static int srv_parse_check_rise(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3064 char **errmsg)
3065{
3066 int err_code = 0;
3067
3068 if (!*args[*cur_arg + 1]) {
3069 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3070 goto error;
3071 }
3072
3073 srv->check.rise = atol(args[*cur_arg+1]);
3074 if (srv->check.rise <= 0) {
3075 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3076 goto error;
3077 }
3078
3079 if (srv->check.health)
3080 srv->check.health = srv->check.rise;
3081
3082 out:
3083 return err_code;
3084
3085 error:
3086 deinit_srv_agent_check(srv);
3087 err_code |= ERR_ALERT | ERR_FATAL;
3088 goto out;
Christopher Fauletce8111e2020-04-06 15:04:11 +02003089}
3090
3091/* Parse the "fall" server keyword */
3092static int srv_parse_check_fall(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3093 char **errmsg)
3094{
3095 int err_code = 0;
3096
3097 if (!*args[*cur_arg + 1]) {
3098 memprintf(errmsg, "'%s' expects an integer argument.", args[*cur_arg]);
3099 goto error;
3100 }
3101
3102 srv->check.fall = atol(args[*cur_arg+1]);
3103 if (srv->check.fall <= 0) {
3104 memprintf(errmsg, "'%s' has to be > 0.", args[*cur_arg]);
3105 goto error;
3106 }
3107
3108 out:
3109 return err_code;
3110
3111 error:
3112 deinit_srv_agent_check(srv);
3113 err_code |= ERR_ALERT | ERR_FATAL;
3114 goto out;
Christopher Fauletce8111e2020-04-06 15:04:11 +02003115}
3116
3117/* Parse the "inter" server keyword */
3118static int srv_parse_check_inter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3119 char **errmsg)
3120{
3121 const char *err = NULL;
3122 unsigned int delay;
3123 int err_code = 0;
3124
3125 if (!*(args[*cur_arg+1])) {
3126 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3127 goto error;
3128 }
3129
3130 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3131 if (err == PARSE_TIME_OVER) {
3132 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3133 args[*cur_arg+1], args[*cur_arg], srv->id);
3134 goto error;
3135 }
3136 else if (err == PARSE_TIME_UNDER) {
3137 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3138 args[*cur_arg+1], args[*cur_arg], srv->id);
3139 goto error;
3140 }
3141 else if (err) {
3142 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3143 *err, srv->id);
3144 goto error;
3145 }
3146 if (delay <= 0) {
3147 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3148 delay, args[*cur_arg], srv->id);
3149 goto error;
3150 }
3151 srv->check.inter = delay;
3152
3153 out:
3154 return err_code;
3155
3156 error:
3157 err_code |= ERR_ALERT | ERR_FATAL;
3158 goto out;
3159}
3160
3161
3162/* Parse the "fastinter" server keyword */
3163static int srv_parse_check_fastinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3164 char **errmsg)
3165{
3166 const char *err = NULL;
3167 unsigned int delay;
3168 int err_code = 0;
3169
3170 if (!*(args[*cur_arg+1])) {
3171 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3172 goto error;
3173 }
3174
3175 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3176 if (err == PARSE_TIME_OVER) {
3177 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3178 args[*cur_arg+1], args[*cur_arg], srv->id);
3179 goto error;
3180 }
3181 else if (err == PARSE_TIME_UNDER) {
3182 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3183 args[*cur_arg+1], args[*cur_arg], srv->id);
3184 goto error;
3185 }
3186 else if (err) {
3187 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3188 *err, srv->id);
3189 goto error;
3190 }
3191 if (delay <= 0) {
3192 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3193 delay, args[*cur_arg], srv->id);
3194 goto error;
3195 }
3196 srv->check.fastinter = delay;
3197
3198 out:
3199 return err_code;
3200
3201 error:
3202 err_code |= ERR_ALERT | ERR_FATAL;
3203 goto out;
3204}
3205
3206
3207/* Parse the "downinter" server keyword */
3208static int srv_parse_check_downinter(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3209 char **errmsg)
3210{
3211 const char *err = NULL;
3212 unsigned int delay;
3213 int err_code = 0;
3214
3215 if (!*(args[*cur_arg+1])) {
3216 memprintf(errmsg, "'%s' expects a delay as argument.", args[*cur_arg]);
3217 goto error;
3218 }
3219
3220 err = parse_time_err(args[*cur_arg+1], &delay, TIME_UNIT_MS);
3221 if (err == PARSE_TIME_OVER) {
3222 memprintf(errmsg, "timer overflow in argument <%s> to <%s> of server %s, maximum value is 2147483647 ms (~24.8 days).",
3223 args[*cur_arg+1], args[*cur_arg], srv->id);
3224 goto error;
3225 }
3226 else if (err == PARSE_TIME_UNDER) {
3227 memprintf(errmsg, "timer underflow in argument <%s> to <%s> of server %s, minimum non-null value is 1 ms.",
3228 args[*cur_arg+1], args[*cur_arg], srv->id);
3229 goto error;
3230 }
3231 else if (err) {
3232 memprintf(errmsg, "unexpected character '%c' in 'agent-inter' argument of server %s.",
3233 *err, srv->id);
3234 goto error;
3235 }
3236 if (delay <= 0) {
3237 memprintf(errmsg, "invalid value %d for argument '%s' of server %s.",
3238 delay, args[*cur_arg], srv->id);
3239 goto error;
3240 }
3241 srv->check.downinter = delay;
3242
3243 out:
3244 return err_code;
3245
3246 error:
3247 err_code |= ERR_ALERT | ERR_FATAL;
3248 goto out;
3249}
3250
3251/* Parse the "port" server keyword */
3252static int srv_parse_check_port(char **args, int *cur_arg, struct proxy *curpx, struct server *srv,
3253 char **errmsg)
3254{
3255 int err_code = 0;
3256
3257 if (!*(args[*cur_arg+1])) {
3258 memprintf(errmsg, "'%s' expects a port number as argument.", args[*cur_arg]);
3259 goto error;
3260 }
3261
3262 global.maxsock++;
3263 srv->check.port = atol(args[*cur_arg+1]);
3264 srv->flags |= SRV_F_CHECKPORT;
3265
3266 out:
3267 return err_code;
3268
3269 error:
3270 err_code |= ERR_ALERT | ERR_FATAL;
3271 goto out;
3272}
3273
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003274static struct cfg_kw_list cfg_kws = {ILH, {
Christopher Faulete9111b62020-04-09 18:12:08 +02003275 { CFG_LISTEN, "http-check", proxy_parse_httpcheck },
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003276 { 0, NULL, NULL },
3277}};
3278
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003279static struct srv_kw_list srv_kws = { "CHK", { }, {
Christopher Fauletce8111e2020-04-06 15:04:11 +02003280 { "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 +02003281 { "agent-addr", srv_parse_agent_addr, 1, 1 }, /* Enable an auxiliary agent check */
3282 { "agent-check", srv_parse_agent_check, 0, 1 }, /* Enable agent checks */
3283 { "agent-inter", srv_parse_agent_inter, 1, 1 }, /* Set the interval between two agent checks */
3284 { "agent-port", srv_parse_agent_port, 1, 1 }, /* Set the TCP port used for agent checks. */
3285 { "agent-send", srv_parse_agent_send, 1, 1 }, /* Set string to send to agent. */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003286 { "check", srv_parse_check, 0, 1 }, /* Enable health checks */
Christopher Fauletedc6ed92020-04-23 16:27:59 +02003287 { "check-proto", srv_parse_check_proto, 1, 1 }, /* Set the mux protocol for health checks */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003288 { "check-send-proxy", srv_parse_check_send_proxy, 0, 1 }, /* Enable PROXY protocol for health checks */
3289 { "check-via-socks4", srv_parse_check_via_socks4, 0, 1 }, /* Enable socks4 proxy for health checks */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003290 { "no-agent-check", srv_parse_no_agent_check, 0, 1 }, /* Do not enable any auxiliary agent check */
Christopher Fauletce8111e2020-04-06 15:04:11 +02003291 { "no-check", srv_parse_no_check, 0, 1 }, /* Disable health checks */
3292 { "no-check-send-proxy", srv_parse_no_check_send_proxy, 0, 1 }, /* Disable PROXY protol for health checks */
3293 { "rise", srv_parse_check_rise, 1, 1 }, /* Set rise value for health checks */
3294 { "fall", srv_parse_check_fall, 1, 1 }, /* Set fall value for health checks */
3295 { "inter", srv_parse_check_inter, 1, 1 }, /* Set inter value for health checks */
3296 { "fastinter", srv_parse_check_fastinter, 1, 1 }, /* Set fastinter value for health checks */
3297 { "downinter", srv_parse_check_downinter, 1, 1 }, /* Set downinter value for health checks */
3298 { "port", srv_parse_check_port, 1, 1 }, /* Set the TCP port used for health checks. */
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003299 { NULL, NULL, 0 },
3300}};
3301
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003302INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Christopher Fauletcbba66c2020-04-06 14:26:30 +02003303INITCALL1(STG_REGISTER, srv_register_keywords, &srv_kws);
Christopher Fauletfd6c2292020-03-25 18:20:15 +01003304
Willy Tarreaubd741542010-03-16 18:46:54 +01003305/*
Willy Tarreaubaaee002006-06-26 02:48:02 +02003306 * Local variables:
3307 * c-indent-level: 8
3308 * c-basic-offset: 8
3309 * End:
3310 */