MINOR: stats: define the concept of domain for statistics

The domain option will be used to have statistics attached to other
objects than proxies/listeners/servers. At the moment, only the PROXY
domain is available.

Add an argument 'domain' on the 'show stats' cli command to specify the
domain. Only 'domain proxy' is available now. If not specified, proxy
will be considered the default domain.

For HTML output, only proxy statistics will be displayed.
diff --git a/doc/management.txt b/doc/management.txt
index adbad95..9a749e4 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -944,6 +944,8 @@
 CSV output format for monitoring tools. The same format is provided on the
 Unix socket.
 
+Statistics are regroup in categories labelled as domains, corresponding to the
+multiple components of HAProxy. Only the proxy domain is available.
 
 9.1. CSV format
 ---------------
@@ -2423,12 +2425,13 @@
   The special id "all" dumps the states of all sessions, which must be avoided
   as much as possible as it is highly CPU intensive and can take a lot of time.
 
-show stat [{<iid>|<proxy>} <type> <sid>] [typed|json] [desc]
-  Dump statistics using the CSV format; using the extended typed output
-  format described in the section above if "typed" is passed after the
-  other arguments; or in JSON if "json" is passed after the other arguments
-  . By passing <id>, <type> and <sid>, it is possible to dump only selected
-  items :
+show stat [domain <domain>] [{<iid>|<proxy>} <type> <sid>] [typed|json] [desc]
+  Dump statistics. The domain is used to select which statistics to print; only
+  proxy is available for now. By default, the CSV format is used; you can
+  activate the extended typed output format described in the section above if
+  "typed" is passed after the other arguments; or in JSON if "json" is passed
+  after the other arguments. By passing <id>, <type> and <sid>, it is possible
+  to dump only selected items :
     - <iid> is a proxy ID, -1 to dump everything. Alternatively, a proxy name
       <proxy> may be specified. In this case, this proxy's ID will be used as
       the ID selector.
diff --git a/include/haproxy/applet-t.h b/include/haproxy/applet-t.h
index 7c97f19..eca55c9 100644
--- a/include/haproxy/applet-t.h
+++ b/include/haproxy/applet-t.h
@@ -120,6 +120,7 @@
 		struct {
 			void *obj1;             /* context pointer used in stats dump */
 			void *obj2;             /* context pointer used in stats dump */
+			uint32_t domain;        /* set the stats to used, for now only proxy stats are supported */
 			int scope_str;		/* limit scope to a frontend/backend substring */
 			int scope_len;		/* length of the string above in the buffer */
 			int px_st;		/* STAT_PX_ST* */
diff --git a/include/haproxy/stats-t.h b/include/haproxy/stats-t.h
index 139b546..be4c686 100644
--- a/include/haproxy/stats-t.h
+++ b/include/haproxy/stats-t.h
@@ -50,6 +50,8 @@
 #define STATS_TYPE_SV  2
 #define STATS_TYPE_SO  3
 
+#define STATS_DOMAIN  (0)               /* used for bitshifting, type of statistics, for now only proxy is available */
+
 /* HTTP stats : applet.st0 */
 enum {
 	STAT_HTTP_INIT = 0,  /* Initial state */
@@ -450,5 +452,12 @@
 	} u;
 };
 
+/* stats_domain is used in a flag as a 1 byte field */
+enum stats_domain {
+	STATS_DOMAIN_PROXY = 0,
+	STATS_DOMAIN_COUNT,
+
+	STATS_DOMAIN_MASK  = 0xff
+};
 
 #endif /* _HAPROXY_STATS_T_H */
diff --git a/src/stats.c b/src/stats.c
index 66b8a49..16e0c10 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -257,6 +257,11 @@
 /* one line of stats */
 static THREAD_LOCAL struct field stats[ST_F_TOTAL_FIELDS];
 
+static inline uint8_t stats_get_domain(uint32_t domain)
+{
+	return domain >> STATS_DOMAIN & STATS_DOMAIN_MASK;
+}
+
 static void stats_dump_json_schema(struct buffer *out);
 
 int stats_putchk(struct channel *chn, struct htx *htx, struct buffer *chk)
