diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index 362650567..6cca84fd6 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -27,18 +27,22 @@ #include #include -#define STAT_FMT_HTML 0x1 -#define STAT_SHOW_STAT 0x2 -#define STAT_SHOW_INFO 0x4 +/* Flags for session->data_ctx.stats.flags */ +#define STAT_FMT_CSV 0x00000001 /* dump the stats in CSV format instead of HTML */ +#define STAT_SHOW_STAT 0x00000002 /* dump the stats part */ +#define STAT_SHOW_INFO 0x00000004 /* dump the info part */ +#define STAT_HIDE_DOWN 0x00000008 /* hide 'down' servers in the stats page */ +#define STAT_NO_REFRESH 0x00000010 /* do not automatically refresh the stats page */ +#define STAT_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */ #define STATS_TYPE_FE 0 #define STATS_TYPE_BE 1 #define STATS_TYPE_SV 2 int stats_parse_global(const char **args, char *err, int errlen); -int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags); -int stats_dump_http(struct session *s, struct uri_auth *uri, int flags); -int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags); +int stats_dump_raw(struct session *s, struct uri_auth *uri); +int stats_dump_http(struct session *s, struct uri_auth *uri); +int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri); #endif /* _PROTO_DUMPSTATS_H */ diff --git a/include/types/session.h b/include/types/session.h index 18d5115f0..45e38fd83 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -2,7 +2,7 @@ include/types/session.h This file defines everything related to sessions. - Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu + Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -78,13 +78,6 @@ #define SN_FINST_SHIFT 16 /* bit shift */ /* unused: 0x00080000 */ -/* Note: those flags must move to another place */ -#define SN_STAT_HIDEDWN 0x00100000 /* hide 'down' servers in the stats page */ -#define SN_STAT_NORFRSH 0x00200000 /* do not automatically refresh the stats page */ -#define SN_STAT_FMTCSV 0x00400000 /* dump the stats in CSV format instead of HTML */ -#define SN_STAT_BOUND 0x00800000 /* bound statistics to selected proxies/types/services */ - - /* WARNING: if new fields are added, they must be initialized in event_accept() * and freed in session_free() ! */ @@ -127,6 +120,7 @@ struct session { struct proxy *px; struct server *sv; short px_st, sv_st; /* DATA_ST_INIT or DATA_ST_DATA */ + unsigned int flags; /* STAT_* */ int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */ } stats; } data_ctx; /* used by produce_content to dump the stats right now */ diff --git a/src/dumpstats.c b/src/dumpstats.c index 99ac770f6..a9fb84dc7 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -177,12 +177,12 @@ int print_csv_header(struct chunk *msg, int size) /* * Produces statistics data for the session . Expects to be called with - * s->cli_state == CL_STSHUTR. It *may* make use of informations from - * and . + * s->cli_state == CL_STSHUTR. It *may* make use of informations from . + * 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; @@ -202,7 +202,7 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags) /* 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; @@ -213,8 +213,7 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags) 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" @@ -256,13 +255,13 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags) 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; @@ -293,10 +292,11 @@ int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags) * 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; @@ -316,9 +316,9 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) "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); @@ -344,7 +344,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) /* 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), "Statistics Report for " PRODUCT_NAME "\n" @@ -435,7 +435,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) * 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), "

" PRODUCT_NAME "%s

\n" @@ -480,39 +480,39 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) actconn ); - if (s->flags & SN_STAT_HIDEDWN) + if (s->data_ctx.stats.flags & STAT_HIDE_DOWN) chunk_printf(&msg, sizeof(trash), "
  • Show all servers
    \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), "
  • Hide 'DOWN' servers
    \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), "
  • Enable refresh
    \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), "
  • Disable refresh
    \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), "
  • Refresh now
    \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), "
  • CSV export
    \n", @@ -536,8 +536,6 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) 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; @@ -549,7 +547,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) 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; @@ -561,7 +559,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) /* 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), "\n"); if (buffer_write_chunk(rep, &msg) != 0) return 0; @@ -587,7 +585,7 @@ int stats_dump_http(struct session *s, struct uri_auth *uri, int flags) * 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 */ @@ -624,7 +622,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, 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; @@ -632,7 +630,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, /* 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), "\n" @@ -667,8 +665,9 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, 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 */ "" @@ -742,7 +741,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, 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; @@ -773,13 +772,13 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, 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", @@ -952,8 +951,9 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, 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 */ "" @@ -1047,7 +1047,7 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, /* 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), "
    Frontend
    Backend

    \n"); if (buffer_write_chunk(rep, &msg) != 0) diff --git a/src/proto_http.c b/src/proto_http.c index d83e601c8..ba79c2b6c 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3557,8 +3557,7 @@ int produce_content(struct session *s) } 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 */ @@ -4828,6 +4827,8 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) 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; @@ -4841,7 +4842,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) 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++; @@ -4851,7 +4852,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) 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++; @@ -4861,12 +4862,14 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) 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. */ @@ -4925,7 +4928,7 @@ int stats_check_uri_auth(struct session *t, struct proxy *backend) 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; diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 76709c137..556649c35 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -1356,38 +1356,39 @@ void process_uxst_session(struct task *t, struct timeval *next) /* 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 the task's expiration date. + * for now. It only knows states SV_STIDLE, SV_STCONN, SV_STDATA, and + * SV_STCLOSE. Returns in 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); @@ -1422,28 +1423,24 @@ void process_uxst_stats(struct task *t, struct timeval *next) 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; } } @@ -1451,16 +1448,21 @@ void process_uxst_stats(struct task *t, struct timeval *next) 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);