MEDIUM: checks: Add a shared list of tcp-check rules

A global list to tcp-check ruleset can now be used to share common rulesets with
all backends without any duplication. It is mandatory to convert all specific
protocol checks (redis, pgsql...) to tcp-check healthchecks.

To do so, a flag is now attached to each tcp-check ruleset to know if it is a
shared ruleset or not. tcp-check rules defined in a backend are still directly
attached to the proxy and not shared. In addition a second flag is used to know
if the ruleset is inherited from the defaults section.
diff --git a/include/proto/checks.h b/include/proto/checks.h
index a0a8329..fb33746 100644
--- a/include/proto/checks.h
+++ b/include/proto/checks.h
@@ -62,6 +62,7 @@
 	LIST_ADDQ(&tcp_check_keywords.list, &kw_list->list);
 }
 
+void deinit_proxy_tcpcheck(struct proxy *px);
 /* Declared here, but the definitions are in flt_spoe.c */
 int spoe_prepare_healthcheck_request(char **req, int *len);
 int spoe_handle_healthcheck_response(char *frame, size_t size, char *err, int errlen);
diff --git a/include/types/checks.h b/include/types/checks.h
index 10e0b59..c23a58e 100644
--- a/include/types/checks.h
+++ b/include/types/checks.h
@@ -171,7 +171,7 @@
 	char desc[HCHK_DESC_LEN];		/* health check description */
 	char use_ssl;				/* use SSL for health checks (1: on, 0: server mode, -1: off) */
 	int send_proxy;				/* send a PROXY protocol header with checks */
-	struct list *tcpcheck_rules;		/* tcp-check send / expect rules */
+	struct tcpcheck_rules *tcpcheck_rules;	/* tcp-check send / expect rules */
 	struct tcpcheck_rule *current_step;     /* current step when using tcpcheck */
 	int inter, fastinter, downinter;        /* checks: time in milliseconds */
 	enum chk_result result;                 /* health-check result : CHK_RES_* */
@@ -292,4 +292,22 @@
 	};
 };
 
+#define TCPCHK_RULES_NONE   0x00000000
+#define TCPCHK_RULES_SHARED 0x00000001 /* Set for shared list of tcp-check rules */
+#define TCPCHK_RULES_DEF    0x00000002 /* Ruleset inherited from the default section */
+
+/* a list of tcp-check rules */
+struct tcpcheck_rules {
+	unsigned int flags; /* flags applied to the rules */
+	struct list *list; /* the list of tcpcheck_rules */
+};
+
+/* A list of tcp-check rules with a name */
+struct tcpcheck_ruleset {
+	char *name;         /* the ruleset name */
+	struct list rules;  /* the list of tcpcheck_rule */
+	struct list list;   /* used to chain rulesets */
+};
+
+
 #endif /* _TYPES_CHECKS_H */
diff --git a/include/types/proxy.h b/include/types/proxy.h
index 36eb515..270445a 100644
--- a/include/types/proxy.h
+++ b/include/types/proxy.h
@@ -281,7 +281,7 @@
 
 struct email_alert {
 	struct list list;
-	struct list tcpcheck_rules;
+	struct tcpcheck_rules rules;
 	struct server *srv;
 };
 
@@ -422,7 +422,7 @@
 	struct stktable *table;			/* table for storing sticking streams */
 
 	struct task *task;			/* the associated task, mandatory to manage rate limiting, stopping and resource shortage, NULL if disabled */
