[MEDIUM] Implement "track [<backend>/]<server>"

This patch implements ability to set the current state of one server
by tracking another one. It:
 - adds two variables: *tracknext, *tracked to struct server
 - implements findserver(), similar to findproxy()
 - adds "track" keyword accepting both "proxy/server" and "server" (assuming current proxy)
 - verifies if both checks and tracking is not enabled at the same time
 - changes set_server_down() to notify tracking server
 - creates set_server_up(), set_server_disabled(), set_server_enabled() by
   moving the code from process_chk() and adding notifications
 - changes stats to show a name of tracked server instead of Chk/Dwn/Dwntime(html)
   or by adding new variable (csv)

Changes from the previuos version:
 - it is possibile to track independently of the declaration order
 - one extra comma bug is fixed
 - new condition to check if there is no disable-on-404 inconsistency
diff --git a/TODO b/TODO
index ae120a1..74b3cce 100644
--- a/TODO
+++ b/TODO
@@ -174,9 +174,6 @@
     filters and backend, on which every entity could rely.
   - implement 'on uri <uri> <proxy>', 'on host <host> <proxy>'
   - remove the first now useless hop in hdr_idx
-  - implement "track XXX.YYY" for each server as an alternative to
-    health checks. This will automatically set the server state to
-    the same as server YYY of proxy XXX.
   - balance on URI hash (specify length or depth)
   - balance on any header hash (eg: host)
   - balance with redirections to real servers
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 900b9a7..504f8a9 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -3893,6 +3893,13 @@
   as the backend "source" keyword, except that it only applies to the server
   referencing it. Please consult the "source" keyword for details.
 
+track [<proxy>/]<server>
+  This option enables ability to set the current state of the server by
+  tracking another one. Only a server with checks enabled can be tracked
+  so it is not possible for example to track a server that tracks another
+  one. If <proxy> is omitted the current one is used. If disable-on-404 is
+  used, it has to be enabled on both proxies.
+
 weight <weight>
   The "weight" parameter is used to adjust the server's weight relative to
   other servers. All servers will receive a load proportional to their weight
diff --git a/include/proto/proxy.h b/include/proto/proxy.h
index 55f9aab..7268bfa 100644
--- a/include/proto/proxy.h
+++ b/include/proto/proxy.h
@@ -36,6 +36,7 @@
 const char *proxy_cap_str(int cap);
 const char *proxy_mode_str(int mode);
 struct proxy *findproxy(const char *name, int mode, int cap);
+struct server *findserver(const struct proxy *px, const char *name);
 int proxy_parse_timeout(const char **args, struct proxy *proxy,
 			struct proxy *defpx, char *err, int errlen);
 
diff --git a/include/types/server.h b/include/types/server.h
index c93236c..0e2183e 100644
--- a/include/types/server.h
+++ b/include/types/server.h
@@ -91,6 +91,8 @@
 	struct sockaddr_in tproxy_addr;		/* non-local address we want to bind to for connect() */
 #endif
 
+	struct server *tracknext, *tracked;	/* next server in a tracking list, tracked server */
+	char *trackit;				/* temporary variable to make assignment deferrable */
 	struct sockaddr_in check_addr;		/* the address to check, if different from <addr> */
 	short check_port;			/* the port to use for the health checks */
 	int health;				/* 0->rise-1 = bad; rise->rise+fall-1 = good */
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 13b0e36..25d954a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1625,6 +1625,18 @@
 				newsrv->slowstart = (val + 999) / 1000;
 				cur_arg += 2;
 			}
