From 9186126e1cd2e68da575d285fe07a730f0459bb0 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 17 Oct 2007 17:06:05 +0200 Subject: [PATCH] [MEDIUM] moved stats and buffer generic functions to new files Neither the primitives used to write data to a buffer, nor the stats dump functions are HTTP-specific anymore. Move them to dedicated files --- Makefile | 1 + Makefile.bsd | 1 + Makefile.osx | 1 + include/proto/dumpstats.h | 41 +++ include/proto/senddata.h | 40 +++ src/dumpstats.c | 591 ++++++++++++++++++++++++++++++++++++ src/proto_http.c | 608 +------------------------------------- src/senddata.c | 88 ++++++ 8 files changed, 778 insertions(+), 593 deletions(-) create mode 100644 include/proto/dumpstats.h create mode 100644 include/proto/senddata.h create mode 100644 src/dumpstats.c create mode 100644 src/senddata.c diff --git a/Makefile b/Makefile index 7a0a0774ca..e0aca9b349 100644 --- a/Makefile +++ b/Makefile @@ -231,6 +231,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \ src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \ src/checks.o src/queue.o src/client.o src/proxy.o src/proto_uxst.o \ src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \ + src/senddata.o src/dumpstats.o \ src/session.o src/hdr_idx.o src/ev_select.o src/acl.o src/memory.o haproxy: $(OBJS) $(OPT_OBJS) diff --git a/Makefile.bsd b/Makefile.bsd index cf0269432f..6f1ad2bc63 100644 --- a/Makefile.bsd +++ b/Makefile.bsd @@ -103,6 +103,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \ src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \ src/checks.o src/queue.o src/client.o src/proxy.o src/proto_uxst.o \ src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \ + src/senddata.o src/dumpstats.o \ src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o \ src/ev_kqueue.o src/acl.o src/memory.o diff --git a/Makefile.osx b/Makefile.osx index a1a6c470f3..a92e857fd4 100644 --- a/Makefile.osx +++ b/Makefile.osx @@ -100,6 +100,7 @@ OBJS = src/haproxy.o src/sessionhash.o src/base64.o src/protocols.o \ src/time.o src/fd.o src/regex.o src/cfgparse.o src/server.o \ src/checks.o src/queue.o src/client.o src/proxy.o src/proto_uxst.o \ src/proto_http.o src/stream_sock.o src/appsession.o src/backend.o \ + src/senddata.o src/dumpstats.o \ src/session.o src/hdr_idx.o src/ev_select.o src/ev_poll.o src/acl.o \ src/memory.o diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h new file mode 100644 index 0000000000..3fc14e7554 --- /dev/null +++ b/include/proto/dumpstats.h @@ -0,0 +1,41 @@ +/* + include/proto/dumpstats.h + This file contains definitions of some primitives to dedicated to + statistics output. + + Copyright (C) 2000-2007 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 + License as published by the Free Software Foundation, version 2.1 + exclusively. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _PROTO_DUMPSTATS_H +#define _PROTO_DUMPSTATS_H + +#include +#include +#include + +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); + + +#endif /* _PROTO_DUMPSTATS_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/include/proto/senddata.h b/include/proto/senddata.h new file mode 100644 index 0000000000..5cb5b6441b --- /dev/null +++ b/include/proto/senddata.h @@ -0,0 +1,40 @@ +/* + include/proto/senddata.h + This file contains definitions of some primitives to send data to + clients over a socket and a buffer. + + Copyright (C) 2000-2007 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 + License as published by the Free Software Foundation, version 2.1 + exclusively. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef _PROTO_SENDDATA_H +#define _PROTO_SENDDATA_H + +#include +#include +#include + +void client_retnclose(struct session *s, const struct chunk *msg); +void client_return(struct session *s, const struct chunk *msg); + +#endif /* _PROTO_SENDDATA_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/src/dumpstats.c b/src/dumpstats.c new file mode 100644 index 0000000000..4e2929c591 --- /dev/null +++ b/src/dumpstats.c @@ -0,0 +1,591 @@ +/* + * Functions dedicated to statistics output + * + * Copyright 2000-2007 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * Produces statistics data for the session . Expects to be called with + * 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. + * 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) +{ + struct buffer *rep = s->rep; + struct proxy *px; + struct chunk msg; + unsigned int up; + + msg.len = 0; + msg.str = trash; + + switch (s->data_state) { + case DATA_ST_INIT: + /* the function had not been called yet */ + s->flags |= SN_SELF_GEN; // more data will follow + + chunk_printf(&msg, sizeof(trash), + "HTTP/1.0 200 OK\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "Content-Type: text/html\r\n"); + + if (uri->refresh > 0 && !(s->flags & SN_STAT_NORFRSH)) + chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n", + uri->refresh); + + chunk_printf(&msg, sizeof(trash), "\r\n"); + + s->txn.status = 200; + client_retnclose(s, &msg); // send the start of the response. + msg.len = 0; + + if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is + s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_R; + + if (s->txn.meth == HTTP_METH_HEAD) { + /* that's all we return in case of HEAD request */ + s->data_state = DATA_ST_FIN; + s->flags &= ~SN_SELF_GEN; + return 1; + } + + s->data_state = DATA_ST_HEAD; /* let's start producing data */ + /* fall through */ + + case DATA_ST_HEAD: + /* WARNING! This must fit in the first buffer !!! */ + chunk_printf(&msg, sizeof(trash), + "Statistics Report for " PRODUCT_NAME "\n" + "\n" + "\n"); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_state = DATA_ST_INFO; + /* fall through */ + + case DATA_ST_INFO: + up = (now.tv_sec - start_date.tv_sec); + + /* WARNING! this has to fit the first packet too. + * We are around 3.5 kB, add adding entries will + * become tricky if we want to support 4kB buffers ! + */ + chunk_printf(&msg, sizeof(trash), + "