-	struct list *tcpcheck_rules;		/* tcp-check send / expect rules */
+	struct tcpcheck_rules tcpcheck_rules;   /* tcp-check send / expect rules */
 	int grace;				/* grace time after stop request */
 	int check_len;				/* Length of the HTTP or SSL3 request */
 	char *check_req;			/* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index 00b1423..f098b16 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -303,15 +303,8 @@
 			}
 			curproxy->check_body_len = defproxy.check_body_len;
 
-			if ((curproxy->options2 & PR_O2_CHK_ANY) == PR_O2_TCPCHK_CHK) {
-				curproxy->tcpcheck_rules =  calloc(1, sizeof(*curproxy->tcpcheck_rules));
-				if (!curproxy->tcpcheck_rules) {
-					ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
-					err_code |= ERR_ALERT | ERR_FATAL;
-					goto out;
-				}
-				LIST_INIT(curproxy->tcpcheck_rules);
-			}
+			curproxy->tcpcheck_rules.flags = (defproxy.tcpcheck_rules.flags | TCPCHK_RULES_DEF);
+			curproxy->tcpcheck_rules.list  = defproxy.tcpcheck_rules.list;
 
 			if (defproxy.expect_str) {
 				curproxy->expect_str = strdup(defproxy.expect_str);
@@ -540,6 +533,8 @@
 
 		proxy_release_conf_errors(&defproxy);
 
+		deinit_proxy_tcpcheck(&defproxy);
+
 		/* we cannot free uri_auth because it might already be used */
 		init_default_instance();
 		curproxy = &defproxy;
@@ -2669,18 +2664,31 @@
 				goto out;
 		}
 		else if (!strcmp(args[1], "tcp-check")) {
+			struct tcpcheck_rules *rules = &curproxy->tcpcheck_rules;
+
 			/* use raw TCPCHK send/expect to check servers' health */
 			if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[1], NULL))
 				err_code |= ERR_WARN;
 
