[MEDIUM] stats: add support for changing frontend's maxconn at runtime

The new "set maxconn frontend XXX" statement on the stats socket allows
the admin to change a frontend's maxconn value. If some connections are
queued, they will immediately be accepted up to the new limit. If the
limit is lowered, new connections acceptation might be delayed. This can
be used to temporarily reduce or increase the impact of a specific frontend's
traffic on the whole process.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 537ab6f..cd5f979 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -9483,6 +9483,15 @@
 quit
   Close the connection when in interactive mode.
 
+set maxconn frontend <frontend> <value>
+  Dynamically change the specified frontend's maxconn setting. Any non-null
+  positive value is allowed, but setting values larger than the global maxconn
+  does not make much sense. If the limit is increased and connections were
+  pending, they will immediately be accepted. If it is lowered to a value below
+  the current number of connections, new connections acceptation will be
+  delayed until the threshold is reached. The frontend might be specified by
+  either its name or its numeric ID prefixed with a sharp ('#').
+
 set timeout cli <delay>
   Change the CLI interface timeout for current connection. This can be useful
   during long debugging sessions where the user needs to constantly inspect
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 9caebdd..9c89226 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -48,6 +48,7 @@
 #include <proto/freq_ctr.h>
 #include <proto/log.h>
 #include <proto/pipe.h>
+#include <proto/protocols.h>
 #include <proto/proto_uxst.h>
 #include <proto/proxy.h>
 #include <proto/session.h>
@@ -83,6 +84,7 @@
 	"  set timeout    : change a timeout setting\n"
 	"  disable server : set a server in maintenance mode\n"
 	"  enable server  : re-enable a server that was previously in maintenance mode\n"
+	"  set maxconn    : change a maxconn setting\n"
 	"";
 
 static const char stats_permission_denied_msg[] =
@@ -958,6 +960,66 @@
 				return 1;
 			}
 		}
+		else if (strcmp(args[1], "maxconn") == 0) {
+			if (strcmp(args[2], "frontend") == 0) {
+				struct proxy *px;
+				struct listener *l;
+				int v;
+
+				if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
+					si->applet.ctx.cli.msg = stats_permission_denied_msg;
+					si->applet.st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+
+				if (!*args[3]) {
+					si->applet.ctx.cli.msg = "Frontend name expected.\n";
+					si->applet.st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+
+				px = findproxy(args[3], PR_CAP_FE);
+				if (!px) {
+					si->applet.ctx.cli.msg = "No such frontend.\n";
+					si->applet.st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+
+				if (!*args[4]) {
+					si->applet.ctx.cli.msg = "Integer value expected.\n";
+					si->applet.st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+
+				v = atoi(args[4]);
+				/* check for unlimited values, we restore default setting (cfg_maxpconn) */
+				if (v < 1) {
+					si->applet.ctx.cli.msg = "Value out of range.\n";
+					si->applet.st0 = STAT_CLI_PRINT;
+					return 1;
+				}
+
+				/* OK, the value is fine, so we assign it to the proxy and to all of
+				 * its listeners. The blocked ones will be dequeued.
+				 */
+				px->maxconn = v;
+				for (l = px->listen; l != NULL; l = l->next) {
+					l->maxconn = v;
+					if (l->state == LI_FULL)
+						resume_listener(l);
+				}
+
+				if (px->maxconn > px->feconn && !LIST_ISEMPTY(&s->fe->listener_queue))
+					dequeue_all_listeners(&s->fe->listener_queue);
+
+				return 1;
+			}
+			else {
+				si->applet.ctx.cli.msg = "'set maxconn' only supports 'frontend'.\n";
+				si->applet.st0 = STAT_CLI_PRINT;
+				return 1;
+			}
+		}
 		else { /* unknown "set" parameter */
 			return 0;
 		}