blob: dd047b76c249b71f710cd0afd84f273fc6b41431 [file] [log] [blame]
Willy Tarreau91861262007-10-17 17:06:05 +02001/*
2 * Functions dedicated to statistics output
3 *
Willy Tarreaua206fa92009-01-25 14:02:00 +01004 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
Willy Tarreau91861262007-10-17 17:06:05 +02005 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <ctype.h>
14#include <errno.h>
15#include <fcntl.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
Willy Tarreaufbee7132007-10-18 13:53:22 +020019#include <pwd.h>
20#include <grp.h>
Willy Tarreau91861262007-10-17 17:06:05 +020021
22#include <sys/socket.h>
23#include <sys/stat.h>
24#include <sys/types.h>
25
Willy Tarreau10522fd2008-07-09 20:12:41 +020026#include <common/cfgparse.h>
Willy Tarreau91861262007-10-17 17:06:05 +020027#include <common/compat.h>
28#include <common/config.h>
29#include <common/debug.h>
30#include <common/memory.h>
31#include <common/mini-clist.h>
32#include <common/standard.h>
Willy Tarreau0c303ee2008-07-07 00:09:58 +020033#include <common/ticks.h>
Willy Tarreau91861262007-10-17 17:06:05 +020034#include <common/time.h>
35#include <common/uri_auth.h>
36#include <common/version.h>
37
Willy Tarreau91861262007-10-17 17:06:05 +020038#include <types/global.h>
Willy Tarreau91861262007-10-17 17:06:05 +020039
40#include <proto/backend.h>
41#include <proto/buffers.h>
42#include <proto/dumpstats.h>
43#include <proto/fd.h>
Willy Tarreaua206fa92009-01-25 14:02:00 +010044#include <proto/pipe.h>
Willy Tarreaufbee7132007-10-18 13:53:22 +020045#include <proto/proto_uxst.h>
Willy Tarreau91861262007-10-17 17:06:05 +020046#include <proto/session.h>
Krzysztof Oledzki85130942007-10-22 16:21:10 +020047#include <proto/server.h>
Willy Tarreaudded32d2008-11-30 19:48:07 +010048#include <proto/stream_interface.h>
Willy Tarreau91861262007-10-17 17:06:05 +020049
Willy Tarreaufbee7132007-10-18 13:53:22 +020050/* This function parses a "stats" statement in the "global" section. It returns
51 * -1 if there is any error, otherwise zero. If it returns -1, it may write an
52 * error message into ther <err> buffer, for at most <errlen> bytes, trailing
53 * zero included. The trailing '\n' must not be written. The function must be
54 * called with <args> pointing to the first word after "stats".
55 */
Willy Tarreau10522fd2008-07-09 20:12:41 +020056static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
57 struct proxy *defpx, char *err, int errlen)
Willy Tarreaufbee7132007-10-18 13:53:22 +020058{
Willy Tarreau10522fd2008-07-09 20:12:41 +020059 args++;
Willy Tarreaufbee7132007-10-18 13:53:22 +020060 if (!strcmp(args[0], "socket")) {
61 struct sockaddr_un su;
62 int cur_arg;
63
64 if (*args[1] == 0) {
65 snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
66 return -1;
67 }
68
69 if (global.stats_sock.state != LI_NEW) {
70 snprintf(err, errlen, "'stats socket' already specified in global section");
71 return -1;
72 }
73
74 su.sun_family = AF_UNIX;
75 strncpy(su.sun_path, args[1], sizeof(su.sun_path));
76 su.sun_path[sizeof(su.sun_path) - 1] = 0;
77 memcpy(&global.stats_sock.addr, &su, sizeof(su)); // guaranteed to fit
78
79 global.stats_sock.state = LI_INIT;
Willy Tarreau6fb42e02007-10-28 17:02:33 +010080 global.stats_sock.options = LI_O_NONE;
Willy Tarreaufbee7132007-10-18 13:53:22 +020081 global.stats_sock.accept = uxst_event_accept;
Willy Tarreaub1356cf2008-12-07 16:06:43 +010082 global.stats_sock.handler = uxst_process_session;
83 global.stats_sock.analysers = AN_REQ_UNIX_STATS;
Willy Tarreaufbee7132007-10-18 13:53:22 +020084 global.stats_sock.private = NULL;
85
86 cur_arg = 2;
87 while (*args[cur_arg]) {
88 if (!strcmp(args[cur_arg], "uid")) {
89 global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]);
90 cur_arg += 2;
91 }
92 else if (!strcmp(args[cur_arg], "gid")) {
93 global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
94 cur_arg += 2;
95 }
96 else if (!strcmp(args[cur_arg], "mode")) {
97 global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
98 cur_arg += 2;
99 }
100 else if (!strcmp(args[cur_arg], "user")) {
101 struct passwd *user;
102 user = getpwnam(args[cur_arg + 1]);
103 if (!user) {
104 snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
105 args[cur_arg + 1]);
106 return -1;
107 }
108 global.stats_sock.perm.ux.uid = user->pw_uid;
109 cur_arg += 2;
110 }
111 else if (!strcmp(args[cur_arg], "group")) {
112 struct group *group;
113 group = getgrnam(args[cur_arg + 1]);
114 if (!group) {
115 snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
116 args[cur_arg + 1]);
117 return -1;
118 }
119 global.stats_sock.perm.ux.gid = group->gr_gid;
120 cur_arg += 2;
121 }
122 else {
123 snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', and 'mode'");
124 return -1;
125 }
126 }
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100127
Willy Tarreaufbee7132007-10-18 13:53:22 +0200128 uxst_add_listener(&global.stats_sock);
129 global.maxsock++;
130 }
131 else if (!strcmp(args[0], "timeout")) {
Willy Tarreaub3f32f52007-12-02 22:15:14 +0100132 unsigned timeout;
133 const char *res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
134
135 if (res) {
136 snprintf(err, errlen, "unexpected character '%c' in 'stats timeout' in 'global' section", *res);
137 return -1;
138 }
Willy Tarreaufbee7132007-10-18 13:53:22 +0200139
Willy Tarreaub3f32f52007-12-02 22:15:14 +0100140 if (!timeout) {
Willy Tarreaufbee7132007-10-18 13:53:22 +0200141 snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'");
142 return -1;
143 }
Willy Tarreau0c303ee2008-07-07 00:09:58 +0200144 global.stats_timeout = MS_TO_TICKS(timeout);
Willy Tarreaufbee7132007-10-18 13:53:22 +0200145 }
146 else if (!strcmp(args[0], "maxconn")) {
147 int maxconn = atol(args[1]);
148
149 if (maxconn <= 0) {
150 snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'");
151 return -1;
152 }
153 global.maxsock -= global.stats_sock.maxconn;
154 global.stats_sock.maxconn = maxconn;
155 global.maxsock += global.stats_sock.maxconn;
156 }
157 else {
158 snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section");
159 return -1;
160 }
161 return 0;
162}
163
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100164int print_csv_header(struct chunk *msg, int size)
165{
166 return chunk_printf(msg, size,
167 "# pxname,svname,"
168 "qcur,qmax,"
169 "scur,smax,slim,stot,"
170 "bin,bout,"
171 "dreq,dresp,"
172 "ereq,econ,eresp,"
173 "wretr,wredis,"
174 "status,weight,act,bck,"
175 "chkfail,chkdown,lastchg,downtime,qlimit,"
Krzysztof Piotr Oledzkif58a9622008-02-23 01:19:10 +0100176 "pid,iid,sid,throttle,lbtot,tracked,type,"
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100177 "\n");
178}
179
Willy Tarreau91861262007-10-17 17:06:05 +0200180/*
181 * Produces statistics data for the session <s>. Expects to be called with
Willy Tarreau1ae3a052008-08-16 10:56:30 +0200182 * client socket shut down on input. It *may* make use of informations from
183 * <uri>. s->data_ctx must have been zeroed first, and the flags properly set.
Willy Tarreau3e76e722007-10-17 18:57:38 +0200184 * It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
185 * dump is finished and the session must be closed, or -1 in case of any error.
Willy Tarreau0a464892008-12-07 18:30:00 +0100186 * It automatically clears the HIJACK bit from the response buffer.
Willy Tarreau3e76e722007-10-17 18:57:38 +0200187 */
Willy Tarreau0a464892008-12-07 18:30:00 +0100188int stats_dump_raw(struct session *s, struct buffer *rep, struct uri_auth *uri)
Willy Tarreau3e76e722007-10-17 18:57:38 +0200189{
Willy Tarreau3e76e722007-10-17 18:57:38 +0200190 struct proxy *px;
191 struct chunk msg;
Willy Tarreaua8efd362008-01-03 10:19:15 +0100192 unsigned int up;
Willy Tarreau3e76e722007-10-17 18:57:38 +0200193
194 msg.len = 0;
195 msg.str = trash;
196
197 switch (s->data_state) {
198 case DATA_ST_INIT:
199 /* the function had not been called yet, let's prepare the
200 * buffer for a response.
201 */
Willy Tarreaudded32d2008-11-30 19:48:07 +0100202 stream_int_retnclose(rep->cons, &msg);
Willy Tarreau3e76e722007-10-17 18:57:38 +0200203 s->data_state = DATA_ST_HEAD;
204 /* fall through */
205
206 case DATA_ST_HEAD:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100207 if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
Willy Tarreaua8efd362008-01-03 10:19:15 +0100208 print_csv_header(&msg, sizeof(trash));
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200209 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreaua8efd362008-01-03 10:19:15 +0100210 return 0;
211 }
Willy Tarreau3e76e722007-10-17 18:57:38 +0200212
213 s->data_state = DATA_ST_INFO;
214 /* fall through */
215
216 case DATA_ST_INFO:
Willy Tarreaua8efd362008-01-03 10:19:15 +0100217 up = (now.tv_sec - start_date.tv_sec);
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100218 if (s->data_ctx.stats.flags & STAT_SHOW_INFO) {
Willy Tarreaua8efd362008-01-03 10:19:15 +0100219 chunk_printf(&msg, sizeof(trash),
220 "Name: " PRODUCT_NAME "\n"
221 "Version: " HAPROXY_VERSION "\n"
222 "Release_date: " HAPROXY_DATE "\n"
223 "Nbproc: %d\n"
224 "Process_num: %d\n"
225 "Pid: %d\n"
226 "Uptime: %dd %dh%02dm%02ds\n"
227 "Uptime_sec: %d\n"
228 "Memmax_MB: %d\n"
229 "Ulimit-n: %d\n"
230 "Maxsock: %d\n"
231 "Maxconn: %d\n"
Willy Tarreaua206fa92009-01-25 14:02:00 +0100232 "Maxpipes: %d\n"
Willy Tarreaua8efd362008-01-03 10:19:15 +0100233 "CurrConns: %d\n"
Willy Tarreaua206fa92009-01-25 14:02:00 +0100234 "PipesUsed: %d\n"
235 "PipesFree: %d\n"
Willy Tarreaua8efd362008-01-03 10:19:15 +0100236 "",
237 global.nbproc,
238 relative_pid,
239 pid,
240 up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
241 up,
242 global.rlimit_memmax,
243 global.rlimit_nofile,
Willy Tarreaua206fa92009-01-25 14:02:00 +0100244 global.maxsock, global.maxconn, global.maxpipes,
245 actconn, pipes_used, pipes_free
Willy Tarreaua8efd362008-01-03 10:19:15 +0100246 );
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200247 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreaua8efd362008-01-03 10:19:15 +0100248 return 0;
249 }
250
Willy Tarreau3e76e722007-10-17 18:57:38 +0200251 s->data_ctx.stats.px = proxy;
252 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100253
254 s->data_ctx.stats.sv = NULL;
255 s->data_ctx.stats.sv_st = 0;
256
Willy Tarreau3e76e722007-10-17 18:57:38 +0200257 s->data_state = DATA_ST_LIST;
258 /* fall through */
259
260 case DATA_ST_LIST:
261 /* dump proxies */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100262 if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
Willy Tarreaua8efd362008-01-03 10:19:15 +0100263 while (s->data_ctx.stats.px) {
264 px = s->data_ctx.stats.px;
265 /* skip the disabled proxies and non-networked ones */
266 if (px->state != PR_STSTOPPED &&
267 (px->cap & (PR_CAP_FE | PR_CAP_BE)))
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100268 if (stats_dump_proxy(s, px, NULL) == 0)
Willy Tarreaua8efd362008-01-03 10:19:15 +0100269 return 0;
Willy Tarreau3e76e722007-10-17 18:57:38 +0200270
Willy Tarreaua8efd362008-01-03 10:19:15 +0100271 s->data_ctx.stats.px = px->next;
272 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
273 }
274 /* here, we just have reached the last proxy */
Willy Tarreau3e76e722007-10-17 18:57:38 +0200275 }
Willy Tarreau3e76e722007-10-17 18:57:38 +0200276
277 s->data_state = DATA_ST_END;
278 /* fall through */
279
280 case DATA_ST_END:
281 s->data_state = DATA_ST_FIN;
Willy Tarreau0a464892008-12-07 18:30:00 +0100282 /* fall through */
Willy Tarreau3e76e722007-10-17 18:57:38 +0200283
284 case DATA_ST_FIN:
Willy Tarreau0a464892008-12-07 18:30:00 +0100285 buffer_stop_hijack(rep);
Willy Tarreau3e76e722007-10-17 18:57:38 +0200286 return 1;
287
288 default:
289 /* unknown state ! */
Willy Tarreau0a464892008-12-07 18:30:00 +0100290 buffer_stop_hijack(rep);
Willy Tarreau3e76e722007-10-17 18:57:38 +0200291 return -1;
292 }
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100293}
294
295
296/* This function is called to send output to the response buffer. It simply
297 * calls stats_dump_raw(), and releases the buffer's hijack bit when the dump
Willy Tarreau01bf8672008-12-07 18:03:29 +0100298 * is finished.
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100299 */
Willy Tarreau0a464892008-12-07 18:30:00 +0100300void stats_dump_raw_to_buffer(struct session *s, struct buffer *rep)
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100301{
302 if (s->ana_state != STATS_ST_REP)
Willy Tarreau01bf8672008-12-07 18:03:29 +0100303 return;
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100304
Willy Tarreau0a464892008-12-07 18:30:00 +0100305 if (stats_dump_raw(s, rep, NULL) != 0)
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100306 s->ana_state = STATS_ST_CLOSE;
Willy Tarreau01bf8672008-12-07 18:03:29 +0100307 return;
Willy Tarreau3e76e722007-10-17 18:57:38 +0200308}
309
310
311/*
312 * Produces statistics data for the session <s>. Expects to be called with
Willy Tarreau1ae3a052008-08-16 10:56:30 +0200313 * client socket shut down on input. It stops by itself by unsetting the
Willy Tarreau72b179a2008-08-28 16:01:32 +0200314 * BF_HIJACK flag from the buffer, which it uses to keep on being called
Willy Tarreau1ae3a052008-08-16 10:56:30 +0200315 * when there is free space in the buffer, of simply by letting an empty buffer
316 * upon return.s->data_ctx must have been zeroed before the first call, and the
317 * flags set. It returns 0 if it had to stop writing data and an I/O is needed,
318 * 1 if the dump is finished and the session must be closed, or -1 in case of
319 * any error.
Willy Tarreau91861262007-10-17 17:06:05 +0200320 */
Willy Tarreau0a464892008-12-07 18:30:00 +0100321int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
Willy Tarreau91861262007-10-17 17:06:05 +0200322{
Willy Tarreau91861262007-10-17 17:06:05 +0200323 struct proxy *px;
324 struct chunk msg;
325 unsigned int up;
326
327 msg.len = 0;
328 msg.str = trash;
329
330 switch (s->data_state) {
331 case DATA_ST_INIT:
Willy Tarreau91861262007-10-17 17:06:05 +0200332 chunk_printf(&msg, sizeof(trash),
333 "HTTP/1.0 200 OK\r\n"
334 "Cache-Control: no-cache\r\n"
335 "Connection: close\r\n"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200336 "Content-Type: %s\r\n",
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100337 (s->data_ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
Willy Tarreau91861262007-10-17 17:06:05 +0200338
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100339 if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH))
Willy Tarreau91861262007-10-17 17:06:05 +0200340 chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n",
341 uri->refresh);
342
343 chunk_printf(&msg, sizeof(trash), "\r\n");
344
345 s->txn.status = 200;
Willy Tarreaudded32d2008-11-30 19:48:07 +0100346 stream_int_retnclose(rep->cons, &msg); // send the start of the response.
Willy Tarreau91861262007-10-17 17:06:05 +0200347 msg.len = 0;
348
349 if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
350 s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
351 if (!(s->flags & SN_FINST_MASK))
352 s->flags |= SN_FINST_R;
353
354 if (s->txn.meth == HTTP_METH_HEAD) {
355 /* that's all we return in case of HEAD request */
356 s->data_state = DATA_ST_FIN;
Willy Tarreau72b179a2008-08-28 16:01:32 +0200357 buffer_stop_hijack(rep);
Willy Tarreau91861262007-10-17 17:06:05 +0200358 return 1;
359 }
360
361 s->data_state = DATA_ST_HEAD; /* let's start producing data */
362 /* fall through */
363
364 case DATA_ST_HEAD:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100365 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +0200366 /* WARNING! This must fit in the first buffer !!! */
367 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200368 "<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
369 "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
370 "<style type=\"text/css\"><!--\n"
371 "body {"
372 " font-family: helvetica, arial;"
373 " font-size: 12px;"
374 " font-weight: normal;"
375 " color: black;"
376 " background: white;"
377 "}\n"
378 "th,td {"
379 " font-size: 0.8em;"
380 " align: center;"
381 "}\n"
382 "h1 {"
383 " font-size: xx-large;"
384 " margin-bottom: 0.5em;"
385 "}\n"
386 "h2 {"
387 " font-family: helvetica, arial;"
388 " font-size: x-large;"
389 " font-weight: bold;"
390 " font-style: italic;"
391 " color: #6020a0;"
392 " margin-top: 0em;"
393 " margin-bottom: 0em;"
394 "}\n"
395 "h3 {"
396 " font-family: helvetica, arial;"
397 " font-size: 16px;"
398 " font-weight: bold;"
399 " color: #b00040;"
400 " background: #e8e8d0;"
401 " margin-top: 0em;"
402 " margin-bottom: 0em;"
403 "}\n"
404 "li {"
405 " margin-top: 0.25em;"
406 " margin-right: 2em;"
407 "}\n"
408 ".hr {margin-top: 0.25em;"
409 " border-color: black;"
410 " border-bottom-style: solid;"
411 "}\n"
412 ".pxname {background: #b00040;color: #ffff40;font-weight: bold;}\n"
413 ".titre {background: #20D0D0;color: #000000;font-weight: bold;}\n"
414 ".total {background: #20D0D0;color: #ffff80;}\n"
415 ".frontend {background: #e8e8d0;}\n"
416 ".backend {background: #e8e8d0;}\n"
417 ".active0 {background: #ff9090;}\n"
418 ".active1 {background: #ffd020;}\n"
419 ".active2 {background: #ffffa0;}\n"
420 ".active3 {background: #c0ffc0;}\n"
Willy Tarreau2ea81932007-11-30 12:04:38 +0100421 ".active4 {background: #ffffa0;}\n" /* NOLB state shows same as going down */
422 ".active5 {background: #a0e0a0;}\n" /* NOLB state shows darker than up */
423 ".active6 {background: #e0e0e0;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +0200424 ".backup0 {background: #ff9090;}\n"
425 ".backup1 {background: #ff80ff;}\n"
426 ".backup2 {background: #c060ff;}\n"
427 ".backup3 {background: #b0d0ff;}\n"
Willy Tarreau2ea81932007-11-30 12:04:38 +0100428 ".backup4 {background: #c060ff;}\n" /* NOLB state shows same as going down */
429 ".backup5 {background: #90b0e0;}\n" /* NOLB state shows same as going down */
430 ".backup6 {background: #e0e0e0;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +0200431 "table.tbl { border-collapse: collapse; border-style: none;}\n"
432 "table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
433 "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
434 "table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
435 "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
436 "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
437 "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
438 "-->\n"
439 "</style></head>\n");
Willy Tarreau55bb8452007-10-17 18:44:57 +0200440 } else {
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100441 print_csv_header(&msg, sizeof(trash));
Willy Tarreau55bb8452007-10-17 18:44:57 +0200442 }
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200443 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +0200444 return 0;
445
446 s->data_state = DATA_ST_INFO;
447 /* fall through */
448
449 case DATA_ST_INFO:
450 up = (now.tv_sec - start_date.tv_sec);
451
452 /* WARNING! this has to fit the first packet too.
453 * We are around 3.5 kB, add adding entries will
454 * become tricky if we want to support 4kB buffers !
455 */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100456 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +0200457 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200458 "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
459 PRODUCT_NAME "%s</a></h1>\n"
460 "<h2>Statistics Report for pid %d</h2>\n"
461 "<hr width=\"100%%\" class=\"hr\">\n"
462 "<h3>&gt; General process information</h3>\n"
463 "<table border=0 cols=4><tr><td align=\"left\" nowrap width=\"1%%\">\n"
Willy Tarreaua8efd362008-01-03 10:19:15 +0100464 "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
Willy Tarreau91861262007-10-17 17:06:05 +0200465 "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
466 "<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
Willy Tarreaua206fa92009-01-25 14:02:00 +0100467 "<b>maxsock = </b> %d ; <b>maxconn = </b> %d ; <b>maxpipes = </b> %d<br>\n"
468 "current conns = %d ; current pipes = %d/%d<br>\n"
Willy Tarreau91861262007-10-17 17:06:05 +0200469 "</td><td align=\"center\" nowrap>\n"
470 "<table class=\"lgd\"><tr>\n"
471 "<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
472 "<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
473 "</tr><tr>\n"
474 "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
475 "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
476 "</tr><tr>\n"
477 "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
478 "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
479 "</tr><tr>\n"
480 "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
Willy Tarreau2ea81932007-11-30 12:04:38 +0100481 "<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
Willy Tarreau91861262007-10-17 17:06:05 +0200482 "</tr></table>\n"
Willy Tarreau2ea81932007-11-30 12:04:38 +0100483 "Note: UP with load-balancing disabled is reported as \"NOLB\"."
Willy Tarreau91861262007-10-17 17:06:05 +0200484 "</td>"
485 "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
486 "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
487 "",
488 (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
Willy Tarreaua8efd362008-01-03 10:19:15 +0100489 pid, pid,
490 relative_pid, global.nbproc,
Willy Tarreau91861262007-10-17 17:06:05 +0200491 up / 86400, (up % 86400) / 3600,
492 (up % 3600) / 60, (up % 60),
493 global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
494 global.rlimit_memmax ? " MB" : "",
495 global.rlimit_nofile,
Willy Tarreaua206fa92009-01-25 14:02:00 +0100496 global.maxsock, global.maxconn, global.maxpipes,
497 actconn, pipes_used, pipes_used+pipes_free
Willy Tarreau91861262007-10-17 17:06:05 +0200498 );
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100499
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100500 if (s->data_ctx.stats.flags & STAT_HIDE_DOWN)
Willy Tarreau55bb8452007-10-17 18:44:57 +0200501 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200502 "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
503 uri->uri_prefix,
504 "",
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100505 (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
Willy Tarreau55bb8452007-10-17 18:44:57 +0200506 else
507 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200508 "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
509 uri->uri_prefix,
510 ";up",
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100511 (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
Willy Tarreau91861262007-10-17 17:06:05 +0200512
Willy Tarreau55bb8452007-10-17 18:44:57 +0200513 if (uri->refresh > 0) {
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100514 if (s->data_ctx.stats.flags & STAT_NO_REFRESH)
Willy Tarreau55bb8452007-10-17 18:44:57 +0200515 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200516 "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
517 uri->uri_prefix,
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100518 (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
Willy Tarreau91861262007-10-17 17:06:05 +0200519 "");
Willy Tarreau55bb8452007-10-17 18:44:57 +0200520 else
521 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200522 "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
523 uri->uri_prefix,
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100524 (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
Willy Tarreau91861262007-10-17 17:06:05 +0200525 ";norefresh");
Willy Tarreau55bb8452007-10-17 18:44:57 +0200526 }
Willy Tarreau91861262007-10-17 17:06:05 +0200527
Willy Tarreau55bb8452007-10-17 18:44:57 +0200528 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200529 "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
530 uri->uri_prefix,
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100531 (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
532 (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
Willy Tarreau91861262007-10-17 17:06:05 +0200533
Willy Tarreau55bb8452007-10-17 18:44:57 +0200534 chunk_printf(&msg, sizeof(trash),
Willy Tarreau5031e6a2007-10-18 11:05:48 +0200535 "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
536 uri->uri_prefix,
537 (uri->refresh > 0) ? ";norefresh" : "");
538
539 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200540 "</td>"
541 "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
542 "<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
543 "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
544 "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
545 "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
546 "</ul>"
547 "</td>"
548 "</tr></table>\n"
549 ""
550 );
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100551
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200552 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +0200553 return 0;
554 }
Willy Tarreau91861262007-10-17 17:06:05 +0200555
Willy Tarreau91861262007-10-17 17:06:05 +0200556 s->data_ctx.stats.px = proxy;
557 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
558 s->data_state = DATA_ST_LIST;
559 /* fall through */
560
561 case DATA_ST_LIST:
562 /* dump proxies */
563 while (s->data_ctx.stats.px) {
564 px = s->data_ctx.stats.px;
565 /* skip the disabled proxies and non-networked ones */
566 if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100567 if (stats_dump_proxy(s, px, uri) == 0)
Willy Tarreau91861262007-10-17 17:06:05 +0200568 return 0;
569
570 s->data_ctx.stats.px = px->next;
571 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
572 }
573 /* here, we just have reached the last proxy */
574
575 s->data_state = DATA_ST_END;
576 /* fall through */
577
578 case DATA_ST_END:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100579 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +0200580 chunk_printf(&msg, sizeof(trash), "</body></html>\n");
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200581 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +0200582 return 0;
583 }
Willy Tarreau91861262007-10-17 17:06:05 +0200584
585 s->data_state = DATA_ST_FIN;
586 /* fall through */
587
588 case DATA_ST_FIN:
Willy Tarreau72b179a2008-08-28 16:01:32 +0200589 buffer_stop_hijack(rep);
Willy Tarreau91861262007-10-17 17:06:05 +0200590 return 1;
591
592 default:
593 /* unknown state ! */
Willy Tarreau72b179a2008-08-28 16:01:32 +0200594 buffer_stop_hijack(rep);
Willy Tarreau91861262007-10-17 17:06:05 +0200595 return -1;
596 }
597}
598
599
600/*
601 * Dumps statistics for a proxy.
602 * Returns 0 if it had to stop dumping data because of lack of buffer space,
603 * ot non-zero if everything completed.
604 */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100605int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
Willy Tarreau91861262007-10-17 17:06:05 +0200606{
607 struct buffer *rep = s->rep;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100608 struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */
Willy Tarreau91861262007-10-17 17:06:05 +0200609 struct chunk msg;
610
611 msg.len = 0;
612 msg.str = trash;
613
614 switch (s->data_ctx.stats.px_st) {
615 case DATA_ST_PX_INIT:
616 /* we are on a new proxy */
617
618 if (uri && uri->scope) {
619 /* we have a limited scope, we have to check the proxy name */
620 struct stat_scope *scope;
621 int len;
622
623 len = strlen(px->id);
624 scope = uri->scope;
625
626 while (scope) {
627 /* match exact proxy name */
628 if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
629 break;
630
631 /* match '.' which means 'self' proxy */
Willy Tarreau1388a3a2007-10-18 16:38:37 +0200632 if (!strcmp(scope->px_id, ".") && px == s->be)
Willy Tarreau91861262007-10-17 17:06:05 +0200633 break;
634 scope = scope->next;
635 }
636
637 /* proxy name not found : don't dump anything */
638 if (scope == NULL)
639 return 1;
640 }
641
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100642 if ((s->data_ctx.stats.flags & STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100643 (px->uuid != s->data_ctx.stats.iid))
644 return 1;
645
Willy Tarreau91861262007-10-17 17:06:05 +0200646 s->data_ctx.stats.px_st = DATA_ST_PX_TH;
647 /* fall through */
648
649 case DATA_ST_PX_TH:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100650 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +0200651 /* print a new table */
652 chunk_printf(&msg, sizeof(trash),
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100653 "<table cols=\"26\" class=\"tbl\" width=\"100%%\">\n"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200654 "<tr align=\"center\" class=\"titre\">"
655 "<th colspan=2 class=\"pxname\">%s</th>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100656 "<th colspan=24 class=\"empty\"></th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200657 "</tr>\n"
658 "<tr align=\"center\" class=\"titre\">"
659 "<th rowspan=2></th>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100660 "<th colspan=3>Queue</th><th colspan=5>Sessions</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200661 "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200662 "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100663 "<th colspan=8>Server</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200664 "</tr>\n"
665 "<tr align=\"center\" class=\"titre\">"
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200666 "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100667 "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200668 "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200669 "<th>Resp</th><th>Retr</th><th>Redis</th>"
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200670 "<th>Status</th><th>Wght</th><th>Act</th>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100671 "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
672 "<th>Thrtle</th>\n"
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200673 "</tr>",
Willy Tarreau55bb8452007-10-17 18:44:57 +0200674 px->id);
675
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200676 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +0200677 return 0;
678 }
Willy Tarreau91861262007-10-17 17:06:05 +0200679
680 s->data_ctx.stats.px_st = DATA_ST_PX_FE;
681 /* fall through */
682
683 case DATA_ST_PX_FE:
684 /* print the frontend */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100685 if ((px->cap & PR_CAP_FE) &&
686 (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) {
687 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +0200688 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200689 /* name, queue */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200690 "<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=3></td>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100691 /* sessions : current, max, limit, total, lbtot */
Willy Tarreau55bb8452007-10-17 18:44:57 +0200692 "<td align=right>%d</td><td align=right>%d</td>"
693 "<td align=right>%d</td><td align=right>%d</td>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100694 "<td align=right></td>"
Willy Tarreau91861262007-10-17 17:06:05 +0200695 /* bytes : in, out */
696 "<td align=right>%lld</td><td align=right>%lld</td>"
697 /* denied: req, resp */
698 "<td align=right>%d</td><td align=right>%d</td>"
699 /* errors : request, connect, response */
700 "<td align=right>%d</td><td align=right></td><td align=right></td>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200701 /* warnings: retries, redispatches */
702 "<td align=right></td><td align=right></td>"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200703 /* server status : reflect frontend status */
Willy Tarreau91861262007-10-17 17:06:05 +0200704 "<td align=center>%s</td>"
705 /* rest of server: nothing */
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100706 "<td align=center colspan=7></td></tr>"
Willy Tarreau91861262007-10-17 17:06:05 +0200707 "",
708 px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
709 px->bytes_in, px->bytes_out,
710 px->denied_req, px->denied_resp,
711 px->failed_req,
712 px->state == PR_STRUN ? "OPEN" :
713 px->state == PR_STIDLE ? "FULL" : "STOP");
Willy Tarreau55bb8452007-10-17 18:44:57 +0200714 } else {
715 chunk_printf(&msg, sizeof(trash),
716 /* pxid, name, queue cur, queue max, */
717 "%s,FRONTEND,,,"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100718 /* sessions : current, max, limit, total */
Willy Tarreau55bb8452007-10-17 18:44:57 +0200719 "%d,%d,%d,%d,"
720 /* bytes : in, out */
721 "%lld,%lld,"
722 /* denied: req, resp */
723 "%d,%d,"
724 /* errors : request, connect, response */
725 "%d,,,"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200726 /* warnings: retries, redispatches */
727 ",,"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200728 /* server status : reflect frontend status */
729 "%s,"
730 /* rest of server: nothing */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200731 ",,,,,,,,"
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100732 /* pid, iid, sid, throttle, lbtot, tracked, type */
733 "%d,%d,0,,,,%d,"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200734 "\n",
735 px->id,
736 px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
737 px->bytes_in, px->bytes_out,
738 px->denied_req, px->denied_resp,
739 px->failed_req,
740 px->state == PR_STRUN ? "OPEN" :
Willy Tarreaudcd47712007-11-04 23:35:08 +0100741 px->state == PR_STIDLE ? "FULL" : "STOP",
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100742 relative_pid, px->uuid, STATS_TYPE_FE);
Willy Tarreau55bb8452007-10-17 18:44:57 +0200743 }
Willy Tarreau91861262007-10-17 17:06:05 +0200744
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200745 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +0200746 return 0;
747 }
748
749 s->data_ctx.stats.sv = px->srv; /* may be NULL */
750 s->data_ctx.stats.px_st = DATA_ST_PX_SV;
751 /* fall through */
752
753 case DATA_ST_PX_SV:
754 /* stats.sv has been initialized above */
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100755 for (; s->data_ctx.stats.sv != NULL; s->data_ctx.stats.sv = sv->next) {
756
Willy Tarreau2ea81932007-11-30 12:04:38 +0100757 int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */
Willy Tarreau91861262007-10-17 17:06:05 +0200758
759 sv = s->data_ctx.stats.sv;
760
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100761 if (s->data_ctx.stats.flags & STAT_BOUND) {
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100762 if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV)))
763 break;
764
765 if (s->data_ctx.stats.sid != -1 && sv->puid != s->data_ctx.stats.sid)
766 continue;
767 }
768
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100769 if (sv->tracked)
770 svs = sv->tracked;
771 else
772 svs = sv;
773
Willy Tarreau91861262007-10-17 17:06:05 +0200774 /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100775 if (!(svs->state & SRV_CHECKED))
Willy Tarreau2ea81932007-11-30 12:04:38 +0100776 sv_state = 6;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100777 else if (svs->state & SRV_RUNNING) {
778 if (svs->health == svs->rise + svs->fall - 1)
Willy Tarreau91861262007-10-17 17:06:05 +0200779 sv_state = 3; /* UP */
780 else
781 sv_state = 2; /* going down */
Willy Tarreau2ea81932007-11-30 12:04:38 +0100782
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100783 if (svs->state & SRV_GOINGDOWN)
Willy Tarreau2ea81932007-11-30 12:04:38 +0100784 sv_state += 2;
785 }
Willy Tarreau91861262007-10-17 17:06:05 +0200786 else
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100787 if (svs->health)
Willy Tarreau91861262007-10-17 17:06:05 +0200788 sv_state = 1; /* going up */
789 else
790 sv_state = 0; /* DOWN */
791
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100792 if ((sv_state == 0) && (s->data_ctx.stats.flags & STAT_HIDE_DOWN)) {
Willy Tarreau91861262007-10-17 17:06:05 +0200793 /* do not report servers which are DOWN */
794 s->data_ctx.stats.sv = sv->next;
795 continue;
796 }
797
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100798 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau2ea81932007-11-30 12:04:38 +0100799 static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d &uarr;",
800 "UP %d/%d &darr;", "UP",
801 "NOLB %d/%d &darr;", "NOLB",
802 "<i>no check</i>" };
Willy Tarreau55bb8452007-10-17 18:44:57 +0200803 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200804 /* name */
805 "<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200806 /* queue : current, max, limit */
807 "<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100808 /* sessions : current, max, limit, total, lbtot */
Willy Tarreau55bb8452007-10-17 18:44:57 +0200809 "<td align=right>%d</td><td align=right>%d</td>"
810 "<td align=right>%s</td><td align=right>%d</td>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100811 "<td align=right>%d</td>"
Willy Tarreau91861262007-10-17 17:06:05 +0200812 /* bytes : in, out */
813 "<td align=right>%lld</td><td align=right>%lld</td>"
814 /* denied: req, resp */
815 "<td align=right></td><td align=right>%d</td>"
816 /* errors : request, connect, response */
817 "<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200818 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +0100819 "<td align=right>%u</td><td align=right>%u</td>"
Willy Tarreau91861262007-10-17 17:06:05 +0200820 "",
821 (sv->state & SRV_BACKUP) ? "backup" : "active",
822 sv_state, sv->id,
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200823 sv->nbpend, sv->nbpend_max, LIM2A0(sv->maxqueue, "-"),
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100824 sv->cur_sess, sv->cur_sess_max, LIM2A1(sv->maxconn, "-"),
825 sv->cum_sess, sv->cum_lbconn,
Willy Tarreau91861262007-10-17 17:06:05 +0200826 sv->bytes_in, sv->bytes_out,
827 sv->failed_secu,
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200828 sv->failed_conns, sv->failed_resp,
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +0100829 sv->retries, sv->redispatches);
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100830
Willy Tarreau55bb8452007-10-17 18:44:57 +0200831 /* status */
832 chunk_printf(&msg, sizeof(trash), "<td nowrap>");
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200833
834 if (sv->state & SRV_CHECKED)
835 chunk_printf(&msg, sizeof(trash), "%s ",
836 human_time(now.tv_sec - sv->last_change, 1));
837
Willy Tarreau55bb8452007-10-17 18:44:57 +0200838 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200839 srv_hlt_st[sv_state],
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100840 (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
841 (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
Willy Tarreau91861262007-10-17 17:06:05 +0200842
Willy Tarreau55bb8452007-10-17 18:44:57 +0200843 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200844 /* weight */
845 "</td><td>%d</td>"
846 /* act, bck */
847 "<td>%s</td><td>%s</td>"
848 "",
Willy Tarreau5542af62007-12-03 02:04:00 +0100849 (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau91861262007-10-17 17:06:05 +0200850 (sv->state & SRV_BACKUP) ? "-" : "Y",
851 (sv->state & SRV_BACKUP) ? "Y" : "-");
852
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200853 /* check failures: unique, fatal, down time */
Willy Tarreau55bb8452007-10-17 18:44:57 +0200854 if (sv->state & SRV_CHECKED)
855 chunk_printf(&msg, sizeof(trash),
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200856 "<td align=right>%d</td><td align=right>%d</td>"
857 "<td nowrap align=right>%s</td>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100858 "",
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100859 svs->failed_checks, svs->down_trans,
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200860 human_time(srv_downtime(sv), 1));
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100861 else if (sv != svs)
862 chunk_printf(&msg, sizeof(trash),
863 "<td nowrap colspan=3>via %s/%s</td>", svs->proxy->id, svs->id );
Willy Tarreau55bb8452007-10-17 18:44:57 +0200864 else
865 chunk_printf(&msg, sizeof(trash),
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100866 "<td colspan=3></td>");
867
868 /* throttle */
869 if ((sv->state & SRV_WARMINGUP) &&
870 now.tv_sec < sv->last_change + sv->slowstart &&
871 now.tv_sec >= sv->last_change) {
872 unsigned int ratio;
873 ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
874 chunk_printf(&msg, sizeof(trash),
875 "<td>%d %%</td></tr>\n", ratio);
876 } else {
877 chunk_printf(&msg, sizeof(trash),
878 "<td>-</td></tr>\n");
879 }
Willy Tarreau55bb8452007-10-17 18:44:57 +0200880 } else {
Willy Tarreau2ea81932007-11-30 12:04:38 +0100881 static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,",
882 "UP %d/%d,", "UP,",
883 "NOLB %d/%d,", "NOLB,",
884 "no check," };
Willy Tarreau55bb8452007-10-17 18:44:57 +0200885 chunk_printf(&msg, sizeof(trash),
886 /* pxid, name */
887 "%s,%s,"
888 /* queue : current, max */
889 "%d,%d,"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100890 /* sessions : current, max, limit, total */
Willy Tarreau55bb8452007-10-17 18:44:57 +0200891 "%d,%d,%s,%d,"
892 /* bytes : in, out */
893 "%lld,%lld,"
894 /* denied: req, resp */
895 ",%d,"
896 /* errors : request, connect, response */
897 ",%d,%d,"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200898 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +0100899 "%u,%u,"
Willy Tarreau55bb8452007-10-17 18:44:57 +0200900 "",
901 px->id, sv->id,
902 sv->nbpend, sv->nbpend_max,
Willy Tarreaudcd47712007-11-04 23:35:08 +0100903 sv->cur_sess, sv->cur_sess_max, LIM2A0(sv->maxconn, ""), sv->cum_sess,
Willy Tarreau55bb8452007-10-17 18:44:57 +0200904 sv->bytes_in, sv->bytes_out,
905 sv->failed_secu,
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200906 sv->failed_conns, sv->failed_resp,
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +0100907 sv->retries, sv->redispatches);
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100908
Willy Tarreau55bb8452007-10-17 18:44:57 +0200909 /* status */
910 chunk_printf(&msg, sizeof(trash),
911 srv_hlt_st[sv_state],
912 (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
913 (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
Willy Tarreau91861262007-10-17 17:06:05 +0200914
Willy Tarreau55bb8452007-10-17 18:44:57 +0200915 chunk_printf(&msg, sizeof(trash),
916 /* weight, active, backup */
917 "%d,%d,%d,"
918 "",
Willy Tarreau5542af62007-12-03 02:04:00 +0100919 (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau55bb8452007-10-17 18:44:57 +0200920 (sv->state & SRV_BACKUP) ? 0 : 1,
921 (sv->state & SRV_BACKUP) ? 1 : 0);
922
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200923 /* check failures: unique, fatal; last change, total downtime */
Willy Tarreau55bb8452007-10-17 18:44:57 +0200924 if (sv->state & SRV_CHECKED)
925 chunk_printf(&msg, sizeof(trash),
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200926 "%d,%d,%d,%d,",
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200927 sv->failed_checks, sv->down_trans,
928 now.tv_sec - sv->last_change, srv_downtime(sv));
Willy Tarreau55bb8452007-10-17 18:44:57 +0200929 else
930 chunk_printf(&msg, sizeof(trash),
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200931 ",,,,");
932
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100933 /* queue limit, pid, iid, sid, */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200934 chunk_printf(&msg, sizeof(trash),
Willy Tarreaudcd47712007-11-04 23:35:08 +0100935 "%s,"
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100936 "%d,%d,%d,",
Willy Tarreaudcd47712007-11-04 23:35:08 +0100937 LIM2A0(sv->maxqueue, ""),
938 relative_pid, px->uuid, sv->puid);
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100939
940 /* throttle */
941 if ((sv->state & SRV_WARMINGUP) &&
942 now.tv_sec < sv->last_change + sv->slowstart &&
943 now.tv_sec >= sv->last_change) {
944 unsigned int ratio;
945 ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100946 chunk_printf(&msg, sizeof(trash), "%d", ratio);
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100947 }
948
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100949 /* sessions: lbtot */
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100950 chunk_printf(&msg, sizeof(trash), ",%d,", sv->cum_lbconn);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100951
952 /* tracked */
953 if (sv->tracked)
Krzysztof Piotr Oledzkif58a9622008-02-23 01:19:10 +0100954 chunk_printf(&msg, sizeof(trash), "%s/%s,",
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +0100955 sv->tracked->proxy->id, sv->tracked->id);
956 else
957 chunk_printf(&msg, sizeof(trash), ",");
958
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100959 /* type, then EOL */
960 chunk_printf(&msg, sizeof(trash), "%d,\n", STATS_TYPE_SV);
Willy Tarreau55bb8452007-10-17 18:44:57 +0200961 }
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +0200962 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +0200963 return 0;
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100964 } /* for sv */
Willy Tarreau91861262007-10-17 17:06:05 +0200965
966 s->data_ctx.stats.px_st = DATA_ST_PX_BE;
967 /* fall through */
968
969 case DATA_ST_PX_BE:
970 /* print the backend */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100971 if ((px->cap & PR_CAP_BE) &&
972 (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
973 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +0200974 chunk_printf(&msg, sizeof(trash),
Willy Tarreau91861262007-10-17 17:06:05 +0200975 /* name */
976 "<tr align=center class=\"backend\"><td>Backend</td>"
977 /* queue : current, max */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +0200978 "<td align=right>%d</td><td align=right>%d</td><td></td>"
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100979 /* sessions : current, max, limit, total, lbtot */
980 "<td align=right>%d</td><td align=right>%d</td>"
981 "<td align=right>%d</td><td align=right>%d</td>"
982 "<td align=right>%d</td>"
Willy Tarreau91861262007-10-17 17:06:05 +0200983 /* bytes : in, out */
984 "<td align=right>%lld</td><td align=right>%lld</td>"
985 /* denied: req, resp */
986 "<td align=right>%d</td><td align=right>%d</td>"
987 /* errors : request, connect, response */
988 "<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +0200989 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +0100990 "<td align=right>%u</td><td align=right>%u</td>"
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200991 /* backend status: reflect backend status (up/down): we display UP
Willy Tarreau91861262007-10-17 17:06:05 +0200992 * if the backend has known working servers or if it has no server at
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200993 * all (eg: for stats). Then we display the total weight, number of
Willy Tarreau91861262007-10-17 17:06:05 +0200994 * active and backups. */
Krzysztof Oledzki85130942007-10-22 16:21:10 +0200995 "<td align=center nowrap>%s %s</td><td align=center>%d</td>"
996 "<td align=center>%d</td><td align=center>%d</td>",
Willy Tarreau91861262007-10-17 17:06:05 +0200997 px->nbpend /* or px->totpend ? */, px->nbpend_max,
Willy Tarreauddbb82f2007-12-05 10:34:49 +0100998 px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, px->cum_lbconn,
Willy Tarreau91861262007-10-17 17:06:05 +0200999 px->bytes_in, px->bytes_out,
1000 px->denied_req, px->denied_resp,
1001 px->failed_conns, px->failed_resp,
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001002 px->retries, px->redispatches,
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001003 human_time(now.tv_sec - px->last_change, 1),
Willy Tarreau2ea81932007-11-30 12:04:38 +01001004 (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" :
1005 "<font color=\"red\"><b>DOWN</b></font>",
Willy Tarreau5542af62007-12-03 02:04:00 +01001006 (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau20697042007-11-15 23:26:18 +01001007 px->srv_act, px->srv_bck);
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001008
1009 chunk_printf(&msg, sizeof(trash),
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001010 /* rest of backend: nothing, down transitions, total downtime, throttle */
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001011 "<td align=center>&nbsp;</td><td align=\"right\">%d</td>"
1012 "<td align=\"right\" nowrap>%s</td>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001013 "<td></td>"
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001014 "</tr>",
1015 px->down_trans,
1016 px->srv?human_time(be_downtime(px), 1):"&nbsp;");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001017 } else {
1018 chunk_printf(&msg, sizeof(trash),
1019 /* pxid, name */
1020 "%s,BACKEND,"
1021 /* queue : current, max */
1022 "%d,%d,"
Willy Tarreauddbb82f2007-12-05 10:34:49 +01001023 /* sessions : current, max, limit, total */
Willy Tarreau55bb8452007-10-17 18:44:57 +02001024 "%d,%d,%d,%d,"
1025 /* bytes : in, out */
1026 "%lld,%lld,"
1027 /* denied: req, resp */
1028 "%d,%d,"
1029 /* errors : request, connect, response */
1030 ",%d,%d,"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001031 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzki25b501a2008-01-06 16:36:16 +01001032 "%u,%u,"
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001033 /* backend status: reflect backend status (up/down): we display UP
Willy Tarreau55bb8452007-10-17 18:44:57 +02001034 * if the backend has known working servers or if it has no server at
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001035 * all (eg: for stats). Then we display the total weight, number of
Willy Tarreau55bb8452007-10-17 18:44:57 +02001036 * active and backups. */
1037 "%s,"
1038 "%d,%d,%d,"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001039 /* rest of backend: nothing, down transitions, last change, total downtime */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +02001040 ",%d,%d,%d,,"
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01001041 /* pid, iid, sid, throttle, lbtot, tracked, type */
1042 "%d,%d,0,,%d,,%d,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001043 "\n",
1044 px->id,
1045 px->nbpend /* or px->totpend ? */, px->nbpend_max,
1046 px->beconn, px->beconn_max, px->fullconn, px->cum_beconn,
1047 px->bytes_in, px->bytes_out,
1048 px->denied_req, px->denied_resp,
1049 px->failed_conns, px->failed_resp,
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001050 px->retries, px->redispatches,
Willy Tarreau20697042007-11-15 23:26:18 +01001051 (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
Willy Tarreau5542af62007-12-03 02:04:00 +01001052 (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau20697042007-11-15 23:26:18 +01001053 px->srv_act, px->srv_bck,
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001054 px->down_trans, now.tv_sec - px->last_change,
Willy Tarreau20697042007-11-15 23:26:18 +01001055 px->srv?be_downtime(px):0,
Willy Tarreauddbb82f2007-12-05 10:34:49 +01001056 relative_pid, px->uuid,
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01001057 px->cum_lbconn, STATS_TYPE_BE);
Willy Tarreau55bb8452007-10-17 18:44:57 +02001058 }
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +02001059 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +02001060 return 0;
1061 }
Willy Tarreaub1356cf2008-12-07 16:06:43 +01001062
Willy Tarreau91861262007-10-17 17:06:05 +02001063 s->data_ctx.stats.px_st = DATA_ST_PX_END;
1064 /* fall through */
1065
1066 case DATA_ST_PX_END:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001067 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +02001068 chunk_printf(&msg, sizeof(trash), "</table><p>\n");
Willy Tarreau91861262007-10-17 17:06:05 +02001069
Krzysztof Piotr Oledzki8e4b21d2008-04-20 21:34:47 +02001070 if (buffer_write_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +02001071 return 0;
1072 }
Willy Tarreau91861262007-10-17 17:06:05 +02001073
1074 s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
1075 /* fall through */
1076
1077 case DATA_ST_PX_FIN:
1078 return 1;
1079
1080 default:
1081 /* unknown state, we should put an abort() here ! */
1082 return 1;
1083 }
1084}
1085
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001086
1087/* This function is called to send output to the response buffer.
1088 * It dumps the sessions states onto the output buffer <rep>.
1089 * Expects to be called with client socket shut down on input.
1090 * s->data_ctx must have been zeroed first, and the flags properly set.
1091 * It automatically clears the HIJACK bit from the response buffer.
1092 */
1093void stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
1094{
1095 struct chunk msg;
1096
1097 if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
1098 /* If we're forced to shut down, we might have to remove our
1099 * reference to the last session being dumped.
1100 */
1101 if (s->data_state == DATA_ST_LIST) {
Willy Tarreaufd3828e2009-02-22 15:17:24 +01001102 if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) {
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001103 LIST_DEL(&s->data_ctx.sess.bref.users);
Willy Tarreaufd3828e2009-02-22 15:17:24 +01001104 LIST_INIT(&s->data_ctx.sess.bref.users);
1105 }
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001106 }
1107 s->data_state = DATA_ST_FIN;
1108 buffer_stop_hijack(rep);
1109 s->ana_state = STATS_ST_CLOSE;
1110 return;
1111 }
1112
1113 if (s->ana_state != STATS_ST_REP)
1114 return;
1115
1116 msg.len = 0;
1117 msg.str = trash;
1118
1119 switch (s->data_state) {
1120 case DATA_ST_INIT:
1121 /* the function had not been called yet, let's prepare the
1122 * buffer for a response. We initialize the current session
Willy Tarreaufd3828e2009-02-22 15:17:24 +01001123 * pointer to the first in the global list. When a target
1124 * session is being destroyed, it is responsible for updating
1125 * this pointer. We know we have reached the end when this
1126 * pointer points back to the head of the sessions list.
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001127 */
1128 stream_int_retnclose(rep->cons, &msg);
1129 LIST_INIT(&s->data_ctx.sess.bref.users);
1130 s->data_ctx.sess.bref.ref = sessions.n;
1131 s->data_state = DATA_ST_LIST;
1132 /* fall through */
1133
1134 case DATA_ST_LIST:
Willy Tarreaufd3828e2009-02-22 15:17:24 +01001135 /* first, let's detach the back-ref from a possible previous session */
1136 if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) {
1137 LIST_DEL(&s->data_ctx.sess.bref.users);
1138 LIST_INIT(&s->data_ctx.sess.bref.users);
1139 }
1140
1141 /* and start from where we stopped */
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001142 while (s->data_ctx.sess.bref.ref != &sessions) {
1143 char pn[INET6_ADDRSTRLEN + strlen(":65535")];
1144 struct session *curr_sess;
1145
1146 curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list);
1147
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001148 chunk_printf(&msg, sizeof(trash),
1149 "%p: proto=%s",
1150 curr_sess,
1151 curr_sess->listener->proto->name);
1152
1153 switch (curr_sess->listener->proto->sock_family) {
1154 case AF_INET:
1155 inet_ntop(AF_INET,
1156 (const void *)&((struct sockaddr_in *)&curr_sess->cli_addr)->sin_addr,
1157 pn, sizeof(pn));
1158
1159 chunk_printf(&msg, sizeof(trash),
1160 " src=%s:%d fe=%s be=%s srv=%s",
1161 pn,
1162 ntohs(((struct sockaddr_in *)&curr_sess->cli_addr)->sin_port),
1163 curr_sess->fe->id,
1164 curr_sess->be->id,
1165 curr_sess->srv ? curr_sess->srv->id : "<none>"
1166 );
1167 break;
1168 case AF_INET6:
1169 inet_ntop(AF_INET6,
1170 (const void *)&((struct sockaddr_in6 *)(&curr_sess->cli_addr))->sin6_addr,
1171 pn, sizeof(pn));
1172
1173 chunk_printf(&msg, sizeof(trash),
1174 " src=%s:%d fe=%s be=%s srv=%s",
1175 pn,
1176 ntohs(((struct sockaddr_in6 *)&curr_sess->cli_addr)->sin6_port),
1177 curr_sess->fe->id,
1178 curr_sess->be->id,
1179 curr_sess->srv ? curr_sess->srv->id : "<none>"
1180 );
1181
1182 break;
1183 case AF_UNIX:
1184 /* no more information to print right now */
1185 break;
1186 }
1187
1188 chunk_printf(&msg, sizeof(trash),
Willy Tarreau8a5c6262008-12-08 00:16:21 +01001189 " si=(%d,%d) as=%d ts=%02x age=%s",
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001190 curr_sess->si[0].state, curr_sess->si[1].state,
Willy Tarreau8a5c6262008-12-08 00:16:21 +01001191 curr_sess->ana_state, curr_sess->task->state,
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001192 human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1));
1193
Willy Tarreau8a5c6262008-12-08 00:16:21 +01001194 if (curr_sess->task->state & TASK_IN_RUNQUEUE)
1195 chunk_printf(&msg, sizeof(trash), " run(nice=%d)\n", curr_sess->task->nice);
1196 else
1197 chunk_printf(&msg, sizeof(trash),
1198 " exp=%s\n",
1199 curr_sess->task->expire ?
1200 human_time(TICKS_TO_MS(tick_remain(now_ms, curr_sess->task->expire)),
1201 TICKS_TO_MS(1000))
1202 : "never");
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001203
1204 if (buffer_write_chunk(rep, &msg) >= 0) {
Willy Tarreaufd3828e2009-02-22 15:17:24 +01001205 /* let's try again later from this session. We add ourselves into
1206 * this session's users so that it can remove us upon termination.
1207 */
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001208 LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
1209 return;
1210 }
1211
1212 s->data_ctx.sess.bref.ref = curr_sess->list.n;
1213 }
1214 s->data_state = DATA_ST_FIN;
1215 /* fall through */
1216
1217 default:
1218 s->data_state = DATA_ST_FIN;
1219 buffer_stop_hijack(rep);
1220 s->ana_state = STATS_ST_CLOSE;
1221 return;
1222 }
1223}
1224
Willy Tarreau74808cb2009-03-04 15:53:18 +01001225/* print a line of error buffer (limited to 70 bytes) to <out>. The format is :
1226 * <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
1227 * which is 60 chars per line. Non-printable chars \t, \n, \r and \e are
1228 * encoded in C format. Other non-printable chars are encoded "\xHH". Original
1229 * lines are respected within the limit of 70 output chars. Lines that are
1230 * continuation of a previous truncated line begin with "+" instead of " "
1231 * after the offset. The new pointer is returned.
1232 */
1233static int dump_error_line(struct chunk *out, int size,
1234 struct error_snapshot *err, int *line, int ptr)
1235{
1236 int end;
1237 unsigned char c;
1238
1239 end = out->len + 80;
1240 if (end > size)
1241 return ptr;
1242
1243 chunk_printf(out, size, " %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
1244
1245 while (ptr < err->len) {
1246 c = err->buf[ptr];
1247 if (isprint(c)) {
1248 if (out->len > end - 2)
1249 break;
1250 out->str[out->len++] = c;
1251 } else if (c == '\t' || c == '\n' || c == '\r' || c == '\e') {
1252 if (out->len > end - 3)
1253 break;
1254 out->str[out->len++] = '\\';
1255 switch (c) {
1256 case '\t': c = 't'; break;
1257 case '\n': c = 'n'; break;
1258 case '\r': c = 'r'; break;
1259 case '\e': c = 'e'; break;
1260 }
1261 out->str[out->len++] = c;
1262 } else {
1263 if (out->len > end - 5)
1264 break;
1265 out->str[out->len++] = '\\';
1266 out->str[out->len++] = 'x';
1267 out->str[out->len++] = hextab[(c >> 4) & 0xF];
1268 out->str[out->len++] = hextab[c & 0xF];
1269 }
1270 if (err->buf[ptr++] == '\n') {
1271 /* we had a line break, let's return now */
1272 out->str[out->len++] = '\n';
1273 *line = ptr;
1274 return ptr;
1275 }
1276 }
1277 /* we have an incomplete line, we return it as-is */
1278 out->str[out->len++] = '\n';
1279 return ptr;
1280}
1281
1282/* This function is called to send output to the response buffer.
1283 * It dumps the errors logged in proxies onto the output buffer <rep>.
1284 * Expects to be called with client socket shut down on input.
1285 * s->data_ctx must have been zeroed first, and the flags properly set.
1286 * It automatically clears the HIJACK bit from the response buffer.
1287 */
1288void stats_dump_errors_to_buffer(struct session *s, struct buffer *rep)
1289{
1290 extern const char *monthname[12];
1291 struct chunk msg;
1292
1293 if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
1294 s->data_state = DATA_ST_FIN;
1295 buffer_stop_hijack(rep);
1296 s->ana_state = STATS_ST_CLOSE;
1297 return;
1298 }
1299
1300 if (s->ana_state != STATS_ST_REP)
1301 return;
1302
1303 msg.len = 0;
1304 msg.str = trash;
1305
1306 if (!s->data_ctx.errors.px) {
1307 /* the function had not been called yet, let's prepare the
1308 * buffer for a response.
1309 */
1310 stream_int_retnclose(rep->cons, &msg);
1311 s->data_ctx.errors.px = proxy;
1312 s->data_ctx.errors.buf = 0;
1313 s->data_ctx.errors.bol = 0;
1314 s->data_ctx.errors.ptr = -1;
1315 }
1316
1317 /* we have two inner loops here, one for the proxy, the other one for
1318 * the buffer.
1319 */
1320 while (s->data_ctx.errors.px) {
1321 struct error_snapshot *es;
1322
1323 if (s->data_ctx.errors.buf == 0)
1324 es = &s->data_ctx.errors.px->invalid_req;
1325 else
1326 es = &s->data_ctx.errors.px->invalid_rep;
1327
1328 if (!es->when.tv_sec)
1329 goto next;
1330
1331 if (s->data_ctx.errors.iid >= 0 &&
1332 s->data_ctx.errors.px->uuid != s->data_ctx.errors.iid &&
1333 es->oe->uuid != s->data_ctx.errors.iid)
1334 goto next;
1335
1336 if (s->data_ctx.errors.ptr < 0) {
1337 /* just print headers now */
1338
1339 char pn[INET6_ADDRSTRLEN];
1340 struct tm tm;
1341
1342 get_localtime(es->when.tv_sec, &tm);
1343 chunk_printf(&msg, sizeof(trash), "\n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
1344 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
1345 tm.tm_hour, tm.tm_min, tm.tm_sec, es->when.tv_usec/1000);
1346
1347
1348 if (es->src.ss_family == AF_INET)
1349 inet_ntop(AF_INET,
1350 (const void *)&((struct sockaddr_in *)&es->src)->sin_addr,
1351 pn, sizeof(pn));
1352 else
1353 inet_ntop(AF_INET6,
1354 (const void *)&((struct sockaddr_in6 *)(&es->src))->sin6_addr,
1355 pn, sizeof(pn));
1356
1357 switch (s->data_ctx.errors.buf) {
1358 case 0:
1359 chunk_printf(&msg, sizeof(trash),
1360 " frontend %s (#%d): invalid request\n"
1361 " src %s, session #%d, backend %s (#%d), server %s (#%d)\n"
1362 " request length %d bytes, error at position %d:\n\n",
1363 s->data_ctx.errors.px->id, s->data_ctx.errors.px->uuid,
1364 pn, es->sid, es->oe->id, es->oe->uuid,
1365 es->srv ? es->srv->id : "<NONE>",
1366 es->srv ? es->srv->puid : -1,
1367 es->len, es->pos);
1368 break;
1369 case 1:
1370 chunk_printf(&msg, sizeof(trash),
1371 " backend %s (#%d) : invalid response\n"
1372 " src %s, session #%d, frontend %s (#%d), server %s (#%d)\n"
1373 " response length %d bytes, error at position %d:\n\n",
1374 s->data_ctx.errors.px->id, s->data_ctx.errors.px->uuid,
1375 pn, es->sid, es->oe->id, es->oe->uuid,
1376 es->srv ? es->srv->id : "<NONE>",
1377 es->srv ? es->srv->puid : -1,
1378 es->len, es->pos);
1379 break;
1380 }
1381
1382 if (buffer_write_chunk(rep, &msg) >= 0) {
1383 /* Socket buffer full. Let's try again later from the same point */
1384 return;
1385 }
1386 s->data_ctx.errors.ptr = 0;
1387 s->data_ctx.errors.sid = es->sid;
1388 }
1389
1390 if (s->data_ctx.errors.sid != es->sid) {
1391 /* the snapshot changed while we were dumping it */
1392 chunk_printf(&msg, sizeof(trash),
1393 " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
1394 if (buffer_write_chunk(rep, &msg) >= 0)
1395 return;
1396 goto next;
1397 }
1398
1399 /* OK, ptr >= 0, so we have to dump the current line */
1400 while (s->data_ctx.errors.ptr < es->len) {
1401 int newptr;
1402 int newline;
1403
1404 newline = s->data_ctx.errors.bol;
1405 newptr = dump_error_line(&msg, sizeof(trash), es, &newline, s->data_ctx.errors.ptr);
1406 if (newptr == s->data_ctx.errors.ptr)
1407 return;
1408
1409 if (buffer_write_chunk(rep, &msg) >= 0) {
1410 /* Socket buffer full. Let's try again later from the same point */
1411 return;
1412 }
1413 s->data_ctx.errors.ptr = newptr;
1414 s->data_ctx.errors.bol = newline;
1415 };
1416 next:
1417 s->data_ctx.errors.bol = 0;
1418 s->data_ctx.errors.ptr = -1;
1419 s->data_ctx.errors.buf++;
1420 if (s->data_ctx.errors.buf > 1) {
1421 s->data_ctx.errors.buf = 0;
1422 s->data_ctx.errors.px = s->data_ctx.errors.px->next;
1423 }
1424 }
1425
1426 /* dump complete */
1427 buffer_stop_hijack(rep);
1428 s->ana_state = STATS_ST_CLOSE;
1429}
1430
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01001431
Willy Tarreau10522fd2008-07-09 20:12:41 +02001432static struct cfg_kw_list cfg_kws = {{ },{
1433 { CFG_GLOBAL, "stats", stats_parse_global },
1434 { 0, NULL, NULL },
1435}};
1436
1437__attribute__((constructor))
1438static void __dumpstats_module_init(void)
1439{
1440 cfg_register_keywords(&cfg_kws);
1441}
1442
Willy Tarreau91861262007-10-17 17:06:05 +02001443/*
1444 * Local variables:
1445 * c-indent-level: 8
1446 * c-basic-offset: 8
1447 * End:
1448 */