+			if (rules->flags & TCPCHK_RULES_DEF) {
+				/* Only shared ruleset can be inherited from the default section */
+				rules->flags = 0;
+				rules->list  = NULL;
+			}
+			else if (rules->list && (rules->flags & TCPCHK_RULES_SHARED)) {
+				ha_alert("parsing [%s:%d] : A shared tcp-check ruleset alreayd configured.\n", file, linenum);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
-			if ((curproxy != &defproxy) && !curproxy->tcpcheck_rules) {
-				curproxy->tcpcheck_rules =  calloc(1, sizeof(*curproxy->tcpcheck_rules));
-				if (!curproxy->tcpcheck_rules) {
+			if (curproxy != &defproxy && !rules->list) {
+				rules->list = calloc(1, sizeof(*rules->list));
+				if (!rules->list) {
 					ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum);
 					err_code |= ERR_ALERT | ERR_FATAL;
 					goto out;
 				}
-				LIST_INIT(curproxy->tcpcheck_rules);
+				LIST_INIT(rules->list);
 			}
 
 			free(curproxy->check_req);
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 1391033..6bd8160 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3133,7 +3133,7 @@
 			memcpy(curproxy->check_req, sslv3_client_hello_pkt, curproxy->check_len);
 		}
 
-		if (curproxy->tcpcheck_rules != NULL &&
+		if (curproxy->tcpcheck_rules.list != NULL &&
 		    (curproxy->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK) {
 			ha_warning("config : %s '%s' uses tcp-check rules without 'option tcp-check', so the rules are ignored.\n",
 				   proxy_type_str(curproxy), curproxy->id);
diff --git a/src/checks.c b/src/checks.c
index d7c6647..d955994 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -76,6 +76,10 @@
 
 static int srv_check_healthcheck_port(struct check *chk);
 
+/* Global list to share all tcp-checks */
+struct list tcpchecks_list = LIST_HEAD_INIT(tcpchecks_list);
+
+
 DECLARE_STATIC_POOL(pool_head_email_alert,   "email_alert",   sizeof(struct email_alert));
 DECLARE_STATIC_POOL(pool_head_tcpcheck_rule, "tcpcheck_rule", sizeof(struct tcpcheck_rule));
 
@@ -1556,11 +1560,11 @@
 /* returns the first NON-COMMENT tcp-check rule from list <list> or NULL if
  * none was found.
  */
-static struct tcpcheck_rule *get_first_tcpcheck_rule(struct list *list)
+static struct tcpcheck_rule *get_first_tcpcheck_rule(struct tcpcheck_rules *rules)
 {
 	struct tcpcheck_rule *r;
 
-	list_for_each_entry(r, list, list) {
+	list_for_each_entry(r, rules->list, list) {
 		if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
 			return r;
 	}
@@ -1571,15 +1575,15 @@
  * NULL if non was found. If <start> is NULL, it relies on
  * get_first_tcpcheck_rule().
  */
-static struct tcpcheck_rule *get_next_tcpcheck_rule(struct list *list, struct tcpcheck_rule *start)
+static struct tcpcheck_rule *get_next_tcpcheck_rule(struct tcpcheck_rules *rules, struct tcpcheck_rule *start)
 {
 	struct tcpcheck_rule *r;
 
 	if (!start)
-		return get_first_tcpcheck_rule(list);
+		return get_first_tcpcheck_rule(rules);
 
 	r = LIST_NEXT(&start->list, typeof(r), list);
-	list_for_each_entry_from(r, list, list) {
+	list_for_each_entry_from(r, rules->list, list) {
 		if (r->action != TCPCHK_ACT_COMMENT && r->action != TCPCHK_ACT_ACTION_KW)
 			return r;
 	}
@@ -2775,7 +2779,7 @@
 	}
 
 	rule = LIST_PREV(&rule->list, typeof(cur), list);
-	list_for_each_entry_from_rev(rule, check->tcpcheck_rules, list) {
+	list_for_each_entry_from_rev(rule, check->tcpcheck_rules->list, list) {
 		if (rule->action == TCPCHK_ACT_COMMENT) {
 			ret = rule->comment;
 			break;
@@ -3307,10 +3311,10 @@
 			goto out_end_tcpcheck;
 		}
 		vars_init(&check->vars, SCOPE_CHECK);
-		rule = LIST_NEXT(check->tcpcheck_rules, typeof(rule), list);
+		rule = LIST_NEXT(check->tcpcheck_rules->list, typeof(rule), list);
 	}
 
-	list_for_each_entry_from(rule, check->tcpcheck_rules, list) {
+	list_for_each_entry_from(rule, check->tcpcheck_rules->list, list) {
 		enum tcpcheck_eval_ret eval_ret;
 
 		switch (rule->action) {
@@ -3534,9 +3538,13 @@
 	if (!alert)
 		return;
 
-	list_for_each_entry_safe(rule, back, &alert->tcpcheck_rules, list) {
-		LIST_DEL(&rule->list);
-		free_tcpcheck(rule, 1);
+	if (alert->rules.list) {
+		list_for_each_entry_safe(rule, back, alert->rules.list, list) {
+			LIST_DEL(&rule->list);
+			free_tcpcheck(rule, 1);
+		}
+		free(alert->rules.list);
+		alert->rules.list = NULL;
 	}
 	pool_free(pool_head_email_alert, alert);
 }
@@ -3562,7 +3570,7 @@
 			alert = LIST_NEXT(&q->email_alerts, typeof(alert), list);
 			LIST_DEL(&alert->list);
 			t->expire             = now_ms;
-			check->tcpcheck_rules = &alert->tcpcheck_rules;
+			check->tcpcheck_rules = &alert->rules;
 			check->status         = HCHK_STATUS_INI;
 			check->state         |= CHK_ST_ENABLED;
 		}
@@ -3571,7 +3579,7 @@
 		if (check->state & CHK_ST_INPROGRESS)
 			break;
 
-		alert = container_of(check->tcpcheck_rules, typeof(*alert), tcpcheck_rules);
+		alert = container_of(check->tcpcheck_rules, typeof(*alert), rules);
 		email_alert_free(alert);
 		check->tcpcheck_rules = NULL;
 		check->server         = NULL;
@@ -3653,7 +3661,7 @@
 }
 
 
-static int add_tcpcheck_expect_str(struct list *list, const char *str)
+static int add_tcpcheck_expect_str(struct tcpcheck_rules *rules, const char *str)
 {
 	struct tcpcheck_rule *tcpcheck, *prev_check;
 	struct tcpcheck_expect *expect;
@@ -3676,7 +3684,7 @@
 	 * in a chain of one or more expect rule, potentially itself.
 	 */
 	tcpcheck->expect.head = tcpcheck;
-	list_for_each_entry_rev(prev_check, list, list) {
+	list_for_each_entry_rev(prev_check, rules->list, list) {
 		if (prev_check->action == TCPCHK_ACT_EXPECT) {
 			if (prev_check->expect.inverse)
 				tcpcheck->expect.head = prev_check;
@@ -3685,11 +3693,11 @@
 		if (prev_check->action != TCPCHK_ACT_COMMENT && prev_check->action != TCPCHK_ACT_ACTION_KW)
 			break;
 	}
-	LIST_ADDQ(list, &tcpcheck->list);
+	LIST_ADDQ(rules->list, &tcpcheck->list);
 	return 1;
 }
 
-static int add_tcpcheck_send_strs(struct list *list, const char * const *strs)
+static int add_tcpcheck_send_strs(struct tcpcheck_rules *rules, const char * const *strs)
 {
 	struct tcpcheck_rule *tcpcheck;
 	struct tcpcheck_send *send;
@@ -3719,7 +3727,7 @@
 		for (in = strs[i]; (*dst = *in++); dst++);
 	*dst = 0;
 
-	LIST_ADDQ(list, &tcpcheck->list);
+	LIST_ADDQ(rules->list, &tcpcheck->list);
 	return 1;
 }
 
@@ -3733,7 +3741,11 @@
 	if ((alert = pool_alloc(pool_head_email_alert)) == NULL)
 		goto error;
 	LIST_INIT(&alert->list);
-	LIST_INIT(&alert->tcpcheck_rules);
+	alert->rules.flags = 0;
+	alert->rules.list = calloc(1, sizeof(*alert->rules.list));
+	if (!alert->rules.list)
+		goto error;
+	LIST_INIT(alert->rules.list);
 	alert->srv = s;
 
 	if ((tcpcheck = pool_alloc(pool_head_tcpcheck_rule)) == NULL)
@@ -3742,45 +3754,45 @@
 	tcpcheck->action       = TCPCHK_ACT_CONNECT;
 	tcpcheck->comment      = NULL;
 
-	LIST_ADDQ(&alert->tcpcheck_rules, &tcpcheck->list);
+	LIST_ADDQ(alert->rules.list, &tcpcheck->list);
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "220 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "220 "))
 		goto error;
 
 	{
 		const char * const strs[4] = { "EHLO ", p->email_alert.myhostname, "\r\n" };
-		if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))
+		if (!add_tcpcheck_send_strs(&alert->rules, strs))
 			goto error;
 	}
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
 		goto error;
 
 	{
 		const char * const strs[4] = { "MAIL FROM:<", p->email_alert.from, ">\r\n" };
-		if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))
+		if (!add_tcpcheck_send_strs(&alert->rules, strs))
 			goto error;
 	}
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
 		goto error;
 
 	{
 		const char * const strs[4] = { "RCPT TO:<", p->email_alert.to, ">\r\n" };
-		if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))
+		if (!add_tcpcheck_send_strs(&alert->rules, strs))
 			goto error;
 	}
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
 		goto error;
 
 	{
 		const char * const strs[2] = { "DATA\r\n" };
-		if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))
+		if (!add_tcpcheck_send_strs(&alert->rules, strs))
 			goto error;
 	}
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "354 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "354 "))
 		goto error;
 
 	{
@@ -3804,20 +3816,20 @@
 			goto error;
 		}
 
-		if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))
+		if (!add_tcpcheck_send_strs(&alert->rules, strs))
 			goto error;
 	}
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "250 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "250 "))
 		goto error;
 
 	{
 		const char * const strs[2] = { "QUIT\r\n" };
-		if (!add_tcpcheck_send_strs(&alert->tcpcheck_rules, strs))
+		if (!add_tcpcheck_send_strs(&alert->rules, strs))
 			goto error;
 	}
 
