MINOR: include comment in tcpcheck error log

tcpcheck error messages include the step id where the error occurs.
In some cases, this is not enough. Now, HAProxy also use the comment
field of the latest tcpcheck rule which has been run.
This commit allows HAProxy to parse a new directive in the tcpcheck
ruleset: 'comment'.
It is used to setup comments on the current tcpcheck rules.
diff --git a/include/types/checks.h b/include/types/checks.h
index 252928f..02fc743 100644
--- a/include/types/checks.h
+++ b/include/types/checks.h
@@ -204,6 +204,7 @@
 	TCPCHK_ACT_SEND        = 0,             /* send action, regular string format */
 	TCPCHK_ACT_EXPECT,                      /* expect action, either regular or binary string */
 	TCPCHK_ACT_CONNECT,                     /* connect action, to probe a new port */
+	TCPCHK_ACT_COMMENT,                     /* no action, simply a comment used for logs */
 };
 
 /* flags used by tcpcheck_rule->conn_opts */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 96ee798..39ba145 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2106,7 +2106,7 @@
 		rc = PR_CAP_LISTEN;
  	else if (!strcmp(args[0], "frontend"))
 		rc = PR_CAP_FE | PR_CAP_RS;
- 	else if (!strcmp(args[0], "backend"))
+	else if (!strcmp(args[0], "backend"))
 		rc = PR_CAP_BE | PR_CAP_RS;
  	else if (!strcmp(args[0], "ruleset"))
 		rc = PR_CAP_RS;
@@ -4683,7 +4683,26 @@
 		if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
 			err_code |= ERR_WARN;
 
