diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index eb44a3632..4c3837b42 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -58,8 +58,10 @@ /* status codes (strictly 4 chars) used in the URL to display a message */ #define STAT_STATUS_UNKN "UNKN" /* an unknown error occured, shouldn't happen */ #define STAT_STATUS_DONE "DONE" /* the action is successful */ +#define STAT_STATUS_PART "PART" /* the action is partially successful */ #define STAT_STATUS_NONE "NONE" /* nothing happened (no action chosen or servers state didn't change) */ -#define STAT_STATUS_EXCD "EXCD" /* an error occured becayse the buffer couldn't store all data */ +#define STAT_STATUS_ERRP "ERRP" /* an error occured due to invalid values in parameters */ +#define STAT_STATUS_EXCD "EXCD" /* an error occured because the buffer couldn't store all data */ #define STAT_STATUS_DENY "DENY" /* action denied */ extern struct si_applet http_stats_applet; diff --git a/include/types/proto_http.h b/include/types/proto_http.h index f1b3eef52..a7b16aa25 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -251,6 +251,13 @@ enum { HTTP_ERR_SIZE }; +/* Actions available for the stats admin forms */ +enum { + ST_ADM_ACTION_NONE = 0, + ST_ADM_ACTION_DISABLE, + ST_ADM_ACTION_ENABLE, +}; + /* This is an HTTP message, as described in RFC2616. It can be either a request * message or a response message. * diff --git a/src/dumpstats.c b/src/dumpstats.c index 142429b8c..16d07ddb2 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -2037,6 +2037,26 @@ static int stats_dump_http(struct stream_interface *si, struct uri_auth *uri) "Nothing has changed." "\n", uri->uri_prefix); } + else if (strcmp(si->applet.ctx.stats.st_code, STAT_STATUS_PART) == 0) { + chunk_printf(&msg, + "

" + "[X] " + "Action partially processed.
" + "Some server names are probably unknown or ambiguous (duplicated names in the backend)." + "
\n", uri->uri_prefix); + } + else if (strcmp(si->applet.ctx.stats.st_code, STAT_STATUS_ERRP) == 0) { + chunk_printf(&msg, + "

" + "[X] " + "Action not processed because of invalid parameters." + "" + "
\n", uri->uri_prefix); + } else if (strcmp(si->applet.ctx.stats.st_code, STAT_STATUS_EXCD) == 0) { chunk_printf(&msg, "

" diff --git a/src/proto_http.c b/src/proto_http.c index ed8e01842..bf91328fe 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2545,13 +2545,18 @@ int http_wait_for_request(struct session *s, struct buffer *req, int an_bit) */ int http_process_req_stat_post(struct stream_interface *si, struct http_txn *txn, struct buffer *req) { - struct proxy *px; - struct server *sv; + struct proxy *px = NULL; + struct server *sv = NULL; - char *backend = NULL; - int action = 0; + char key[LINESIZE]; + int action = ST_ADM_ACTION_NONE; + int reprocess = 0; + + int total_servers = 0; + int altered_servers = 0; char *first_param, *cur_param, *next_param, *end_params; + char *st_cur_param, *st_next_param; first_param = req->data + txn->req.eoh + 2; end_params = first_param + txn->req.body_len; @@ -2578,15 +2583,21 @@ int http_process_req_stat_post(struct stream_interface *si, struct http_txn *txn * From the html form, the backend and the action are at the end. */ while (cur_param > first_param) { - char *key, *value; + char *value; + int poffset, plen; cur_param--; if ((*cur_param == '&') || (cur_param == first_param)) { + reprocess_servers: /* Parse the key */ - key = cur_param; - if (cur_param != first_param) { - /* delimit the string for the next loop */ - *key++ = '\0'; + poffset = (cur_param != first_param ? 1 : 0); + plen = next_param - cur_param + (cur_param == first_param ? 1 : 0); + if ((plen > 0) && (plen <= sizeof(key))) { + strncpy(key, cur_param + poffset, plen); + key[plen - 1] = '\0'; + } else { + si->applet.ctx.stats.st_code = STAT_STATUS_EXCD; + goto out; } /* Parse the value */ @@ -2603,45 +2614,91 @@ int http_process_req_stat_post(struct stream_interface *si, struct http_txn *txn break; /* Now we can check the key to see what to do */ - if (!backend && strcmp(key, "b") == 0) { - backend = value; + if (!px && (strcmp(key, "b") == 0)) { + if ((px = findproxy(value, PR_CAP_BE)) == NULL) { + /* the backend name is unknown or ambiguous (duplicate names) */ + si->applet.ctx.stats.st_code = STAT_STATUS_ERRP; + goto out; + } } - else if (!action && strcmp(key, "action") == 0) { + else if (!action && (strcmp(key, "action") == 0)) { if (strcmp(value, "disable") == 0) { - action = 1; + action = ST_ADM_ACTION_DISABLE; } else if (strcmp(value, "enable") == 0) { - action = 2; - } else { - /* unknown action, no need to continue */ - break; + action = ST_ADM_ACTION_ENABLE; + } + else { + si->applet.ctx.stats.st_code = STAT_STATUS_ERRP; + goto out; } } else if (strcmp(key, "s") == 0) { - if (backend && action && get_backend_server(backend, value, &px, &sv)) { + if (!(px && action)) { + /* + * Indicates that we'll need to reprocess the parameters + * as soon as backend and action are known + */ + if (!reprocess) { + st_cur_param = cur_param; + st_next_param = next_param; + } + reprocess = 1; + } + else if ((sv = findserver(px, value)) != NULL) { switch (action) { - case 1: + case ST_ADM_ACTION_DISABLE: if ((px->state != PR_STSTOPPED) && !(sv->state & SRV_MAINTAIN)) { /* Not already in maintenance, we can change the server state */ sv->state |= SRV_MAINTAIN; set_server_down(sv); - si->applet.ctx.stats.st_code = STAT_STATUS_DONE; + altered_servers++; + total_servers++; } break; - case 2: + case ST_ADM_ACTION_ENABLE: if ((px->state != PR_STSTOPPED) && (sv->state & SRV_MAINTAIN)) { /* Already in maintenance, we can change the server state */ set_server_up(sv); sv->health = sv->rise; /* up, but will fall down at first failure */ - si->applet.ctx.stats.st_code = STAT_STATUS_DONE; + altered_servers++; + total_servers++; } break; } + } else { + /* the server name is unknown or ambiguous (duplicate names) */ + total_servers++; } } + if (reprocess && px && action) { + /* Now, we know the backend and the action chosen by the user. + * We can safely restart from the first server parameter + * to reprocess them + */ + cur_param = st_cur_param; + next_param = st_next_param; + reprocess = 0; + goto reprocess_servers; + } + next_param = cur_param; } } + + if (total_servers == 0) { + si->applet.ctx.stats.st_code = STAT_STATUS_NONE; + } + else if (altered_servers == 0) { + si->applet.ctx.stats.st_code = STAT_STATUS_ERRP; + } + else if (altered_servers == total_servers) { + si->applet.ctx.stats.st_code = STAT_STATUS_DONE; + } + else { + si->applet.ctx.stats.st_code = STAT_STATUS_PART; + } + out: return 1; } @@ -7124,6 +7181,10 @@ int stats_check_uri(struct stream_interface *si, struct http_txn *txn, struct pr si->applet.ctx.stats.st_code = STAT_STATUS_DONE; else if (memcmp(h, STAT_STATUS_NONE, 4) == 0) si->applet.ctx.stats.st_code = STAT_STATUS_NONE; + else if (memcmp(h, STAT_STATUS_PART, 4) == 0) + si->applet.ctx.stats.st_code = STAT_STATUS_PART; + else if (memcmp(h, STAT_STATUS_ERRP, 4) == 0) + si->applet.ctx.stats.st_code = STAT_STATUS_ERRP; else if (memcmp(h, STAT_STATUS_EXCD, 4) == 0) si->applet.ctx.stats.st_code = STAT_STATUS_EXCD; else if (memcmp(h, STAT_STATUS_DENY, 4) == 0)