-	if (!add_tcpcheck_expect_str(&alert->tcpcheck_rules, "221 "))
+	if (!add_tcpcheck_expect_str(&alert->rules, "221 "))
 		goto error;
 
 	HA_SPIN_LOCK(EMAIL_ALERTS_LOCK, &q->lock);
@@ -3915,13 +3927,23 @@
 	struct tcpcheck_rule *chk;
 	int ret = 0;
 
-	if (!px->tcpcheck_rules)
+	if ((px->options2 & PR_O2_CHK_ANY) != PR_O2_TCPCHK_CHK)
 		goto out;
 
+	if (!px->tcpcheck_rules.list) {
+		px->tcpcheck_rules.list = calloc(1, sizeof(*px->tcpcheck_rules.list));
+		if (!px->tcpcheck_rules.list) {
+			ha_alert("config : proxy '%s': out of memory.\n", px->id);
+			ret |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+		LIST_INIT(px->tcpcheck_rules.list);
+	}
+
 	/* If there is no connect rule preceeding all send / expect rules, an
 	 * implicit one is inserted before all others
 	 */
-	chk = get_first_tcpcheck_rule(px->tcpcheck_rules);
+	chk = get_first_tcpcheck_rule(&px->tcpcheck_rules);
 	if (!chk || chk->action != TCPCHK_ACT_CONNECT) {
 		chk = calloc(1, sizeof(*chk));
 		if (!chk) {
@@ -3932,7 +3954,7 @@
 		}
 		chk->action = TCPCHK_ACT_CONNECT;
 		chk->connect.options = TCPCHK_OPT_DEFAULT_CONNECT;
-		LIST_ADD(px->tcpcheck_rules, &chk->list);
+		LIST_ADD(px->tcpcheck_rules.list, &chk->list);
 	}
 
   out:
@@ -3974,7 +3996,7 @@
 	    (!is_inet_addr(&srv->check.addr) && (is_addr(&srv->check.addr) || !is_inet_addr(&srv->addr))))
 		goto init;
 