+			else if (!strcmp(args[cur_arg], "track")) {
+
+				if (!*args[cur_arg + 1]) {
+					Alert("parsing [%s:%d]: 'track' expects [<proxy>/]<server> as argument.\n",
+						file, linenum);
+					return -1;
+				}
+
+				newsrv->trackit = strdup(args[cur_arg + 1]);
+
+				cur_arg += 2;
+			}
 			else if (!strcmp(args[cur_arg], "check")) {
 				global.maxsock++;
 				do_check = 1;
@@ -1684,13 +1696,19 @@
 				return -1;
 			}
 			else {
-				Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'redir', 'check', 'inter', 'fastinter', 'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
+				Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'redir', 'check', 'track', 'inter', 'fastinter', 'downinter', 'rise', 'fall', 'addr', 'port', 'source', 'minconn', 'maxconn', 'maxqueue', 'slowstart' and 'weight'.\n",
 				      file, linenum, newsrv->id);
 				return -1;
 			}
 		}
 
 		if (do_check) {
+			if (newsrv->trackit) {
+				Alert("parsing [%s:%d]: unable to enable checks and tracking at the same time!\n",
+					file, linenum);
+				return -1;
+			}
+
 			if (!newsrv->check_port && newsrv->check_addr.sin_port)
 				newsrv->check_port = newsrv->check_addr.sin_port;
 
@@ -2913,6 +2931,7 @@
 		/*
 		 * If this server supports a maxconn parameter, it needs a dedicated
 		 * tasks to fill the emptied slots when a connection leaves.
+		 * Also, resolve deferred tracking dependency if needed.
 		 */
 		newsrv = curproxy->srv;
 		while (newsrv != NULL) {
@@ -2950,6 +2969,65 @@
 				tv_eternity(&t->expire);
 				task_queue(t);
 			}
+
+			if (newsrv->trackit) {
+				struct proxy *px;
+				struct server *srv;
+				char *pname, *sname;
+
+				pname = newsrv->trackit;
+				sname = strrchr(pname, '/');
+
+				if (sname)
+					*sname++ = '\0';
+				else {
+					sname = pname;
+					pname = NULL;
+				}
+
+				if (pname) {
+					px = findproxy(pname, curproxy->mode, PR_CAP_BE);
+					if (!px) {
+						Alert("parsing %s, %s '%s', server '%s': unable to find required proxy '%s' for tracking.\n",
+							file, proxy_type_str(curproxy), curproxy->id,
+							newsrv->id, pname);
+						return -1;
+					}
+				} else
+					px = curproxy;
+
+				srv = findserver(px, sname);
+				if (!srv) {
+					Alert("parsing %s, %s '%s', server '%s': unable to find required server '%s' for tracking.\n",
+						file, proxy_type_str(curproxy), curproxy->id,
+						newsrv->id, sname);
+					return -1;
+				}
+
+				if (!(srv->state & SRV_CHECKED)) {
+					Alert("parsing %s, %s '%s', server '%s': unable to use %s/%s for "
+						"tracing as it does not have checks enabled.\n",
+						file, proxy_type_str(curproxy), curproxy->id,
+						newsrv->id, px->id, srv->id);
+					return -1;
+				}
+
+				if (curproxy != px &&
+					(curproxy->options & PR_O_DISABLE404) != (px->options & PR_O_DISABLE404)) {
+					Alert("parsing %s, %s '%s', server '%s': unable to use %s/%s for"
+						"tracing: disable-on-404 option inconsistency.\n",
+						file, proxy_type_str(curproxy), curproxy->id,
+						newsrv->id, px->id, srv->id);
+					return -1;
+				}
+
+				newsrv->tracked = srv;
+				newsrv->tracknext = srv->tracknext;
+				srv->tracknext = newsrv;
+
+				free(newsrv->trackit);
+			}
+
 			newsrv = newsrv->next;
 		}
 
diff --git a/src/checks.c b/src/checks.c
index 1a5bf71..53b7e54 100644
--- a/src/checks.c
+++ b/src/checks.c
@@ -2,6 +2,7 @@
  * Health-checks functions.
  *
  * Copyright 2000-2008 Willy Tarreau <w@1wt.eu>
+ * Copyright 2007-2008 Krzysztof Piotr Oledzki <ole@ans.pl>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -34,6 +35,7 @@
 #include <types/session.h>
 
 #include <proto/backend.h>
+#include <proto/buffers.h>
 #include <proto/fd.h>
 #include <proto/log.h>
 #include <proto/queue.h>