" + PRODUCT_NAME "%s

\n" + "

Statistics Report for pid %d

\n" + "
\n" + "

> General process information

\n" + "" + "" + "" + "
\n" + "

pid = %d (nbproc = %d)
\n" + "uptime = %dd %dh%02dm%02ds
\n" + "system limits : memmax = %s%s ; ulimit-n = %d
\n" + "maxsock = %d
\n" + "maxconn = %d (current conns = %d)
\n" + "

\n" + "\n" + "" + "" + "\n" + "" + "" + "\n" + "" + "" + "\n" + "" + "" + "
 active UP  backup UP
active UP, going down backup UP, going down
active DOWN, going up backup DOWN, going up
active or backup DOWN  not checked
\n" + "
" + "Display option:
    " + "", + (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING), + pid, pid, global.nbproc, + up / 86400, (up % 86400) / 3600, + (up % 3600) / 60, (up % 60), + global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited", + global.rlimit_memmax ? " MB" : "", + global.rlimit_nofile, + global.maxsock, + global.maxconn, + actconn + ); + + if (s->flags & SN_STAT_HIDEDWN) + chunk_printf(&msg, sizeof(trash), + "
  • Show all servers
    \n", + uri->uri_prefix, + "", + (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : ""); + else + chunk_printf(&msg, sizeof(trash), + "
  • Hide 'DOWN' servers
    \n", + uri->uri_prefix, + ";up", + (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : ""); + + if (uri->refresh > 0) { + if (s->flags & SN_STAT_NORFRSH) + chunk_printf(&msg, sizeof(trash), + "
  • Enable refresh
    \n", + uri->uri_prefix, + (s->flags & SN_STAT_HIDEDWN) ? ";up" : "", + ""); + else + chunk_printf(&msg, sizeof(trash), + "
  • Disable refresh
    \n", + uri->uri_prefix, + (s->flags & SN_STAT_HIDEDWN) ? ";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" : ""); + + chunk_printf(&msg, sizeof(trash), + "
" + "External ressources:" + "
\n" + "" + ); + + if (buffer_write_chunk(rep, &msg) != 0) + 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; + /* fall through */ + + case DATA_ST_LIST: + /* dump proxies */ + 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, uri, flags) == 0) + return 0; + + s->data_ctx.stats.px = px->next; + s->data_ctx.stats.px_st = DATA_ST_PX_INIT; + } + /* here, we just have reached the last proxy */ + + s->data_state = DATA_ST_END; + /* fall through */ + + case DATA_ST_END: + chunk_printf(&msg, sizeof(trash), "\n"); + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_state = DATA_ST_FIN; + /* fall through */ + + case DATA_ST_FIN: + s->flags &= ~SN_SELF_GEN; + return 1; + + default: + /* unknown state ! */ + s->flags &= ~SN_SELF_GEN; + return -1; + } +} + + +/* + * Dumps statistics for a proxy. + * 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) +{ + struct buffer *rep = s->rep; + struct server *sv; + struct chunk msg; + + msg.len = 0; + msg.str = trash; + + switch (s->data_ctx.stats.px_st) { + case DATA_ST_PX_INIT: + /* we are on a new proxy */ + + if (uri && uri->scope) { + /* we have a limited scope, we have to check the proxy name */ + struct stat_scope *scope; + int len; + + len = strlen(px->id); + scope = uri->scope; + + while (scope) { + /* match exact proxy name */ + if (scope->px_len == len && !memcmp(px->id, scope->px_id, len)) + break; + + /* match '.' which means 'self' proxy */ + if (!strcmp(scope->px_id, ".") && px == s->fe) + break; + scope = scope->next; + } + + /* proxy name not found : don't dump anything */ + if (scope == NULL) + return 1; + } + + s->data_ctx.stats.px_st = DATA_ST_PX_TH; + /* fall through */ + + case DATA_ST_PX_TH: + /* print a new table */ + chunk_printf(&msg, sizeof(trash), + "\n" + "" + "" + "" + "\n" + "" + "" + "" + "" + "" + "\n" + "" + "" + "" + "" + "" + "\n" + "", + px->id); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_ctx.stats.px_st = DATA_ST_PX_FE; + /* fall through */ + + case DATA_ST_PX_FE: + /* print the frontend */ + if (px->cap & PR_CAP_FE) { + chunk_printf(&msg, sizeof(trash), + /* name, queue */ + "" + /* sessions : current, max, limit, cumul. */ + "" + /* bytes : in, out */ + "" + /* denied: req, resp */ + "" + /* errors : request, connect, response */ + "" + /* server status : reflect backend status */ + "" + /* rest of server: nothing */ + "" + "", + px->feconn, px->feconn_max, px->maxconn, px->cum_feconn, + px->bytes_in, px->bytes_out, + px->denied_req, px->denied_resp, + px->failed_req, + px->state == PR_STRUN ? "OPEN" : + px->state == PR_STIDLE ? "FULL" : "STOP"); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + } + + s->data_ctx.stats.sv = px->srv; /* may be NULL */ + s->data_ctx.stats.px_st = DATA_ST_PX_SV; + /* fall through */ + + case DATA_ST_PX_SV: + /* stats.sv has been initialized above */ + while (s->data_ctx.stats.sv != NULL) { + static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d ↑", "UP %d/%d ↓", "UP", "no check" }; + int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4=unchecked */ + + sv = s->data_ctx.stats.sv; + + /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ + if (!(sv->state & SRV_CHECKED)) + sv_state = 4; + else if (sv->state & SRV_RUNNING) + if (sv->health == sv->rise + sv->fall - 1) + sv_state = 3; /* UP */ + else + sv_state = 2; /* going down */ + else + if (sv->health) + sv_state = 1; /* going up */ + else + sv_state = 0; /* DOWN */ + + if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) { + /* do not report servers which are DOWN */ + s->data_ctx.stats.sv = sv->next; + continue; + } + + chunk_printf(&msg, sizeof(trash), + /* name */ + "" + /* queue : current, max */ + "" + /* sessions : current, max, limit, cumul */ + "" + /* bytes : in, out */ + "" + /* denied: req, resp */ + "" + /* errors : request, connect, response */ + "\n" + "", + (sv->state & SRV_BACKUP) ? "backup" : "active", + sv_state, sv->id, + sv->nbpend, sv->nbpend_max, + sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess, + sv->bytes_in, sv->bytes_out, + sv->failed_secu, + sv->failed_conns, sv->failed_resp); + + /* status */ + chunk_printf(&msg, sizeof(trash), "" + /* act, bck */ + "" + "", + sv->uweight, + (sv->state & SRV_BACKUP) ? "-" : "Y", + (sv->state & SRV_BACKUP) ? "Y" : "-"); + + /* check failures : unique, fatal */ + if (sv->state & SRV_CHECKED) + chunk_printf(&msg, sizeof(trash), + "\n", + sv->failed_checks, sv->down_trans); + else + chunk_printf(&msg, sizeof(trash), + "\n"); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_ctx.stats.sv = sv->next; + } /* while sv */ + + s->data_ctx.stats.px_st = DATA_ST_PX_BE; + /* fall through */ + + case DATA_ST_PX_BE: + /* print the backend */ + if (px->cap & PR_CAP_BE) { + int gcd = 1; + + if (px->map_state & PR_MAP_RECALC) + recalc_server_map(px); + + /* The GCD which was computed causes the total effective + * weight to appear lower than all weights. Let's + * recompute it. + */ + if (px->srv && px->srv->eweight) + gcd = px->srv->uweight / px->srv->eweight; + + chunk_printf(&msg, sizeof(trash), + /* name */ + "" + /* queue : current, max */ + "" + /* sessions : current, max, limit, cumul. */ + "" + /* bytes : in, out */ + "" + /* denied: req, resp */ + "" + /* errors : request, connect, response */ + "\n" + /* server status : reflect backend status (up/down) : we display UP + * if the backend has known working servers or if it has no server at + * all (eg: for stats). Tthen we display the total weight, number of + * active and backups. */ + "" + "" + /* rest of server: nothing */ + "" + "", + px->nbpend /* or px->totpend ? */, px->nbpend_max, + px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, + px->bytes_in, px->bytes_out, + px->denied_req, px->denied_resp, + px->failed_conns, px->failed_resp, + (px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN", + px->srv_map_sz * gcd, px->srv_act, px->srv_bck); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + } + + s->data_ctx.stats.px_st = DATA_ST_PX_END; + /* fall through */ + + case DATA_ST_PX_END: + chunk_printf(&msg, sizeof(trash), "
%s
QueueSessionsBytesDeniedErrorsServer
CurMaxCurMaxLimitCumulInOutReqRespReqConnRespStatusWeightActBckCheckDown
Frontend%d%d%d%d%lld%lld%d%d%d%s
%s%d%d%d%d%s%d%lld%lld%d%d%d"); + chunk_printf(&msg, sizeof(trash), + srv_hlt_st[sv_state], + (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), + (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); + + chunk_printf(&msg, sizeof(trash), + /* weight */ + "%d%s%s%d%d
Backend%d%d%d%d%d%d%lld%lld%d%d%d%d%s%d%d%d

\n"); + + if (buffer_write_chunk(rep, &msg) != 0) + return 0; + + s->data_ctx.stats.px_st = DATA_ST_PX_FIN; + /* fall through */ + + case DATA_ST_PX_FIN: + return 1; + + default: + /* unknown state, we should put an abort() here ! */ + return 1; + } +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/src/proto_http.c b/src/proto_http.c index c218e6bb96..82b8cea968 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -46,11 +46,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -533,44 +535,6 @@ int http_find_header(const char *name, return http_find_header2(name, strlen(name), sol, idx, ctx); } -/* - * returns a message to the client ; the connection is shut down for read, - * and the request is cleared so that no server connection can be initiated. - * The client must be in a valid state for this (HEADER, DATA ...). - * Nothing is performed on the server side. The message is contained in a - * "chunk". If it is null, then an empty message is used. - * The reply buffer doesn't need to be empty before this. - */ -void client_retnclose(struct session *s, const struct chunk *msg) -{ - EV_FD_CLR(s->cli_fd, DIR_RD); - EV_FD_SET(s->cli_fd, DIR_WR); - buffer_shutr(s->req); - if (!tv_add_ifset(&s->rep->wex, &now, &s->fe->clitimeout)) - tv_eternity(&s->rep->wex); - s->cli_state = CL_STSHUTR; - buffer_flush(s->rep); - if (msg->len) - buffer_write(s->rep, msg->str, msg->len); - s->req->l = 0; -} - - -/* - * returns a message into the rep buffer, and flushes the req buffer. - * The reply buffer doesn't need to be empty before this. The message - * is contained in a "chunk". If it is null, then an empty message is - * used. - */ -void client_return(struct session *s, const struct chunk *msg) -{ - buffer_flush(s->rep); - if (msg->len) - buffer_write(s->rep, msg->str, msg->len); - s->req->l = 0; -} - - /* This function turns the server state into the SV_STCLOSE, and sets * indicators accordingly. Note that if is 0, or if the message * pointer is NULL, then no message is returned. @@ -3423,563 +3387,21 @@ int produce_content(struct session *s) } else if (s->data_source == DATA_SRC_STATS) { /* dump server statistics */ - return produce_content_stats(s); + int ret = stats_dump_http(s, s->be->uri_auth, 0); + if (ret >= 0) + return ret; + /* -1 indicates an error */ } - else { - /* unknown data source */ - s->txn.status = 500; - client_retnclose(s, error_message(s, HTTP_ERR_500)); - if (!(s->flags & SN_ERR_MASK)) - s->flags |= SN_ERR_PRXCOND; - if (!(s->flags & SN_FINST_MASK)) - s->flags |= SN_FINST_R; - s->flags &= ~SN_SELF_GEN; - return 1; - } -} - -/* - * Produces statistics data for the session . Expects to be called with - * 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. - * It returns 1 if it changes the session state from CL_STSHUTR, otherwise 0. - */ -int produce_content_stats(struct session *s) -{ - struct buffer *rep = s->rep; - struct proxy *px; - struct chunk msg; - unsigned int up; - - msg.len = 0; - msg.str = trash; - - switch (s->data_state) { - case DATA_ST_INIT: - /* the function had not been called yet */ - s->flags |= SN_SELF_GEN; // more data will follow - - chunk_printf(&msg, sizeof(trash), - "HTTP/1.0 200 OK\r\n" - "Cache-Control: no-cache\r\n" - "Connection: close\r\n" - "Content-Type: text/html\r\n"); - - if (s->be->uri_auth->refresh > 0 && !(s->flags & SN_STAT_NORFRSH)) - chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n", - s->be->uri_auth->refresh); - - chunk_printf(&msg, sizeof(trash), "\r\n"); - - s->txn.status = 200; - client_retnclose(s, &msg); // send the start of the response. - msg.len = 0; - - if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is - s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy - if (!(s->flags & SN_FINST_MASK)) - s->flags |= SN_FINST_R; - - if (s->txn.meth == HTTP_METH_HEAD) { - /* that's all we return in case of HEAD request */ - s->data_state = DATA_ST_FIN; - s->flags &= ~SN_SELF_GEN; - return 1; - } - - s->data_state = DATA_ST_HEAD; /* let's start producing data */ - /* fall through */ - - case DATA_ST_HEAD: - /* WARNING! This must fit in the first buffer !!! */ - chunk_printf(&msg, sizeof(trash), - "Statistics Report for " PRODUCT_NAME "\n" - "\n" - "\n"); - - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - - s->data_state = DATA_ST_INFO; - /* fall through */ - - case DATA_ST_INFO: - up = (now.tv_sec - start_date.tv_sec); - - /* WARNING! this has to fit the first packet too. - * We are around 3.5 kB, add adding entries will - * become tricky if we want to support 4kB buffers ! - */ - chunk_printf(&msg, sizeof(trash), - "

" - PRODUCT_NAME "%s

\n" - "

Statistics Report for pid %d

\n" - "
\n" - "

> General process information

\n" - "" - "" - "" - "
\n" - "

pid = %d (nbproc = %d)
\n" - "uptime = %dd %dh%02dm%02ds
\n" - "system limits : memmax = %s%s ; ulimit-n = %d
\n" - "maxsock = %d
\n" - "maxconn = %d (current conns = %d)
\n" - "

\n" - "\n" - "" - "" - "\n" - "" - "" - "\n" - "" - "" - "\n" - "" - "" - "
 active UP  backup UP
active UP, going down backup UP, going down
active DOWN, going up backup DOWN, going up
active or backup DOWN  not checked
\n" - "
" - "Display option:
    " - "", - (s->be->uri_auth->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING), - pid, pid, global.nbproc, - up / 86400, (up % 86400) / 3600, - (up % 3600) / 60, (up % 60), - global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited", - global.rlimit_memmax ? " MB" : "", - global.rlimit_nofile, - global.maxsock, - global.maxconn, - actconn - ); - - if (s->flags & SN_STAT_HIDEDWN) - chunk_printf(&msg, sizeof(trash), - "
  • Show all servers
    \n", - s->be->uri_auth->uri_prefix, - "", - (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : ""); - else - chunk_printf(&msg, sizeof(trash), - "
  • Hide 'DOWN' servers
    \n", - s->be->uri_auth->uri_prefix, - ";up", - (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : ""); - - if (s->be->uri_auth->refresh > 0) { - if (s->flags & SN_STAT_NORFRSH) - chunk_printf(&msg, sizeof(trash), - "
  • Enable refresh
    \n", - s->be->uri_auth->uri_prefix, - (s->flags & SN_STAT_HIDEDWN) ? ";up" : "", - ""); - else - chunk_printf(&msg, sizeof(trash), - "
  • Disable refresh
    \n", - s->be->uri_auth->uri_prefix, - (s->flags & SN_STAT_HIDEDWN) ? ";up" : "", - ";norefresh"); - } - - chunk_printf(&msg, sizeof(trash), - "
  • Refresh now
    \n", - s->be->uri_auth->uri_prefix, - (s->flags & SN_STAT_HIDEDWN) ? ";up" : "", - (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : ""); - - chunk_printf(&msg, sizeof(trash), - "
" - "External ressources:" - "
\n" - "" - ); - - if (buffer_write_chunk(rep, &msg) != 0) - 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; - /* fall through */ - - case DATA_ST_LIST: - /* dump proxies */ - 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 (produce_content_stats_proxy(s, px) == 0) - return 0; - - s->data_ctx.stats.px = px->next; - s->data_ctx.stats.px_st = DATA_ST_PX_INIT; - } - /* here, we just have reached the last proxy */ - - s->data_state = DATA_ST_END; - /* fall through */ - - case DATA_ST_END: - chunk_printf(&msg, sizeof(trash), "\n"); - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - - s->data_state = DATA_ST_FIN; - /* fall through */ - - case DATA_ST_FIN: - s->flags &= ~SN_SELF_GEN; - return 1; - - default: - /* unknown state ! */ - s->txn.status = 500; - client_retnclose(s, error_message(s, HTTP_ERR_500)); - if (!(s->flags & SN_ERR_MASK)) - s->flags |= SN_ERR_PRXCOND; - if (!(s->flags & SN_FINST_MASK)) - s->flags |= SN_FINST_R; - s->flags &= ~SN_SELF_GEN; - return 1; - } -} - - -/* - * Dumps statistics for a proxy. - * Returns 0 if it had to stop dumping data because of lack of buffer space, - * ot non-zero if everything completed. - */ -int produce_content_stats_proxy(struct session *s, struct proxy *px) -{ - struct buffer *rep = s->rep; - struct server *sv; - struct chunk msg; - - msg.len = 0; - msg.str = trash; - - switch (s->data_ctx.stats.px_st) { - case DATA_ST_PX_INIT: - /* we are on a new proxy */ - - if (s->be->uri_auth && s->be->uri_auth->scope) { - /* we have a limited scope, we have to check the proxy name */ - struct stat_scope *scope; - int len; - - len = strlen(px->id); - scope = s->be->uri_auth->scope; - - while (scope) { - /* match exact proxy name */ - if (scope->px_len == len && !memcmp(px->id, scope->px_id, len)) - break; - - /* match '.' which means 'self' proxy */ - if (!strcmp(scope->px_id, ".") && px == s->fe) - break; - scope = scope->next; - } - - /* proxy name not found : don't dump anything */ - if (scope == NULL) - return 1; - } - - s->data_ctx.stats.px_st = DATA_ST_PX_TH; - /* fall through */ - - case DATA_ST_PX_TH: - /* print a new table */ - chunk_printf(&msg, sizeof(trash), - "\n" - "" - "" - "" - "\n" - "" - "" - "" - "" - "" - "\n" - "" - "" - "" - "" - "" - "\n" - "", - px->id); - - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - - s->data_ctx.stats.px_st = DATA_ST_PX_FE; - /* fall through */ - - case DATA_ST_PX_FE: - /* print the frontend */ - if (px->cap & PR_CAP_FE) { - chunk_printf(&msg, sizeof(trash), - /* name, queue */ - "" - /* sessions : current, max, limit, cumul. */ - "" - /* bytes : in, out */ - "" - /* denied: req, resp */ - "" - /* errors : request, connect, response */ - "" - /* server status : reflect backend status */ - "" - /* rest of server: nothing */ - "" - "", - px->feconn, px->feconn_max, px->maxconn, px->cum_feconn, - px->bytes_in, px->bytes_out, - px->denied_req, px->denied_resp, - px->failed_req, - px->state == PR_STRUN ? "OPEN" : - px->state == PR_STIDLE ? "FULL" : "STOP"); - - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - } - - s->data_ctx.stats.sv = px->srv; /* may be NULL */ - s->data_ctx.stats.px_st = DATA_ST_PX_SV; - /* fall through */ - - case DATA_ST_PX_SV: - /* stats.sv has been initialized above */ - while (s->data_ctx.stats.sv != NULL) { - static char *srv_hlt_st[5] = { "DOWN", "DN %d/%d ↑", "UP %d/%d ↓", "UP", "no check" }; - int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4=unchecked */ - - sv = s->data_ctx.stats.sv; - - /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */ - if (!(sv->state & SRV_CHECKED)) - sv_state = 4; - else if (sv->state & SRV_RUNNING) - if (sv->health == sv->rise + sv->fall - 1) - sv_state = 3; /* UP */ - else - sv_state = 2; /* going down */ - else - if (sv->health) - sv_state = 1; /* going up */ - else - sv_state = 0; /* DOWN */ - - if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) { - /* do not report servers which are DOWN */ - s->data_ctx.stats.sv = sv->next; - continue; - } - - chunk_printf(&msg, sizeof(trash), - /* name */ - "" - /* queue : current, max */ - "" - /* sessions : current, max, limit, cumul */ - "" - /* bytes : in, out */ - "" - /* denied: req, resp */ - "" - /* errors : request, connect, response */ - "\n" - "", - (sv->state & SRV_BACKUP) ? "backup" : "active", - sv_state, sv->id, - sv->nbpend, sv->nbpend_max, - sv->cur_sess, sv->cur_sess_max, sv->maxconn ? ultoa(sv->maxconn) : "-", sv->cum_sess, - sv->bytes_in, sv->bytes_out, - sv->failed_secu, - sv->failed_conns, sv->failed_resp); - - /* status */ - chunk_printf(&msg, sizeof(trash), "" - /* act, bck */ - "" - "", - sv->uweight, - (sv->state & SRV_BACKUP) ? "-" : "Y", - (sv->state & SRV_BACKUP) ? "Y" : "-"); - - /* check failures : unique, fatal */ - if (sv->state & SRV_CHECKED) - chunk_printf(&msg, sizeof(trash), - "\n", - sv->failed_checks, sv->down_trans); - else - chunk_printf(&msg, sizeof(trash), - "\n"); - - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - - s->data_ctx.stats.sv = sv->next; - } /* while sv */ - - s->data_ctx.stats.px_st = DATA_ST_PX_BE; - /* fall through */ - - case DATA_ST_PX_BE: - /* print the backend */ - if (px->cap & PR_CAP_BE) { - int gcd = 1; - - if (px->map_state & PR_MAP_RECALC) - recalc_server_map(px); - - /* The GCD which was computed causes the total effective - * weight to appear lower than all weights. Let's - * recompute it. - */ - if (px->srv && px->srv->eweight) - gcd = px->srv->uweight / px->srv->eweight; - - chunk_printf(&msg, sizeof(trash), - /* name */ - "" - /* queue : current, max */ - "" - /* sessions : current, max, limit, cumul. */ - "" - /* bytes : in, out */ - "" - /* denied: req, resp */ - "" - /* errors : request, connect, response */ - "\n" - /* server status : reflect backend status (up/down) : we display UP - * if the backend has known working servers or if it has no server at - * all (eg: for stats). Tthen we display the total weight, number of - * active and backups. */ - "" - "" - /* rest of server: nothing */ - "" - "", - px->nbpend /* or px->totpend ? */, px->nbpend_max, - px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, - px->bytes_in, px->bytes_out, - px->denied_req, px->denied_resp, - px->failed_conns, px->failed_resp, - (px->srv_map_sz > 0 || !px->srv) ? "UP" : "DOWN", - px->srv_map_sz * gcd, px->srv_act, px->srv_bck); - - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - } - - s->data_ctx.stats.px_st = DATA_ST_PX_END; - /* fall through */ - - case DATA_ST_PX_END: - chunk_printf(&msg, sizeof(trash), "
%s
QueueSessionsBytesDeniedErrorsServer
CurMaxCurMaxLimitCumulInOutReqRespReqConnRespStatusWeightActBckCheckDown
Frontend%d%d%d%d%lld%lld%d%d%d%s
%s%d%d%d%d%s%d%lld%lld%d%d%d"); - chunk_printf(&msg, sizeof(trash), - srv_hlt_st[sv_state], - (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health), - (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise)); - - chunk_printf(&msg, sizeof(trash), - /* weight */ - "%d%s%s%d%d
Backend%d%d%d%d%d%d%lld%lld%d%d%d%d%s%d%d%d

\n"); - - if (buffer_write_chunk(rep, &msg) != 0) - return 0; - - s->data_ctx.stats.px_st = DATA_ST_PX_FIN; - /* fall through */ - - case DATA_ST_PX_FIN: - return 1; - - default: - /* unknown state, we should put an abort() here ! */ - return 1; - } + /* unknown data source or internal error */ + s->txn.status = 500; + client_retnclose(s, error_message(s, HTTP_ERR_500)); + if (!(s->flags & SN_ERR_MASK)) + s->flags |= SN_ERR_PRXCOND; + if (!(s->flags & SN_FINST_MASK)) + s->flags |= SN_FINST_R; + s->flags &= ~SN_SELF_GEN; + return 1; } diff --git a/src/senddata.c b/src/senddata.c new file mode 100644 index 0000000000..e81a2de87e --- /dev/null +++ b/src/senddata.c @@ -0,0 +1,88 @@ +/* + * Helper functions to send data over a socket and buffer. + * Should probably move somewhere else, but where ? + * + * Copyright 2000-2007 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * returns a message to the client ; the connection is shut down for read, + * and the request is cleared so that no server connection can be initiated. + * The client must be in a valid state for this (HEADER, DATA ...). + * Nothing is performed on the server side. The message is contained in a + * "chunk". If it is null, then an empty message is used. + * The reply buffer doesn't need to be empty before this. + */ +void client_retnclose(struct session *s, const struct chunk *msg) +{ + EV_FD_CLR(s->cli_fd, DIR_RD); + EV_FD_SET(s->cli_fd, DIR_WR); + buffer_shutr(s->req); + if (!tv_add_ifset(&s->rep->wex, &now, &s->rep->wto)) + tv_eternity(&s->rep->wex); + s->cli_state = CL_STSHUTR; + buffer_flush(s->rep); + if (msg && msg->len) + buffer_write(s->rep, msg->str, msg->len); + s->req->l = 0; +} + + +/* + * returns a message into the rep buffer, and flushes the req buffer. + * The reply buffer doesn't need to be empty before this. The message + * is contained in a "chunk". If it is null, then an empty message is + * used. + */ +void client_return(struct session *s, const struct chunk *msg) +{ + buffer_flush(s->rep); + if (msg && msg->len) + buffer_write(s->rep, msg->str, msg->len); + s->req->l = 0; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */