diff --git a/doc/configuration.txt b/doc/configuration.txt
index ed09c32e0..2e2493e9f 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -9513,6 +9513,22 @@ clear table
[ data. ] | [ key ]
$ echo "show table http_proxy" | socat stdio /tmp/sock1
>>> # table: http_proxy, type: ip, size:204800, used:1
+disable frontend
+ Mark the frontend as temporarily stopped. This corresponds to the mode which
+ is used during a soft restart : the frontend releases the port but can be
+ enabled again if needed. This should be used with care as some non-Linux OSes
+ are unable to enable it back. This is intended to be used in environments
+ where stopping a proxy is not even imaginable but a misconfigured proxy must
+ be fixed. That way it's possible to release the port and bind it into another
+ process to restore operations. The frontend will appear with status "STOP"
+ on the stats page.
+
+ The frontend may be specified either by its name or by its numeric ID,
+ prefixed with a sharp ('#').
+
+ This command is restricted and can only be issued on sockets configured for
+ level "admin".
+
disable server /
Mark the server DOWN for maintenance. In this mode, no more checks will be
performed on the server until it leaves maintenance.
@@ -9528,6 +9544,19 @@ disable server /
This command is restricted and can only be issued on sockets configured for
level "admin".
+enable frontend
+ Resume a frontend which was temporarily stopped. It is possible that some of
+ the listening ports won't be able to bind anymore (eg: if another process
+ took them since the 'disable frontend' operation). If this happens, an error
+ is displayed. Some operating systems might not be able to resume a frontend
+ which was disabled.
+
+ The frontend may be specified either by its name or by its numeric ID,
+ prefixed with a sharp ('#').
+
+ This command is restricted and can only be issued on sockets configured for
+ level "admin".
+
enable server /
If the server was previously marked as DOWN for maintenance, this marks the
server UP and checks are re-enabled.
@@ -9784,6 +9813,21 @@ show table [ data. ] | [ key ]
| fgrep 'key=' | cut -d' ' -f2 | cut -d= -f2 > abusers-ip.txt
( or | awk '/key/{ print a[split($2,a,"=")]; }' )
+shutdown frontend
+ Completely delete the specified frontend. All the ports it was bound to will
+ be released. It will not be possible to enable the frontend anymore after
+ this operation. This is intended to be used in environments where stopping a
+ proxy is not even imaginable but a misconfigured proxy must be fixed. That
+ way it's possible to release the port and bind it into another process to
+ restore operations. The frontend will not appear at all on the stats page
+ once it is terminated.
+
+ The frontend may be specified either by its name or by its numeric ID,
+ prefixed with a sharp ('#').
+
+ This command is restricted and can only be issued on sockets configured for
+ level "admin".
+
/*
* Local variables:
* fill-column: 79
diff --git a/include/proto/proxy.h b/include/proto/proxy.h
index 2f82397e7..b2e97fe7d 100644
--- a/include/proto/proxy.h
+++ b/include/proto/proxy.h
@@ -33,6 +33,7 @@ int start_proxies(int verbose);
struct task *manage_proxy(struct task *t);
void soft_stop(void);
int pause_proxy(struct proxy *p);
+int resume_proxy(struct proxy *p);
void stop_proxy(struct proxy *p);
void pause_proxies(void);
void resume_proxies(void);
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 964ba83b3..3c2190691 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -82,8 +82,9 @@ static const char stats_sock_usage_msg[] =
" get weight : report a server's current weight\n"
" set weight : change a server's weight\n"
" 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"
+ " disable : put a server or frontend in maintenance mode\n"
+ " enable : re-enable a server or frontend which is in maintenance mode\n"
+ " shutdown : irreversibly stop a frontend (eg: to release listening ports)\n"
" set maxconn : change a maxconn setting\n"
" set rate-limit : change a rate limiting value\n"
"";
@@ -662,6 +663,35 @@ err_args:
si->applet.st0 = STAT_CLI_PRINT;
}
+/* Expects to find a frontend named and returns it, otherwise displays various
+ * adequate error messages and returns NULL. This function also expects the session
+ * level to be admin.
+ */
+static struct proxy *expect_frontend_admin(struct session *s, struct stream_interface *si, const char *arg)
+{
+ struct proxy *px;
+
+ 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 NULL;
+ }
+
+ if (!*arg) {
+ si->applet.ctx.cli.msg = "A frontend name is expected.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return NULL;
+ }
+
+ px = findproxy(arg, PR_CAP_FE);
+ if (!px) {
+ si->applet.ctx.cli.msg = "No such frontend.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return NULL;
+ }
+ return px;
+}
+
/* Processes the stats interpreter on the statistics socket. This function is
* called from an applet running in a stream interface. The function returns 1
* if the request was understood, otherwise zero. It sets si->applet.st0 to a value
@@ -974,24 +1004,9 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
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;
+ px = expect_frontend_admin(s, si, args[3]);
+ if (!px)
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";
@@ -1167,8 +1182,36 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
return 1;
}
+ else if (strcmp(args[1], "frontend") == 0) {
+ struct proxy *px;
+
+ px = expect_frontend_admin(s, si, args[2]);
+ if (!px)
+ return 1;
+
+ if (px->state == PR_STSTOPPED) {
+ si->applet.ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ if (px->state != PR_STPAUSED) {
+ si->applet.ctx.cli.msg = "Frontend is already enabled.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ if (!resume_proxy(px)) {
+ si->applet.ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+ return 1;
+ }
else { /* unknown "enable" parameter */
- return 0;
+ si->applet.ctx.cli.msg = "'enable' only supports 'frontend' and 'server'.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
}
}
else if (strcmp(args[0], "disable") == 0) {
@@ -1215,8 +1258,63 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line)
return 1;
}
+ else if (strcmp(args[1], "frontend") == 0) {
+ struct proxy *px;
+
+ px = expect_frontend_admin(s, si, args[2]);
+ if (!px)
+ return 1;
+
+ if (px->state == PR_STSTOPPED) {
+ si->applet.ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ if (px->state == PR_STPAUSED) {
+ si->applet.ctx.cli.msg = "Frontend is already disabled.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ if (!pause_proxy(px)) {
+ si->applet.ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+ return 1;
+ }
else { /* unknown "disable" parameter */
- return 0;
+ si->applet.ctx.cli.msg = "'disable' only supports 'frontend' and 'server'.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+ }
+ else if (strcmp(args[0], "shutdown") == 0) {
+ if (strcmp(args[1], "frontend") == 0) {
+ struct proxy *px;
+
+ px = expect_frontend_admin(s, si, args[2]);
+ if (!px)
+ return 1;
+
+ if (px->state == PR_STSTOPPED) {
+ si->applet.ctx.cli.msg = "Frontend was already shut down.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
+ }
+
+ Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
+ px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
+ send_log(px, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
+ px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
+ stop_proxy(px);
+ return 1;
+ }
+ else { /* unknown "disable" parameter */
+ si->applet.ctx.cli.msg = "'shutdown' only supports 'frontend'.\n";
+ si->applet.st0 = STAT_CLI_PRINT;
+ return 1;
}
}
else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */