/*
* Produces statistics data for the session <s>. Expects to be called with
- * s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>
- * and <flags>.
+ * s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>.
+ * s->data_ctx must have been zeroed first, and the flags properly set.
* It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
* dump is finished and the session must be closed, or -1 in case of any error.
*/
-int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
+int stats_dump_raw(struct session *s, struct uri_auth *uri)
{
struct buffer *rep = s->rep;
struct proxy *px;
/* fall through */
case DATA_ST_HEAD:
- if (flags & STAT_SHOW_STAT) {
+ if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
print_csv_header(&msg, sizeof(trash));
if (buffer_write_chunk(rep, &msg) != 0)
return 0;
case DATA_ST_INFO:
up = (now.tv_sec - start_date.tv_sec);
-
- if (flags & STAT_SHOW_INFO) {
+ if (s->data_ctx.stats.flags & STAT_SHOW_INFO) {
chunk_printf(&msg, sizeof(trash),
"Name: " PRODUCT_NAME "\n"
"Version: " HAPROXY_VERSION "\n"
case DATA_ST_LIST:
/* dump proxies */
- if (flags & STAT_SHOW_STAT) {
+ if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
while (s->data_ctx.stats.px) {
px = s->data_ctx.stats.px;
/* skip the disabled proxies and non-networked ones */
if (px->state != PR_STSTOPPED &&
(px->cap & (PR_CAP_FE | PR_CAP_BE)))
- if (stats_dump_proxy(s, px, NULL, 0) == 0)
+ if (stats_dump_proxy(s, px, NULL) == 0)
return 0;
s->data_ctx.stats.px = px->next;
* s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN
* flag from the session, which it uses to keep on being called when there is
* free space in the buffer, of simply by letting an empty buffer upon return.
+ * s->data_ctx must have been zeroed before the first call, and the flags set.
* It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
* dump is finished and the session must be closed, or -1 in case of any error.
*/
-int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
+int stats_dump_http(struct session *s, struct uri_auth *uri)
{
struct buffer *rep = s->rep;
struct proxy *px;
"Cache-Control: no-cache\r\n"
"Connection: close\r\n"
"Content-Type: %s\r\n",
- (flags & STAT_FMT_HTML) ? "text/html" : "text/plain");
+ (s->data_ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
- if (uri->refresh > 0 && !(s->flags & SN_STAT_NORFRSH))
+ if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH))
chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n",
uri->refresh);
/* fall through */
case DATA_ST_HEAD:
- if (flags & STAT_FMT_HTML) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
/* WARNING! This must fit in the first buffer !!! */
chunk_printf(&msg, sizeof(trash),
"<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
* We are around 3.5 kB, add adding entries will
* become tricky if we want to support 4kB buffers !
*/
- if (flags & STAT_FMT_HTML) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, sizeof(trash),
"<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
PRODUCT_NAME "%s</a></h1>\n"
actconn
);
- if (s->flags & SN_STAT_HIDEDWN)
+ if (s->data_ctx.stats.flags & STAT_HIDE_DOWN)
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
uri->uri_prefix,
"",
- (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
+ (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
else
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
uri->uri_prefix,
";up",
- (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
+ (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
if (uri->refresh > 0) {
- if (s->flags & SN_STAT_NORFRSH)
+ if (s->data_ctx.stats.flags & STAT_NO_REFRESH)
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
uri->uri_prefix,
- (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
+ (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
"");
else
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
uri->uri_prefix,
- (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
+ (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
";norefresh");
}
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
uri->uri_prefix,
- (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
- (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
+ (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
+ (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
chunk_printf(&msg, sizeof(trash),
"<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
return 0;
}
- memset(&s->data_ctx, 0, sizeof(s->data_ctx));
-
s->data_ctx.stats.px = proxy;
s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
s->data_state = DATA_ST_LIST;
px = s->data_ctx.stats.px;
/* skip the disabled proxies and non-networked ones */
if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
- if (stats_dump_proxy(s, px, uri, flags) == 0)
+ if (stats_dump_proxy(s, px, uri) == 0)
return 0;
s->data_ctx.stats.px = px->next;
/* fall through */
case DATA_ST_END:
- if (flags & STAT_FMT_HTML) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, sizeof(trash), "</body></html>\n");
if (buffer_write_chunk(rep, &msg) != 0)
return 0;
* Returns 0 if it had to stop dumping data because of lack of buffer space,
* ot non-zero if everything completed.
*/
-int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags)
+int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
{
struct buffer *rep = s->rep;
struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */
return 1;
}
- if ((s->flags & SN_STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
+ if ((s->data_ctx.stats.flags & STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
(px->uuid != s->data_ctx.stats.iid))
return 1;
/* fall through */
case DATA_ST_PX_TH:
- if (flags & STAT_FMT_HTML) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
/* print a new table */
chunk_printf(&msg, sizeof(trash),
"<table cols=\"26\" class=\"tbl\" width=\"100%%\">\n"
case DATA_ST_PX_FE:
/* print the frontend */
- if ((px->cap & PR_CAP_FE) && (!(s->flags & SN_STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) {
- if (flags & STAT_FMT_HTML) {
+ if ((px->cap & PR_CAP_FE) &&
+ (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, sizeof(trash),
/* name, queue */
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=3></td>"
sv = s->data_ctx.stats.sv;
- if (s->flags & SN_STAT_BOUND) {
+ if (s->data_ctx.stats.flags & STAT_BOUND) {
if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV)))
break;
else
sv_state = 0; /* DOWN */
- if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) {
+ if ((sv_state == 0) && (s->data_ctx.stats.flags & STAT_HIDE_DOWN)) {
/* do not report servers which are DOWN */
s->data_ctx.stats.sv = sv->next;
continue;
}
- if (flags & STAT_FMT_HTML) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d ↑",
"UP %d/%d ↓", "UP",
"NOLB %d/%d ↓", "NOLB",
case DATA_ST_PX_BE:
/* print the backend */
- if ((px->cap & PR_CAP_BE) && (!(s->flags & SN_STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
- if (flags & STAT_FMT_HTML) {
+ if ((px->cap & PR_CAP_BE) &&
+ (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, sizeof(trash),
/* name */
"<tr align=center class=\"backend\"><td>Backend</td>"
/* fall through */
case DATA_ST_PX_END:
- if (flags & STAT_FMT_HTML) {
+ if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
chunk_printf(&msg, sizeof(trash), "</table><p>\n");
if (buffer_write_chunk(rep, &msg) != 0)
}
else if (s->data_source == DATA_SRC_STATS) {
/* dump server statistics */
- int ret = stats_dump_http(s, s->be->uri_auth,
- (s->flags & SN_STAT_FMTCSV) ? 0 : STAT_FMT_HTML);
+ int ret = stats_dump_http(s, s->be->uri_auth);
if (ret >= 0)
return ret;
/* -1 indicates an error */
int authenticated, cur_idx;
char *h;
+ memset(&t->data_ctx.stats, 0, sizeof(t->data_ctx.stats));
+
/* check URI size */
if (uri_auth->uri_len > txn->req.sl.rq.u_l)
return 0;
h += uri_auth->uri_len;
while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 3) {
if (memcmp(h, ";up", 3) == 0) {
- t->flags |= SN_STAT_HIDEDWN;
+ t->data_ctx.stats.flags |= STAT_HIDE_DOWN;
break;
}
h++;
h = t->req->data + txn->req.sl.rq.u + uri_auth->uri_len;
while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 10) {
if (memcmp(h, ";norefresh", 10) == 0) {
- t->flags |= SN_STAT_NORFRSH;
+ t->data_ctx.stats.flags |= STAT_NO_REFRESH;
break;
}
h++;
h = t->req->data + txn->req.sl.rq.u + uri_auth->uri_len;
while (h <= t->req->data + txn->req.sl.rq.u + txn->req.sl.rq.u_l - 4) {
if (memcmp(h, ";csv", 4) == 0) {
- t->flags |= SN_STAT_FMTCSV;
+ t->data_ctx.stats.flags |= STAT_FMT_CSV;
break;
}
h++;
}
+ t->data_ctx.stats.flags |= STAT_SHOW_STAT | STAT_SHOW_INFO;
+
/* we are in front of a interceptable URI. Let's check
* if there's an authentication and if it's valid.
*/
return 1;
}
- /* The request is valid, the user is authenticate. Let's start sending
+ /* The request is valid, the user is authenticated. Let's start sending
* data.
*/
t->cli_state = CL_STSHUTR;
/* Processes data exchanges on the statistics socket. The client processing
* is called and the task is put back in the wait queue or it is cleared.
* In order to ease the transition, we simply simulate the server status
- * for now. It only knows states SV_STIDLE, SV_STDATA and SV_STCLOSE. Returns
- * in <next> the task's expiration date.
+ * for now. It only knows states SV_STIDLE, SV_STCONN, SV_STDATA, and
+ * SV_STCLOSE. Returns in <next> the task's expiration date.
*/
void process_uxst_stats(struct task *t, struct timeval *next)
{
struct session *s = t->context;
struct listener *listener;
int fsm_resync = 0;
+ int last_rep_l;
- /* we need to be in DATA phase on the "server" side */
- if (s->srv_state == SV_STIDLE) {
- s->srv_state = SV_STDATA;
- s->data_source = DATA_SRC_STATS;
- }
-
do {
+ char *args[MAX_UXST_ARGS + 1];
+ char *line, *p;
+ int arg;
+
fsm_resync = process_uxst_cli(s);
- if (s->srv_state != SV_STDATA)
- continue;
if (s->cli_state == CL_STCLOSE || s->cli_state == CL_STSHUTW) {
s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- continue;
+ break;
}
- if (s->data_state == DATA_ST_INIT) {
-
- char *args[MAX_UXST_ARGS + 1];
- char *line, *p;
- int arg;
+ switch (s->srv_state) {
+ case SV_STIDLE:
+ /* stats output not initialized yet */
+ memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
+ s->data_source = DATA_SRC_STATS;
+ s->srv_state = SV_STCONN;
+ fsm_resync |= 1;
+ break;
+ case SV_STCONN: /* will change to SV_STANALYZE */
+ /* stats initialized, but waiting for the command */
line = s->req->data;
p = memchr(line, '\n', s->req->l);
if (!strcmp(args[0], "show")) {
if (!strcmp(args[1], "stat")) {
if (*args[2] && *args[3] && *args[4]) {
- s->flags |= SN_STAT_BOUND;
+ s->data_ctx.stats.flags |= STAT_BOUND;
s->data_ctx.stats.iid = atoi(args[2]);
s->data_ctx.stats.type = atoi(args[3]);
s->data_ctx.stats.sid = atoi(args[4]);
}
- /* send the stats, and changes the data_state */
- if (stats_dump_raw(s, NULL, STAT_SHOW_STAT) != 0) {
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- }
-
+ s->data_ctx.stats.flags |= STAT_SHOW_STAT;
+ s->data_ctx.stats.flags |= STAT_FMT_CSV;
+ s->srv_state = SV_STDATA;
+ fsm_resync |= 1;
continue;
}
if (!strcmp(args[1], "info")) {
- /* send the stats, and changes the data_state */
- if (stats_dump_raw(s, NULL, STAT_SHOW_INFO) != 0) {
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- }
-
+ s->data_ctx.stats.flags |= STAT_SHOW_INFO;
+ s->data_ctx.stats.flags |= STAT_FMT_CSV;
+ s->srv_state = SV_STDATA;
+ fsm_resync |= 1;
continue;
}
}
s->srv_state = SV_STCLOSE;
fsm_resync |= 1;
continue;
- }
- /* OK we have some remaining data to process. Just for the
- * sake of an exercice, we copy the req into the resp,
- * and flush the req. This produces a simple echo function.
- */
- if (stats_dump_raw(s, NULL, 0) != 0) {
- s->srv_state = SV_STCLOSE;
- fsm_resync |= 1;
- continue;
+ case SV_STDATA:
+ /* OK we have to process the request. Since it is possible
+ * that we get there with the client output paused, we
+ * will simply check that we have really sent some data
+ * and wake the client up if needed.
+ */
+ last_rep_l = s->rep->l;
+ if (stats_dump_raw(s, NULL) != 0) {
+ s->srv_state = SV_STCLOSE;
+ fsm_resync |= 1;
+ }
+ if (s->rep->l != last_rep_l)
+ fsm_resync |= 1;
+ break;
}
} while (fsm_resync);