@@ -120,9 +122,11 @@
  */
 static void set_server_down(struct server *s)
 {
+	struct server *srv;
+	struct chunk msg;
 	int xferred;
 
-	if (s->health == s->rise) {
+	if (s->health == s->rise || s->tracked) {
 		int srv_was_paused = s->state & SRV_GOINGDOWN;
 
 		s->last_change = now.tv_sec;
@@ -134,10 +138,21 @@
 		 * to another server or to the proxy itself.
 		 */
 		xferred = redistribute_pending(s);
-		sprintf(trash, "%sServer %s/%s is DOWN. %d active and %d backup servers left.%s"
+
+		msg.len = 0;
+		msg.str = trash;
+
+		chunk_printf(&msg, sizeof(trash),
+			"%sServer %s/%s is DOWN", s->state & SRV_BACKUP ? "Backup " : "",
+			s->proxy->id, s->id);
+
+		if (s->tracked)
+			chunk_printf(&msg, sizeof(trash), " via %s/%s",
+				s->tracked->proxy->id, s->tracked->id);
+
+		chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers left.%s"
 			" %d sessions active, %d requeued, %d remaining in queue.\n",
-			s->state & SRV_BACKUP ? "Backup " : "",
-			s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
+			s->proxy->srv_act, s->proxy->srv_bck,
 			(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
 			s->cur_sess, xferred, s->nbpend);
 
@@ -153,10 +168,167 @@
 			set_backend_down(s->proxy);
 
 		s->down_trans++;
+
+		if (s->state && SRV_CHECKED)
+			for(srv = s->tracknext; srv; srv = srv->tracknext)
+				set_server_down(srv);
 	}
+
 	s->health = 0; /* failure */
 }
 
+static void set_server_up(struct server *s) {
+
+	struct server *srv;
+	struct chunk msg;
+	int xferred;
+
+	if (s->health == s->rise || s->tracked) {
+		if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
+			if (s->proxy->last_change < now.tv_sec)		// ignore negative times
+				s->proxy->down_time += now.tv_sec - s->proxy->last_change;
+			s->proxy->last_change = now.tv_sec;
+		}
+
+		if (s->last_change < now.tv_sec)			// ignore negative times
+			s->down_time += now.tv_sec - s->last_change;
+
+		s->last_change = now.tv_sec;
+		s->state |= SRV_RUNNING;
+
+		if (s->slowstart > 0) {
+			s->state |= SRV_WARMINGUP;
+			if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
+				/* For dynamic algorithms, start at the first step of the weight,
+				 * without multiplying by BE_WEIGHT_SCALE.
+				 */
+				s->eweight = s->uweight;
+				if (s->proxy->lbprm.update_server_eweight)
+					s->proxy->lbprm.update_server_eweight(s);
+			}
+		}
+		s->proxy->lbprm.set_server_status_up(s);
+
+		/* check if we can handle some connections queued at the proxy. We
+		 * will take as many as we can handle.
+		 */
+		xferred = check_for_pending(s);
+
+		msg.len = 0;
+		msg.str = trash;
+
+		chunk_printf(&msg, sizeof(trash),
+			"%sServer %s/%s is UP", s->state & SRV_BACKUP ? "Backup " : "",
+			s->proxy->id, s->id);
+
+		if (s->tracked)
+			chunk_printf(&msg, sizeof(trash), " via %s/%s",
+				s->tracked->proxy->id, s->tracked->id);
+
+		chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers online.%s"
+			" %d sessions requeued, %d total in queue.\n",
+			s->proxy->srv_act, s->proxy->srv_bck,
+			(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
+			s->cur_sess, xferred, s->nbpend);
+
+		Warning("%s", trash);
+		send_log(s->proxy, LOG_NOTICE, "%s", trash);
+
+		if (s->state && SRV_CHECKED)
+			for(srv = s->tracknext; srv; srv = srv->tracknext)
+				set_server_up(srv);
+	}
+
+	if (s->health >= s->rise)
+		s->health = s->rise + s->fall - 1; /* OK now */
+
+}
+
+static void set_server_disabled(struct server *s) {
+
+	struct server *srv;
+	struct chunk msg;
+	int xferred;
+
+	s->state |= SRV_GOINGDOWN;
+	s->proxy->lbprm.set_server_status_down(s);
+
+	/* we might have sessions queued on this server and waiting for
+	 * a connection. Those which are redispatchable will be queued
+	 * to another server or to the proxy itself.
+	 */
+	xferred = redistribute_pending(s);
+
+	msg.len = 0;
+	msg.str = trash;
+
+	chunk_printf(&msg, sizeof(trash),
+		"Load-balancing on %sServer %s/%s is disabled",
+		s->state & SRV_BACKUP ? "Backup " : "",
+		s->proxy->id, s->id);
+
+	if (s->tracked)
+		chunk_printf(&msg, sizeof(trash), " via %s/%s",
+			s->tracked->proxy->id, s->tracked->id);
+
+
+	chunk_printf(&msg, sizeof(trash),". %d active and %d backup servers online.%s"
+		" %d sessions requeued, %d total in queue.\n",
+		s->proxy->srv_act, s->proxy->srv_bck,
+		(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
+		xferred, s->nbpend);
+
+	Warning("%s", trash);
+
+	send_log(s->proxy, LOG_NOTICE, "%s", trash);
+
+	if (!s->proxy->srv_bck && !s->proxy->srv_act)
+		set_backend_down(s->proxy);
+
+	if (s->state && SRV_CHECKED)
+		for(srv = s->tracknext; srv; srv = srv->tracknext)
+			set_server_disabled(srv);
+}
+
+static void set_server_enabled(struct server *s) {
+
+	struct server *srv;
+	struct chunk msg;
+	int xferred;
+
+	s->state &= ~SRV_GOINGDOWN;
+	s->proxy->lbprm.set_server_status_up(s);
+
+	/* check if we can handle some connections queued at the proxy. We
+	 * will take as many as we can handle.
+	 */
+	xferred = check_for_pending(s);
+
+	msg.len = 0;
+	msg.str = trash;
+
+	chunk_printf(&msg, sizeof(trash),
+		"Load-balancing on %sServer %s/%s is enabled again",
+		s->state & SRV_BACKUP ? "Backup " : "",
+		s->proxy->id, s->id);
+
+	if (s->tracked)
+		chunk_printf(&msg, sizeof(trash), " via %s/%s",
+			s->tracked->proxy->id, s->tracked->id);
+
+	chunk_printf(&msg, sizeof(trash), ". %d active and %d backup servers online.%s"
+		" %d sessions requeued, %d total in queue.\n",
+		s->proxy->srv_act, s->proxy->srv_bck,
+		(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
+		xferred, s->nbpend);
+
+	Warning("%s", trash);
+	send_log(s->proxy, LOG_NOTICE, "%s", trash);
+
+	if (s->state && SRV_CHECKED)
+		for(srv = s->tracknext; srv; srv = srv->tracknext)
+			set_server_enabled(srv);
+}
 
 /*
  * This function is used only for server health-checks. It handles
@@ -364,7 +536,6 @@
 	__label__ new_chk, out;
 	struct server *s = t->context;
 	struct sockaddr_in sa;
-	int xferred;
 	int fd;
 	int rv;
 
@@ -575,103 +746,18 @@
 			/* we may have to add/remove this server from the LB group */
 			if ((s->state & SRV_RUNNING) && (s->proxy->options & PR_O_DISABLE404)) {
 				if ((s->state & SRV_GOINGDOWN) &&
-				    ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING)) {
-					/* server enabled again */
-					s->state &= ~SRV_GOINGDOWN;
-					s->proxy->lbprm.set_server_status_up(s);
-
-					/* check if we can handle some connections queued at the proxy. We
-					 * will take as many as we can handle.
-					 */
-					xferred = check_for_pending(s);
-
-					sprintf(trash,
-						"Load-balancing on %sServer %s/%s is enabled again. %d active and %d backup servers online.%s"
-						" %d sessions requeued, %d total in queue.\n",
-						s->state & SRV_BACKUP ? "Backup " : "",
-						s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
-						(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
-						xferred, s->nbpend);
-
-					Warning("%s", trash);
-					send_log(s->proxy, LOG_NOTICE, "%s", trash);
-				}
+				    ((s->result & (SRV_CHK_RUNNING|SRV_CHK_DISABLE)) == SRV_CHK_RUNNING))
+					set_server_enabled(s);
 				else if (!(s->state & SRV_GOINGDOWN) &&
 					 ((s->result & (SRV_CHK_RUNNING | SRV_CHK_DISABLE)) ==
-					  (SRV_CHK_RUNNING | SRV_CHK_DISABLE))) {
-					/* server disabled */
-					s->state |= SRV_GOINGDOWN;
-					s->proxy->lbprm.set_server_status_down(s);
-
-					/* we might have sessions queued on this server and waiting for
-					 * a connection. Those which are redispatchable will be queued
-					 * to another server or to the proxy itself.
-					 */
-					xferred = redistribute_pending(s);
-
-					sprintf(trash,
-						"Load-balancing on %sServer %s/%s is disabled. %d active and %d backup servers online.%s"
-						" %d sessions requeued, %d total in queue.\n",
-						s->state & SRV_BACKUP ? "Backup " : "",
-						s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
-						(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
-						xferred, s->nbpend);
-
-					Warning("%s", trash);
-
-					send_log(s->proxy, LOG_NOTICE, "%s", trash);
-					if (!s->proxy->srv_bck && !s->proxy->srv_act)
-						set_backend_down(s->proxy);
-				}
+					  (SRV_CHK_RUNNING | SRV_CHK_DISABLE)))
+					set_server_disabled(s);
 			}
 
 			if (s->health < s->rise + s->fall - 1) {
 				s->health++; /* was bad, stays for a while */
 
-				if (s->health == s->rise) {
-					if (s->proxy->srv_bck == 0 && s->proxy->srv_act == 0) {
-						if (s->proxy->last_change < now.tv_sec)		// ignore negative times
-							s->proxy->down_time += now.tv_sec - s->proxy->last_change;
-						s->proxy->last_change = now.tv_sec;
-					}
-
-					if (s->last_change < now.tv_sec)			// ignore negative times
-						s->down_time += now.tv_sec - s->last_change;
-
-					s->last_change = now.tv_sec;
-					s->state |= SRV_RUNNING;
-					if (s->slowstart > 0) {
-						s->state |= SRV_WARMINGUP;
-						if (s->proxy->lbprm.algo & BE_LB_PROP_DYN) {
-							/* For dynamic algorithms, start at the first step of the weight,
-							 * without multiplying by BE_WEIGHT_SCALE.
-							 */
-							s->eweight = s->uweight;
-							if (s->proxy->lbprm.update_server_eweight)
-								s->proxy->lbprm.update_server_eweight(s);
-						}
-					}
-					s->proxy->lbprm.set_server_status_up(s);
-
-					/* check if we can handle some connections queued at the proxy. We
-					 * will take as many as we can handle.
-					 */
-					xferred = check_for_pending(s);
-
-					sprintf(trash,
-						"%sServer %s/%s is UP. %d active and %d backup servers online.%s"
-						" %d sessions requeued, %d total in queue.\n",
-						s->state & SRV_BACKUP ? "Backup " : "",
-						s->proxy->id, s->id, s->proxy->srv_act, s->proxy->srv_bck,
-						(s->proxy->srv_bck && !s->proxy->srv_act) ? " Running on backup." : "",
-						xferred, s->nbpend);
-
-					Warning("%s", trash);
-					send_log(s->proxy, LOG_NOTICE, "%s", trash);
-				}
-
-				if (s->health >= s->rise)
-					s->health = s->rise + s->fall - 1; /* OK now */
+				set_server_up(s);
 			}
 			s->curfd = -1; /* no check running anymore */
 			fd_delete(fd);
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 59ee93b..71e382a 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -171,7 +171,7 @@
 			    "wretr,wredis,"
 			    "status,weight,act,bck,"
 			    "chkfail,chkdown,lastchg,downtime,qlimit,"
