MINOR: cli/proxy: add a new "show servers conn" command

This command reuses the existing "show servers state" to also dump the
state of active and idle connections. The main use is to serve as a
debugging tool to troubleshot connection reuse issues.
diff --git a/doc/management.txt b/doc/management.txt
index 0877992..0585d38 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -2290,6 +2290,21 @@
   Dumps the current profiling settings, one per line, as well as the command
   needed to change them.
 
+show servers conn [<backend>]
+  Dump the current and idle connections state of the servers belonging to the
+  designated backend (or all backends if none specified). A backend name or
+  identifier may be used.
+
+  The output consists in a header line showing the fields titles, then one
+  server per line with for each, the backend name and ID, server name and ID,
+  the address, port and a series or values. The number of fields varies
+  depending on thread count.
+
+  Given the threaded nature of idle connections, it's important to understand
+  that some values may change once read, and that as such, consistency within a
+  line isn't granted. This output is mostly provided as a debugging tool and is
+  not relevant to be routinely monitored nor graphed.
+
 show servers state [<backend>]
   Dump the state of the servers found in the running configuration. A backend
   name or identifier may be provided to limit the output to this backend only.
diff --git a/src/proxy.c b/src/proxy.c
index ebbfe49..93c4665 100644
--- a/src/proxy.c
+++ b/src/proxy.c
@@ -1723,14 +1723,17 @@
 }
 
 
-/* parse a "show servers" CLI line, returns 0 if it wants to start the dump or
- * 1 if it stops immediately. If an argument is specified, it will set the proxy
- * pointer into cli.p0 and its ID into cli.i0.
+/* parse a "show servers [state|conn]" CLI line, returns 0 if it wants to start
+ * the dump or 1 if it stops immediately. If an argument is specified, it will
+ * set the proxy pointer into cli.p0 and its ID into cli.i0. It sets cli.o0 to
+ * 0 for "state", or 1 for "conn".
  */
 static int cli_parse_show_servers(char **args, char *payload, struct appctx *appctx, void *private)
 {
 	struct proxy *px;
 
+	appctx->ctx.cli.o0 = *args[2] == 'c'; // "conn" vs "state"
+
 	/* check if a backend name has been provided */
 	if (*args[3]) {
 		/* read server state from local file */
@@ -1794,19 +1797,38 @@
 		if (srv->srvrq && srv->srvrq->name)
 			srvrecord = srv->srvrq->name;
 
+		if (appctx->ctx.cli.o0 == 0) {
+			/* show servers state */
+			chunk_printf(&trash,
+			             "%d %s "
+			             "%d %s %s "
+			             "%d %d %d %d %ld "
+			             "%d %d %d %d %d "
+			             "%d %d %s %u %s"
+			             "\n",
+			             px->uuid, px->id,
+			             srv->puid, srv->id, srv_addr,
+			             srv->cur_state, srv->cur_admin, srv->uweight, srv->iweight, (long int)srv_time_since_last_change,
+			             srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state,
+			             bk_f_forced_id, srv_f_forced_id, srv->hostname ? srv->hostname : "-", srv->svc_port,
+			             srvrecord ? srvrecord : "-");
+		} else {
+			/* show servers conn */
+			int thr;
+
+			chunk_printf(&trash,
+			             "%s/%s %d/%d %s %u - %u %u %u %u %u %u %d %u",
+			             px->id, srv->id, px->uuid, srv->puid, srv_addr,srv->svc_port,
+			             srv->pool_purge_delay,
+			             srv->curr_used_conns, srv->max_used_conns, srv->est_need_conns,
+			             srv->curr_idle_nb, srv->curr_safe_nb, (int)srv->max_idle_conns, srv->curr_idle_conns);
+
+			for (thr = 0; thr < global.nbthread; thr++)
+				chunk_appendf(&trash, " %u", srv->curr_idle_thr[thr]);
+
-		chunk_printf(&trash,
-				"%d %s "
-				"%d %s %s "
-				"%d %d %d %d %ld "
-				"%d %d %d %d %d "
-				"%d %d %s %u %s"
-				"\n",
-				px->uuid, px->id,
-				srv->puid, srv->id, srv_addr,
-				srv->cur_state, srv->cur_admin, srv->uweight, srv->iweight, (long int)srv_time_since_last_change,
-				srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state,
-				bk_f_forced_id, srv_f_forced_id, srv->hostname ? srv->hostname : "-", srv->svc_port,
-				srvrecord ? srvrecord : "-");
+			chunk_appendf(&trash, "\n");
+		}
+
 		if (ci_putchk(si_ic(si), &trash) == -1) {
 			si_rx_room_blk(si);
 			return 0;
@@ -1833,7 +1855,13 @@
 	}
 
 	if (appctx->st2 == STAT_ST_HEAD) {
-		chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES);
+		if (appctx->ctx.cli.o0 == 0)
+			chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES);
+		else
+			chunk_printf(&trash,
+			             "# bkname/svname bkid/svid addr port - purge_delay used_cur used_max need_est unsafe_nb safe_nb idle_lim idle_cur idle_per_thr[%d]\n",
+			             global.nbthread);
+
 		if (ci_putchk(si_ic(si), &trash) == -1) {
 			si_rx_room_blk(si);
 			return 0;
@@ -2343,6 +2371,7 @@
 	{ { "disable", "frontend",  NULL }, "disable frontend : temporarily disable specific frontend", cli_parse_disable_frontend, NULL, NULL },
 	{ { "enable", "frontend",  NULL }, "enable frontend : re-enable specific frontend", cli_parse_enable_frontend, NULL, NULL },
 	{ { "set", "maxconn", "frontend",  NULL }, "set maxconn frontend : change a frontend's maxconn setting", cli_parse_set_maxconn_frontend, NULL },
+	{ { "show","servers", "conn",  NULL }, "show servers conn [id]: dump server connections status (for backend <id>)", cli_parse_show_servers, cli_io_handler_servers_state },
 	{ { "show","servers", "state",  NULL }, "show servers state [id]: dump volatile server information (for backend <id>)", cli_parse_show_servers, cli_io_handler_servers_state },
 	{ { "show", "backend", NULL }, "show backend   : list backends in the current running config", NULL, cli_io_handler_show_backend },
 	{ { "shutdown", "frontend",  NULL }, "shutdown frontend : stop a specific frontend", cli_parse_shutdown_frontend, NULL, NULL },