MINOR: stats: add config "stats show modules"

By default, hide the extra statistics on the html page. Define a new
flag STAT_SHMODULES which is activated if the config "stats show
modules" is set.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index b01129f..f6eacfb 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -10036,6 +10036,22 @@
   See also: "stats enable", "stats uri".
 
 
+stats show-modules
+  Enable display of extra statistics module on the statistics page
+  May be used in sections :   defaults | frontend | listen | backend
+                                 yes   |    yes   |   yes  |   yes
+  Arguments : none
+
+  New columns are added at the end of the line containing the extra statistics
+  values as a tooltip.
+
+  Though this statement alone is enough to enable statistics reporting, it is
+  recommended to set all other settings in order to avoid relying on default
+  unobvious parameters. Default behavior is not to show this information.
+
+  See also: "stats enable", "stats uri".
+
+
 stats show-node [ <name> ]
   Enable reporting of a host name on the statistics page.
   May be used in sections :   defaults | frontend | listen | backend
diff --git a/include/haproxy/stats-t.h b/include/haproxy/stats-t.h
index 96dcf18..d148651 100644
--- a/include/haproxy/stats-t.h
+++ b/include/haproxy/stats-t.h
@@ -39,6 +39,7 @@
 #define STAT_SHDESC     0x00000400      /* conf: show description */
 #define STAT_SHLGNDS    0x00000800      /* conf: show legends */
 #define STAT_SHOW_FDESC 0x00001000      /* show the field descriptions when possible */
+#define STAT_SHMODULES  0x00002000      /* conf: show modules */
 
 #define STAT_BOUND      0x00800000	/* bound statistics to selected proxies/types/services */
 #define STAT_STARTED    0x01000000	/* some output has occurred */
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index b8f3795..c2db644 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -1923,6 +1923,12 @@
 				err_code |= ERR_ALERT | ERR_ABORT;
 				goto out;
 			}
+		} else if (!strcmp(args[1], "show-modules")) {
+			if (!stats_set_flag(&curproxy->uri_auth, STAT_SHMODULES)) {
+				ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
+				err_code |= ERR_ALERT | ERR_ABORT;
+				goto out;
+			}
 		} else if (!strcmp(args[1], "show-node")) {
 
 			if (*args[2]) {
diff --git a/src/stats.c b/src/stats.c
index ae2f58d..f84488a 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -764,7 +764,9 @@
 
 /* Dump all fields from <stats> into <out> using the HTML format. A column is
  * reserved for the checkbox is STAT_ADMIN is set in <flags>. Some extra info
- * are provided if STAT_SHLGNDS is present in <flags>.
+ * are provided if STAT_SHLGNDS is present in <flags>. The statistics from
+ * extra modules are displayed at the end of the lines if STAT_SHMODULES is
+ * present in <flags>.
  */
 static int stats_dump_fields_html(struct buffer *out,
 				  const struct field *stats,
@@ -922,23 +924,25 @@
 		              U2H(stats[ST_F_EREQ].u.u64),
 		              field_str(stats, ST_F_STATUS));
 
-		list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-			chunk_appendf(out,
-			              "<td><u>%s<div class=tips><table class=det>",
-			              mod->name);
-			if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE) {
-				for (j = 0; j < mod->stats_count; ++j) {
-					chunk_appendf(out,
-					              "<tr><th>%s</th><td>%s</td></tr>",
-					              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-					++i;
+		if (flags & STAT_SHMODULES) {
+			list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+				chunk_appendf(out,
+				              "<td><u>%s<div class=tips><table class=det>",
+				              mod->name);
+				if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_FE) {
+					for (j = 0; j < mod->stats_count; ++j) {
+						chunk_appendf(out,
+						              "<tr><th>%s</th><td>%s</td></tr>",
+						              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+						++i;
+					}
+				} else {
+					i += mod->stats_count;
 				}
-			} else {
-				i += mod->stats_count;
-			}
 
-			chunk_appendf(out,
-			              "</table></div></u></td>");
+				chunk_appendf(out,
+				              "</table></div></u></td>");
+			}
 		}
 
 		chunk_appendf(out, "</tr>");
@@ -1004,23 +1008,25 @@
 		              U2H(stats[ST_F_EREQ].u.u64),
 		              field_str(stats, ST_F_STATUS));
 
-		list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-			chunk_appendf(out,
-			              "<td><u>%s<div class=tips><table class=det>",
-			              mod->name);
-			if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI) {
-				for (j = 0; j < mod->stats_count; ++j) {
-					chunk_appendf(out,
-					              "<tr><th>%s</th><td>%s</td></tr>",
-					              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-					++i;
+		if (flags & STAT_SHMODULES) {
+			list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+				chunk_appendf(out,
+				              "<td><u>%s<div class=tips><table class=det>",
+				              mod->name);
+				if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_LI) {
+					for (j = 0; j < mod->stats_count; ++j) {
+						chunk_appendf(out,
+						              "<tr><th>%s</th><td>%s</td></tr>",
+						              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+						++i;
+					}
+				} else {
+					i += mod->stats_count;
 				}
-			} else {
-				i += mod->stats_count;
-			}
 
-			chunk_appendf(out,
-			              "</table></div></u></td>");
+				chunk_appendf(out,
+				              "</table></div></u></td>");
+			}
 		}
 
 		chunk_appendf(out, "</tr>");
@@ -1340,23 +1346,25 @@
 		else
 			chunk_appendf(out, "<td class=ac>-</td>");
 
-		list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-			chunk_appendf(out,
-			              "<td><u>%s<div class=tips><table class=det>",
-			              mod->name);
-			if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV) {
-				for (j = 0; j < mod->stats_count; ++j) {
-					chunk_appendf(out,
-					              "<tr><th>%s</th><td>%s</td></tr>",
-					              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-					++i;
+		if (flags & STAT_SHMODULES) {
+			list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+				chunk_appendf(out,
+				              "<td><u>%s<div class=tips><table class=det>",
+				              mod->name);
+				if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_SRV) {
+					for (j = 0; j < mod->stats_count; ++j) {
+						chunk_appendf(out,
+						              "<tr><th>%s</th><td>%s</td></tr>",
+						              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+						++i;
+					}
+				} else {
+					i += mod->stats_count;
 				}
-			} else {
-				i += mod->stats_count;
-			}
 
-			chunk_appendf(out,
-			              "</table></div></u></td>");
+				chunk_appendf(out,
+				              "</table></div></u></td>");
+			}
 		}
 
 		chunk_appendf(out, "</tr>\n");
@@ -1529,23 +1537,26 @@
 		              stats[ST_F_CHKDOWN].u.u32,
 		              stats[ST_F_DOWNTIME].type ? human_time(stats[ST_F_DOWNTIME].u.u32, 1) : "&nbsp;");
 