-			    "pid,iid,sid,throttle,lbtot,"
+			    "pid,iid,sid,throttle,lbtot,tracked,"
 			    "\n");
 }
 
@@ -587,7 +587,7 @@
 int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags)
 {
 	struct buffer *rep = s->rep;
-	struct server *sv;
+	struct server *sv, *svs;	/* server and server-state, server-state=server or server->tracked */
 	struct chunk msg;
 
 	msg.len = 0;
@@ -706,8 +706,8 @@
 				     "%s,"
 				     /* rest of server: nothing */
 				     ",,,,,,,,"
-				     /* pid, iid, sid, throttle, lbtot, */
-				     "%d,%d,0,,,"
+				     /* pid, iid, sid, throttle, lbtot, tracked*/
+				     "%d,%d,0,,,,"
 				     "\n",
 				     px->id,
 				     px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
@@ -734,20 +734,25 @@
 
 			sv = s->data_ctx.stats.sv;
 
+			if (sv->tracked)
+				svs = sv->tracked;
+			else
+				svs = sv;
+
 			/* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
-			if (!(sv->state & SRV_CHECKED))
+			if (!(svs->state & SRV_CHECKED))
 				sv_state = 6;
-			else if (sv->state & SRV_RUNNING) {
-				if (sv->health == sv->rise + sv->fall - 1)
+			else if (svs->state & SRV_RUNNING) {
+				if (svs->health == svs->rise + svs->fall - 1)
 					sv_state = 3; /* UP */
 				else
 					sv_state = 2; /* going down */
 
-				if (sv->state & SRV_GOINGDOWN)
+				if (svs->state & SRV_GOINGDOWN)
 					sv_state += 2;
 			}
 			else