@@ -531,7 +536,8 @@
 static int stats_dump_fields_typed(struct buffer *out,
                                    const struct field *stats,
                                    size_t stats_count,
-                                   unsigned int flags)
+                                   unsigned int flags,
+                                   enum stats_domain domain)
 {
 	int field;
 
@@ -539,14 +545,23 @@
 		if (!stats[field].type)
 			continue;
 
-		chunk_appendf(out, "%c.%u.%u.%d.%s.%u:",
-		              stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE ? 'F' :
-		              stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE ? 'B' :
-		              stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO ? 'L' :
-		              stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV ? 'S' :
-		              '?',
-		              stats[ST_F_IID].u.u32, stats[ST_F_SID].u.u32,
-		              field, stat_fields[field].name, stats[ST_F_PID].u.u32);
+		switch (domain) {
+		case STATS_DOMAIN_PROXY:
+			chunk_appendf(out, "%c.%u.%u.%d.%s.%u:",
+			              stats[ST_F_TYPE].u.u32 == STATS_TYPE_FE ? 'F' :
+			              stats[ST_F_TYPE].u.u32 == STATS_TYPE_BE ? 'B' :
+			              stats[ST_F_TYPE].u.u32 == STATS_TYPE_SO ? 'L' :
+			              stats[ST_F_TYPE].u.u32 == STATS_TYPE_SV ? 'S' :
+			              '?',
+			              stats[ST_F_IID].u.u32, stats[ST_F_SID].u.u32,
+			              field,
+			              stat_fields[field].name,
+			              stats[ST_F_PID].u.u32);
+			break;
+
+		default:
+			break;
+		}
 
 		if (!stats_emit_field_tags(out, &stats[field], ':'))
 			return 0;
@@ -640,7 +655,8 @@
 /* Dump all fields from <stats> into <out> using a typed "field:desc:type:value" format */
 static int stats_dump_fields_json(struct buffer *out,
                                   const struct field *stats, size_t stats_count,
-                                  unsigned int flags)
+                                  unsigned int flags,
+                                  enum stats_domain domain)
 {
 	int field;
 	int started = 0;
@@ -661,12 +677,14 @@
 		started = 1;
 
 		old_len = out->data;
-		stats_print_proxy_field_json(out, &stats[field],
-			                     stat_fields[field].name, field,
-			                     stats[ST_F_TYPE].u.u32,
-			                     stats[ST_F_IID].u.u32,
-			                     stats[ST_F_SID].u.u32,
-			                     stats[ST_F_PID].u.u32);
+		if (domain == STATS_DOMAIN_PROXY) {
+			stats_print_proxy_field_json(out, &stats[field],
+			                             stat_fields[field].name, field,
+			                             stats[ST_F_TYPE].u.u32,
+			                             stats[ST_F_IID].u.u32,
+			                             stats[ST_F_SID].u.u32,
+			                             stats[ST_F_PID].u.u32);
+		}
 
 		if (old_len == out->data)
 			goto err;
@@ -1408,9 +1426,9 @@
 	if (appctx->ctx.stats.flags & STAT_FMT_HTML)
 		ret = stats_dump_fields_html(&trash, stats, appctx->ctx.stats.flags);
 	else if (appctx->ctx.stats.flags & STAT_FMT_TYPED)
-		ret = stats_dump_fields_typed(&trash, stats, stats_count, appctx->ctx.stats.flags);
+		ret = stats_dump_fields_typed(&trash, stats, stats_count, appctx->ctx.stats.flags, appctx->ctx.stats.domain);
 	else if (appctx->ctx.stats.flags & STAT_FMT_JSON)
-		ret = stats_dump_fields_json(&trash, stats, stats_count, appctx->ctx.stats.flags);
+		ret = stats_dump_fields_json(&trash, stats, stats_count, appctx->ctx.stats.flags, appctx->ctx.stats.domain);
 	else
 		ret = stats_dump_fields_csv(&trash, stats, stats_count, appctx->ctx.stats.flags);
 
@@ -3339,6 +3357,9 @@
 	struct channel *res = si_ic(si);
 	struct htx *req_htx, *res_htx;
 
+	/* only proxy stats are available via http */
+	appctx->ctx.stats.domain = STATS_DOMAIN_PROXY;
+
 	res_htx = htx_from_buf(&res->buf);
 
 	if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
@@ -3932,7 +3953,19 @@
 	if ((strm_li(si_strm(appctx->owner))->bind_conf->level & ACCESS_LVL_MASK) >= ACCESS_LVL_OPER)
 		appctx->ctx.stats.flags |= STAT_SHLGNDS;
 
+	/* proxy is the default domain */
+	appctx->ctx.stats.domain = STATS_DOMAIN_PROXY;
+	if (!strcmp(args[arg], "domain")) {
+		++args;
+
+		if (!strcmp(args[arg], "proxy"))
+			++args;
+		else
+			return cli_err(appctx, "Invalid statistics domain.\n");
+	}
+
-	if (*args[arg] && *args[arg+1] && *args[arg+2]) {
+	if (appctx->ctx.stats.domain == STATS_DOMAIN_PROXY
+	    && *args[arg] && *args[arg+1] && *args[arg+2]) {
 		struct proxy *px;
 
 		px = proxy_find_by_name(args[arg], 0, 0);