-		if (strcmp(args[1], "connect") == 0) {
+		if (strcmp(args[1], "comment") == 0) {
+			int cur_arg;
+			struct tcpcheck_rule *tcpcheck;
+
+			cur_arg = 1;
+			tcpcheck = (struct tcpcheck_rule *)calloc(1, sizeof(*tcpcheck));
+			tcpcheck->action = TCPCHK_ACT_COMMENT;
+
+			if (!*args[cur_arg + 1]) {
+				Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+					file, linenum, args[cur_arg]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+
+			tcpcheck->comment = strdup(args[cur_arg + 1]);
+
+			LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
+		}
+		else if (strcmp(args[1], "connect") == 0) {
 			const char *ptr_arg;
 			int cur_arg;
 			struct tcpcheck_rule *tcpcheck;
@@ -4693,6 +4712,13 @@
 			l = (struct list *)&curproxy->tcpcheck_rules;
 			if (l->p != l->n) {
 				tcpcheck = (struct tcpcheck_rule *)l->n;
+				while (tcpcheck->action == TCPCHK_ACT_COMMENT) {
+					tcpcheck = (struct tcpcheck_rule *)tcpcheck->list.n;
+				}
+				/* we've reached the end of the list, and the list is full of comments */
+				if (tcpcheck == (struct tcpcheck_rule *)l)
+					tcpcheck = NULL;
+
 				if (tcpcheck && tcpcheck->action != TCPCHK_ACT_CONNECT) {
 					Alert("parsing [%s:%d] : first step MUST also be a 'connect' when there is a 'connect' step in the tcp-check ruleset.\n",
 					      file, linenum);
@@ -4731,11 +4757,22 @@
 					cur_arg++;
 				}
 #endif /* USE_OPENSSL */
+				/* comment for this tcpcheck line */
+				else if (strcmp(args[cur_arg], "comment") == 0) {
+					if (!*args[cur_arg + 1]) {
+						Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+							file, linenum, args[cur_arg]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					tcpcheck->comment = strdup(args[cur_arg + 1]);
+					cur_arg += 2;
+				}
 				else {
 #ifdef USE_OPENSSL
-					Alert("parsing [%s:%d] : '%s %s' expects 'port', 'send-proxy' or 'ssl' but got '%s' as argument.\n",
+					Alert("parsing [%s:%d] : '%s %s' expects 'comment', 'port', 'send-proxy' or 'ssl' but got '%s' as argument.\n",
 #else /* USE_OPENSSL */
-					Alert("parsing [%s:%d] : '%s %s' expects 'port', 'send-proxy' or but got '%s' as argument.\n",
+					Alert("parsing [%s:%d] : '%s %s' expects 'comment', 'port', 'send-proxy' or but got '%s' as argument.\n",
 #endif /* USE_OPENSSL */
 					      file, linenum, args[0], args[1], args[cur_arg]);
 					err_code |= ERR_ALERT | ERR_FATAL;
@@ -4763,6 +4800,17 @@
 				tcpcheck->string = strdup(args[2]);
 				tcpcheck->expect_regex = NULL;
 
+				/* comment for this tcpcheck line */
+				if (strcmp(args[3], "comment") == 0) {
+					if (!*args[4]) {
+						Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+							file, linenum, args[3]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					tcpcheck->comment = strdup(args[4]);
+				}
+
 				LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
 			}
 		}
@@ -4788,6 +4836,17 @@
 				}
 				tcpcheck->expect_regex = NULL;
 
+				/* comment for this tcpcheck line */
+				if (strcmp(args[3], "comment") == 0) {
+					if (!*args[4]) {
+						Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+							file, linenum, args[3]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					tcpcheck->comment = strdup(args[4]);
+				}
+
 				LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
 			}
 		}
@@ -4839,6 +4898,18 @@
 				tcpcheck->expect_regex = NULL;
 				tcpcheck->inverse = inverse;
 
+				/* tcpcheck comment */
+				cur_arg += 2;
+				if (strcmp(args[cur_arg], "comment") == 0) {
+					if (!*args[cur_arg + 1]) {
+						Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+							file, linenum, args[cur_arg + 1]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					tcpcheck->comment = strdup(args[cur_arg + 1]);
+				}
+
 				LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
 			}
 			else if (strcmp(ptr_arg, "string") == 0) {
@@ -4859,6 +4930,18 @@
 				tcpcheck->expect_regex = NULL;
 				tcpcheck->inverse = inverse;
 
+				/* tcpcheck comment */
+				cur_arg += 2;
+				if (strcmp(args[cur_arg], "comment") == 0) {
+					if (!*args[cur_arg + 1]) {
+						Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+							file, linenum, args[cur_arg + 1]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					tcpcheck->comment = strdup(args[cur_arg + 1]);
+				}
+
 				LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
 			}
 			else if (strcmp(ptr_arg, "rstring") == 0) {
@@ -4887,6 +4970,18 @@
 				}
 				tcpcheck->inverse = inverse;
 
+				/* tcpcheck comment */
+				cur_arg += 2;
+				if (strcmp(args[cur_arg], "comment") == 0) {
+					if (!*args[cur_arg + 1]) {
+						Alert("parsing [%s:%d] : '%s' expects a comment string.\n",
+							file, linenum, args[cur_arg + 1]);
+						err_code |= ERR_ALERT | ERR_FATAL;
+						goto out;
+					}
+					tcpcheck->comment = strdup(args[cur_arg + 1]);
+				}
+
 				LIST_ADDQ(&curproxy->tcpcheck_rules, &tcpcheck->list);
 			}
 			else {
@@ -4897,7 +4992,7 @@
 			}
 		}
 		else {
-			Alert("parsing [%s:%d] : '%s' only supports 'connect', 'send' or 'expect'.\n", file, linenum, args[0]);
+			Alert("parsing [%s:%d] : '%s' only supports 'comment', 'connect', 'send' or 'expect'.\n", file, linenum, args[0]);
 			err_code |= ERR_ALERT | ERR_FATAL;
 			goto out;
 		}
diff --git a/src/checks.c b/src/checks.c
index b66b9f1..26cccae 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -62,6 +62,7 @@
 
 static int httpchk_expect(struct server *s, int done);
 static int tcpcheck_get_step_id(struct check *);
+static char * tcpcheck_get_step_comment(struct check *, int);
 static void tcpcheck_main(struct connection *);
 
 static const struct check_status check_statuses[HCHK_STATUS_SIZE] = {
@@ -608,6 +609,7 @@
 	const char *err_msg;
 	struct chunk *chk;
 	int step;
+	char *comment;
 
 	if (check->result != CHK_RES_UNKNOWN)
 		return;
@@ -648,6 +650,10 @@
 			else if (check->last_started_step && check->last_started_step->action == TCPCHK_ACT_SEND) {
 				chunk_appendf(chk, " (send)");
 			}
+
+			comment = tcpcheck_get_step_comment(check, step);
+			if (comment)
+				chunk_appendf(chk, " comment: '%s'", comment);
 		}
 	}
 
@@ -2382,9 +2388,44 @@
 	return i;
 }
 
+/*
+ * return the latest known comment before (including) the given stepid
+ * returns NULL if no comment found
+ */
+static char * tcpcheck_get_step_comment(struct check *check, int stepid)
+{
+	struct tcpcheck_rule *cur = NULL;
+	char *ret = NULL;
+	int i = 0;
+
+	/* not even started anything yet, return latest comment found before any action */
+	if (!check->current_step) {
+		list_for_each_entry(cur, check->tcpcheck_rules, list) {
+			if (cur->action == TCPCHK_ACT_COMMENT)
+				ret = cur->comment;
+			else
+				goto return_comment;
+		}
+	}
+
+	i = 1;
+	list_for_each_entry(cur, check->tcpcheck_rules, list) {
+		if (cur->comment)
+			ret = cur->comment;
+
+		if (i >= stepid)
+			goto return_comment;
+
+		++i;
+	}
+
+ return_comment:
+	return ret;
+}
+
 static void tcpcheck_main(struct connection *conn)
 {
-	char *contentptr;
+	char *contentptr, *comment;
 	struct tcpcheck_rule *cur, *next;
 	int done = 0, ret = 0, step = 0;
 	struct check *check = conn->owner;
@@ -2483,6 +2524,12 @@
 
 		/* have 'next' point to the next rule or NULL if we're on the last one */
 		next = (struct tcpcheck_rule *)cur->list.n;
+
+		/* bypass all comment rules */
+		while (next->action == TCPCHK_ACT_COMMENT)
+			next = (struct tcpcheck_rule *)next->list.n;
+
+		/* NULL if we're on the last rule */
 		if (&next->list == head)
 			next = NULL;
 
@@ -2572,6 +2619,9 @@
 				step = tcpcheck_get_step_id(check);
 				chunk_printf(&trash, "TCPCHK error establishing connection at step %d: %s",
 						step, strerror(errno));
+				comment = tcpcheck_get_step_comment(check, step);
+				if (comment)
+					chunk_appendf(&trash, " comment: '%s'", comment);
 				set_server_check_status(check, HCHK_STATUS_L4CON, trash.str);
 				goto out_end_tcpcheck;
 			case SF_ERR_PRXCOND:
@@ -2579,12 +2629,20 @@
 			case SF_ERR_INTERNAL:
 				step = tcpcheck_get_step_id(check);
 				chunk_printf(&trash, "TCPCHK error establishing connection at step %d", step);
+				comment = tcpcheck_get_step_comment(check, step);
+				if (comment)
+					chunk_appendf(&trash, " comment: '%s'", comment);
 				set_server_check_status(check, HCHK_STATUS_SOCKERR, trash.str);
 				goto out_end_tcpcheck;
 			}
 
 			/* allow next rule */
 			cur = (struct tcpcheck_rule *)cur->list.n;
+
+			/* bypass all comment rules */
+			while (cur->action == TCPCHK_ACT_COMMENT)
+				cur = (struct tcpcheck_rule *)cur->list.n;
+
 			check->current_step = cur;
 
 			/* don't do anything until the connection is established */
@@ -2640,6 +2698,11 @@
 
 			/* go to next rule and try to send */
 			cur = (struct tcpcheck_rule *)cur->list.n;
+
+			/* bypass all comment rules */
+			while (cur->action == TCPCHK_ACT_COMMENT)
+				cur = (struct tcpcheck_rule *)cur->list.n;
+
 			check->current_step = cur;
 		} /* end 'send' */
 		else if (check->current_step->action == TCPCHK_ACT_EXPECT) {
@@ -2688,6 +2751,9 @@
 				/* empty response */
 				step = tcpcheck_get_step_id(check);
 				chunk_printf(&trash, "TCPCHK got an empty response at step %d", step);
+				comment = tcpcheck_get_step_comment(check, step);
+				if (comment)
+					chunk_appendf(&trash, " comment: '%s'", comment);
 				set_server_check_status(check, HCHK_STATUS_L7RSP, trash.str);
 
 				goto out_end_tcpcheck;
@@ -2719,13 +2785,23 @@
 					/* we were looking for a regex */
 						chunk_printf(&trash, "TCPCHK matched unwanted content (regex) at step %d", step);
 					}
+					comment = tcpcheck_get_step_comment(check, step);
+					if (comment)
+						chunk_appendf(&trash, " comment: '%s'", comment);
 					set_server_check_status(check, HCHK_STATUS_L7RSP, trash.str);
 					goto out_end_tcpcheck;
 				}
 				/* matched and was supposed to => OK, next step */
 				else {
-					cur = (struct tcpcheck_rule*)cur->list.n;
+					/* allow next rule */
+					cur = (struct tcpcheck_rule *)cur->list.n;
+
+					/* bypass all comment rules */
+					while (cur->action == TCPCHK_ACT_COMMENT)
+						cur = (struct tcpcheck_rule *)cur->list.n;
+
 					check->current_step = cur;
+
 					if (check->current_step->action == TCPCHK_ACT_EXPECT)
 						goto tcpcheck_expect;
 					__conn_data_stop_recv(conn);
@@ -2735,6 +2811,15 @@
 			/* not matched */
 				/* not matched and was not supposed to => OK, next step */
 				if (cur->inverse) {
+					/* allow next rule */
+					cur = (struct tcpcheck_rule *)cur->list.n;
+
+					/* bypass all comment rules */
+					while (cur->action == TCPCHK_ACT_COMMENT)
+						cur = (struct tcpcheck_rule *)cur->list.n;
+
+					check->current_step = cur;
+
 					cur = (struct tcpcheck_rule*)cur->list.n;
 					check->current_step = cur;
 					if (check->current_step->action == TCPCHK_ACT_EXPECT)
@@ -2753,6 +2838,9 @@
 						chunk_printf(&trash, "TCPCHK did not match content (regex) at step %d",
 								step);
 					}
+					comment = tcpcheck_get_step_comment(check, step);
+					if (comment)
+						chunk_appendf(&trash, " comment: '%s'", comment);
 					set_server_check_status(check, HCHK_STATUS_L7RSP, trash.str);
 					goto out_end_tcpcheck;
 				}