-				if (sv->health)
+				if (svs->health)
 					sv_state = 1; /* going up */
 				else
 					sv_state = 0; /* DOWN */
@@ -800,8 +805,8 @@
 
 				chunk_printf(&msg, sizeof(trash),
 				     srv_hlt_st[sv_state],
-				     (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
-				     (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
+				     (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
+				     (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
 
 				chunk_printf(&msg, sizeof(trash),
 				     /* weight */
@@ -819,8 +824,11 @@
 					     "<td align=right>%d</td><td align=right>%d</td>"
 					     "<td nowrap align=right>%s</td>"
 					     "",
-					     sv->failed_checks, sv->down_trans,
+					     svs->failed_checks, svs->down_trans,
 					     human_time(srv_downtime(sv), 1));
+				else if (sv != svs)
+					chunk_printf(&msg, sizeof(trash),
+					     "<td nowrap colspan=3>via %s/%s</td>", svs->proxy->id, svs->id );
 				else
 					chunk_printf(&msg, sizeof(trash),
 					     "<td colspan=3></td>");
@@ -908,6 +916,14 @@
 
 				/* sessions: lbtot */
 				chunk_printf(&msg, sizeof(trash), ",%d", sv->cum_lbconn);
+
+				/* tracked */
+				if (sv->tracked)
+					chunk_printf(&msg, sizeof(trash), ",%s/%s",
+						sv->tracked->proxy->id, sv->tracked->id);
+				else
+					chunk_printf(&msg, sizeof(trash), ",");
+
 				/* ',' then EOL */
 				chunk_printf(&msg, sizeof(trash), ",\n");
 			}
@@ -991,8 +1007,8 @@
 				     "%d,%d,%d,"
 				     /* rest of backend: nothing, down transitions, last change, total downtime */
 				     ",%d,%d,%d,,"
-				     /* pid, iid, sid, throttle, lbtot, */
-				     "%d,%d,0,,%d,"
+				     /* pid, iid, sid, throttle, lbtot, tracked,*/
+				     "%d,%d,0,,%d,,"
 				     "\n",
 				     px->id,
 				     px->nbpend /* or px->totpend ? */, px->nbpend_max,
diff --git a/src/proxy.c b/src/proxy.c
index 281ee8e..402fc88 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -176,7 +176,7 @@
 
 struct proxy *findproxy(const char *name, int mode, int cap) {
 
-	struct proxy *curproxy, *target=NULL;
+	struct proxy *curproxy, *target = NULL;
 
 	for (curproxy = proxy; curproxy; curproxy = curproxy->next) {
 		if ((curproxy->cap & cap)!=cap || strcmp(curproxy->id, name))
@@ -204,6 +204,37 @@
 }
 
 /*
+ * This function finds a server with matching name within selected proxy.
+ * It also checks if there are more matching servers with
+ * requested name as this often leads into unexpected situations.
+ */
+
+struct server *findserver(const struct proxy *px, const char *name) {
+
+	struct server *cursrv, *target = NULL;
+
+	if (!px)
+		return NULL;
+
+	for (cursrv = px->srv; cursrv; cursrv = cursrv->next) {
+		if (strcmp(cursrv->id, name))
+			continue;
+
+		if (!target) {
+			target = cursrv;
+			continue;
+		}
+
+		Alert("Refusing to use duplicated server '%s' fould in proxy: %s!\n",
+			name, px->id);
+
+		return NULL;
+	}
+
+	return target;
+}
+
+/*
  * This function creates all proxy sockets. It should be done very early,
  * typically before privileges are dropped. The sockets will be registered
  * but not added to any fd_set, in order not to loose them across the fork().