MINOR: checks: Support custom functions to eval a tcp-check expect rules

It is now possible to set a custom function to evaluate a tcp-check expect
rule. It is an internal and not documentd option because the right pointer of
function must be set and it is not possible to express it in the
configuration. It will be used to convert some protocol healthchecks to
tcp-checks.

Custom functions must have the following signature:

  enum tcpcheck_eval_ret (*custom)(struct check *, struct tcpcheck_rule *, int);
diff --git a/include/types/checks.h b/include/types/checks.h
index 2205633..30f7d75 100644
--- a/include/types/checks.h
+++ b/include/types/checks.h
@@ -253,11 +253,12 @@
 };
 
 enum tcpcheck_expect_type {
-	TCPCHK_EXPECT_UNDEF = 0, /* Match is not used. */
-	TCPCHK_EXPECT_STRING, /* Matches a string. */
-	TCPCHK_EXPECT_REGEX, /* Matches a regular pattern. */
+	TCPCHK_EXPECT_UNDEF = 0,    /* Match is not used. */
+	TCPCHK_EXPECT_STRING,       /* Matches a string. */
+	TCPCHK_EXPECT_REGEX,        /* Matches a regular pattern. */
 	TCPCHK_EXPECT_REGEX_BINARY, /* Matches a regular pattern on a hex-encoded text. */
-	TCPCHK_EXPECT_BINARY, /* Matches a binary sequence. */
+	TCPCHK_EXPECT_BINARY,       /* Matches a binary sequence. */
+	TCPCHK_EXPECT_CUSTOM,       /* Execute a custom function. */
 };
 
 struct tcpcheck_expect {
@@ -265,6 +266,9 @@
 	union {
 		char *string;           /* Matching a literal string / binary anywhere in the response. */
 		struct my_regex *regex; /* Matching a regex pattern. */
+
+		/* custom function to eval epxect rule */
+		enum tcpcheck_eval_ret (*custom)(struct check *, struct tcpcheck_rule *, int);
 	};
 	struct tcpcheck_rule *head;     /* first expect of a chain. */
 	int length;                     /* Size in bytes of the pattern referenced by string / binary. */
diff --git a/src/checks.c b/src/checks.c
index 6a28b54..c6bced8 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -664,6 +664,9 @@
 				case TCPCHK_EXPECT_REGEX_BINARY:
 					chunk_appendf(chk, " (expect binary regex)");
 					break;
+				case TCPCHK_EXPECT_CUSTOM:
+					chunk_appendf(chk, " (expect custom function)");
+					break;
 				case TCPCHK_EXPECT_UNDEF:
 					chunk_appendf(chk, " (undefined expect!)");
 					break;
@@ -2803,6 +2806,9 @@
 			}
 		}
 		break;
+	case TCPCHK_EXPECT_CUSTOM:
+		chunk_appendf(msg, " (custom function) at step %d", tcpcheck_get_step_id(check, rule));
+		break;
 	case TCPCHK_EXPECT_UNDEF:
 		/* Should never happen. */
 		return;
@@ -3193,6 +3199,10 @@
 		else
 			match = regex_exec2(expect->regex, b_head(&trash), MIN(b_data(&trash), b_size(&trash)-1));
 		break;
+	case TCPCHK_EXPECT_CUSTOM:
+		if (expect->custom)
+			ret = expect->custom(check, rule, last_read);
+		goto out;
 	case TCPCHK_EXPECT_UNDEF:
 		/* Should never happen. */
 		ret = TCPCHK_EVAL_STOP;
@@ -3540,6 +3550,7 @@
 		case TCPCHK_EXPECT_REGEX_BINARY:
 			regex_free(rule->expect.regex);
 			break;
+		case TCPCHK_EXPECT_CUSTOM:
 		case TCPCHK_EXPECT_UNDEF:
 			break;
 		}
@@ -4676,8 +4687,8 @@
 	int inverse = 0, with_capture = 0;
 
 	str = on_success_msg = on_error_msg = comment = pattern = NULL;
-	if (!*(args[cur_arg+1]) || !*(args[cur_arg+2])) {
-		memprintf(errmsg, "expects a pattern (type+string) as arguments");
+	if (!*(args[cur_arg+1])) {
+		memprintf(errmsg, "expects at least a matching pattern as arguments");
 		goto error;
 	}
 
@@ -4730,6 +4741,17 @@
 			cur_arg++;
 			pattern = args[cur_arg];
 		}
+		else if (strcmp(args[cur_arg], "custom") == 0) {
+			if (in_pattern) {
+				memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
+				goto error;
+			}
+			if (type != TCPCHK_EXPECT_UNDEF) {
+				memprintf(errmsg, "only on pattern expected");
+				goto error;
+			}
+			type = TCPCHK_EXPECT_CUSTOM;
+		}
 		else if (strcmp(args[cur_arg], "comment") == 0) {
 			if (in_pattern) {
 				memprintf(errmsg, "[!] not supported with '%s'", args[cur_arg]);
@@ -4941,6 +4963,9 @@
 		if (!chk->expect.regex)
 			goto error;
 		break;
+	case TCPCHK_EXPECT_CUSTOM:
+		chk->expect.custom = NULL; /* Must be defined by the caller ! */
+		break;
 	case TCPCHK_EXPECT_UNDEF:
 		free(chk);
 		memprintf(errmsg, "pattern not found");