MINOR: cli: new stats socket command: show servers state

new command 'show servers state' which dumps all variable parameters
of a server during an HAProxy process life.
Purpose is to dump current server state at current run time in order to
read them right after the reload.

The format of the output is versionned and we support version 1 for now.
diff --git a/include/types/applet.h b/include/types/applet.h
index 2e8a297..6b46f49 100644
--- a/include/types/applet.h
+++ b/include/types/applet.h
@@ -112,6 +112,9 @@
 		struct {
 			struct dns_resolvers *ptr;
 		} resolvers;
+		struct {
+			struct proxy *backend;
+		} server_state;
 	} ctx;					/* used by stats I/O handlers to dump the stats */
 };
 
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 92f2dea..27f8340 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -92,6 +92,7 @@
 	STAT_CLI_O_POOLS,    /* dump memory pools */
 	STAT_CLI_O_TLSK,     /* list all TLS ticket keys references */
 	STAT_CLI_O_RESOLVERS,/* dump a resolver's section nameservers counters */
+	STAT_CLI_O_SERVERS_STATE, /* dump server state and changing information */
 };
 
 /* Actions available for the stats admin forms */
@@ -128,6 +129,7 @@
 };
 
 static int stats_dump_info_to_buffer(struct stream_interface *si);
+static int stats_dump_servers_state_to_buffer(struct stream_interface *si);
 static int stats_dump_pools_to_buffer(struct stream_interface *si);
 static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess);
 static int stats_dump_sess_to_buffer(struct stream_interface *si);
@@ -144,11 +146,14 @@
 #endif
 static void cli_release_handler(struct appctx *appctx);
 
+static void dump_servers_state(struct proxy *backend, struct chunk *buf);
+
 /*
  * cli_io_handler()
  *     -> stats_dump_sess_to_buffer()     // "show sess"
  *     -> stats_dump_errors_to_buffer()   // "show errors"
  *     -> stats_dump_info_to_buffer()     // "show info"
+ *     -> stats_dump_servers_state_to_buffer() // "show servers state [<backend name>]"
  *     -> stats_dump_stat_to_buffer()     // "show stat"
  *        -> stats_dump_resolvers_to_buffer() // "show stat resolver <id>"
  *        -> stats_dump_csv_header()
@@ -188,6 +193,7 @@
 	"  show errors    : report last request and response errors for each proxy\n"
 	"  show sess [id] : report the list of current sessions or dump this session\n"
 	"  show table [id]: report table usage stats or dump this table's contents\n"
+	"  show servers state [id]: dump volatile server information (for backend <id>)\n"
 	"  get weight     : report a server's current weight\n"
 	"  set weight     : change a server's weight\n"
 	"  set server     : change a server's state, weight or address\n"
@@ -1189,6 +1195,24 @@
 			appctx->st2 = STAT_ST_INIT;
 			appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer
 		}
+		else if (strcmp(args[1], "servers") == 0 && strcmp(args[2], "state") == 0) {
+			appctx->ctx.server_state.backend = NULL;
+
+			/* check if a backend name has been provided */
+			if (*args[3]) {
+				/* read server state from local file */
+				appctx->ctx.server_state.backend = proxy_be_by_name(args[3]);
+
+				if (appctx->ctx.server_state.backend == NULL) {
+					appctx->ctx.cli.msg = "Can't find backend.\n";
+					appctx->st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+			}
+			appctx->st2 = STAT_ST_INIT;
+			appctx->st0 = STAT_CLI_O_SERVERS_STATE; // stats_dump_servers_state_to_buffer
+			return 1;
+		}
 		else if (strcmp(args[1], "pools") == 0) {
 			appctx->st2 = STAT_ST_INIT;
 			appctx->st0 = STAT_CLI_O_POOLS; // stats_dump_pools_to_buffer
@@ -2476,6 +2500,10 @@
 				if (stats_dump_info_to_buffer(si))
 					appctx->st0 = STAT_CLI_PROMPT;
 				break;
+			case STAT_CLI_O_SERVERS_STATE:
+				if (stats_dump_servers_state_to_buffer(si))
+					appctx->st0 = STAT_CLI_PROMPT;
+				break;
 			case STAT_CLI_O_STAT:
 				if (stats_dump_stat_to_buffer(si, NULL))
 					appctx->st0 = STAT_CLI_PROMPT;
@@ -2694,6 +2722,95 @@
 	return 1;
 }
 
+/* dumps server state information into <buf> for all the servers found in <backend>
+ * These information are all the parameters which may change during HAProxy runtime.
+ * By default, we only export to the last known server state file format.
+ * These information can be used at next startup to recover same level of server state.
+ */
+static void dump_servers_state(struct proxy *backend, struct chunk *buf)
+{
+	struct server *srv;
+	char srv_addr[INET6_ADDRSTRLEN + 1];
+	time_t srv_time_since_last_change;
+	int bk_f_forced_id, srv_f_forced_id;
+
+	/* we don't want to report any state if the backend is not enabled on this process */
+	if (backend->bind_proc && !(backend->bind_proc & (1UL << (relative_pid - 1))))
+		return;
+
+	srv = backend->srv;
+
+	while (srv) {
+		srv_addr[0] = '\0';
+		srv_time_since_last_change = 0;
+		bk_f_forced_id = 0;
+		srv_f_forced_id = 0;
+
+		switch (srv->addr.ss_family) {
+			case AF_INET:
+				inet_ntop(srv->addr.ss_family, &((struct sockaddr_in *)&srv->addr)->sin_addr,
+					  srv_addr, INET_ADDRSTRLEN + 1);
+				break;
+			case AF_INET6:
+				inet_ntop(srv->addr.ss_family, &((struct sockaddr_in6 *)&srv->addr)->sin6_addr,
+					  srv_addr, INET6_ADDRSTRLEN + 1);
+				break;
+		}
+		srv_time_since_last_change = now.tv_sec - srv->last_change;
+		bk_f_forced_id = backend->options & PR_O_FORCED_ID ? 1 : 0;
+		srv_f_forced_id = srv->flags & SRV_F_FORCED_ID ? 1 : 0;
+
+		chunk_appendf(buf,
+				"%d %s "
+				"%d %s %s "
+				"%d %d %d %d %ld "
+				"%d %d %d %d %d "
+				"%d %d"
+				"\n",
+				backend->uuid, backend->id,
+				srv->puid, srv->id, srv_addr,
+				srv->state, srv->admin, srv->uweight, srv->iweight, 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 = srv->next;
+	}
+}
+
+/* Parses backend list or simply use backend name provided by the user to return
+ * states of servers to stdout.
+ */
+static int stats_dump_servers_state_to_buffer(struct stream_interface *si)
+{
+	struct appctx *appctx = __objt_appctx(si->end);
+	extern struct proxy *proxy;
+	struct proxy *curproxy;
+
+	chunk_reset(&trash);
+
+	chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES);
+
+	if (appctx->ctx.server_state.backend) {
+		dump_servers_state(appctx->ctx.server_state.backend, &trash);
+	}
+	else {
+		for (curproxy = proxy; curproxy != NULL; curproxy = curproxy->next) {
+			/* servers are only in backends */
+			if (!(curproxy->cap & PR_CAP_BE))
+				continue;
+
+			dump_servers_state(curproxy, &trash);
+		}
+	}
+
+	if (bi_putchk(si_ic(si), &trash) == -1) {
+		si_applet_cant_put(si);
+		return 0;
+	}
+
+	return 1;
+}
+
 /* This function dumps memory usage information onto the stream interface's
  * read buffer. It returns 0 as long as it does not complete, non-zero upon
  * completion. No state is used.