-	if (!srv->proxy->tcpcheck_rules || LIST_ISEMPTY(srv->proxy->tcpcheck_rules)) {
+	if (!srv->proxy->tcpcheck_rules.list || LIST_ISEMPTY(srv->proxy->tcpcheck_rules.list)) {
 		ha_alert("config: %s '%s': server '%s' has neither service port nor check port.\n",
 			 proxy_type_str(srv->proxy), srv->proxy->id, srv->id);
 		ret |= ERR_ALERT | ERR_ABORT;
@@ -3982,7 +4004,7 @@
 	}
 
 	/* search the first action (connect / send / expect) in the list */
-	r = get_first_tcpcheck_rule(srv->proxy->tcpcheck_rules);
+	r = get_first_tcpcheck_rule(&srv->proxy->tcpcheck_rules);
 	if (!r || (r->action != TCPCHK_ACT_CONNECT) || (!r->connect.port && !get_host_port(&r->connect.addr))) {
 		ha_alert("config: %s '%s': server '%s' has neither service port nor check port "
 			 "nor tcp_check rule 'connect' with port information.\n",
@@ -3992,7 +4014,7 @@
 	}
 
 	/* scan the tcp-check ruleset to ensure a port has been configured */
-	list_for_each_entry(r, srv->proxy->tcpcheck_rules, list) {
+	list_for_each_entry(r, srv->proxy->tcpcheck_rules.list, list) {
 		if ((r->action == TCPCHK_ACT_CONNECT) && (!r->connect.port || !get_host_port(&r->connect.addr))) {
 			ha_alert("config: %s '%s': server '%s' has neither service port nor check port, "
 				 "and a tcp_check rule 'connect' with no port information.\n",
@@ -4043,19 +4065,22 @@
 	return ret;
 }
 
-static void deinit_proxy_tcpcheck(struct proxy *px)
+void deinit_proxy_tcpcheck(struct proxy *px)
 {
 	struct tcpcheck_rule *chk, *back;
 
-	if (!px->tcpcheck_rules)
-		return;
+	if (!px->tcpcheck_rules.list || (px->tcpcheck_rules.flags & TCPCHK_RULES_SHARED))
+		goto end;
 
-	list_for_each_entry_safe(chk, back, px->tcpcheck_rules, list) {
+	list_for_each_entry_safe(chk, back, px->tcpcheck_rules.list, list) {
 		LIST_DEL(&chk->list);
 		free_tcpcheck(chk, 0);
 	}
-	free(px->tcpcheck_rules);
-	px->tcpcheck_rules = NULL;
+	free(px->tcpcheck_rules.list);
+
+  end:
+	px->tcpcheck_rules.flags = 0;
+	px->tcpcheck_rules.list  = NULL;
 }
 
 static void deinit_srv_check(struct server *srv)
@@ -4072,6 +4097,22 @@
 	free(srv->agent.send_string);
 }
 
+static void deinit_tcpchecks()
+{
+	struct tcpcheck_ruleset *rs, *rsb;
+	struct tcpcheck_rule *r, *rb;
+
+	list_for_each_entry_safe(rs, rsb, &tcpchecks_list, list) {
+		LIST_DEL(&rs->list);
+		list_for_each_entry_safe(r, rb, &rs->rules, list) {
+			LIST_DEL(&r->list);
+			free_tcpcheck(r, 0);
+		}
+		free(rs->name);
+		free(rs);
+	}
+}
+
 
 REGISTER_POST_PROXY_CHECK(check_proxy_tcpcheck);
 REGISTER_POST_SERVER_CHECK(init_srv_check);
@@ -4080,6 +4121,7 @@
 REGISTER_PROXY_DEINIT(deinit_proxy_tcpcheck);
 REGISTER_SERVER_DEINIT(deinit_srv_check);
 REGISTER_SERVER_DEINIT(deinit_srv_agent_check);
+REGISTER_POST_DEINIT(deinit_tcpchecks);
 
 struct action_kw_list tcp_check_keywords = {
 	.list = LIST_HEAD_INIT(tcp_check_keywords.list),
@@ -4098,7 +4140,8 @@
 
 /* Create a tcp-check rule resulting from parsing a custom keyword. */
 static struct tcpcheck_rule *parse_tcpcheck_action(char **args, int cur_arg, struct proxy *px,
-						   struct list *rules, struct action_kw *kw, char **errmsg)
+						   struct list *rules, struct action_kw *kw,
+						   const char *file, int line, char **errmsg)
 {
 	struct tcpcheck_rule *chk = NULL;
 	struct act_rule *actrule = NULL;
@@ -4321,7 +4364,7 @@
 }
 
 static struct tcpcheck_rule *parse_tcpcheck_send(char **args, int cur_arg, struct proxy *px, struct list *rules,
-						 char **errmsg)
+						 const char *file, int line, char **errmsg)
 {
 	struct tcpcheck_rule *chk = NULL;
 	char *comment = NULL, *data = NULL;
@@ -4409,7 +4452,8 @@
 	return NULL;
 }
 
-static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct list *rules, char **errmsg)
+static struct tcpcheck_rule *parse_tcpcheck_comment(char **args, int cur_arg, struct proxy *px, struct list *rules,
+						    const char *file, int line, char **errmsg)
 {
 	struct tcpcheck_rule *chk = NULL;
 	char *comment = NULL;
@@ -4439,7 +4483,8 @@
 	return NULL;
 }
 
-static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct list *rules, char **errmsg)
+static struct tcpcheck_rule *parse_tcpcheck_expect(char **args, int cur_arg, struct proxy *px, struct list *rules,
+						   const char *file, int line, char **errmsg)
 {
 	struct tcpcheck_rule *prev_check, *chk = NULL;
 	char *str = NULL, *comment = NULL, *pattern = NULL;
@@ -4611,7 +4656,7 @@
 				struct proxy *defpx, const char *file, int line,
 				char **errmsg)
 {
-	struct list *rules = curpx->tcpcheck_rules;
+	struct tcpcheck_rules *rules = &curpx->tcpcheck_rules;
 	struct tcpcheck_rule *chk = NULL;
 	int index, cur_arg, ret = 0;
 
@@ -4623,31 +4668,40 @@
 		goto error;
 	}
 
+	if (rules->flags & TCPCHK_RULES_DEF) {
+		/* Only shared ruleset can be inherited from the default section */
+		rules->flags = 0;
+		rules->list  = NULL;
+	}
+	if (rules->list && (rules->flags & TCPCHK_RULES_SHARED)) {
+		memprintf(errmsg, "%s : A shared tcp-check ruleset already configured.", args[0]);
+		goto error;
+	}
+
-	if (!rules) {
-		rules = calloc(1, sizeof(*rules));
-		if (!rules) {
+	if (!rules->list) {
+		rules->list = calloc(1, sizeof(*rules->list));
+		if (!rules->list) {
 			memprintf(errmsg, "%s : out of memory.", args[0]);
 			goto error;
 		}
-		LIST_INIT(rules);
-		curpx->tcpcheck_rules = rules;
+		LIST_INIT(rules->list);
 	}
 
 	index = 0;
-	if (!LIST_ISEMPTY(rules)) {
-		chk = LIST_PREV(rules, typeof(chk), list);
+	if (!LIST_ISEMPTY(rules->list)) {
+		chk = LIST_PREV(rules->list, typeof(chk), list);
 		index = chk->index + 1;
 	}
 
 	cur_arg = 1;
 	if (strcmp(args[cur_arg], "connect") == 0)
-		chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules, file, line, errmsg);
+		chk = parse_tcpcheck_connect(args, cur_arg, curpx, rules->list, file, line, errmsg);
 	else if (strcmp(args[cur_arg], "send") == 0 || strcmp(args[cur_arg], "send-binary") == 0)
-		chk = parse_tcpcheck_send(args, cur_arg, curpx, rules, errmsg);
+		chk = parse_tcpcheck_send(args, cur_arg, curpx, rules->list, file, line, errmsg);
 	else if (strcmp(args[cur_arg], "expect") == 0)
-		chk = parse_tcpcheck_expect(args, cur_arg, rules, errmsg);
+		chk = parse_tcpcheck_expect(args, cur_arg, curpx, rules->list, file, line, errmsg);
 	else if (strcmp(args[cur_arg], "comment") == 0)
-		chk = parse_tcpcheck_comment(args, cur_arg, rules, errmsg);
+		chk = parse_tcpcheck_comment(args, cur_arg, curpx, rules->list, file, line, errmsg);
 	else {
 		struct action_kw *kw = action_kw_tcp_check_lookup(args[cur_arg]);
 
@@ -4658,7 +4712,7 @@
 				  args[0], (*trash.area ? ", " : ""), trash.area, args[1]);
 			goto error;
 		}
-		chk = parse_tcpcheck_action(args, cur_arg, curpx, rules, kw, errmsg);
+		chk = parse_tcpcheck_action(args, cur_arg, curpx, rules->list, kw, file, line, errmsg);
 	}
 
 	if (!chk) {
@@ -4669,12 +4723,11 @@
 
 	/* No error: add the tcp-check rule in the list */
 	chk->index = index;
-	LIST_ADDQ(rules, &chk->list);
+	LIST_ADDQ(rules->list, &chk->list);
 	return ret;
 
   error:
-	if (rules)
-		deinit_proxy_tcpcheck(curpx);
+	deinit_proxy_tcpcheck(curpx);
 	return -1;
 }
 
diff --git a/src/server.c b/src/server.c
index e5652f2..8307ae2 100644
--- a/src/server.c
+++ b/src/server.c
@@ -1867,7 +1867,7 @@
 	srv->check.status = HCHK_STATUS_INI;
 	srv->check.server = srv;
 	srv->check.proxy = proxy;
-	srv->check.tcpcheck_rules = proxy->tcpcheck_rules;
+	srv->check.tcpcheck_rules = &proxy->tcpcheck_rules;
 
 	srv->agent.status = HCHK_STATUS_INI;
 	srv->agent.server = srv;