[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
This commit is contained in:
parent
e6ad2b165e
commit
9186126e1c
1
Makefile
1
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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 <common/config.h>
|
||||
#include <types/buffers.h>
|
||||
#include <types/session.h>
|
||||
|
||||
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:
|
||||
*/
|
|
@ -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 <common/config.h>
|
||||
#include <types/buffers.h>
|
||||
#include <types/session.h>
|
||||
|
||||
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:
|
||||
*/
|
|
@ -0,0 +1,591 @@
|
|||
/*
|
||||
* Functions dedicated to statistics output
|
||||
*
|
||||
* Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/mini-clist.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/time.h>
|
||||
#include <common/uri_auth.h>
|
||||
#include <common/version.h>
|
||||
|
||||
#include <types/client.h>
|
||||
#include <types/global.h>
|
||||
#include <types/polling.h>
|
||||
#include <types/proxy.h>
|
||||
#include <types/server.h>
|
||||
|
||||
#include <proto/backend.h>
|
||||
#include <proto/buffers.h>
|
||||
#include <proto/dumpstats.h>
|
||||
#include <proto/fd.h>
|
||||
#include <proto/senddata.h>
|
||||
#include <proto/session.h>
|
||||
|
||||
/*
|
||||
* Produces statistics data for the session <s>. 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),
|
||||
"<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
|
||||
"<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
|
||||
"<style type=\"text/css\"><!--\n"
|
||||
"body {"
|
||||
" font-family: helvetica, arial;"
|
||||
" font-size: 12px;"
|
||||
" font-weight: normal;"
|
||||
" color: black;"
|
||||
" background: white;"
|
||||
"}\n"
|
||||
"th,td {"
|
||||
" font-size: 0.8em;"
|
||||
" align: center;"
|
||||
"}\n"
|
||||
"h1 {"
|
||||
" font-size: xx-large;"
|
||||
" margin-bottom: 0.5em;"
|
||||
"}\n"
|
||||
"h2 {"
|
||||
" font-family: helvetica, arial;"
|
||||
" font-size: x-large;"
|
||||
" font-weight: bold;"
|
||||
" font-style: italic;"
|
||||
" color: #6020a0;"
|
||||
" margin-top: 0em;"
|
||||
" margin-bottom: 0em;"
|
||||
"}\n"
|
||||
"h3 {"
|
||||
" font-family: helvetica, arial;"
|
||||
" font-size: 16px;"
|
||||
" font-weight: bold;"
|
||||
" color: #b00040;"
|
||||
" background: #e8e8d0;"
|
||||
" margin-top: 0em;"
|
||||
" margin-bottom: 0em;"
|
||||
"}\n"
|
||||
"li {"
|
||||
" margin-top: 0.25em;"
|
||||
" margin-right: 2em;"
|
||||
"}\n"
|
||||
".hr {margin-top: 0.25em;"
|
||||
" border-color: black;"
|
||||
" border-bottom-style: solid;"
|
||||
"}\n"
|
||||
".pxname {background: #b00040;color: #ffff40;font-weight: bold;}\n"
|
||||
".titre {background: #20D0D0;color: #000000;font-weight: bold;}\n"
|
||||
".total {background: #20D0D0;color: #ffff80;}\n"
|
||||
".frontend {background: #e8e8d0;}\n"
|
||||
".backend {background: #e8e8d0;}\n"
|
||||
".active0 {background: #ff9090;}\n"
|
||||
".active1 {background: #ffd020;}\n"
|
||||
".active2 {background: #ffffa0;}\n"
|
||||
".active3 {background: #c0ffc0;}\n"
|
||||
".active4 {background: #e0e0e0;}\n"
|
||||
".backup0 {background: #ff9090;}\n"
|
||||
".backup1 {background: #ff80ff;}\n"
|
||||
".backup2 {background: #c060ff;}\n"
|
||||
".backup3 {background: #b0d0ff;}\n"
|
||||
".backup4 {background: #e0e0e0;}\n"
|
||||
"table.tbl { border-collapse: collapse; border-style: none;}\n"
|
||||
"table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
|
||||
"table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
|
||||
"table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
|
||||
"table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
|
||||
"table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
|
||||
"table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
|
||||
"-->\n"
|
||||
"</style></head>\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),
|
||||
"<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
|
||||
PRODUCT_NAME "%s</a></h1>\n"
|
||||
"<h2>Statistics Report for pid %d</h2>\n"
|
||||
"<hr width=\"100%%\" class=\"hr\">\n"
|
||||
"<h3>> General process information</h3>\n"
|
||||
"<table border=0 cols=4><tr><td align=\"left\" nowrap width=\"1%%\">\n"
|
||||
"<p><b>pid = </b> %d (nbproc = %d)<br>\n"
|
||||
"<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
|
||||
"<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
|
||||
"<b>maxsock = </b> %d<br>\n"
|
||||
"<b>maxconn = </b> %d (current conns = %d)<br>\n"
|
||||
"</td><td align=\"center\" nowrap>\n"
|
||||
"<table class=\"lgd\"><tr>\n"
|
||||
"<td class=\"active3\"> </td><td class=\"noborder\">active UP </td>"
|
||||
"<td class=\"backup3\"> </td><td class=\"noborder\">backup UP </td>"
|
||||
"</tr><tr>\n"
|
||||
"<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
|
||||
"<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
|
||||
"</tr><tr>\n"
|
||||
"<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
|
||||
"<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
|
||||
"</tr><tr>\n"
|
||||
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
|
||||
"<td class=\"active4\"></td><td class=\"noborder\">not checked </td>"
|
||||
"</tr></table>\n"
|
||||
"</td>"
|
||||
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
|
||||
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
|
||||
"",
|
||||
(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),
|
||||
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
|
||||
uri->uri_prefix,
|
||||
"",
|
||||
(s->flags & SN_STAT_NORFRSH) ? ";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" : "");
|
||||
|
||||
if (uri->refresh > 0) {
|
||||
if (s->flags & SN_STAT_NORFRSH)
|
||||
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" : "",
|
||||
"");
|
||||
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" : "",
|
||||
";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" : "");
|
||||
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"</td>"
|
||||
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
|
||||
"<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
|
||||
"<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
|
||||
"<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
|
||||
"<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
|
||||
"</ul>"
|
||||
"</td>"
|
||||
"</tr></table>\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), "</body></html>\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),
|
||||
"<table cols=\"20\" class=\"tbl\" width=\"100%%\">\n"
|
||||
"<tr align=\"center\" class=\"titre\">"
|
||||
"<th colspan=2 class=\"pxname\">%s</th>"
|
||||
"<th colspan=18 class=\"empty\"></th>"
|
||||
"</tr>\n"
|
||||
"<tr align=\"center\" class=\"titre\">"
|
||||
"<th rowspan=2></th>"
|
||||
"<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
|
||||
"<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
|
||||
"<th colspan=3>Errors</th><th colspan=6>Server</th>"
|
||||
"</tr>\n"
|
||||
"<tr align=\"center\" class=\"titre\">"
|
||||
"<th>Cur</th><th>Max</th><th>Cur</th><th>Max</th>"
|
||||
"<th>Limit</th><th>Cumul</th><th>In</th><th>Out</th>"
|
||||
"<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
|
||||
"<th>Resp</th><th>Status</th><th>Weight</th><th>Act</th>"
|
||||
"<th>Bck</th><th>Check</th><th>Down</th></tr>\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 */
|
||||
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>"
|
||||
/* sessions : current, max, limit, cumul. */
|
||||
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
|
||||
/* bytes : in, out */
|
||||
"<td align=right>%lld</td><td align=right>%lld</td>"
|
||||
/* denied: req, resp */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* errors : request, connect, response */
|
||||
"<td align=right>%d</td><td align=right></td><td align=right></td>"
|
||||
/* server status : reflect backend status */
|
||||
"<td align=center>%s</td>"
|
||||
/* rest of server: nothing */
|
||||
"<td align=center colspan=5></td></tr>"
|
||||
"",
|
||||
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", "<i>no check</i>" };
|
||||
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 */
|
||||
"<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
|
||||
/* queue : current, max */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* sessions : current, max, limit, cumul */
|
||||
"<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>"
|
||||
/* bytes : in, out */
|
||||
"<td align=right>%lld</td><td align=right>%lld</td>"
|
||||
/* denied: req, resp */
|
||||
"<td align=right></td><td align=right>%d</td>"
|
||||
/* errors : request, connect, response */
|
||||
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\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), "<td nowrap>");
|
||||
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 */
|
||||
"</td><td>%d</td>"
|
||||
/* act, bck */
|
||||
"<td>%s</td><td>%s</td>"
|
||||
"",
|
||||
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),
|
||||
"<td align=right>%d</td><td align=right>%d</td></tr>\n",
|
||||
sv->failed_checks, sv->down_trans);
|
||||
else
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"<td colspan=2></td></tr>\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 */
|
||||
"<tr align=center class=\"backend\"><td>Backend</td>"
|
||||
/* queue : current, max */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* sessions : current, max, limit, cumul. */
|
||||
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
|
||||
/* bytes : in, out */
|
||||
"<td align=right>%lld</td><td align=right>%lld</td>"
|
||||
/* denied: req, resp */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* errors : request, connect, response */
|
||||
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\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. */
|
||||
"<td align=center>%s</td><td align=center>%d</td>"
|
||||
"<td align=center>%d</td><td align=center>%d</td>"
|
||||
/* rest of server: nothing */
|
||||
"<td align=center colspan=2></td></tr>"
|
||||
"",
|
||||
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), "</table><p>\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:
|
||||
*/
|
608
src/proto_http.c
608
src/proto_http.c
|
@ -46,11 +46,13 @@
|
|||
#include <proto/acl.h>
|
||||
#include <proto/backend.h>
|
||||
#include <proto/buffers.h>
|
||||
#include <proto/dumpstats.h>
|
||||
#include <proto/fd.h>
|
||||
#include <proto/log.h>
|
||||
#include <proto/hdr_idx.h>
|
||||
#include <proto/proto_http.h>
|
||||
#include <proto/queue.h>
|
||||
#include <proto/senddata.h>
|
||||
#include <proto/session.h>
|
||||
#include <proto/task.h>
|
||||
|
||||
|
@ -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 <status> 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 <s>. 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),
|
||||
"<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
|
||||
"<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
|
||||
"<style type=\"text/css\"><!--\n"
|
||||
"body {"
|
||||
" font-family: helvetica, arial;"
|
||||
" font-size: 12px;"
|
||||
" font-weight: normal;"
|
||||
" color: black;"
|
||||
" background: white;"
|
||||
"}\n"
|
||||
"th,td {"
|
||||
" font-size: 0.8em;"
|
||||
" align: center;"
|
||||
"}\n"
|
||||
"h1 {"
|
||||
" font-size: xx-large;"
|
||||
" margin-bottom: 0.5em;"
|
||||
"}\n"
|
||||
"h2 {"
|
||||
" font-family: helvetica, arial;"
|
||||
" font-size: x-large;"
|
||||
" font-weight: bold;"
|
||||
" font-style: italic;"
|
||||
" color: #6020a0;"
|
||||
" margin-top: 0em;"
|
||||
" margin-bottom: 0em;"
|
||||
"}\n"
|
||||
"h3 {"
|
||||
" font-family: helvetica, arial;"
|
||||
" font-size: 16px;"
|
||||
" font-weight: bold;"
|
||||
" color: #b00040;"
|
||||
" background: #e8e8d0;"
|
||||
" margin-top: 0em;"
|
||||
" margin-bottom: 0em;"
|
||||
"}\n"
|
||||
"li {"
|
||||
" margin-top: 0.25em;"
|
||||
" margin-right: 2em;"
|
||||
"}\n"
|
||||
".hr {margin-top: 0.25em;"
|
||||
" border-color: black;"
|
||||
" border-bottom-style: solid;"
|
||||
"}\n"
|
||||
".pxname {background: #b00040;color: #ffff40;font-weight: bold;}\n"
|
||||
".titre {background: #20D0D0;color: #000000;font-weight: bold;}\n"
|
||||
".total {background: #20D0D0;color: #ffff80;}\n"
|
||||
".frontend {background: #e8e8d0;}\n"
|
||||
".backend {background: #e8e8d0;}\n"
|
||||
".active0 {background: #ff9090;}\n"
|
||||
".active1 {background: #ffd020;}\n"
|
||||
".active2 {background: #ffffa0;}\n"
|
||||
".active3 {background: #c0ffc0;}\n"
|
||||
".active4 {background: #e0e0e0;}\n"
|
||||
".backup0 {background: #ff9090;}\n"
|
||||
".backup1 {background: #ff80ff;}\n"
|
||||
".backup2 {background: #c060ff;}\n"
|
||||
".backup3 {background: #b0d0ff;}\n"
|
||||
".backup4 {background: #e0e0e0;}\n"
|
||||
"table.tbl { border-collapse: collapse; border-style: none;}\n"
|
||||
"table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
|
||||
"table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
|
||||
"table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
|
||||
"table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
|
||||
"table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
|
||||
"table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
|
||||
"-->\n"
|
||||
"</style></head>\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),
|
||||
"<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
|
||||
PRODUCT_NAME "%s</a></h1>\n"
|
||||
"<h2>Statistics Report for pid %d</h2>\n"
|
||||
"<hr width=\"100%%\" class=\"hr\">\n"
|
||||
"<h3>> General process information</h3>\n"
|
||||
"<table border=0 cols=4><tr><td align=\"left\" nowrap width=\"1%%\">\n"
|
||||
"<p><b>pid = </b> %d (nbproc = %d)<br>\n"
|
||||
"<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
|
||||
"<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
|
||||
"<b>maxsock = </b> %d<br>\n"
|
||||
"<b>maxconn = </b> %d (current conns = %d)<br>\n"
|
||||
"</td><td align=\"center\" nowrap>\n"
|
||||
"<table class=\"lgd\"><tr>\n"
|
||||
"<td class=\"active3\"> </td><td class=\"noborder\">active UP </td>"
|
||||
"<td class=\"backup3\"> </td><td class=\"noborder\">backup UP </td>"
|
||||
"</tr><tr>\n"
|
||||
"<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
|
||||
"<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
|
||||
"</tr><tr>\n"
|
||||
"<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
|
||||
"<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
|
||||
"</tr><tr>\n"
|
||||
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
|
||||
"<td class=\"active4\"></td><td class=\"noborder\">not checked </td>"
|
||||
"</tr></table>\n"
|
||||
"</td>"
|
||||
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
|
||||
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
|
||||
"",
|
||||
(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),
|
||||
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
|
||||
s->be->uri_auth->uri_prefix,
|
||||
"",
|
||||
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
|
||||
else
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\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),
|
||||
"<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
|
||||
s->be->uri_auth->uri_prefix,
|
||||
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
|
||||
"");
|
||||
else
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
|
||||
s->be->uri_auth->uri_prefix,
|
||||
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
|
||||
";norefresh");
|
||||
}
|
||||
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
|
||||
s->be->uri_auth->uri_prefix,
|
||||
(s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
|
||||
(s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
|
||||
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"</td>"
|
||||
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
|
||||
"<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
|
||||
"<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
|
||||
"<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
|
||||
"<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
|
||||
"</ul>"
|
||||
"</td>"
|
||||
"</tr></table>\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), "</body></html>\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),
|
||||
"<table cols=\"20\" class=\"tbl\" width=\"100%%\">\n"
|
||||
"<tr align=\"center\" class=\"titre\">"
|
||||
"<th colspan=2 class=\"pxname\">%s</th>"
|
||||
"<th colspan=18 class=\"empty\"></th>"
|
||||
"</tr>\n"
|
||||
"<tr align=\"center\" class=\"titre\">"
|
||||
"<th rowspan=2></th>"
|
||||
"<th colspan=2>Queue</th><th colspan=4>Sessions</th>"
|
||||
"<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
|
||||
"<th colspan=3>Errors</th><th colspan=6>Server</th>"
|
||||
"</tr>\n"
|
||||
"<tr align=\"center\" class=\"titre\">"
|
||||
"<th>Cur</th><th>Max</th><th>Cur</th><th>Max</th>"
|
||||
"<th>Limit</th><th>Cumul</th><th>In</th><th>Out</th>"
|
||||
"<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
|
||||
"<th>Resp</th><th>Status</th><th>Weight</th><th>Act</th>"
|
||||
"<th>Bck</th><th>Check</th><th>Down</th></tr>\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 */
|
||||
"<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=2></td>"
|
||||
/* sessions : current, max, limit, cumul. */
|
||||
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
|
||||
/* bytes : in, out */
|
||||
"<td align=right>%lld</td><td align=right>%lld</td>"
|
||||
/* denied: req, resp */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* errors : request, connect, response */
|
||||
"<td align=right>%d</td><td align=right></td><td align=right></td>"
|
||||
/* server status : reflect backend status */
|
||||
"<td align=center>%s</td>"
|
||||
/* rest of server: nothing */
|
||||
"<td align=center colspan=5></td></tr>"
|
||||
"",
|
||||
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", "<i>no check</i>" };
|
||||
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 */
|
||||
"<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
|
||||
/* queue : current, max */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* sessions : current, max, limit, cumul */
|
||||
"<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td><td align=right>%d</td>"
|
||||
/* bytes : in, out */
|
||||
"<td align=right>%lld</td><td align=right>%lld</td>"
|
||||
/* denied: req, resp */
|
||||
"<td align=right></td><td align=right>%d</td>"
|
||||
/* errors : request, connect, response */
|
||||
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\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), "<td nowrap>");
|
||||
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 */
|
||||
"</td><td>%d</td>"
|
||||
/* act, bck */
|
||||
"<td>%s</td><td>%s</td>"
|
||||
"",
|
||||
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),
|
||||
"<td align=right>%d</td><td align=right>%d</td></tr>\n",
|
||||
sv->failed_checks, sv->down_trans);
|
||||
else
|
||||
chunk_printf(&msg, sizeof(trash),
|
||||
"<td colspan=2></td></tr>\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 */
|
||||
"<tr align=center class=\"backend\"><td>Backend</td>"
|
||||
/* queue : current, max */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* sessions : current, max, limit, cumul. */
|
||||
"<td align=right>%d</td><td align=right>%d</td><td align=right>%d</td><td align=right>%d</td>"
|
||||
/* bytes : in, out */
|
||||
"<td align=right>%lld</td><td align=right>%lld</td>"
|
||||
/* denied: req, resp */
|
||||
"<td align=right>%d</td><td align=right>%d</td>"
|
||||
/* errors : request, connect, response */
|
||||
"<td align=right></td><td align=right>%d</td><td align=right>%d</td>\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. */
|
||||
"<td align=center>%s</td><td align=center>%d</td>"
|
||||
"<td align=center>%d</td><td align=center>%d</td>"
|
||||
/* rest of server: nothing */
|
||||
"<td align=center colspan=2></td></tr>"
|
||||
"",
|
||||
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), "</table><p>\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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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 <w@1wt.eu>
|
||||
*
|
||||
* 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 <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <common/compat.h>
|
||||
#include <common/config.h>
|
||||
#include <common/debug.h>
|
||||
#include <common/memory.h>
|
||||
#include <common/standard.h>
|
||||
#include <common/time.h>
|
||||
#include <common/version.h>
|
||||
|
||||
#include <types/client.h>
|
||||
#include <types/global.h>
|
||||
#include <types/polling.h>
|
||||
#include <types/proxy.h>
|
||||
#include <types/server.h>
|
||||
|
||||
#include <proto/backend.h>
|
||||
#include <proto/buffers.h>
|
||||
#include <proto/fd.h>
|
||||
#include <proto/senddata.h>
|
||||
#include <proto/session.h>
|
||||
|
||||
/*
|
||||
* 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:
|
||||
*/
|
Loading…
Reference in New Issue