MEDIUM: Add external check

Add an external check which makes use of an external process to
check the status of a server.
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 762978a..6a331d1 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -856,6 +856,9 @@
 		}
 		global.gid = atol(args[1]);
 	}
+	else if (!strcmp(args[0], "external-check")) {
+		global.external_check = 1;
+	}
 	/* user/group name handling */
 	else if (!strcmp(args[0], "user")) {
 		struct passwd *ha_user;
@@ -2102,6 +2105,11 @@
 		curproxy->conf.used_listener_id = EB_ROOT;
 		curproxy->conf.used_server_id = EB_ROOT;
 
+		if (defproxy.check_path)
+			curproxy->check_path = strdup(defproxy.check_path);
+		if (defproxy.check_command)
+			curproxy->check_command = strdup(defproxy.check_command);
+
 		goto out;
 	}
 	else if (!strcmp(args[0], "defaults")) {  /* use this one to assign default values */
@@ -2110,6 +2118,8 @@
 		 * config parsing to free all default values.
 		 */
 		free(defproxy.check_req);
+		free(defproxy.check_command);
+		free(defproxy.check_path);
 		free(defproxy.cookie_name);
 		free(defproxy.rdp_cookie_name);
 		free(defproxy.cookie_domain);
@@ -2617,6 +2627,41 @@
 			err_code |= ERR_ALERT | ERR_FATAL;
 		}
 	}/* end else if (!strcmp(args[0], "cookie"))  */
+	else if (!strcmp(args[0], "external-check")) {
+		if (*(args[1]) == 0) {
+			Alert("parsing [%s:%d] : missing argument after '%s'.\n",
+			      file, linenum, args[0]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+                }
+
+		if (!strcmp(args[1], "command")) {
+			if (*(args[1]) == 0) {
+				Alert("parsing [%s:%d] : missing argument after '%s'.\n",
+				      file, linenum, args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			free(curproxy->check_command);
+			curproxy->check_command = strdup(args[2]);
+		}
+		else if (!strcmp(args[1], "path")) {
+			if (*(args[1]) == 0) {
+				Alert("parsing [%s:%d] : missing argument after '%s'.\n",
+				      file, linenum, args[1]);
+				err_code |= ERR_ALERT | ERR_FATAL;
+				goto out;
+			}
+			free(curproxy->check_path);
+			curproxy->check_path = strdup(args[2]);
+		}
+		else {
+			Alert("parsing [%s:%d] : external-check: unknown argument '%s'.\n",
+			      file, linenum, args[1]);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+	}/* end else if (!strcmp(args[0], "external-check"))  */
 	else if (!strcmp(args[0], "persist")) {  /* persist */
 		if (*(args[1]) == 0) {
 			Alert("parsing [%s:%d] : missing persist method.\n",
@@ -4024,6 +4069,13 @@
 			curproxy->options2 &= ~PR_O2_CHK_ANY;
 			curproxy->options2 |= PR_O2_TCPCHK_CHK;
 		}
+		else if (!strcmp(args[1], "external-check")) {
+			/* excute an external command to check servers' health */
+			free(curproxy->check_req);
+			curproxy->check_req = NULL;
+			curproxy->options2 &= ~PR_O2_CHK_ANY;
+			curproxy->options2 |= PR_O2_EXT_CHK;
+		}
 		else if (!strcmp(args[1], "forwardfor")) {
 			int cur_arg;
 
@@ -6081,6 +6133,48 @@
 			}
 		}
 
+		if ((curproxy->options2 & PR_O2_CHK_ANY) == PR_O2_EXT_CHK) {
+			if (!global.external_check) {
+				Alert("Proxy '%s' : '%s' unable to find required 'global.external-check'.\n",
+				      curproxy->id, "option external-check");
+				cfgerr++;
+			}
+			if (!curproxy->check_command) {
+				Alert("Proxy '%s' : '%s' unable to find required 'external-check command'.\n",
+				      curproxy->id, "option external-check");
+				cfgerr++;
+			}
+		}
+
+		if (curproxy->check_command) {
+			int clear = 0;
+			if ((curproxy->options2 & PR_O2_CHK_ANY) != PR_O2_EXT_CHK) {
+				Warning("config : '%s' will be ignored for %s '%s' (requires 'option external-check').\n",
+					"external-check command", proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+				clear = 1;
+			}
+			if (curproxy->check_command[0] != '/' && !curproxy->check_path) {
+				Alert("Proxy '%s': '%s' does not have a leading '/' and 'external-command path' is not set.\n",
+				      curproxy->id, "external-check command");
+				cfgerr++;
+			}
+			if (clear) {
+				free(curproxy->check_command);
+				curproxy->check_command = NULL;
+			}
+		}
+
+		if (curproxy->check_path) {
+			if ((curproxy->options2 & PR_O2_CHK_ANY) != PR_O2_EXT_CHK) {
+				Warning("config : '%s' will be ignored for %s '%s' (requires 'option external-check').\n",
+					"external-check path", proxy_type_str(curproxy), curproxy->id);
+				err_code |= ERR_WARN;
+				free(curproxy->check_path);
+				curproxy->check_path = NULL;
+			}
+		}
+
 		/* if a default backend was specified, let's find it */
 		if (curproxy->defbe.name) {
 			struct proxy *target;