-		list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-			chunk_appendf(out,
-			              "<td><u>%s<div class=tips><table class=det>",
-			              mod->name);
-			if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE) {
-				for (j = 0; j < mod->stats_count; ++j) {
-					chunk_appendf(out,
-					              "<tr><th>%s</th><td>%s</td></tr>",
-					              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
-					++i;
+		if (flags & STAT_SHMODULES) {
+			list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+				chunk_appendf(out,
+				              "<td><u>%s<div class=tips><table class=det>",
+				              mod->name);
+				if (stats_px_get_cap(mod->domain_flags) & STATS_PX_CAP_BE) {
+					for (j = 0; j < mod->stats_count; ++j) {
+						chunk_appendf(out,
+						              "<tr><th>%s</th><td>%s</td></tr>",
+						              mod->stats[j].desc, field_to_html_str(&stats[ST_F_TOTAL_FIELDS + i]));
+						++i;
+					}
+				} else {
+					i += mod->stats_count;
 				}
-			} else {
-				i += mod->stats_count;
+				chunk_appendf(out,
+				              "</table></div></u></td>");
 			}
-			chunk_appendf(out,
-			              "</table></div></u></td>");
 		}
+
 		chunk_appendf(out, "</tr>");
 	}
 
@@ -2316,19 +2327,24 @@
 			chunk_appendf(&trash, "<th rowspan=2></th>");
 	}
 
-	// calculate the count of module for colspan attribute
-	list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-		++stats_module_len;
-	}
-
 	chunk_appendf(&trash,
 	              "<th rowspan=2></th>"
 	              "<th colspan=3>Queue</th>"
 	              "<th colspan=3>Session rate</th><th colspan=6>Sessions</th>"
 	              "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
 	              "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
-	              "<th colspan=9>Server</th>"
-	              "<th colspan=%d>Extra modules</th>"
+	              "<th colspan=9>Server</th>");
+
+	if (appctx->ctx.stats.flags & STAT_SHMODULES) {
+		// calculate the count of module for colspan attribute
+		list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+			++stats_module_len;
+		}
+		chunk_appendf(&trash, "<th colspan=%d>Extra modules</th>",
+		              stats_module_len);
+	}
+
+	chunk_appendf(&trash,
 	              "</tr>\n"
 	              "<tr class=\"titre\">"
 	              "<th>Cur</th><th>Max</th><th>Limit</th>"
@@ -2338,10 +2354,12 @@
 	              "<th>Resp</th><th>Retr</th><th>Redis</th>"
 	              "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
 	              "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
-	              "<th>Thrtle</th>\n", stats_module_len);
+	              "<th>Thrtle</th>\n");
 
-	list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
-		chunk_appendf(&trash, "<th>%s</th>", mod->name);
+	if (appctx->ctx.stats.flags & STAT_SHMODULES) {
+		list_for_each_entry(mod, &stats_module_list[STATS_DOMAIN_PROXY], list) {
+			chunk_appendf(&trash, "<th>%s</th>", mod->name);
+		}
 	}
 
 	chunk_appendf(&trash, "</tr>");