blob: 4f84298116517d34792f657a25b6e3359865eafe [file] [log] [blame]
Willy Tarreau91861262007-10-17 17:06:05 +02001/*
Willy Tarreaueb472682010-05-28 18:46:57 +02002 * Functions dedicated to statistics output and the stats socket
Willy Tarreau91861262007-10-17 17:06:05 +02003 *
Willy Tarreaueb472682010-05-28 18:46:57 +02004 * Copyright 2000-2010 Willy Tarreau <w@1wt.eu>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02005 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
Willy Tarreau91861262007-10-17 17:06:05 +02006 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
14#include <ctype.h>
15#include <errno.h>
16#include <fcntl.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
Willy Tarreaufbee7132007-10-18 13:53:22 +020020#include <pwd.h>
21#include <grp.h>
Willy Tarreau91861262007-10-17 17:06:05 +020022
23#include <sys/socket.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26
Willy Tarreau10522fd2008-07-09 20:12:41 +020027#include <common/cfgparse.h>
Willy Tarreau91861262007-10-17 17:06:05 +020028#include <common/compat.h>
29#include <common/config.h>
30#include <common/debug.h>
31#include <common/memory.h>
32#include <common/mini-clist.h>
33#include <common/standard.h>
Willy Tarreau0c303ee2008-07-07 00:09:58 +020034#include <common/ticks.h>
Willy Tarreau91861262007-10-17 17:06:05 +020035#include <common/time.h>
36#include <common/uri_auth.h>
37#include <common/version.h>
38
Willy Tarreau91861262007-10-17 17:06:05 +020039#include <types/global.h>
Willy Tarreau91861262007-10-17 17:06:05 +020040
41#include <proto/backend.h>
42#include <proto/buffers.h>
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +020043#include <proto/checks.h>
Willy Tarreau91861262007-10-17 17:06:05 +020044#include <proto/dumpstats.h>
45#include <proto/fd.h>
Willy Tarreau7f062c42009-03-05 18:43:00 +010046#include <proto/freq_ctr.h>
Willy Tarreaueb472682010-05-28 18:46:57 +020047#include <proto/log.h>
Willy Tarreaua206fa92009-01-25 14:02:00 +010048#include <proto/pipe.h>
Willy Tarreaufbee7132007-10-18 13:53:22 +020049#include <proto/proto_uxst.h>
Willy Tarreau89a63132009-08-16 17:41:45 +020050#include <proto/proxy.h>
Willy Tarreau91861262007-10-17 17:06:05 +020051#include <proto/session.h>
Krzysztof Oledzki85130942007-10-22 16:21:10 +020052#include <proto/server.h>
Willy Tarreaudded32d2008-11-30 19:48:07 +010053#include <proto/stream_interface.h>
Willy Tarreaueb472682010-05-28 18:46:57 +020054#include <proto/stream_sock.h>
Willy Tarreau4726f532009-03-07 17:25:21 +010055#include <proto/task.h>
Willy Tarreau91861262007-10-17 17:06:05 +020056
Willy Tarreau5ca791d2009-08-16 19:06:42 +020057const char stats_sock_usage_msg[] =
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +020058 "Unknown command. Please enter one of the following commands only :\n"
Willy Tarreau2f6bf2b2009-10-10 15:26:26 +020059 " clear counters : clear max statistics counters (add 'all' for all counters)\n"
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +020060 " help : this message\n"
61 " prompt : toggle interactive mode with prompt\n"
62 " quit : disconnect\n"
63 " show info : report information about the running process\n"
64 " show stat : report counters for each proxy and server\n"
65 " show errors : report last request and response errors for each proxy\n"
Willy Tarreau66dc20a2010-03-05 17:53:32 +010066 " show sess [id] : report the list of current sessions or dump this session\n"
Willy Tarreau38338fa2009-10-10 18:37:29 +020067 " get weight : report a server's current weight\n"
Willy Tarreau4483d432009-10-10 19:30:08 +020068 " set weight : change a server's weight\n"
Willy Tarreau7aabd112010-01-26 10:59:06 +010069 " set timeout : change a timeout setting\n"
Cyril Bontécd19e512010-01-31 22:34:03 +010070 " disable server : set a server in maintenance mode\n"
71 " enable server : re-enable a server that was previously in maintenance mode\n"
Willy Tarreau9a42c0d2009-09-22 19:31:03 +020072 "";
Willy Tarreau5ca791d2009-08-16 19:06:42 +020073
Willy Tarreau6162db22009-10-10 17:13:00 +020074const char stats_permission_denied_msg[] =
75 "Permission denied\n"
76 "";
77
Willy Tarreaudecd14d2010-06-01 18:03:19 +020078/* This function is called from the session-level accept() in order to instanciate
Willy Tarreaueb472682010-05-28 18:46:57 +020079 * a new stats socket. It returns a positive value upon success, 0 if the connection
80 * needs to be closed and ignored, or a negative value upon critical failure.
81 */
Willy Tarreaudecd14d2010-06-01 18:03:19 +020082int stats_accept(struct session *s)
Willy Tarreaueb472682010-05-28 18:46:57 +020083{
Willy Tarreaudecd14d2010-06-01 18:03:19 +020084 /* we have a dedicated I/O handler for the stats */
Willy Tarreaueb472682010-05-28 18:46:57 +020085 stream_int_register_handler(&s->si[1], stats_io_handler);
86 s->si[1].private = s;
87 s->si[1].st1 = 0;
88 s->si[1].st0 = STAT_CLI_INIT;
89
Willy Tarreaudecd14d2010-06-01 18:03:19 +020090 tv_zero(&s->logs.tv_request);
91 s->logs.t_queue = 0;
92 s->logs.t_connect = 0;
93 s->logs.t_data = 0;
94 s->logs.t_close = 0;
95 s->logs.bytes_in = s->logs.bytes_out = 0;
96 s->logs.prx_queue_size = 0; /* we get the number of pending conns before us */
97 s->logs.srv_queue_size = 0; /* we will get this number soon */
Willy Tarreaueb472682010-05-28 18:46:57 +020098
99 s->data_state = DATA_ST_INIT;
100 s->data_source = DATA_SRC_NONE;
Willy Tarreaueb472682010-05-28 18:46:57 +0200101
Willy Tarreaueb472682010-05-28 18:46:57 +0200102 s->req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */
103
Willy Tarreaudecd14d2010-06-01 18:03:19 +0200104 if (s->listener->timeout) {
105 s->req->rto = *s->listener->timeout;
106 s->rep->wto = *s->listener->timeout;
Willy Tarreaueb472682010-05-28 18:46:57 +0200107 }
Willy Tarreaueb472682010-05-28 18:46:57 +0200108 return 1;
Willy Tarreaueb472682010-05-28 18:46:57 +0200109}
110
Willy Tarreaufbee7132007-10-18 13:53:22 +0200111/* This function parses a "stats" statement in the "global" section. It returns
112 * -1 if there is any error, otherwise zero. If it returns -1, it may write an
113 * error message into ther <err> buffer, for at most <errlen> bytes, trailing
114 * zero included. The trailing '\n' must not be written. The function must be
115 * called with <args> pointing to the first word after "stats".
116 */
Willy Tarreau10522fd2008-07-09 20:12:41 +0200117static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
118 struct proxy *defpx, char *err, int errlen)
Willy Tarreaufbee7132007-10-18 13:53:22 +0200119{
Willy Tarreau10522fd2008-07-09 20:12:41 +0200120 args++;
Willy Tarreaufbee7132007-10-18 13:53:22 +0200121 if (!strcmp(args[0], "socket")) {
122 struct sockaddr_un su;
123 int cur_arg;
124
125 if (*args[1] == 0) {
126 snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
127 return -1;
128 }
129
130 if (global.stats_sock.state != LI_NEW) {
131 snprintf(err, errlen, "'stats socket' already specified in global section");
132 return -1;
133 }
134
135 su.sun_family = AF_UNIX;
136 strncpy(su.sun_path, args[1], sizeof(su.sun_path));
137 su.sun_path[sizeof(su.sun_path) - 1] = 0;
138 memcpy(&global.stats_sock.addr, &su, sizeof(su)); // guaranteed to fit
139
Willy Tarreau89a63132009-08-16 17:41:45 +0200140 if (!global.stats_fe) {
141 if ((global.stats_fe = (struct proxy *)calloc(1, sizeof(struct proxy))) == NULL) {
142 snprintf(err, errlen, "out of memory");
143 return -1;
144 }
145
146 LIST_INIT(&global.stats_fe->pendconns);
147 LIST_INIT(&global.stats_fe->acl);
148 LIST_INIT(&global.stats_fe->block_cond);
149 LIST_INIT(&global.stats_fe->redirect_rules);
150 LIST_INIT(&global.stats_fe->mon_fail_cond);
151 LIST_INIT(&global.stats_fe->switching_rules);
152 LIST_INIT(&global.stats_fe->tcp_req.inspect_rules);
153
154 /* Timeouts are defined as -1, so we cannot use the zeroed area
155 * as a default value.
156 */
157 proxy_reset_timeouts(global.stats_fe);
158
159 global.stats_fe->last_change = now.tv_sec;
160 global.stats_fe->id = strdup("GLOBAL");
161 global.stats_fe->cap = PR_CAP_FE;
Willy Tarreaueb472682010-05-28 18:46:57 +0200162 global.stats_fe->maxconn = global.stats_sock.maxconn;
Willy Tarreau89a63132009-08-16 17:41:45 +0200163 }
164
Willy Tarreaufbee7132007-10-18 13:53:22 +0200165 global.stats_sock.state = LI_INIT;
Willy Tarreau6fb42e02007-10-28 17:02:33 +0100166 global.stats_sock.options = LI_O_NONE;
Willy Tarreaudecd14d2010-06-01 18:03:19 +0200167 global.stats_sock.accept = session_accept;
168 global.stats_fe->accept = stats_accept;
Willy Tarreau104eb362009-08-16 18:51:29 +0200169 global.stats_sock.handler = process_session;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200170 global.stats_sock.analysers = 0;
Willy Tarreau2c9f5b12009-08-16 19:12:36 +0200171 global.stats_sock.nice = -64; /* we want to boost priority for local stats */
Willy Tarreaueb472682010-05-28 18:46:57 +0200172 global.stats_sock.frontend = global.stats_fe;
Willy Tarreau6162db22009-10-10 17:13:00 +0200173 global.stats_sock.perm.ux.level = ACCESS_LVL_OPER; /* default access level */
Willy Tarreau89a63132009-08-16 17:41:45 +0200174
175 global.stats_fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
176 global.stats_sock.timeout = &global.stats_fe->timeout.client;
177
178 global.stats_sock.next = global.stats_fe->listen;
179 global.stats_fe->listen = &global.stats_sock;
Willy Tarreaufbee7132007-10-18 13:53:22 +0200180
181 cur_arg = 2;
182 while (*args[cur_arg]) {
183 if (!strcmp(args[cur_arg], "uid")) {
184 global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]);
185 cur_arg += 2;
186 }
187 else if (!strcmp(args[cur_arg], "gid")) {
188 global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
189 cur_arg += 2;
190 }
191 else if (!strcmp(args[cur_arg], "mode")) {
192 global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
193 cur_arg += 2;
194 }
195 else if (!strcmp(args[cur_arg], "user")) {
196 struct passwd *user;
197 user = getpwnam(args[cur_arg + 1]);
198 if (!user) {
199 snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
200 args[cur_arg + 1]);
201 return -1;
202 }
203 global.stats_sock.perm.ux.uid = user->pw_uid;
204 cur_arg += 2;
205 }
206 else if (!strcmp(args[cur_arg], "group")) {
207 struct group *group;
208 group = getgrnam(args[cur_arg + 1]);
209 if (!group) {
210 snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
211 args[cur_arg + 1]);
212 return -1;
213 }
214 global.stats_sock.perm.ux.gid = group->gr_gid;
215 cur_arg += 2;
216 }
Willy Tarreau6162db22009-10-10 17:13:00 +0200217 else if (!strcmp(args[cur_arg], "level")) {
218 if (!strcmp(args[cur_arg+1], "user"))
219 global.stats_sock.perm.ux.level = ACCESS_LVL_USER;
220 else if (!strcmp(args[cur_arg+1], "operator"))
221 global.stats_sock.perm.ux.level = ACCESS_LVL_OPER;
222 else if (!strcmp(args[cur_arg+1], "admin"))
223 global.stats_sock.perm.ux.level = ACCESS_LVL_ADMIN;
224 else {
225 snprintf(err, errlen, "'stats socket level' only supports 'user', 'operator', and 'admin'");
226 return -1;
227 }
228 cur_arg += 2;
229 }
Willy Tarreaufbee7132007-10-18 13:53:22 +0200230 else {
Willy Tarreau6162db22009-10-10 17:13:00 +0200231 snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', 'level', and 'mode'");
Willy Tarreaufbee7132007-10-18 13:53:22 +0200232 return -1;
233 }
234 }
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100235
Willy Tarreaufbee7132007-10-18 13:53:22 +0200236 uxst_add_listener(&global.stats_sock);
237 global.maxsock++;
238 }
239 else if (!strcmp(args[0], "timeout")) {
Willy Tarreaub3f32f52007-12-02 22:15:14 +0100240 unsigned timeout;
241 const char *res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
242
243 if (res) {
244 snprintf(err, errlen, "unexpected character '%c' in 'stats timeout' in 'global' section", *res);
245 return -1;
246 }
Willy Tarreaufbee7132007-10-18 13:53:22 +0200247
Willy Tarreaub3f32f52007-12-02 22:15:14 +0100248 if (!timeout) {
Willy Tarreaufbee7132007-10-18 13:53:22 +0200249 snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'");
250 return -1;
251 }
Willy Tarreau89a63132009-08-16 17:41:45 +0200252 global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
Willy Tarreaufbee7132007-10-18 13:53:22 +0200253 }
254 else if (!strcmp(args[0], "maxconn")) {
255 int maxconn = atol(args[1]);
256
257 if (maxconn <= 0) {
258 snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'");
259 return -1;
260 }
261 global.maxsock -= global.stats_sock.maxconn;
262 global.stats_sock.maxconn = maxconn;
263 global.maxsock += global.stats_sock.maxconn;
Willy Tarreaueb472682010-05-28 18:46:57 +0200264 if (global.stats_fe)
265 global.stats_fe->maxconn = global.stats_sock.maxconn;
Willy Tarreaufbee7132007-10-18 13:53:22 +0200266 }
267 else {
268 snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section");
269 return -1;
270 }
271 return 0;
272}
273
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +0200274int print_csv_header(struct chunk *msg)
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100275{
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +0200276 return chunk_printf(msg,
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100277 "# pxname,svname,"
278 "qcur,qmax,"
279 "scur,smax,slim,stot,"
280 "bin,bout,"
281 "dreq,dresp,"
282 "ereq,econ,eresp,"
283 "wretr,wredis,"
284 "status,weight,act,bck,"
285 "chkfail,chkdown,lastchg,downtime,qlimit,"
Willy Tarreau8f208ec2009-05-10 19:01:49 +0200286 "pid,iid,sid,throttle,lbtot,tracked,type,"
287 "rate,rate_lim,rate_max,"
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +0200288 "check_status,check_code,check_duration,"
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +0100289 "hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,"
Willy Tarreauae526782010-03-04 20:34:23 +0100290 "req_rate,req_rate_max,req_tot,"
291 "cli_abrt,srv_abrt,"
Willy Tarreau4bab24d2007-11-30 18:16:29 +0100292 "\n");
293}
294
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200295/* Processes the stats interpreter on the statistics socket. This function is
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200296 * called from an applet running in a stream interface. The function returns 1
297 * if the request was understood, otherwise zero. It sets si->st0 to a value
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200298 * designating the function which will have to process the request, which can
299 * also be the print function to display the return message set into cli.msg.
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200300 */
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200301int stats_sock_parse_request(struct stream_interface *si, char *line)
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200302{
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200303 struct session *s = si->private;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200304 char *args[MAX_STATS_ARGS + 1];
305 int arg;
306
307 while (isspace((unsigned char)*line))
308 line++;
309
310 arg = 0;
311 args[arg] = line;
312
313 while (*line && arg < MAX_STATS_ARGS) {
314 if (isspace((unsigned char)*line)) {
315 *line++ = '\0';
316
317 while (isspace((unsigned char)*line))
318 line++;
319
320 args[++arg] = line;
321 continue;
322 }
323
324 line++;
325 }
326
327 while (++arg <= MAX_STATS_ARGS)
328 args[arg] = line;
329
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200330 s->data_ctx.stats.flags = 0;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200331 if (strcmp(args[0], "show") == 0) {
332 if (strcmp(args[1], "stat") == 0) {
333 if (*args[2] && *args[3] && *args[4]) {
334 s->data_ctx.stats.flags |= STAT_BOUND;
335 s->data_ctx.stats.iid = atoi(args[2]);
336 s->data_ctx.stats.type = atoi(args[3]);
337 s->data_ctx.stats.sid = atoi(args[4]);
338 }
339
340 s->data_ctx.stats.flags |= STAT_SHOW_STAT;
341 s->data_ctx.stats.flags |= STAT_FMT_CSV;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200342 s->data_state = DATA_ST_INIT;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200343 si->st0 = STAT_CLI_O_INFO; // stats_dump_raw_to_buffer
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200344 }
345 else if (strcmp(args[1], "info") == 0) {
346 s->data_ctx.stats.flags |= STAT_SHOW_INFO;
347 s->data_ctx.stats.flags |= STAT_FMT_CSV;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200348 s->data_state = DATA_ST_INIT;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200349 si->st0 = STAT_CLI_O_INFO; // stats_dump_raw_to_buffer
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200350 }
351 else if (strcmp(args[1], "sess") == 0) {
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200352 s->data_state = DATA_ST_INIT;
Willy Tarreau6162db22009-10-10 17:13:00 +0200353 if (s->listener->perm.ux.level < ACCESS_LVL_OPER) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200354 s->data_ctx.cli.msg = stats_permission_denied_msg;
355 si->st0 = STAT_CLI_PRINT;
Willy Tarreau6162db22009-10-10 17:13:00 +0200356 return 1;
357 }
Willy Tarreau66dc20a2010-03-05 17:53:32 +0100358 if (*args[2])
359 s->data_ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
360 else
361 s->data_ctx.sess.target = NULL;
362 s->data_ctx.sess.section = 0; /* start with session status */
363 s->data_ctx.sess.pos = 0;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200364 si->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200365 }
366 else if (strcmp(args[1], "errors") == 0) {
Willy Tarreau6162db22009-10-10 17:13:00 +0200367 if (s->listener->perm.ux.level < ACCESS_LVL_OPER) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200368 s->data_ctx.cli.msg = stats_permission_denied_msg;
369 si->st0 = STAT_CLI_PRINT;
Willy Tarreau6162db22009-10-10 17:13:00 +0200370 return 1;
371 }
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200372 if (*args[2])
373 s->data_ctx.errors.iid = atoi(args[2]);
374 else
375 s->data_ctx.errors.iid = -1;
376 s->data_ctx.errors.px = NULL;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200377 s->data_state = DATA_ST_INIT;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200378 si->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200379 }
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +0200380 else { /* neither "stat" nor "info" nor "sess" nor "errors"*/
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200381 return 0;
382 }
383 }
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +0200384 else if (strcmp(args[0], "clear") == 0) {
385 if (strcmp(args[1], "counters") == 0) {
386 struct proxy *px;
387 struct server *sv;
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +0200388 struct listener *li;
Willy Tarreau2f6bf2b2009-10-10 15:26:26 +0200389 int clrall = 0;
390
391 if (strcmp(args[2], "all") == 0)
392 clrall = 1;
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +0200393
Willy Tarreau6162db22009-10-10 17:13:00 +0200394 /* check permissions */
395 if (s->listener->perm.ux.level < ACCESS_LVL_OPER ||
396 (clrall && s->listener->perm.ux.level < ACCESS_LVL_ADMIN)) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200397 s->data_ctx.cli.msg = stats_permission_denied_msg;
398 si->st0 = STAT_CLI_PRINT;
Willy Tarreau6162db22009-10-10 17:13:00 +0200399 return 1;
400 }
401
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +0200402 for (px = proxy; px; px = px->next) {
Willy Tarreau2f6bf2b2009-10-10 15:26:26 +0200403 if (clrall)
404 memset(&px->counters, 0, sizeof(px->counters));
405 else {
406 px->counters.feconn_max = 0;
407 px->counters.beconn_max = 0;
Willy Tarreaud9b587f2010-02-26 10:05:55 +0100408 px->counters.fe_rps_max = 0;
Willy Tarreau2f6bf2b2009-10-10 15:26:26 +0200409 px->counters.fe_sps_max = 0;
410 px->counters.be_sps_max = 0;
411 px->counters.nbpend_max = 0;
412 }
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +0200413
414 for (sv = px->srv; sv; sv = sv->next)
Willy Tarreau2f6bf2b2009-10-10 15:26:26 +0200415 if (clrall)
416 memset(&sv->counters, 0, sizeof(sv->counters));
417 else {
418 sv->counters.cur_sess_max = 0;
419 sv->counters.nbpend_max = 0;
420 sv->counters.sps_max = 0;
421 }
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +0200422
423 for (li = px->listen; li; li = li->next)
Willy Tarreau2f6bf2b2009-10-10 15:26:26 +0200424 if (li->counters) {
425 if (clrall)
426 memset(li->counters, 0, sizeof(*li->counters));
427 else
428 li->counters->conn_max = 0;
429 }
Krzysztof Piotr Oledzki719e7262009-10-04 15:02:46 +0200430 }
431
432 return 1;
433 }
434 else {
435 return 0;
436 }
437 }
Willy Tarreau38338fa2009-10-10 18:37:29 +0200438 else if (strcmp(args[0], "get") == 0) {
439 if (strcmp(args[1], "weight") == 0) {
440 struct proxy *px;
441 struct server *sv;
442
443 /* split "backend/server" and make <line> point to server */
444 for (line = args[2]; *line; line++)
445 if (*line == '/') {
446 *line++ = '\0';
447 break;
448 }
449
450 if (!*line) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200451 s->data_ctx.cli.msg = "Require 'backend/server'.\n";
452 si->st0 = STAT_CLI_PRINT;
Willy Tarreau38338fa2009-10-10 18:37:29 +0200453 return 1;
454 }
455
456 if (!get_backend_server(args[2], line, &px, &sv)) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200457 s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
458 si->st0 = STAT_CLI_PRINT;
Willy Tarreau38338fa2009-10-10 18:37:29 +0200459 return 1;
460 }
461
462 /* return server's effective weight at the moment */
463 snprintf(trash, sizeof(trash), "%d (initial %d)\n", sv->uweight, sv->iweight);
464 buffer_feed(si->ib, trash);
465 return 1;
466 }
467 else { /* not "get weight" */
468 return 0;
469 }
470 }
Willy Tarreau4483d432009-10-10 19:30:08 +0200471 else if (strcmp(args[0], "set") == 0) {
472 if (strcmp(args[1], "weight") == 0) {
473 struct proxy *px;
474 struct server *sv;
475 int w;
476
477 if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200478 s->data_ctx.cli.msg = stats_permission_denied_msg;
479 si->st0 = STAT_CLI_PRINT;
Willy Tarreau4483d432009-10-10 19:30:08 +0200480 return 1;
481 }
482
483 /* split "backend/server" and make <line> point to server */
484 for (line = args[2]; *line; line++)
485 if (*line == '/') {
486 *line++ = '\0';
487 break;
488 }
489
490 if (!*line || !*args[3]) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200491 s->data_ctx.cli.msg = "Require 'backend/server' and 'weight' or 'weight%'.\n";
492 si->st0 = STAT_CLI_PRINT;
Willy Tarreau4483d432009-10-10 19:30:08 +0200493 return 1;
494 }
495
496 if (!get_backend_server(args[2], line, &px, &sv)) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200497 s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
498 si->st0 = STAT_CLI_PRINT;
Willy Tarreau4483d432009-10-10 19:30:08 +0200499 return 1;
500 }
501
502 /* if the weight is terminated with '%', it is set relative to
503 * the initial weight, otherwise it is absolute.
504 */
505 w = atoi(args[3]);
506 if (strchr(args[3], '%') != NULL) {
507 if (w < 0 || w > 100) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200508 s->data_ctx.cli.msg = "Relative weight can only be set between 0 and 100% inclusive.\n";
509 si->st0 = STAT_CLI_PRINT;
Willy Tarreau4483d432009-10-10 19:30:08 +0200510 return 1;
511 }
512 w = sv->iweight * w / 100;
513 }
514 else {
515 if (w < 0 || w > 256) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200516 s->data_ctx.cli.msg = "Absolute weight can only be between 0 and 256 inclusive.\n";
517 si->st0 = STAT_CLI_PRINT;
Willy Tarreau4483d432009-10-10 19:30:08 +0200518 return 1;
519 }
520 }
521
522 if (w && w != sv->iweight && !(px->lbprm.algo & BE_LB_PROP_DYN)) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200523 s->data_ctx.cli.msg = "Backend is using a static LB algorithm and only accepts weights '0%' and '100%'.\n";
524 si->st0 = STAT_CLI_PRINT;
Willy Tarreau4483d432009-10-10 19:30:08 +0200525 return 1;
526 }
527
528 sv->uweight = w;
529
530 if (px->lbprm.algo & BE_LB_PROP_DYN) {
531 /* we must take care of not pushing the server to full throttle during slow starts */
532 if ((sv->state & SRV_WARMINGUP) && (px->lbprm.algo & BE_LB_PROP_DYN))
533 sv->eweight = (BE_WEIGHT_SCALE * (now.tv_sec - sv->last_change) + sv->slowstart - 1) / sv->slowstart;
534 else
535 sv->eweight = BE_WEIGHT_SCALE;
536 sv->eweight *= sv->uweight;
537 } else {
538 sv->eweight = sv->uweight;
539 }
540
541 /* static LB algorithms are a bit harder to update */
542 if (px->lbprm.update_server_eweight)
543 px->lbprm.update_server_eweight(sv);
544 else if (sv->eweight)
545 px->lbprm.set_server_status_up(sv);
546 else
547 px->lbprm.set_server_status_down(sv);
548
549 return 1;
550 }
Willy Tarreau7aabd112010-01-26 10:59:06 +0100551 else if (strcmp(args[1], "timeout") == 0) {
552 if (strcmp(args[2], "cli") == 0) {
553 unsigned timeout;
554 const char *res;
555
556 if (!*args[3]) {
557 s->data_ctx.cli.msg = "Expects an integer value.\n";
558 si->st0 = STAT_CLI_PRINT;
559 return 1;
560 }
561
562 res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
563 if (res || timeout < 1) {
564 s->data_ctx.cli.msg = "Invalid timeout value.\n";
565 si->st0 = STAT_CLI_PRINT;
566 return 1;
567 }
568
569 s->req->rto = s->rep->wto = 1 + MS_TO_TICKS(timeout*1000);
570 return 1;
571 }
572 else {
573 s->data_ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
574 si->st0 = STAT_CLI_PRINT;
575 return 1;
576 }
577 }
578 else { /* unknown "set" parameter */
Willy Tarreau4483d432009-10-10 19:30:08 +0200579 return 0;
580 }
581 }
Cyril Bontécd19e512010-01-31 22:34:03 +0100582 else if (strcmp(args[0], "enable") == 0) {
583 if (strcmp(args[1], "server") == 0) {
584 struct proxy *px;
585 struct server *sv;
586
587 if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
588 s->data_ctx.cli.msg = stats_permission_denied_msg;
589 si->st0 = STAT_CLI_PRINT;
590 return 1;
591 }
592
593 /* split "backend/server" and make <line> point to server */
594 for (line = args[2]; *line; line++)
595 if (*line == '/') {
596 *line++ = '\0';
597 break;
598 }
599
600 if (!*line || !*args[2]) {
601 s->data_ctx.cli.msg = "Require 'backend/server'.\n";
602 si->st0 = STAT_CLI_PRINT;
603 return 1;
604 }
605
606 if (!get_backend_server(args[2], line, &px, &sv)) {
607 s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
608 si->st0 = STAT_CLI_PRINT;
609 return 1;
610 }
611
612 if (sv->state & SRV_MAINTAIN) {
613 /* The server is really in maintenance, we can change the server state */
614 if (sv->tracked) {
615 /* If this server tracks the status of another one,
616 * we must restore the good status.
617 */
618 if (sv->tracked->state & SRV_RUNNING) {
619 set_server_up(sv);
620 } else {
621 sv->state &= ~SRV_MAINTAIN;
622 set_server_down(sv);
623 }
624 } else {
625 set_server_up(sv);
626 }
627 }
628
629 return 1;
630 }
631 else { /* unknown "enable" parameter */
632 return 0;
633 }
634 }
635 else if (strcmp(args[0], "disable") == 0) {
636 if (strcmp(args[1], "server") == 0) {
637 struct proxy *px;
638 struct server *sv;
639
640 if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
641 s->data_ctx.cli.msg = stats_permission_denied_msg;
642 si->st0 = STAT_CLI_PRINT;
643 return 1;
644 }
645
646 /* split "backend/server" and make <line> point to server */
647 for (line = args[2]; *line; line++)
648 if (*line == '/') {
649 *line++ = '\0';
650 break;
651 }
652
653 if (!*line || !*args[2]) {
654 s->data_ctx.cli.msg = "Require 'backend/server'.\n";
655 si->st0 = STAT_CLI_PRINT;
656 return 1;
657 }
658
659 if (!get_backend_server(args[2], line, &px, &sv)) {
660 s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
661 si->st0 = STAT_CLI_PRINT;
662 return 1;
663 }
664
665 if (! (sv->state & SRV_MAINTAIN)) {
666 /* Not already in maintenance, we can change the server state */
667 sv->state |= SRV_MAINTAIN;
668 set_server_down(sv);
669 }
670
671 return 1;
672 }
673 else { /* unknown "disable" parameter */
674 return 0;
675 }
676 }
677 else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200678 return 0;
679 }
680 return 1;
681}
682
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200683/* This I/O handler runs as an applet embedded in a stream interface. It is
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200684 * used to processes I/O from/to the stats unix socket. The system relies on a
685 * state machine handling requests and various responses. We read a request,
686 * then we process it and send the response, and we possibly display a prompt.
687 * Then we can read again. The state is stored in si->st0 and is one of the
688 * STAT_CLI_* constants. si->st1 is used to indicate whether prompt is enabled
689 * or not.
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200690 */
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200691void stats_io_handler(struct stream_interface *si)
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200692{
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200693 struct session *s = si->private;
694 struct buffer *req = si->ob;
695 struct buffer *res = si->ib;
696 int reql;
697 int len;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200698
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200699 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
700 goto out;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200701
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200702 while (1) {
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200703 if (si->st0 == STAT_CLI_INIT) {
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200704 /* Stats output not initialized yet */
705 memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
706 s->data_source = DATA_SRC_STATS;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200707 si->st0 = STAT_CLI_GETREQ;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200708 }
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200709 else if (si->st0 == STAT_CLI_END) {
710 /* Let's close for real now. We just close the request
711 * side, the conditions below will complete if needed.
712 */
713 si->shutw(si);
714 break;
715 }
716 else if (si->st0 == STAT_CLI_GETREQ) {
Willy Tarreau4e33d862009-10-11 23:35:10 +0200717 /* ensure we have some output room left in the event we
718 * would want to return some info right after parsing.
719 */
720 if (buffer_almost_full(si->ib))
721 break;
722
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200723 reql = buffer_si_peekline(si->ob, trash, sizeof(trash));
724 if (reql <= 0) { /* closed or EOL not found */
725 if (reql == 0)
726 break;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200727 si->st0 = STAT_CLI_END;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200728 continue;
729 }
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200730
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200731 /* seek for a possible semi-colon. If we find one, we
732 * replace it with an LF and skip only this part.
733 */
734 for (len = 0; len < reql; len++)
735 if (trash[len] == ';') {
736 trash[len] = '\n';
737 reql = len + 1;
738 break;
739 }
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200740
Willy Tarreau816fc222009-10-04 07:36:58 +0200741 /* now it is time to check that we have a full line,
742 * remove the trailing \n and possibly \r, then cut the
743 * line.
744 */
745 len = reql - 1;
746 if (trash[len] != '\n') {
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200747 si->st0 = STAT_CLI_END;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200748 continue;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200749 }
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200750
Willy Tarreau816fc222009-10-04 07:36:58 +0200751 if (len && trash[len-1] == '\r')
752 len--;
753
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200754 trash[len] = '\0';
755
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200756 si->st0 = STAT_CLI_PROMPT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200757 if (len) {
758 if (strcmp(trash, "quit") == 0) {
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200759 si->st0 = STAT_CLI_END;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200760 continue;
761 }
762 else if (strcmp(trash, "prompt") == 0)
763 si->st1 = !si->st1;
764 else if (strcmp(trash, "help") == 0 ||
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200765 !stats_sock_parse_request(si, trash)) {
766 s->data_ctx.cli.msg = stats_sock_usage_msg;
767 si->st0 = STAT_CLI_PRINT;
768 }
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200769 /* NB: stats_sock_parse_request() may have put
770 * another STAT_CLI_O_* into si->st0.
771 */
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200772 }
773 else if (!si->st1) {
774 /* if prompt is disabled, print help on empty lines,
775 * so that the user at least knows how to enable
776 * prompt and find help.
777 */
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200778 s->data_ctx.cli.msg = stats_sock_usage_msg;
779 si->st0 = STAT_CLI_PRINT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200780 }
781
782 /* re-adjust req buffer */
783 buffer_skip(si->ob, reql);
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200784 req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200785 }
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200786 else { /* output functions: first check if the output buffer is closed then abort */
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200787 if (res->flags & (BF_SHUTR_NOW|BF_SHUTR)) {
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200788 si->st0 = STAT_CLI_END;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200789 continue;
790 }
791
792 switch (si->st0) {
Willy Tarreauea1f5fe2009-10-11 23:12:51 +0200793 case STAT_CLI_PRINT:
794 if (buffer_feed(si->ib, s->data_ctx.cli.msg) < 0)
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200795 si->st0 = STAT_CLI_PROMPT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200796 break;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200797 case STAT_CLI_O_INFO:
Willy Tarreau24955a12009-10-04 11:54:04 +0200798 if (stats_dump_raw_to_buffer(s, res))
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200799 si->st0 = STAT_CLI_PROMPT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200800 break;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200801 case STAT_CLI_O_SESS:
Willy Tarreau7e72a8f2009-10-03 23:55:14 +0200802 if (stats_dump_sess_to_buffer(s, res))
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200803 si->st0 = STAT_CLI_PROMPT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200804 break;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200805 case STAT_CLI_O_ERR: /* errors dump */
Willy Tarreau61b34732009-10-03 23:49:35 +0200806 if (stats_dump_errors_to_buffer(s, res))
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200807 si->st0 = STAT_CLI_PROMPT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200808 break;
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200809 default: /* abnormal state */
810 si->st0 = STAT_CLI_PROMPT;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200811 break;
812 }
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200813
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200814 /* The post-command prompt is either LF alone or LF + '> ' in interactive mode */
815 if (si->st0 == STAT_CLI_PROMPT) {
Willy Tarreau9bcc91e2009-10-10 18:01:44 +0200816 if (buffer_feed(si->ib, si->st1 ? "\n> " : "\n") < 0)
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200817 si->st0 = STAT_CLI_GETREQ;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200818 }
819
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200820 /* If the output functions are still there, it means they require more room. */
Willy Tarreau96fd4b52009-10-04 17:18:35 +0200821 if (si->st0 >= STAT_CLI_OUTPUT)
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200822 break;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200823
824 /* Now we close the output if one of the writers did so,
825 * or if we're not in interactive mode and the request
826 * buffer is empty. This still allows pipelined requests
827 * to be sent in non-interactive mode.
828 */
829 if ((res->flags & (BF_SHUTW|BF_SHUTW_NOW)) || (!si->st1 && !req->send_max)) {
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200830 si->st0 = STAT_CLI_END;
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200831 continue;
832 }
833
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200834 /* switch state back to GETREQ to read next requests */
835 si->st0 = STAT_CLI_GETREQ;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200836 }
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200837 }
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200838
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200839 if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST) && (si->st0 != STAT_CLI_GETREQ)) {
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200840 DPRINTF(stderr, "%s@%d: si to buf closed. req=%08x, res=%08x, st=%d\n",
841 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
842 /* Other size has closed, let's abort if we have no more processing to do
843 * and nothing more to consume. This is comparable to a broken pipe, so
844 * we forward the close to the request side so that it flows upstream to
845 * the client.
846 */
847 si->shutw(si);
848 }
849
Willy Tarreauf5a885f2009-10-04 14:22:18 +0200850 if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && (si->st0 < STAT_CLI_OUTPUT)) {
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200851 DPRINTF(stderr, "%s@%d: buf to si closed. req=%08x, res=%08x, st=%d\n",
852 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
853 /* We have no more processing to do, and nothing more to send, and
854 * the client side has closed. So we'll forward this state downstream
855 * on the response buffer.
856 */
857 si->shutr(si);
858 res->flags |= BF_READ_NULL;
859 }
860
861 /* update all other flags and resync with the other side */
862 si->update(si);
863
864 /* we don't want to expire timeouts while we're processing requests */
865 si->ib->rex = TICK_ETERNITY;
866 si->ob->wex = TICK_ETERNITY;
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200867
Willy Tarreau9a42c0d2009-09-22 19:31:03 +0200868 out:
869 DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rql=%d, rqs=%d, rl=%d, rs=%d\n",
870 __FUNCTION__, __LINE__,
871 si->state, req->flags, res->flags, req->l, req->send_max, res->l, res->send_max);
872
873 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
874 /* check that we have released everything then unregister */
875 stream_int_unregister_handler(si);
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200876 }
Willy Tarreau5ca791d2009-08-16 19:06:42 +0200877}
878
Willy Tarreau24955a12009-10-04 11:54:04 +0200879/* This function is called to send output to the response buffer.
880 * It dumps statistics onto the output buffer <rep> owned by session <s>.
881 * s->data_ctx must have been zeroed first, and the flags properly set.
882 * It returns 0 as long as it does not complete, non-zero upon completion.
883 * Some states are not used but it makes the code more similar to other
884 * functions which handle stats too.
Willy Tarreau3e76e722007-10-17 18:57:38 +0200885 */
Willy Tarreau24955a12009-10-04 11:54:04 +0200886int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep)
Willy Tarreau3e76e722007-10-17 18:57:38 +0200887{
Willy Tarreau3e76e722007-10-17 18:57:38 +0200888 struct proxy *px;
889 struct chunk msg;
Willy Tarreaua8efd362008-01-03 10:19:15 +0100890 unsigned int up;
Willy Tarreau3e76e722007-10-17 18:57:38 +0200891
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +0200892 chunk_init(&msg, trash, sizeof(trash));
Willy Tarreau3e76e722007-10-17 18:57:38 +0200893
894 switch (s->data_state) {
895 case DATA_ST_INIT:
Willy Tarreau24955a12009-10-04 11:54:04 +0200896 /* the function had not been called yet */
Willy Tarreau3e76e722007-10-17 18:57:38 +0200897 s->data_state = DATA_ST_HEAD;
898 /* fall through */
899
900 case DATA_ST_HEAD:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100901 if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +0200902 print_csv_header(&msg);
Willy Tarreau24955a12009-10-04 11:54:04 +0200903 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreaua8efd362008-01-03 10:19:15 +0100904 return 0;
905 }
Willy Tarreau3e76e722007-10-17 18:57:38 +0200906
907 s->data_state = DATA_ST_INFO;
908 /* fall through */
909
910 case DATA_ST_INFO:
Willy Tarreaua8efd362008-01-03 10:19:15 +0100911 up = (now.tv_sec - start_date.tv_sec);
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100912 if (s->data_ctx.stats.flags & STAT_SHOW_INFO) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +0200913 chunk_printf(&msg,
Willy Tarreaua8efd362008-01-03 10:19:15 +0100914 "Name: " PRODUCT_NAME "\n"
915 "Version: " HAPROXY_VERSION "\n"
916 "Release_date: " HAPROXY_DATE "\n"
917 "Nbproc: %d\n"
918 "Process_num: %d\n"
919 "Pid: %d\n"
920 "Uptime: %dd %dh%02dm%02ds\n"
921 "Uptime_sec: %d\n"
922 "Memmax_MB: %d\n"
923 "Ulimit-n: %d\n"
924 "Maxsock: %d\n"
925 "Maxconn: %d\n"
Willy Tarreaua206fa92009-01-25 14:02:00 +0100926 "Maxpipes: %d\n"
Willy Tarreaua8efd362008-01-03 10:19:15 +0100927 "CurrConns: %d\n"
Willy Tarreaua206fa92009-01-25 14:02:00 +0100928 "PipesUsed: %d\n"
929 "PipesFree: %d\n"
Willy Tarreauc7bdf092009-03-21 18:33:52 +0100930 "Tasks: %d\n"
931 "Run_queue: %d\n"
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +0200932 "node: %s\n"
933 "description: %s\n"
Willy Tarreaua8efd362008-01-03 10:19:15 +0100934 "",
935 global.nbproc,
936 relative_pid,
937 pid,
938 up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
939 up,
940 global.rlimit_memmax,
941 global.rlimit_nofile,
Willy Tarreaua206fa92009-01-25 14:02:00 +0100942 global.maxsock, global.maxconn, global.maxpipes,
Willy Tarreauc7bdf092009-03-21 18:33:52 +0100943 actconn, pipes_used, pipes_free,
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +0200944 nb_tasks_cur, run_queue_cur,
945 global.node, global.desc?global.desc:""
Willy Tarreaua8efd362008-01-03 10:19:15 +0100946 );
Willy Tarreau24955a12009-10-04 11:54:04 +0200947 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreaua8efd362008-01-03 10:19:15 +0100948 return 0;
949 }
950
Willy Tarreau3e76e722007-10-17 18:57:38 +0200951 s->data_ctx.stats.px = proxy;
952 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +0100953
954 s->data_ctx.stats.sv = NULL;
955 s->data_ctx.stats.sv_st = 0;
956
Willy Tarreau3e76e722007-10-17 18:57:38 +0200957 s->data_state = DATA_ST_LIST;
958 /* fall through */
959
960 case DATA_ST_LIST:
961 /* dump proxies */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100962 if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
Willy Tarreaua8efd362008-01-03 10:19:15 +0100963 while (s->data_ctx.stats.px) {
964 px = s->data_ctx.stats.px;
965 /* skip the disabled proxies and non-networked ones */
966 if (px->state != PR_STSTOPPED &&
Willy Tarreau24955a12009-10-04 11:54:04 +0200967 (px->cap & (PR_CAP_FE | PR_CAP_BE))) {
Willy Tarreau39f7e6d2008-03-17 21:38:24 +0100968 if (stats_dump_proxy(s, px, NULL) == 0)
Willy Tarreaua8efd362008-01-03 10:19:15 +0100969 return 0;
Willy Tarreau24955a12009-10-04 11:54:04 +0200970 }
Willy Tarreau3e76e722007-10-17 18:57:38 +0200971
Willy Tarreaua8efd362008-01-03 10:19:15 +0100972 s->data_ctx.stats.px = px->next;
973 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
974 }
975 /* here, we just have reached the last proxy */
Willy Tarreau3e76e722007-10-17 18:57:38 +0200976 }
Willy Tarreau3e76e722007-10-17 18:57:38 +0200977
978 s->data_state = DATA_ST_END;
979 /* fall through */
980
981 case DATA_ST_END:
982 s->data_state = DATA_ST_FIN;
Willy Tarreau0a464892008-12-07 18:30:00 +0100983 /* fall through */
Willy Tarreau3e76e722007-10-17 18:57:38 +0200984
985 case DATA_ST_FIN:
986 return 1;
987
988 default:
989 /* unknown state ! */
Willy Tarreau24955a12009-10-04 11:54:04 +0200990 s->data_state = DATA_ST_FIN;
991 return 1;
Willy Tarreau3e76e722007-10-17 18:57:38 +0200992 }
Willy Tarreaub1356cf2008-12-07 16:06:43 +0100993}
994
995
Willy Tarreaub0c9bc42009-10-04 15:56:38 +0200996/* This I/O handler runs as an applet embedded in a stream interface. It is
997 * used to send HTTP stats over a TCP socket. The mechanism is very simple.
998 * si->st0 becomes non-zero once the transfer is finished. The handler
999 * automatically unregisters itself once transfer is complete.
1000 */
1001void http_stats_io_handler(struct stream_interface *si)
1002{
1003 struct session *s = si->private;
1004 struct buffer *req = si->ob;
1005 struct buffer *res = si->ib;
1006
1007 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
1008 goto out;
1009
1010 /* check that the output is not closed */
1011 if (res->flags & (BF_SHUTW|BF_SHUTW_NOW))
1012 si->st0 = 1;
1013
1014 if (!si->st0) {
1015 if (stats_dump_http(s, res, s->be->uri_auth)) {
1016 si->st0 = 1;
1017 si->shutw(si);
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001018 }
1019 }
1020
1021 if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST))
1022 si->shutw(si);
1023
1024 if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && si->st0) {
1025 si->shutr(si);
1026 res->flags |= BF_READ_NULL;
1027 }
1028
1029 /* update all other flags and resync with the other side */
1030 si->update(si);
1031
1032 /* we don't want to expire timeouts while we're processing requests */
1033 si->ib->rex = TICK_ETERNITY;
1034 si->ob->wex = TICK_ETERNITY;
1035
1036 out:
1037 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
1038 /* check that we have released everything then unregister */
1039 stream_int_unregister_handler(si);
1040 }
1041}
1042
1043
Willy Tarreau3e76e722007-10-17 18:57:38 +02001044/*
1045 * Produces statistics data for the session <s>. Expects to be called with
Willy Tarreau1ae3a052008-08-16 10:56:30 +02001046 * client socket shut down on input. It stops by itself by unsetting the
Willy Tarreau72b179a2008-08-28 16:01:32 +02001047 * BF_HIJACK flag from the buffer, which it uses to keep on being called
Willy Tarreau1ae3a052008-08-16 10:56:30 +02001048 * when there is free space in the buffer, of simply by letting an empty buffer
1049 * upon return.s->data_ctx must have been zeroed before the first call, and the
1050 * flags set. It returns 0 if it had to stop writing data and an I/O is needed,
1051 * 1 if the dump is finished and the session must be closed, or -1 in case of
1052 * any error.
Willy Tarreau91861262007-10-17 17:06:05 +02001053 */
Willy Tarreau0a464892008-12-07 18:30:00 +01001054int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
Willy Tarreau91861262007-10-17 17:06:05 +02001055{
Willy Tarreau91861262007-10-17 17:06:05 +02001056 struct proxy *px;
1057 struct chunk msg;
1058 unsigned int up;
1059
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001060 chunk_init(&msg, trash, sizeof(trash));
Willy Tarreau91861262007-10-17 17:06:05 +02001061
1062 switch (s->data_state) {
1063 case DATA_ST_INIT:
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001064 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001065 "HTTP/1.0 200 OK\r\n"
1066 "Cache-Control: no-cache\r\n"
1067 "Connection: close\r\n"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001068 "Content-Type: %s\r\n",
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001069 (s->data_ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
Willy Tarreau91861262007-10-17 17:06:05 +02001070
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001071 if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH))
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001072 chunk_printf(&msg, "Refresh: %d\r\n",
Willy Tarreau91861262007-10-17 17:06:05 +02001073 uri->refresh);
1074
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001075 chunk_printf(&msg, "\r\n");
Willy Tarreau91861262007-10-17 17:06:05 +02001076
1077 s->txn.status = 200;
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001078 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau56a560a2009-09-22 19:27:35 +02001079 return 0;
1080
Willy Tarreau91861262007-10-17 17:06:05 +02001081 if (!(s->flags & SN_ERR_MASK)) // this is not really an error but it is
1082 s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
1083 if (!(s->flags & SN_FINST_MASK))
1084 s->flags |= SN_FINST_R;
1085
1086 if (s->txn.meth == HTTP_METH_HEAD) {
1087 /* that's all we return in case of HEAD request */
1088 s->data_state = DATA_ST_FIN;
Willy Tarreau91861262007-10-17 17:06:05 +02001089 return 1;
1090 }
1091
1092 s->data_state = DATA_ST_HEAD; /* let's start producing data */
1093 /* fall through */
1094
1095 case DATA_ST_HEAD:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001096 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki034550b2010-01-05 18:44:44 +01001097 /* WARNING! This must fit in the first buffer !!! */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001098 chunk_printf(&msg,
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001099 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
1100 "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
Willy Tarreau1d45b7c2009-08-16 10:29:18 +02001101 "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001102 "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
1103 "<style type=\"text/css\"><!--\n"
1104 "body {"
Willy Tarreaua94f2d22009-05-10 20:08:10 +02001105 " font-family: arial, helvetica, sans-serif;"
Willy Tarreau91861262007-10-17 17:06:05 +02001106 " font-size: 12px;"
1107 " font-weight: normal;"
1108 " color: black;"
1109 " background: white;"
1110 "}\n"
1111 "th,td {"
Willy Tarreaua94f2d22009-05-10 20:08:10 +02001112 " font-size: 10px;"
Willy Tarreau91861262007-10-17 17:06:05 +02001113 "}\n"
1114 "h1 {"
Willy Tarreauda6721b2009-07-15 10:07:05 +02001115 " font-size: x-large;"
Willy Tarreau91861262007-10-17 17:06:05 +02001116 " margin-bottom: 0.5em;"
1117 "}\n"
1118 "h2 {"
1119 " font-family: helvetica, arial;"
1120 " font-size: x-large;"
1121 " font-weight: bold;"
1122 " font-style: italic;"
1123 " color: #6020a0;"
1124 " margin-top: 0em;"
1125 " margin-bottom: 0em;"
1126 "}\n"
1127 "h3 {"
1128 " font-family: helvetica, arial;"
1129 " font-size: 16px;"
1130 " font-weight: bold;"
1131 " color: #b00040;"
1132 " background: #e8e8d0;"
1133 " margin-top: 0em;"
1134 " margin-bottom: 0em;"
1135 "}\n"
1136 "li {"
1137 " margin-top: 0.25em;"
1138 " margin-right: 2em;"
1139 "}\n"
1140 ".hr {margin-top: 0.25em;"
1141 " border-color: black;"
1142 " border-bottom-style: solid;"
1143 "}\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001144 ".titre {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001145 ".total {background: #20D0D0;color: #ffff80;}\n"
1146 ".frontend {background: #e8e8d0;}\n"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001147 ".socket {background: #d0d0d0;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001148 ".backend {background: #e8e8d0;}\n"
1149 ".active0 {background: #ff9090;}\n"
1150 ".active1 {background: #ffd020;}\n"
1151 ".active2 {background: #ffffa0;}\n"
1152 ".active3 {background: #c0ffc0;}\n"
Willy Tarreau2ea81932007-11-30 12:04:38 +01001153 ".active4 {background: #ffffa0;}\n" /* NOLB state shows same as going down */
1154 ".active5 {background: #a0e0a0;}\n" /* NOLB state shows darker than up */
1155 ".active6 {background: #e0e0e0;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001156 ".backup0 {background: #ff9090;}\n"
1157 ".backup1 {background: #ff80ff;}\n"
1158 ".backup2 {background: #c060ff;}\n"
1159 ".backup3 {background: #b0d0ff;}\n"
Willy Tarreau2ea81932007-11-30 12:04:38 +01001160 ".backup4 {background: #c060ff;}\n" /* NOLB state shows same as going down */
1161 ".backup5 {background: #90b0e0;}\n" /* NOLB state shows same as going down */
1162 ".backup6 {background: #e0e0e0;}\n"
Cyril Bontécd19e512010-01-31 22:34:03 +01001163 ".maintain {background: #c07820;}\n"
Willy Tarreauda6721b2009-07-15 10:07:05 +02001164 ".rls {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001165 "\n"
Krzysztof Piotr Oledzki1f672852009-10-24 14:24:30 +02001166 "a.px:link {color: #ffff40; text-decoration: none;}"
1167 "a.px:visited {color: #ffff40; text-decoration: none;}"
1168 "a.px:hover {color: #ffffff; text-decoration: none;}"
1169 "a.lfsb:link {color: #000000; text-decoration: none;}"
1170 "a.lfsb:visited {color: #000000; text-decoration: none;}"
1171 "a.lfsb:hover {color: #505050; text-decoration: none;}"
1172 "\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001173 "table.tbl { border-collapse: collapse; border-style: none;}\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001174 "table.tbl td { text-align: right; border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray; white-space: nowrap;}\n"
1175 "table.tbl td.ac { text-align: center;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001176 "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001177 "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
Willy Tarreauda6721b2009-07-15 10:07:05 +02001178 "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001179 "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001180 "\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001181 "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
1182 "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
1183 "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
Willy Tarreaue0454092010-02-26 12:29:07 +01001184 "u {text-decoration:none; border-bottom: 1px dotted black;}\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001185 "-->\n"
Willy Tarreau1d45b7c2009-08-16 10:29:18 +02001186 "</style></head>\n",
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001187 (uri->flags&ST_SHNODE) ? " on " : "",
1188 (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
Willy Tarreau1d45b7c2009-08-16 10:29:18 +02001189 );
Willy Tarreau55bb8452007-10-17 18:44:57 +02001190 } else {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001191 print_csv_header(&msg);
Willy Tarreau55bb8452007-10-17 18:44:57 +02001192 }
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001193 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +02001194 return 0;
1195
1196 s->data_state = DATA_ST_INFO;
1197 /* fall through */
1198
1199 case DATA_ST_INFO:
1200 up = (now.tv_sec - start_date.tv_sec);
1201
1202 /* WARNING! this has to fit the first packet too.
1203 * We are around 3.5 kB, add adding entries will
1204 * become tricky if we want to support 4kB buffers !
1205 */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001206 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001207 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001208 "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
1209 PRODUCT_NAME "%s</a></h1>\n"
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001210 "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001211 "<hr width=\"100%%\" class=\"hr\">\n"
1212 "<h3>&gt; General process information</h3>\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001213 "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
Willy Tarreaua8efd362008-01-03 10:19:15 +01001214 "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001215 "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001216 "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
1217 "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>maxpipes = </b> %d<br>\n"
1218 "current conns = %d; current pipes = %d/%d<br>\n"
1219 "Running tasks: %d/%d<br>\n"
Willy Tarreau91861262007-10-17 17:06:05 +02001220 "</td><td align=\"center\" nowrap>\n"
1221 "<table class=\"lgd\"><tr>\n"
1222 "<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
1223 "<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
1224 "</tr><tr>\n"
1225 "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
1226 "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
1227 "</tr><tr>\n"
1228 "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
1229 "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
1230 "</tr><tr>\n"
1231 "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
Willy Tarreau2ea81932007-11-30 12:04:38 +01001232 "<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
Cyril Bontécd19e512010-01-31 22:34:03 +01001233 "</tr><tr>\n"
1234 "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) &nbsp;</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001235 "</tr></table>\n"
Willy Tarreau2ea81932007-11-30 12:04:38 +01001236 "Note: UP with load-balancing disabled is reported as \"NOLB\"."
Willy Tarreau91861262007-10-17 17:06:05 +02001237 "</td>"
1238 "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
1239 "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
1240 "",
1241 (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001242 pid, (uri->flags&ST_SHNODE) ? " on " : "", (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : "",
1243 (uri->flags&ST_SHDESC)? ": " : "", (uri->flags&ST_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
1244 pid, relative_pid, global.nbproc,
Willy Tarreau91861262007-10-17 17:06:05 +02001245 up / 86400, (up % 86400) / 3600,
1246 (up % 3600) / 60, (up % 60),
1247 global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
1248 global.rlimit_memmax ? " MB" : "",
1249 global.rlimit_nofile,
Willy Tarreaua206fa92009-01-25 14:02:00 +01001250 global.maxsock, global.maxconn, global.maxpipes,
Willy Tarreauc7bdf092009-03-21 18:33:52 +01001251 actconn, pipes_used, pipes_used+pipes_free,
1252 run_queue_cur, nb_tasks_cur
Willy Tarreau91861262007-10-17 17:06:05 +02001253 );
Willy Tarreaub1356cf2008-12-07 16:06:43 +01001254
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001255 if (s->data_ctx.stats.flags & STAT_HIDE_DOWN)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001256 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001257 "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
1258 uri->uri_prefix,
1259 "",
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001260 (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001261 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001262 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001263 "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
1264 uri->uri_prefix,
1265 ";up",
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001266 (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
Willy Tarreau91861262007-10-17 17:06:05 +02001267
Willy Tarreau55bb8452007-10-17 18:44:57 +02001268 if (uri->refresh > 0) {
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001269 if (s->data_ctx.stats.flags & STAT_NO_REFRESH)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001270 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001271 "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
1272 uri->uri_prefix,
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001273 (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
Willy Tarreau91861262007-10-17 17:06:05 +02001274 "");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001275 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001276 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001277 "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
1278 uri->uri_prefix,
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001279 (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
Willy Tarreau91861262007-10-17 17:06:05 +02001280 ";norefresh");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001281 }
Willy Tarreau91861262007-10-17 17:06:05 +02001282
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001283 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001284 "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
1285 uri->uri_prefix,
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001286 (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1287 (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
Willy Tarreau91861262007-10-17 17:06:05 +02001288
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001289 chunk_printf(&msg,
Willy Tarreau5031e6a2007-10-18 11:05:48 +02001290 "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
1291 uri->uri_prefix,
1292 (uri->refresh > 0) ? ";norefresh" : "");
1293
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001294 chunk_printf(&msg,
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001295 "</ul></td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001296 "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
1297 "<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
1298 "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
1299 "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
1300 "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
1301 "</ul>"
1302 "</td>"
1303 "</tr></table>\n"
1304 ""
1305 );
Willy Tarreaub1356cf2008-12-07 16:06:43 +01001306
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001307 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +02001308 return 0;
1309 }
Willy Tarreau91861262007-10-17 17:06:05 +02001310
Willy Tarreau91861262007-10-17 17:06:05 +02001311 s->data_ctx.stats.px = proxy;
1312 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
1313 s->data_state = DATA_ST_LIST;
1314 /* fall through */
1315
1316 case DATA_ST_LIST:
1317 /* dump proxies */
1318 while (s->data_ctx.stats.px) {
Willy Tarreau4e33d862009-10-11 23:35:10 +02001319 if (buffer_almost_full(rep))
1320 return 0;
Willy Tarreau91861262007-10-17 17:06:05 +02001321 px = s->data_ctx.stats.px;
1322 /* skip the disabled proxies and non-networked ones */
1323 if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001324 if (stats_dump_proxy(s, px, uri) == 0)
Willy Tarreau91861262007-10-17 17:06:05 +02001325 return 0;
1326
1327 s->data_ctx.stats.px = px->next;
1328 s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
1329 }
1330 /* here, we just have reached the last proxy */
1331
1332 s->data_state = DATA_ST_END;
1333 /* fall through */
1334
1335 case DATA_ST_END:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001336 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001337 chunk_printf(&msg, "</body></html>\n");
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001338 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +02001339 return 0;
1340 }
Willy Tarreau91861262007-10-17 17:06:05 +02001341
1342 s->data_state = DATA_ST_FIN;
1343 /* fall through */
1344
1345 case DATA_ST_FIN:
Willy Tarreau91861262007-10-17 17:06:05 +02001346 return 1;
1347
1348 default:
1349 /* unknown state ! */
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001350 s->data_state = DATA_ST_FIN;
Willy Tarreau91861262007-10-17 17:06:05 +02001351 return -1;
1352 }
1353}
1354
1355
1356/*
1357 * Dumps statistics for a proxy.
1358 * Returns 0 if it had to stop dumping data because of lack of buffer space,
1359 * ot non-zero if everything completed.
1360 */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001361int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
Willy Tarreau91861262007-10-17 17:06:05 +02001362{
1363 struct buffer *rep = s->rep;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01001364 struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001365 struct listener *l;
Willy Tarreau91861262007-10-17 17:06:05 +02001366 struct chunk msg;
1367
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001368 chunk_init(&msg, trash, sizeof(trash));
Willy Tarreau91861262007-10-17 17:06:05 +02001369
1370 switch (s->data_ctx.stats.px_st) {
1371 case DATA_ST_PX_INIT:
1372 /* we are on a new proxy */
1373
1374 if (uri && uri->scope) {
1375 /* we have a limited scope, we have to check the proxy name */
1376 struct stat_scope *scope;
1377 int len;
1378
1379 len = strlen(px->id);
1380 scope = uri->scope;
1381
1382 while (scope) {
1383 /* match exact proxy name */
1384 if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
1385 break;
1386
1387 /* match '.' which means 'self' proxy */
Willy Tarreau1388a3a2007-10-18 16:38:37 +02001388 if (!strcmp(scope->px_id, ".") && px == s->be)
Willy Tarreau91861262007-10-17 17:06:05 +02001389 break;
1390 scope = scope->next;
1391 }
1392
1393 /* proxy name not found : don't dump anything */
1394 if (scope == NULL)
1395 return 1;
1396 }
1397
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001398 if ((s->data_ctx.stats.flags & STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01001399 (px->uuid != s->data_ctx.stats.iid))
1400 return 1;
1401
Willy Tarreau91861262007-10-17 17:06:05 +02001402 s->data_ctx.stats.px_st = DATA_ST_PX_TH;
1403 /* fall through */
1404
1405 case DATA_ST_PX_TH:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001406 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau55bb8452007-10-17 18:44:57 +02001407 /* print a new table */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001408 chunk_printf(&msg,
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001409 "<table class=\"tbl\" width=\"100%%\">\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001410 "<tr class=\"titre\">"
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01001411 "<th class=\"pxname\" width=\"10%%\"");
1412
1413 if (uri->flags&ST_SHLGNDS) {
1414 /* cap, mode, id */
1415 chunk_printf(&msg, " title=\"cap: %s, mode: %s, id: %d",
1416 proxy_cap_str(px->cap), proxy_mode_str(px->mode),
1417 px->uuid);
1418
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01001419 chunk_printf(&msg, "\"");
1420 }
1421
1422 chunk_printf(&msg,
Willy Tarreaue0454092010-02-26 12:29:07 +01001423 ">%s<a name=\"%s\"></a>"
1424 "<a class=px href=\"#%s\">%s</a>%s</th>"
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001425 "<th class=\"%s\" width=\"90%%\">%s</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001426 "</tr>\n"
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001427 "</table>\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001428 "<table class=\"tbl\" width=\"100%%\">\n"
1429 "<tr class=\"titre\">"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001430 "<th rowspan=2></th>"
Willy Tarreaua3e49422009-05-10 19:19:41 +02001431 "<th colspan=3>Queue</th>"
1432 "<th colspan=3>Session rate</th><th colspan=5>Sessions</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001433 "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001434 "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001435 "<th colspan=9>Server</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001436 "</tr>\n"
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001437 "<tr class=\"titre\">"
Willy Tarreaua3e49422009-05-10 19:19:41 +02001438 "<th>Cur</th><th>Max</th><th>Limit</th>"
Elijah Epifanovacafc5f2007-10-25 20:15:38 +02001439 "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
Willy Tarreaua3e49422009-05-10 19:19:41 +02001440 "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001441 "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001442 "<th>Resp</th><th>Retr</th><th>Redis</th>"
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001443 "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001444 "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
1445 "<th>Thrtle</th>\n"
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001446 "</tr>",
Willy Tarreaue0454092010-02-26 12:29:07 +01001447 (uri->flags & ST_SHLGNDS)?"<u>":"",
Krzysztof Piotr Oledzki1f672852009-10-24 14:24:30 +02001448 px->id, px->id, px->id,
Willy Tarreaue0454092010-02-26 12:29:07 +01001449 (uri->flags & ST_SHLGNDS)?"</u>":"",
Krzysztof Piotr Oledzki48cb2ae2009-10-02 22:51:14 +02001450 px->desc ? "desc" : "empty", px->desc ? px->desc : "");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001451
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001452 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +02001453 return 0;
1454 }
Willy Tarreau91861262007-10-17 17:06:05 +02001455
1456 s->data_ctx.stats.px_st = DATA_ST_PX_FE;
1457 /* fall through */
1458
1459 case DATA_ST_PX_FE:
1460 /* print the frontend */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001461 if ((px->cap & PR_CAP_FE) &&
1462 (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) {
1463 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001464 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001465 /* name, queue */
Krzysztof Piotr Oledzki516ed492009-10-22 22:48:09 +02001466 "<tr class=\"frontend\"><td class=ac>"
Krzysztof Piotr Oledzki1f672852009-10-24 14:24:30 +02001467 "<a name=\"%s/Frontend\"></a>"
1468 "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td><td colspan=3></td>"
Willy Tarreaub44939a2010-02-26 11:35:39 +01001469 "",
1470 px->id, px->id);
1471
1472 if (px->mode == PR_MODE_HTTP) {
1473 chunk_printf(&msg,
1474 /* sessions rate : current, max, limit */
Willy Tarreaue0454092010-02-26 12:29:07 +01001475 "<td title=\"Cur: %u req/s\"><u>%s</u></td><td title=\"Max: %u req/s\"><u>%s</u></td><td>%s</td>"
Willy Tarreaub44939a2010-02-26 11:35:39 +01001476 "",
1477 read_freq_ctr(&px->fe_req_per_sec),
1478 U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
1479 px->counters.fe_rps_max,
1480 U2H2(px->counters.fe_sps_max),
1481 LIM2A2(px->fe_sps_lim, "-"));
1482 } else {
1483 chunk_printf(&msg,
1484 /* sessions rate : current, max, limit */
1485 "<td>%s</td><td>%s</td><td>%s</td>"
1486 "",
1487 U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
1488 U2H1(px->counters.fe_sps_max), LIM2A2(px->fe_sps_lim, "-"));
1489 }
1490
1491 chunk_printf(&msg,
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001492 /* sessions: current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001493 "<td>%s</td><td>%s</td><td>%s</td>"
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001494 "<td"
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02001495 "",
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001496 U2H3(px->feconn), U2H4(px->counters.feconn_max), U2H5(px->maxconn));
1497
1498 /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1499 if (px->mode == PR_MODE_HTTP) {
1500 int i;
1501
Willy Tarreaub44939a2010-02-26 11:35:39 +01001502 chunk_printf(&msg, " title=\"%lld requests:", px->counters.cum_fe_req);
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001503
1504 for (i = 1; i < 6; i++)
Willy Tarreau24657792010-02-26 10:30:28 +01001505 chunk_printf(&msg, " %dxx=%lld,", i, px->counters.fe.http.rsp[i]);
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001506
Willy Tarreau24657792010-02-26 10:30:28 +01001507 chunk_printf(&msg, " other=%lld\"", px->counters.fe.http.rsp[0]);
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001508 }
1509
1510 chunk_printf(&msg,
1511 /* sessions: total, lbtot */
Willy Tarreaue0454092010-02-26 12:29:07 +01001512 ">%s%s%s</td><td></td>"
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001513 /* bytes : in, out */
1514 "<td>%s</td><td>%s</td>"
1515 "",
Willy Tarreaue0454092010-02-26 12:29:07 +01001516 (px->mode == PR_MODE_HTTP)?"<u>":"",
1517 U2H6(px->counters.cum_feconn),
1518 (px->mode == PR_MODE_HTTP)?"</u>":"",
1519 U2H7(px->counters.bytes_in), U2H8(px->counters.bytes_out));
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02001520
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001521 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001522 /* denied: req, resp */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001523 "<td>%s</td><td>%s</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001524 /* errors : request, connect, response */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001525 "<td>%s</td><td></td><td></td>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001526 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001527 "<td></td><td></td>"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001528 /* server status : reflect frontend status */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001529 "<td class=ac>%s</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001530 /* rest of server: nothing */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001531 "<td class=ac colspan=8></td></tr>"
Willy Tarreau91861262007-10-17 17:06:05 +02001532 "",
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02001533 U2H0(px->counters.denied_req), U2H1(px->counters.denied_resp),
1534 U2H2(px->counters.failed_req),
Willy Tarreau91861262007-10-17 17:06:05 +02001535 px->state == PR_STRUN ? "OPEN" :
1536 px->state == PR_STIDLE ? "FULL" : "STOP");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001537 } else {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001538 chunk_printf(&msg,
Willy Tarreau55bb8452007-10-17 18:44:57 +02001539 /* pxid, name, queue cur, queue max, */
1540 "%s,FRONTEND,,,"
Willy Tarreauddbb82f2007-12-05 10:34:49 +01001541 /* sessions : current, max, limit, total */
Willy Tarreau3b88d442009-04-11 20:44:08 +02001542 "%d,%d,%d,%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001543 /* bytes : in, out */
1544 "%lld,%lld,"
1545 /* denied: req, resp */
Willy Tarreau3b88d442009-04-11 20:44:08 +02001546 "%lld,%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001547 /* errors : request, connect, response */
Willy Tarreau3b88d442009-04-11 20:44:08 +02001548 "%lld,,,"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001549 /* warnings: retries, redispatches */
1550 ",,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02001551 /* server status : reflect frontend status */
1552 "%s,"
1553 /* rest of server: nothing */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +02001554 ",,,,,,,,"
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01001555 /* pid, iid, sid, throttle, lbtot, tracked, type */
1556 "%d,%d,0,,,,%d,"
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001557 /* rate, rate_lim, rate_max */
Willy Tarreau8f208ec2009-05-10 19:01:49 +02001558 "%u,%u,%u,"
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001559 /* check_status, check_code, check_duration */
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001560 ",,,",
Willy Tarreau55bb8452007-10-17 18:44:57 +02001561 px->id,
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02001562 px->feconn, px->counters.feconn_max, px->maxconn, px->counters.cum_feconn,
1563 px->counters.bytes_in, px->counters.bytes_out,
1564 px->counters.denied_req, px->counters.denied_resp,
1565 px->counters.failed_req,
Willy Tarreau55bb8452007-10-17 18:44:57 +02001566 px->state == PR_STRUN ? "OPEN" :
Willy Tarreaudcd47712007-11-04 23:35:08 +01001567 px->state == PR_STIDLE ? "FULL" : "STOP",
Willy Tarreau7f062c42009-03-05 18:43:00 +01001568 relative_pid, px->uuid, STATS_TYPE_FE,
Willy Tarreau8f208ec2009-05-10 19:01:49 +02001569 read_freq_ctr(&px->fe_sess_per_sec),
Willy Tarreauac68c5d2009-10-04 23:12:44 +02001570 px->fe_sps_lim, px->counters.fe_sps_max);
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001571
1572 /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
1573 if (px->mode == PR_MODE_HTTP) {
1574 int i;
1575
1576 for (i=1; i<6; i++)
Willy Tarreau24657792010-02-26 10:30:28 +01001577 chunk_printf(&msg, "%lld,", px->counters.fe.http.rsp[i]);
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001578
Willy Tarreau24657792010-02-26 10:30:28 +01001579 chunk_printf(&msg, "%lld,", px->counters.fe.http.rsp[0]);
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001580 } else {
1581 chunk_printf(&msg, ",,,,,,");
1582 }
1583
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01001584 /* failed health analyses */
1585 chunk_printf(&msg, ",");
1586
Willy Tarreaud9b587f2010-02-26 10:05:55 +01001587 /* requests : req_rate, req_rate_max, req_tot, */
1588 chunk_printf(&msg, "%u,%u,%lld,",
1589 read_freq_ctr(&px->fe_req_per_sec),
1590 px->counters.fe_rps_max, px->counters.cum_fe_req);
1591
Willy Tarreauae526782010-03-04 20:34:23 +01001592 /* errors: cli_aborts, srv_aborts */
1593 chunk_printf(&msg, ",,");
1594
Krzysztof Piotr Oledzkide71d162009-10-24 15:36:15 +02001595 /* finish with EOL */
1596 chunk_printf(&msg, "\n");
Willy Tarreau55bb8452007-10-17 18:44:57 +02001597 }
Willy Tarreau91861262007-10-17 17:06:05 +02001598
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02001599 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +02001600 return 0;
1601 }
1602
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001603 s->data_ctx.stats.l = px->listen; /* may be NULL */
1604 s->data_ctx.stats.px_st = DATA_ST_PX_LI;
1605 /* fall through */
1606
1607 case DATA_ST_PX_LI:
1608 /* stats.l has been initialized above */
1609 for (; s->data_ctx.stats.l != NULL; s->data_ctx.stats.l = l->next) {
Willy Tarreau4e33d862009-10-11 23:35:10 +02001610 if (buffer_almost_full(rep))
1611 return 0;
1612
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001613 l = s->data_ctx.stats.l;
1614 if (!l->counters)
1615 continue;
1616
1617 if (s->data_ctx.stats.flags & STAT_BOUND) {
1618 if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SO)))
1619 break;
1620
1621 if (s->data_ctx.stats.sid != -1 && l->luid != s->data_ctx.stats.sid)
1622 continue;
1623 }
1624
1625 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01001626 chunk_printf(&msg, "<tr class=socket><td class=ac");
1627
1628 if (uri->flags&ST_SHLGNDS) {
1629 char str[INET6_ADDRSTRLEN], *fmt = NULL;
1630 int port;
1631
1632 chunk_printf(&msg, " title=\"IP: ");
1633
1634 port = (l->addr.ss_family == AF_INET6)
1635 ? ntohs(((struct sockaddr_in6 *)(&l->addr))->sin6_port)
1636 : ntohs(((struct sockaddr_in *)(&l->addr))->sin_port);
1637
1638 if (l->addr.ss_family == AF_INET) {
1639 if (inet_ntop(AF_INET,
1640 (const void *)&((struct sockaddr_in *)&l->addr)->sin_addr,
1641 str, sizeof(str)))
1642 fmt = "%s:%d";
1643 } else {
1644 if (inet_ntop(AF_INET6,
1645 (const void *)&((struct sockaddr_in6 *)(&l->addr))->sin6_addr,
1646 str, sizeof(str)))
1647 fmt = "[%s]:%d";
1648 }
1649
1650 if (fmt)
1651 chunk_printf(&msg, fmt, str, port);
1652 else
1653 chunk_printf(&msg, "(%s)", strerror(errno));
1654
1655 /* id */
1656 chunk_printf(&msg, ", id: %d", l->luid);
1657
1658 chunk_printf(&msg, "\"");
1659 }
1660
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001661 chunk_printf(&msg,
1662 /* name, queue */
Willy Tarreaue0454092010-02-26 12:29:07 +01001663 ">%s<a name=\"%s/+%s\"></a>"
1664 "<a class=lfsb href=\"#%s/+%s\">%s</a></td><td colspan=3>%s</td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001665 /* sessions rate: current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001666 "<td colspan=3>&nbsp;</td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001667 /* sessions: current, max, limit, total, lbtot */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001668 "<td>%s</td><td>%s</td><td>%s</td>"
1669 "<td>%s</td><td>&nbsp;</td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001670 /* bytes: in, out */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001671 "<td>%s</td><td>%s</td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001672 "",
Willy Tarreaue0454092010-02-26 12:29:07 +01001673 (uri->flags & ST_SHLGNDS)?"<u>":"",
Krzysztof Piotr Oledzki2ec025d2010-01-04 11:33:32 +01001674 px->id, l->name, px->id, l->name, l->name,
Willy Tarreaue0454092010-02-26 12:29:07 +01001675 (uri->flags & ST_SHLGNDS)?"</u>":"",
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001676 U2H3(l->nbconn), U2H4(l->counters->conn_max), U2H5(l->maxconn),
1677 U2H6(l->counters->cum_conn), U2H7(l->counters->bytes_in), U2H8(l->counters->bytes_out));
1678
1679 chunk_printf(&msg,
1680 /* denied: req, resp */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001681 "<td>%s</td><td>%s</td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001682 /* errors: request, connect, response */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001683 "<td>%s</td><td></td><td></td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001684 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001685 "<td></td><td></td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001686 /* server status: reflect listener status */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001687 "<td class=ac>%s</td>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001688 /* rest of server: nothing */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001689 "<td class=ac colspan=8></td></tr>"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001690 "",
1691 U2H0(l->counters->denied_req), U2H1(l->counters->denied_resp),
1692 U2H2(l->counters->failed_req),
1693 (l->nbconn < l->maxconn) ? "OPEN" : "FULL");
1694 } else {
1695 chunk_printf(&msg,
1696 /* pxid, name, queue cur, queue max, */
1697 "%s,%s,,,"
1698 /* sessions: current, max, limit, total */
1699 "%d,%d,%d,%lld,"
1700 /* bytes: in, out */
1701 "%lld,%lld,"
1702 /* denied: req, resp */
1703 "%lld,%lld,"
1704 /* errors: request, connect, response */
1705 "%lld,,,"
1706 /* warnings: retries, redispatches */
1707 ",,"
1708 /* server status: reflect listener status */
1709 "%s,"
1710 /* rest of server: nothing */
1711 ",,,,,,,,"
1712 /* pid, iid, sid, throttle, lbtot, tracked, type */
1713 "%d,%d,%d,,,,%d,"
1714 /* rate, rate_lim, rate_max */
1715 ",,,"
1716 /* check_status, check_code, check_duration */
1717 ",,,"
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02001718 /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
1719 ",,,,,,"
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01001720 /* failed health analyses */
1721 ","
Willy Tarreaud9b587f2010-02-26 10:05:55 +01001722 /* requests : req_rate, req_rate_max, req_tot, */
1723 ",,,"
Willy Tarreauae526782010-03-04 20:34:23 +01001724 /* errors: cli_aborts, srv_aborts */
1725 ",,"
Krzysztof Piotr Oledzkiaeebf9b2009-10-04 15:43:17 +02001726 "\n",
1727 px->id, l->name,
1728 l->nbconn, l->counters->conn_max,
1729 l->maxconn, l->counters->cum_conn,
1730 l->counters->bytes_in, l->counters->bytes_out,
1731 l->counters->denied_req, l->counters->denied_resp,
1732 l->counters->failed_req,
1733 (l->nbconn < l->maxconn) ? "OPEN" : "FULL",
1734 relative_pid, px->uuid, l->luid, STATS_TYPE_SO);
1735 }
1736
1737 if (buffer_feed_chunk(rep, &msg) >= 0)
1738 return 0;
1739 }
1740
Willy Tarreau91861262007-10-17 17:06:05 +02001741 s->data_ctx.stats.sv = px->srv; /* may be NULL */
1742 s->data_ctx.stats.px_st = DATA_ST_PX_SV;
1743 /* fall through */
1744
1745 case DATA_ST_PX_SV:
1746 /* stats.sv has been initialized above */
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01001747 for (; s->data_ctx.stats.sv != NULL; s->data_ctx.stats.sv = sv->next) {
Willy Tarreau2ea81932007-11-30 12:04:38 +01001748 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 +02001749
Willy Tarreau4e33d862009-10-11 23:35:10 +02001750 if (buffer_almost_full(rep))
1751 return 0;
1752
Willy Tarreau91861262007-10-17 17:06:05 +02001753 sv = s->data_ctx.stats.sv;
1754
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001755 if (s->data_ctx.stats.flags & STAT_BOUND) {
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01001756 if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV)))
1757 break;
1758
1759 if (s->data_ctx.stats.sid != -1 && sv->puid != s->data_ctx.stats.sid)
1760 continue;
1761 }
1762
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01001763 if (sv->tracked)
1764 svs = sv->tracked;
1765 else
1766 svs = sv;
1767
Willy Tarreau91861262007-10-17 17:06:05 +02001768 /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01001769 if (!(svs->state & SRV_CHECKED))
Willy Tarreau2ea81932007-11-30 12:04:38 +01001770 sv_state = 6;
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01001771 else if (svs->state & SRV_RUNNING) {
1772 if (svs->health == svs->rise + svs->fall - 1)
Willy Tarreau91861262007-10-17 17:06:05 +02001773 sv_state = 3; /* UP */
1774 else
1775 sv_state = 2; /* going down */
Willy Tarreau2ea81932007-11-30 12:04:38 +01001776
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01001777 if (svs->state & SRV_GOINGDOWN)
Willy Tarreau2ea81932007-11-30 12:04:38 +01001778 sv_state += 2;
1779 }
Willy Tarreau91861262007-10-17 17:06:05 +02001780 else
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01001781 if (svs->health)
Willy Tarreau91861262007-10-17 17:06:05 +02001782 sv_state = 1; /* going up */
1783 else
1784 sv_state = 0; /* DOWN */
1785
Cyril Bonté0dae5852010-02-03 00:26:28 +01001786 if (((sv_state == 0) || (sv->state & SRV_MAINTAIN)) && (s->data_ctx.stats.flags & STAT_HIDE_DOWN)) {
Willy Tarreau91861262007-10-17 17:06:05 +02001787 /* do not report servers which are DOWN */
1788 s->data_ctx.stats.sv = sv->next;
1789 continue;
1790 }
1791
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01001792 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Willy Tarreau2ea81932007-11-30 12:04:38 +01001793 static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d &uarr;",
1794 "UP %d/%d &darr;", "UP",
1795 "NOLB %d/%d &darr;", "NOLB",
1796 "<i>no check</i>" };
Cyril Bonté0dae5852010-02-03 00:26:28 +01001797 if ((sv->state & SRV_MAINTAIN) || (svs->state & SRV_MAINTAIN)) {
Cyril Bontécd19e512010-01-31 22:34:03 +01001798 chunk_printf(&msg,
1799 /* name */
1800 "<tr class=\"maintain\"><td class=ac"
1801 );
1802 }
1803 else {
1804 chunk_printf(&msg,
1805 /* name */
1806 "<tr class=\"%s%d\"><td class=ac",
1807 (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state);
1808 }
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01001809
1810 if (uri->flags&ST_SHLGNDS) {
1811 char str[INET6_ADDRSTRLEN];
1812
1813 chunk_printf(&msg, " title=\"IP: ");
1814
1815 /* IP */
1816 if (inet_ntop(sv->addr.sin_family, &sv->addr.sin_addr, str, sizeof(str)))
1817 chunk_printf(&msg, "%s:%d", str, htons(sv->addr.sin_port));
1818 else
1819 chunk_printf(&msg, "(%s)", strerror(errno));
1820
1821 /* id */
1822 chunk_printf(&msg, ", id: %d", sv->puid);
1823
1824 /* cookie */
1825 if (sv->cookie) {
1826 struct chunk src;
1827
1828 chunk_printf(&msg, ", cookie: '");
1829
1830 chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
1831 chunk_htmlencode(&msg, &src);
1832
1833 chunk_printf(&msg, "'");
1834 }
1835
1836 chunk_printf(&msg, "\"");
1837 }
1838
1839 chunk_printf(&msg,
Willy Tarreaue0454092010-02-26 12:29:07 +01001840 ">%s<a name=\"%s/%s\"></a>"
1841 "<a class=lfsb href=\"#%s/%s\">%s</a>%s</td>"
Elijah Epifanovacafc5f2007-10-25 20:15:38 +02001842 /* queue : current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001843 "<td>%s</td><td>%s</td><td>%s</td>"
Willy Tarreaua3e49422009-05-10 19:19:41 +02001844 /* sessions rate : current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001845 "<td>%s</td><td>%s</td><td></td>"
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02001846 /* sessions: current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001847 "<td>%s</td><td>%s</td><td>%s</td>"
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02001848 "<td"
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02001849 "",
Willy Tarreaue0454092010-02-26 12:29:07 +01001850 (uri->flags & ST_SHLGNDS)?"<u>":"",
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01001851 px->id, sv->id, px->id, sv->id, sv->id,
Willy Tarreaue0454092010-02-26 12:29:07 +01001852 (uri->flags & ST_SHLGNDS)?"</u>":"",
Willy Tarreauac68c5d2009-10-04 23:12:44 +02001853 U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"),
1854 U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max),
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02001855 U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-"));
1856
1857 /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1858 if (px->mode == PR_MODE_HTTP) {
1859 int i;
1860
1861 chunk_printf(&msg, " title=\"rsp codes:");
1862
1863 for (i = 1; i < 6; i++)
1864 chunk_printf(&msg, " %dxx=%lld,", i, sv->counters.p.http.rsp[i]);
1865
1866 chunk_printf(&msg, " other=%lld\"", sv->counters.p.http.rsp[0]);
1867 }
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02001868
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001869 chunk_printf(&msg,
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02001870 /* sessions: total, lbtot */
Willy Tarreaue0454092010-02-26 12:29:07 +01001871 ">%s%s%s</td><td>%s</td>",
1872 (px->mode == PR_MODE_HTTP)?"<u>":"",
1873 U2H0(sv->counters.cum_sess),
1874 (px->mode == PR_MODE_HTTP)?"</u>":"",
1875 U2H1(sv->counters.cum_lbconn));
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02001876
1877 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001878 /* bytes : in, out */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001879 "<td>%s</td><td>%s</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001880 /* denied: req, resp */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001881 "<td></td><td>%s</td>"
Willy Tarreauae526782010-03-04 20:34:23 +01001882 /* errors : request, connect */
1883 "<td></td><td>%s</td>"
1884 /* errors : response */
Willy Tarreau6a8573e2010-03-05 18:15:23 +01001885 "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02001886 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001887 "<td>%lld</td><td>%lld</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001888 "",
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02001889 U2H0(sv->counters.bytes_in), U2H1(sv->counters.bytes_out),
1890 U2H2(sv->counters.failed_secu),
Willy Tarreauae526782010-03-04 20:34:23 +01001891 U2H3(sv->counters.failed_conns),
Willy Tarreau6a8573e2010-03-05 18:15:23 +01001892 sv->counters.cli_aborts,
1893 sv->counters.srv_aborts,
Willy Tarreauae526782010-03-04 20:34:23 +01001894 U2H6(sv->counters.failed_resp),
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02001895 sv->counters.retries, sv->counters.redispatches);
Willy Tarreaub1356cf2008-12-07 16:06:43 +01001896
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001897 /* status, lest check */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001898 chunk_printf(&msg, "<td class=ac>");
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001899
Cyril Bontécd19e512010-01-31 22:34:03 +01001900 if (sv->state & SRV_MAINTAIN) {
1901 chunk_printf(&msg, "%s ",
1902 human_time(now.tv_sec - sv->last_change, 1));
1903 chunk_printf(&msg, "MAINT");
1904 }
Cyril Bonté0dae5852010-02-03 00:26:28 +01001905 else if (svs != sv && svs->state & SRV_MAINTAIN) {
1906 chunk_printf(&msg, "%s ",
1907 human_time(now.tv_sec - svs->last_change, 1));
1908 chunk_printf(&msg, "MAINT(via)");
1909 }
Cyril Bontécd19e512010-01-31 22:34:03 +01001910 else if (svs->state & SRV_CHECKED) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001911 chunk_printf(&msg, "%s ",
Cyril Bonté0dae5852010-02-03 00:26:28 +01001912 human_time(now.tv_sec - svs->last_change, 1));
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001913
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001914 chunk_printf(&msg,
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001915 srv_hlt_st[sv_state],
1916 (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
1917 (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
Krzysztof Piotr Oledzki034550b2010-01-05 18:44:44 +01001918 }
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +02001919
Krzysztof Piotr Oledzki034550b2010-01-05 18:44:44 +01001920 if (sv->state & SRV_CHECKED) {
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001921 chunk_printf(&msg, "</td><td class=ac title=\"%s",
Krzysztof Piotr Oledzkif7089f52009-10-10 21:06:49 +02001922 get_check_status_description(sv->check_status));
1923
1924 if (*sv->check_desc) {
1925 struct chunk src;
1926
1927 chunk_printf(&msg, ": ");
1928
1929 chunk_initlen(&src, sv->check_desc, 0, strlen(sv->check_desc));
1930 chunk_htmlencode(&msg, &src);
1931 }
Krzysztof Piotr Oledzki034550b2010-01-05 18:44:44 +01001932
Willy Tarreaue0454092010-02-26 12:29:07 +01001933 chunk_printf(&msg, "\"><u> %s%s",
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001934 tv_iszero(&sv->check_start)?"":"* ",
1935 get_check_status_info(sv->check_status));
1936
1937 if (sv->check_status >= HCHK_STATUS_L57DATA)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001938 chunk_printf(&msg, "/%d", sv->check_code);
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02001939
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01001940 if (sv->check_status >= HCHK_STATUS_CHECKED && sv->check_duration >= 0)
Willy Tarreaue0454092010-02-26 12:29:07 +01001941 chunk_printf(&msg, " in %lums</u>", sv->check_duration);
Krzysztof Piotr Oledzki034550b2010-01-05 18:44:44 +01001942 } else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001943 chunk_printf(&msg, "</td><td>");
Willy Tarreau91861262007-10-17 17:06:05 +02001944
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001945 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02001946 /* weight */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001947 "</td><td class=ac>%d</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001948 /* act, bck */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001949 "<td class=ac>%s</td><td class=ac>%s</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02001950 "",
Willy Tarreau5542af62007-12-03 02:04:00 +01001951 (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau91861262007-10-17 17:06:05 +02001952 (sv->state & SRV_BACKUP) ? "-" : "Y",
1953 (sv->state & SRV_BACKUP) ? "Y" : "-");
1954
Krzysztof Oledzki85130942007-10-22 16:21:10 +02001955 /* check failures: unique, fatal, down time */
Krzysztof Piotr Oledzki748196e2010-01-04 00:48:43 +01001956 if (sv->state & SRV_CHECKED) {
Willy Tarreaue0454092010-02-26 12:29:07 +01001957 chunk_printf(&msg, "<td title=\"Failed Health Checks%s\"><u>%lld",
Krzysztof Piotr Oledzki748196e2010-01-04 00:48:43 +01001958 svs->observe?"/Health Analyses":"", svs->counters.failed_checks);
1959
1960 if (svs->observe)
1961 chunk_printf(&msg, "/%lld", svs->counters.failed_hana);
1962
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001963 chunk_printf(&msg,
Willy Tarreaue0454092010-02-26 12:29:07 +01001964 "</u></td>"
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01001965 "<td>%lld</td><td>%s</td>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001966 "",
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01001967 svs->counters.down_trans, human_time(srv_downtime(sv), 1));
Krzysztof Piotr Oledzki748196e2010-01-04 00:48:43 +01001968 } else if (sv != svs)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001969 chunk_printf(&msg,
Krzysztof Piotr Oledzki88a14af2010-01-05 18:33:01 +01001970 "<td class=ac colspan=3><a class=lfsb href=\"#%s/%s\">via %s/%s<a></td>",
1971 svs->proxy->id, svs->id, svs->proxy->id, svs->id);
Willy Tarreau55bb8452007-10-17 18:44:57 +02001972 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001973 chunk_printf(&msg,
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001974 "<td colspan=3></td>");
1975
1976 /* throttle */
1977 if ((sv->state & SRV_WARMINGUP) &&
1978 now.tv_sec < sv->last_change + sv->slowstart &&
1979 now.tv_sec >= sv->last_change) {
1980 unsigned int ratio;
1981 ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001982 chunk_printf(&msg,
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001983 "<td class=ac>%d %%</td></tr>\n", ratio);
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001984 } else {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001985 chunk_printf(&msg,
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02001986 "<td class=ac>-</td></tr>\n");
Willy Tarreau4bab24d2007-11-30 18:16:29 +01001987 }
Willy Tarreau55bb8452007-10-17 18:44:57 +02001988 } else {
Willy Tarreau2ea81932007-11-30 12:04:38 +01001989 static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,",
1990 "UP %d/%d,", "UP,",
1991 "NOLB %d/%d,", "NOLB,",
1992 "no check," };
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02001993 chunk_printf(&msg,
Willy Tarreau55bb8452007-10-17 18:44:57 +02001994 /* pxid, name */
1995 "%s,%s,"
1996 /* queue : current, max */
1997 "%d,%d,"
Willy Tarreauddbb82f2007-12-05 10:34:49 +01001998 /* sessions : current, max, limit, total */
Willy Tarreau3b88d442009-04-11 20:44:08 +02001999 "%d,%d,%s,%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02002000 /* bytes : in, out */
2001 "%lld,%lld,"
2002 /* denied: req, resp */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002003 ",%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02002004 /* errors : request, connect, response */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002005 ",%lld,%lld,"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02002006 /* warnings: retries, redispatches */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002007 "%lld,%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02002008 "",
2009 px->id, sv->id,
Willy Tarreauac68c5d2009-10-04 23:12:44 +02002010 sv->nbpend, sv->counters.nbpend_max,
2011 sv->cur_sess, sv->counters.cur_sess_max, LIM2A0(sv->maxconn, ""), sv->counters.cum_sess,
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002012 sv->counters.bytes_in, sv->counters.bytes_out,
2013 sv->counters.failed_secu,
2014 sv->counters.failed_conns, sv->counters.failed_resp,
2015 sv->counters.retries, sv->counters.redispatches);
Willy Tarreaub1356cf2008-12-07 16:06:43 +01002016
Willy Tarreau55bb8452007-10-17 18:44:57 +02002017 /* status */
Cyril Bontécd19e512010-01-31 22:34:03 +01002018 if (sv->state & SRV_MAINTAIN) {
2019 chunk_printf(&msg, "MAINT,");
Cyril Bonté0dae5852010-02-03 00:26:28 +01002020 }
2021 else if (svs != sv && svs->state & SRV_MAINTAIN) {
2022 chunk_printf(&msg, "MAINT(via),");
2023 }
2024 else {
Cyril Bontécd19e512010-01-31 22:34:03 +01002025 chunk_printf(&msg,
2026 srv_hlt_st[sv_state],
2027 (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
2028 (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
2029 }
Willy Tarreau91861262007-10-17 17:06:05 +02002030
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002031 chunk_printf(&msg,
Willy Tarreau55bb8452007-10-17 18:44:57 +02002032 /* weight, active, backup */
2033 "%d,%d,%d,"
2034 "",
Willy Tarreau5542af62007-12-03 02:04:00 +01002035 (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau55bb8452007-10-17 18:44:57 +02002036 (sv->state & SRV_BACKUP) ? 0 : 1,
2037 (sv->state & SRV_BACKUP) ? 1 : 0);
2038
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002039 /* check failures: unique, fatal; last change, total downtime */
Willy Tarreau55bb8452007-10-17 18:44:57 +02002040 if (sv->state & SRV_CHECKED)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002041 chunk_printf(&msg,
Willy Tarreau3b88d442009-04-11 20:44:08 +02002042 "%lld,%lld,%d,%d,",
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002043 sv->counters.failed_checks, sv->counters.down_trans,
Willy Tarreau1772ece2009-04-03 14:49:12 +02002044 (int)(now.tv_sec - sv->last_change), srv_downtime(sv));
Willy Tarreau55bb8452007-10-17 18:44:57 +02002045 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002046 chunk_printf(&msg,
Elijah Epifanovacafc5f2007-10-25 20:15:38 +02002047 ",,,,");
2048
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002049 /* queue limit, pid, iid, sid, */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002050 chunk_printf(&msg,
Willy Tarreaudcd47712007-11-04 23:35:08 +01002051 "%s,"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002052 "%d,%d,%d,",
Willy Tarreaudcd47712007-11-04 23:35:08 +01002053 LIM2A0(sv->maxqueue, ""),
2054 relative_pid, px->uuid, sv->puid);
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002055
2056 /* throttle */
2057 if ((sv->state & SRV_WARMINGUP) &&
2058 now.tv_sec < sv->last_change + sv->slowstart &&
2059 now.tv_sec >= sv->last_change) {
2060 unsigned int ratio;
2061 ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002062 chunk_printf(&msg, "%d", ratio);
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002063 }
2064
Willy Tarreauddbb82f2007-12-05 10:34:49 +01002065 /* sessions: lbtot */
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002066 chunk_printf(&msg, ",%lld,", sv->counters.cum_lbconn);
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01002067
2068 /* tracked */
2069 if (sv->tracked)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002070 chunk_printf(&msg, "%s/%s,",
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01002071 sv->tracked->proxy->id, sv->tracked->id);
2072 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002073 chunk_printf(&msg, ",");
Krzysztof Piotr Oledzkic8b16fc2008-02-18 01:26:35 +01002074
Willy Tarreau7f062c42009-03-05 18:43:00 +01002075 /* type */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002076 chunk_printf(&msg, "%d,", STATS_TYPE_SV);
Willy Tarreau7f062c42009-03-05 18:43:00 +01002077
2078 /* rate */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002079 chunk_printf(&msg, "%u,,%u,",
Willy Tarreau8f208ec2009-05-10 19:01:49 +02002080 read_freq_ctr(&sv->sess_per_sec),
Willy Tarreauac68c5d2009-10-04 23:12:44 +02002081 sv->counters.sps_max);
Willy Tarreau7f062c42009-03-05 18:43:00 +01002082
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002083 if (sv->state & SRV_CHECKED) {
2084 /* check_status */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002085 chunk_printf(&msg, "%s,", get_check_status_info(sv->check_status));
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002086
2087 /* check_code */
2088 if (sv->check_status >= HCHK_STATUS_L57DATA)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002089 chunk_printf(&msg, "%u,", sv->check_code);
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002090 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002091 chunk_printf(&msg, ",");
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002092
2093 /* check_duration */
Krzysztof Piotr Oledzki034550b2010-01-05 18:44:44 +01002094 if (sv->check_status >= HCHK_STATUS_CHECKED)
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002095 chunk_printf(&msg, "%lu,", sv->check_duration);
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002096 else
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002097 chunk_printf(&msg, ",");
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002098
2099 } else {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002100 chunk_printf(&msg, ",,,");
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002101 }
2102
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002103 /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2104 if (px->mode == PR_MODE_HTTP) {
2105 int i;
2106
2107 for (i=1; i<6; i++)
2108 chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[i]);
2109
2110 chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[0]);
2111 } else {
2112 chunk_printf(&msg, ",,,,,,");
2113 }
2114
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01002115 /* failed health analyses */
2116 chunk_printf(&msg, "%lld,", sv->counters.failed_hana);
2117
Willy Tarreaud9b587f2010-02-26 10:05:55 +01002118 /* requests : req_rate, req_rate_max, req_tot, */
2119 chunk_printf(&msg, ",,,");
2120
Willy Tarreauae526782010-03-04 20:34:23 +01002121 /* errors: cli_aborts, srv_aborts */
2122 chunk_printf(&msg, "%lld,%lld,",
2123 sv->counters.cli_aborts, sv->counters.srv_aborts);
2124
Willy Tarreau7f062c42009-03-05 18:43:00 +01002125 /* finish with EOL */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002126 chunk_printf(&msg, "\n");
Willy Tarreau55bb8452007-10-17 18:44:57 +02002127 }
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02002128 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +02002129 return 0;
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01002130 } /* for sv */
Willy Tarreau91861262007-10-17 17:06:05 +02002131
2132 s->data_ctx.stats.px_st = DATA_ST_PX_BE;
2133 /* fall through */
2134
2135 case DATA_ST_PX_BE:
2136 /* print the backend */
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01002137 if ((px->cap & PR_CAP_BE) &&
2138 (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
2139 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002140 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02002141 /* name */
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01002142 "<tr class=\"backend\"><td class=ac");
2143
2144 if (uri->flags&ST_SHLGNDS) {
2145 /* balancing */
Krzysztof Piotr Oledzkic0f0c862010-01-06 15:03:18 +01002146 chunk_printf(&msg, " title=\"balancing: %s",
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01002147 backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
Krzysztof Piotr Oledzkic0f0c862010-01-06 15:03:18 +01002148
2149 /* cookie */
2150 if (px->cookie_name) {
2151 struct chunk src;
2152
2153 chunk_printf(&msg, ", cookie: '");
2154
2155 chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
2156 chunk_htmlencode(&msg, &src);
2157
2158 chunk_printf(&msg, "'");
2159 }
2160
2161 chunk_printf(&msg, "\"");
2162
Krzysztof Piotr Oledzki15514c22010-01-04 16:03:09 +01002163 }
2164
2165 chunk_printf(&msg,
Willy Tarreaue0454092010-02-26 12:29:07 +01002166 ">%s<a name=\"%s/Backend\"></a>"
2167 "<a class=lfsb href=\"#%s/Backend\">Backend</a>%s</td>"
Willy Tarreau91861262007-10-17 17:06:05 +02002168 /* queue : current, max */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002169 "<td>%s</td><td>%s</td><td></td>"
Willy Tarreaua3e49422009-05-10 19:19:41 +02002170 /* sessions rate : current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002171 "<td>%s</td><td>%s</td><td></td>"
Willy Tarreaua3e49422009-05-10 19:19:41 +02002172 "",
Willy Tarreaue0454092010-02-26 12:29:07 +01002173 (uri->flags & ST_SHLGNDS)?"<u>":"",
Krzysztof Piotr Oledzki1f672852009-10-24 14:24:30 +02002174 px->id, px->id,
Willy Tarreaue0454092010-02-26 12:29:07 +01002175 (uri->flags & ST_SHLGNDS)?"</u>":"",
Willy Tarreauac68c5d2009-10-04 23:12:44 +02002176 U2H0(px->nbpend) /* or px->totpend ? */, U2H1(px->counters.nbpend_max),
2177 U2H2(read_freq_ctr(&px->be_sess_per_sec)), U2H3(px->counters.be_sps_max));
Willy Tarreaua3e49422009-05-10 19:19:41 +02002178
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002179 chunk_printf(&msg,
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002180 /* sessions: current, max, limit */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002181 "<td>%s</td><td>%s</td><td>%s</td>"
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002182 "<td"
2183 "",
2184 U2H2(px->beconn), U2H3(px->counters.beconn_max), U2H4(px->fullconn));
2185
2186 /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
2187 if (px->mode == PR_MODE_HTTP) {
2188 int i;
2189
2190 chunk_printf(&msg, " title=\"rsp codes:");
2191
2192 for (i = 1; i < 6; i++)
Willy Tarreau24657792010-02-26 10:30:28 +01002193 chunk_printf(&msg, " %dxx=%lld", i, px->counters.be.http.rsp[i]);
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002194
Willy Tarreau24657792010-02-26 10:30:28 +01002195 chunk_printf(&msg, " other=%lld\"", px->counters.be.http.rsp[0]);
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002196 }
2197
2198 chunk_printf(&msg,
2199 /* sessions: total, lbtot */
Willy Tarreaue0454092010-02-26 12:29:07 +01002200 ">%s%s%s</td><td>%s</td>"
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002201 /* bytes: in, out */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002202 "<td>%s</td><td>%s</td>"
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02002203 "",
Willy Tarreaue0454092010-02-26 12:29:07 +01002204 (px->mode == PR_MODE_HTTP)?"<u>":"",
2205 U2H6(px->counters.cum_beconn),
2206 (px->mode == PR_MODE_HTTP)?"</u>":"",
2207 U2H7(px->counters.cum_lbconn),
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002208 U2H8(px->counters.bytes_in), U2H9(px->counters.bytes_out));
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02002209
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002210 chunk_printf(&msg,
Willy Tarreau91861262007-10-17 17:06:05 +02002211 /* denied: req, resp */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002212 "<td>%s</td><td>%s</td>"
Willy Tarreauae526782010-03-04 20:34:23 +01002213 /* errors : request, connect */
2214 "<td></td><td>%s</td>"
2215 /* errors : response */
Willy Tarreau6a8573e2010-03-05 18:15:23 +01002216 "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02002217 /* warnings: retries, redispatches */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002218 "<td>%lld</td><td>%lld</td>"
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002219 /* backend status: reflect backend status (up/down): we display UP
Willy Tarreau91861262007-10-17 17:06:05 +02002220 * if the backend has known working servers or if it has no server at
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002221 * all (eg: for stats). Then we display the total weight, number of
Willy Tarreau91861262007-10-17 17:06:05 +02002222 * active and backups. */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002223 "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d</td>"
2224 "<td class=ac>%d</td><td class=ac>%d</td>"
Willy Tarreau0a6d2ef2009-03-29 14:46:01 +02002225 "",
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002226 U2H0(px->counters.denied_req), U2H1(px->counters.denied_resp),
Willy Tarreauae526782010-03-04 20:34:23 +01002227 U2H2(px->counters.failed_conns),
Willy Tarreau6a8573e2010-03-05 18:15:23 +01002228 px->counters.cli_aborts,
2229 px->counters.srv_aborts,
Willy Tarreauae526782010-03-04 20:34:23 +01002230 U2H5(px->counters.failed_resp),
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002231 px->counters.retries, px->counters.redispatches,
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002232 human_time(now.tv_sec - px->last_change, 1),
Willy Tarreau2ea81932007-11-30 12:04:38 +01002233 (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" :
2234 "<font color=\"red\"><b>DOWN</b></font>",
Willy Tarreau5542af62007-12-03 02:04:00 +01002235 (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau20697042007-11-15 23:26:18 +01002236 px->srv_act, px->srv_bck);
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002237
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002238 chunk_printf(&msg,
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002239 /* rest of backend: nothing, down transitions, total downtime, throttle */
Krzysztof Piotr Oledzkif2d2b1d2009-10-12 23:09:08 +02002240 "<td class=ac>&nbsp;</td><td>%d</td>"
2241 "<td>%s</td>"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002242 "<td></td>"
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002243 "</tr>",
2244 px->down_trans,
2245 px->srv?human_time(be_downtime(px), 1):"&nbsp;");
Willy Tarreau55bb8452007-10-17 18:44:57 +02002246 } else {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002247 chunk_printf(&msg,
Willy Tarreau55bb8452007-10-17 18:44:57 +02002248 /* pxid, name */
2249 "%s,BACKEND,"
2250 /* queue : current, max */
2251 "%d,%d,"
Willy Tarreauddbb82f2007-12-05 10:34:49 +01002252 /* sessions : current, max, limit, total */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002253 "%d,%d,%d,%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02002254 /* bytes : in, out */
2255 "%lld,%lld,"
2256 /* denied: req, resp */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002257 "%lld,%lld,"
Willy Tarreau55bb8452007-10-17 18:44:57 +02002258 /* errors : request, connect, response */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002259 ",%lld,%lld,"
Krzysztof Oledzki1cf36ba2007-10-18 19:12:30 +02002260 /* warnings: retries, redispatches */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002261 "%lld,%lld,"
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002262 /* backend status: reflect backend status (up/down): we display UP
Willy Tarreau55bb8452007-10-17 18:44:57 +02002263 * if the backend has known working servers or if it has no server at
Krzysztof Oledzki85130942007-10-22 16:21:10 +02002264 * all (eg: for stats). Then we display the total weight, number of
Willy Tarreau55bb8452007-10-17 18:44:57 +02002265 * active and backups. */
2266 "%s,"
2267 "%d,%d,%d,"
Willy Tarreau4bab24d2007-11-30 18:16:29 +01002268 /* rest of backend: nothing, down transitions, last change, total downtime */
Elijah Epifanovacafc5f2007-10-25 20:15:38 +02002269 ",%d,%d,%d,,"
Krzysztof Piotr Oledzki2c6962c2008-03-02 02:42:14 +01002270 /* pid, iid, sid, throttle, lbtot, tracked, type */
Willy Tarreau3b88d442009-04-11 20:44:08 +02002271 "%d,%d,0,,%lld,,%d,"
Willy Tarreau8f208ec2009-05-10 19:01:49 +02002272 /* rate, rate_lim, rate_max, */
2273 "%u,,%u,"
Krzysztof Piotr Oledzki09605412009-09-23 22:09:24 +02002274 /* check_status, check_code, check_duration */
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002275 ",,,",
Willy Tarreau55bb8452007-10-17 18:44:57 +02002276 px->id,
Willy Tarreauac68c5d2009-10-04 23:12:44 +02002277 px->nbpend /* or px->totpend ? */, px->counters.nbpend_max,
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002278 px->beconn, px->counters.beconn_max, px->fullconn, px->counters.cum_beconn,
2279 px->counters.bytes_in, px->counters.bytes_out,
2280 px->counters.denied_req, px->counters.denied_resp,
2281 px->counters.failed_conns, px->counters.failed_resp,
2282 px->counters.retries, px->counters.redispatches,
Willy Tarreau20697042007-11-15 23:26:18 +01002283 (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
Willy Tarreau5542af62007-12-03 02:04:00 +01002284 (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
Willy Tarreau20697042007-11-15 23:26:18 +01002285 px->srv_act, px->srv_bck,
Willy Tarreau1772ece2009-04-03 14:49:12 +02002286 px->down_trans, (int)(now.tv_sec - px->last_change),
Willy Tarreau20697042007-11-15 23:26:18 +01002287 px->srv?be_downtime(px):0,
Willy Tarreauddbb82f2007-12-05 10:34:49 +01002288 relative_pid, px->uuid,
Krzysztof Piotr Oledzki052d4fd2009-10-04 14:52:57 +02002289 px->counters.cum_lbconn, STATS_TYPE_BE,
Willy Tarreau8f208ec2009-05-10 19:01:49 +02002290 read_freq_ctr(&px->be_sess_per_sec),
Willy Tarreauac68c5d2009-10-04 23:12:44 +02002291 px->counters.be_sps_max);
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002292
2293 /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2294 if (px->mode == PR_MODE_HTTP) {
2295 int i;
2296
2297 for (i=1; i<6; i++)
Willy Tarreau24657792010-02-26 10:30:28 +01002298 chunk_printf(&msg, "%lld,", px->counters.be.http.rsp[i]);
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002299
Willy Tarreau24657792010-02-26 10:30:28 +01002300 chunk_printf(&msg, "%lld,", px->counters.be.http.rsp[0]);
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002301 } else {
2302 chunk_printf(&msg, ",,,,,,");
2303 }
2304
Krzysztof Piotr Oledzki97f07b82009-12-15 22:31:24 +01002305 /* failed health analyses */
2306 chunk_printf(&msg, ",");
2307
Willy Tarreaud9b587f2010-02-26 10:05:55 +01002308 /* requests : req_rate, req_rate_max, req_tot, */
2309 chunk_printf(&msg, ",,,");
2310
Willy Tarreauae526782010-03-04 20:34:23 +01002311 /* errors: cli_aborts, srv_aborts */
2312 chunk_printf(&msg, "%lld,%lld,",
2313 px->counters.cli_aborts, px->counters.srv_aborts);
2314
Krzysztof Piotr Oledzki5fb18822009-10-13 21:14:09 +02002315 /* finish with EOL */
2316 chunk_printf(&msg, "\n");
2317
Willy Tarreau55bb8452007-10-17 18:44:57 +02002318 }
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02002319 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau91861262007-10-17 17:06:05 +02002320 return 0;
2321 }
Willy Tarreaub1356cf2008-12-07 16:06:43 +01002322
Willy Tarreau91861262007-10-17 17:06:05 +02002323 s->data_ctx.stats.px_st = DATA_ST_PX_END;
2324 /* fall through */
2325
2326 case DATA_ST_PX_END:
Willy Tarreau39f7e6d2008-03-17 21:38:24 +01002327 if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002328 chunk_printf(&msg, "</table><p>\n");
Willy Tarreau91861262007-10-17 17:06:05 +02002329
Willy Tarreaub0c9bc42009-10-04 15:56:38 +02002330 if (buffer_feed_chunk(rep, &msg) >= 0)
Willy Tarreau55bb8452007-10-17 18:44:57 +02002331 return 0;
2332 }
Willy Tarreau91861262007-10-17 17:06:05 +02002333
2334 s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
2335 /* fall through */
2336
2337 case DATA_ST_PX_FIN:
2338 return 1;
2339
2340 default:
2341 /* unknown state, we should put an abort() here ! */
2342 return 1;
2343 }
2344}
2345
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002346/* This function is called to send output to the response buffer. It dumps a
2347 * complete session state onto the output buffer <rep>. The session has to be
2348 * set in data_ctx.sess.target. It returns 0 if the output buffer is full and
2349 * it needs to be called again, otherwise non-zero. It is designed to be called
2350 * from stats_dump_sess_to_buffer() below.
2351 */
2352
2353/* returns 1 if dump is not complete */
2354int stats_dump_full_sess_to_buffer(struct session *s, struct buffer *rep)
2355{
2356 struct tm tm;
2357 struct chunk msg;
2358 struct session *sess;
2359 extern const char *monthname[12];
2360 char pn[INET6_ADDRSTRLEN];
2361
2362 chunk_init(&msg, trash, sizeof(trash));
2363 sess = s->data_ctx.sess.target;
2364
2365 if (s->data_ctx.sess.section > 0 && s->data_ctx.sess.uid != sess->uniq_id) {
2366 /* session changed, no need to go any further */
2367 chunk_printf(&msg, " *** session terminated while we were watching it ***\n");
2368 if (buffer_feed_chunk(rep, &msg) >= 0)
2369 return 0;
2370 s->data_ctx.sess.target = NULL;
2371 s->data_ctx.sess.uid = 0;
2372 return 1;
2373 }
2374
2375 switch (s->data_ctx.sess.section) {
2376 case 0: /* main status of the session */
2377 s->data_ctx.sess.uid = sess->uniq_id;
2378 s->data_ctx.sess.section = 1;
2379 /* fall through */
2380
2381 case 1:
2382 chunk_printf(&msg,
2383 "%p: id=%u, proto=%s",
2384 sess,
2385 sess->uniq_id,
2386 sess->listener->proto->name);
2387
2388 switch (sess->listener->proto->sock_family) {
2389 case AF_INET:
2390 inet_ntop(AF_INET,
2391 (const void *)&((struct sockaddr_in *)&sess->cli_addr)->sin_addr,
2392 pn, sizeof(pn));
2393
2394 chunk_printf(&msg,
2395 " source=%s:%d\n",
2396 pn,
2397 ntohs(((struct sockaddr_in *)&sess->cli_addr)->sin_port));
2398 break;
2399 case AF_INET6:
2400 inet_ntop(AF_INET6,
2401 (const void *)&((struct sockaddr_in6 *)(&sess->cli_addr))->sin6_addr,
2402 pn, sizeof(pn));
2403
2404 chunk_printf(&msg,
2405 " source=%s:%d\n",
2406 pn,
2407 ntohs(((struct sockaddr_in6 *)&sess->cli_addr)->sin6_port));
2408 break;
2409 case AF_UNIX:
2410 default:
2411 /* no more information to print right now */
2412 chunk_printf(&msg, "\n");
2413 break;
2414 }
2415
2416 chunk_printf(&msg,
2417 " flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n",
Willy Tarreauee28de02010-06-01 09:51:00 +02002418 sess->flags, sess->si[1].conn_retries, sess->srv_conn, sess->pend_pos);
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002419
2420 chunk_printf(&msg,
2421 " frontend=%s (id=%u mode=%s), listener=%s (id=%u)\n",
2422 sess->fe->id, sess->fe->uuid, sess->fe->mode ? "http" : "tcp",
2423 sess->listener ? sess->listener->name ? sess->listener->name : "?" : "?",
2424 sess->listener ? sess->listener->luid : 0);
2425
2426 chunk_printf(&msg,
2427 " backend=%s (id=%u mode=%s) server=%s (id=%u)\n",
2428 sess->be->id, sess->be->uuid, sess->be->mode ? "http" : "tcp",
2429 sess->srv ? sess->srv->id : "<none>",
2430 sess->srv ? sess->srv->puid : 0);
2431
2432 chunk_printf(&msg,
2433 " task=%p (state=0x%02x nice=%d calls=%d exp=%s%s)\n",
2434 sess->task,
2435 sess->task->state,
2436 sess->task->nice, sess->task->calls,
2437 sess->task->expire ?
2438 tick_is_expired(sess->task->expire, now_ms) ? "<PAST>" :
2439 human_time(TICKS_TO_MS(sess->task->expire - now_ms),
2440 TICKS_TO_MS(1000)) : "<NEVER>",
2441 task_in_rq(sess->task) ? ", running" : "");
2442
2443 get_localtime(sess->logs.accept_date.tv_sec, &tm);
2444 chunk_printf(&msg,
2445 " task created [%02d/%s/%04d:%02d:%02d:%02d.%06d] (age=%s)\n",
2446 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
2447 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec),
2448 human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1));
2449
2450 chunk_printf(&msg,
2451 " si[0]=%p (state=%d flags=0x%02x fd=%d exp=%s, et=0x%03x)\n",
2452 &sess->si[0],
2453 sess->si[0].state,
2454 sess->si[0].flags,
2455 sess->si[0].fd,
2456 sess->si[0].exp ?
2457 tick_is_expired(sess->si[0].exp, now_ms) ? "<PAST>" :
2458 human_time(TICKS_TO_MS(sess->si[0].exp - now_ms),
2459 TICKS_TO_MS(1000)) : "<NEVER>",
2460 sess->si[0].err_type);
2461
2462 chunk_printf(&msg,
2463 " si[1]=%p (state=%d flags=0x%02x fd=%d exp=%s, et=0x%03x)\n",
2464 &sess->si[1],
2465 sess->si[1].state,
2466 sess->si[1].flags,
2467 sess->si[1].fd,
2468 sess->si[1].exp ?
2469 tick_is_expired(sess->si[1].exp, now_ms) ? "<PAST>" :
2470 human_time(TICKS_TO_MS(sess->si[1].exp - now_ms),
2471 TICKS_TO_MS(1000)) : "<NEVER>",
2472 sess->si[1].err_type);
2473
2474 chunk_printf(&msg,
2475 " txn=%p (flags=0x%x meth=%d status=%d req.st=%d rsp.st=%d)\n",
2476 &sess->txn, sess->txn.flags, sess->txn.meth, sess->txn.status,
2477 sess->txn.req.msg_state, sess->txn.rsp.msg_state);
2478
2479
2480 chunk_printf(&msg,
2481 " req=%p (f=0x%06x an=0x%x l=%d sndmx=%d pipe=%d fwd=%ld)\n"
2482 " an_exp=%s",
2483 sess->req,
2484 sess->req->flags, sess->req->analysers,
2485 sess->req->l, sess->req->send_max,
2486 sess->req->pipe ? sess->req->pipe->data : 0,
2487 sess->req->to_forward,
2488 sess->req->analyse_exp ?
2489 human_time(TICKS_TO_MS(sess->req->analyse_exp - now_ms),
2490 TICKS_TO_MS(1000)) : "<NEVER>");
2491
2492 chunk_printf(&msg,
2493 " rex=%s",
2494 sess->req->rex ?
2495 human_time(TICKS_TO_MS(sess->req->rex - now_ms),
2496 TICKS_TO_MS(1000)) : "<NEVER>");
2497
2498 chunk_printf(&msg,
2499 " wex=%s\n"
2500 " data=%p r=%d w=%d lr=%d total=%lld\n",
2501 sess->req->wex ?
2502 human_time(TICKS_TO_MS(sess->req->wex - now_ms),
2503 TICKS_TO_MS(1000)) : "<NEVER>",
2504 sess->req->data,
Willy Tarreau4e1554c2010-03-21 23:21:00 +01002505 (int)(sess->req->r - sess->req->data),
2506 (int)(sess->req->w - sess->req->data),
2507 (int)(sess->req->lr - sess->req->data),
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002508 sess->req->total);
2509
2510 chunk_printf(&msg,
2511 " res=%p (f=0x%06x an=0x%x l=%d sndmx=%d pipe=%d fwd=%ld)\n"
2512 " an_exp=%s",
2513 sess->rep,
2514 sess->rep->flags, sess->rep->analysers,
2515 sess->rep->l, sess->rep->send_max,
2516 sess->rep->pipe ? sess->rep->pipe->data : 0,
2517 sess->rep->to_forward,
2518 sess->rep->analyse_exp ?
2519 human_time(TICKS_TO_MS(sess->rep->analyse_exp - now_ms),
2520 TICKS_TO_MS(1000)) : "<NEVER>");
2521
2522 chunk_printf(&msg,
2523 " rex=%s",
2524 sess->rep->rex ?
2525 human_time(TICKS_TO_MS(sess->rep->rex - now_ms),
2526 TICKS_TO_MS(1000)) : "<NEVER>");
2527
2528 chunk_printf(&msg,
2529 " wex=%s\n"
2530 " data=%p r=%d w=%d lr=%d total=%lld\n",
2531 sess->rep->wex ?
2532 human_time(TICKS_TO_MS(sess->rep->wex - now_ms),
2533 TICKS_TO_MS(1000)) : "<NEVER>",
2534 sess->rep->data,
Willy Tarreau4e1554c2010-03-21 23:21:00 +01002535 (int)(sess->rep->r - sess->rep->data),
2536 (int)(sess->rep->w - sess->rep->data),
2537 (int)(sess->rep->lr - sess->rep->data),
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002538 sess->rep->total);
2539
2540 if (buffer_feed_chunk(rep, &msg) >= 0)
2541 return 0;
2542
2543 /* use other states to dump the contents */
2544 }
2545 /* end of dump */
2546 s->data_ctx.sess.uid = 0;
2547 return 1;
2548}
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002549
2550/* This function is called to send output to the response buffer.
2551 * It dumps the sessions states onto the output buffer <rep>.
2552 * Expects to be called with client socket shut down on input.
2553 * s->data_ctx must have been zeroed first, and the flags properly set.
Willy Tarreau7e72a8f2009-10-03 23:55:14 +02002554 * It returns 0 as long as it does not complete, non-zero upon completion.
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002555 */
Willy Tarreau7e72a8f2009-10-03 23:55:14 +02002556int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002557{
2558 struct chunk msg;
2559
2560 if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
2561 /* If we're forced to shut down, we might have to remove our
2562 * reference to the last session being dumped.
2563 */
2564 if (s->data_state == DATA_ST_LIST) {
Willy Tarreaufd3828e2009-02-22 15:17:24 +01002565 if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) {
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002566 LIST_DEL(&s->data_ctx.sess.bref.users);
Willy Tarreaufd3828e2009-02-22 15:17:24 +01002567 LIST_INIT(&s->data_ctx.sess.bref.users);
2568 }
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002569 }
Willy Tarreau7e72a8f2009-10-03 23:55:14 +02002570 return 1;
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002571 }
2572
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002573 chunk_init(&msg, trash, sizeof(trash));
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002574
2575 switch (s->data_state) {
2576 case DATA_ST_INIT:
2577 /* the function had not been called yet, let's prepare the
2578 * buffer for a response. We initialize the current session
Willy Tarreaufd3828e2009-02-22 15:17:24 +01002579 * pointer to the first in the global list. When a target
2580 * session is being destroyed, it is responsible for updating
2581 * this pointer. We know we have reached the end when this
2582 * pointer points back to the head of the sessions list.
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002583 */
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002584 LIST_INIT(&s->data_ctx.sess.bref.users);
2585 s->data_ctx.sess.bref.ref = sessions.n;
2586 s->data_state = DATA_ST_LIST;
2587 /* fall through */
2588
2589 case DATA_ST_LIST:
Willy Tarreaufd3828e2009-02-22 15:17:24 +01002590 /* first, let's detach the back-ref from a possible previous session */
2591 if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) {
2592 LIST_DEL(&s->data_ctx.sess.bref.users);
2593 LIST_INIT(&s->data_ctx.sess.bref.users);
2594 }
2595
2596 /* and start from where we stopped */
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002597 while (s->data_ctx.sess.bref.ref != &sessions) {
2598 char pn[INET6_ADDRSTRLEN + strlen(":65535")];
2599 struct session *curr_sess;
2600
2601 curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list);
2602
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002603 if (s->data_ctx.sess.target) {
2604 if (s->data_ctx.sess.target != curr_sess)
2605 goto next_sess;
2606
2607 LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
2608 /* call the proper dump() function and return if we're missing space */
2609 if (!stats_dump_full_sess_to_buffer(s, rep))
2610 return 0;
2611
2612 /* session dump complete */
2613 LIST_DEL(&s->data_ctx.sess.bref.users);
2614 LIST_INIT(&s->data_ctx.sess.bref.users);
2615 s->data_ctx.sess.target = NULL;
2616 break;
2617 }
2618
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002619 chunk_printf(&msg,
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002620 "%p: proto=%s",
2621 curr_sess,
2622 curr_sess->listener->proto->name);
2623
2624 switch (curr_sess->listener->proto->sock_family) {
2625 case AF_INET:
2626 inet_ntop(AF_INET,
2627 (const void *)&((struct sockaddr_in *)&curr_sess->cli_addr)->sin_addr,
2628 pn, sizeof(pn));
2629
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002630 chunk_printf(&msg,
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002631 " src=%s:%d fe=%s be=%s srv=%s",
2632 pn,
2633 ntohs(((struct sockaddr_in *)&curr_sess->cli_addr)->sin_port),
2634 curr_sess->fe->id,
2635 curr_sess->be->id,
2636 curr_sess->srv ? curr_sess->srv->id : "<none>"
2637 );
2638 break;
2639 case AF_INET6:
2640 inet_ntop(AF_INET6,
2641 (const void *)&((struct sockaddr_in6 *)(&curr_sess->cli_addr))->sin6_addr,
2642 pn, sizeof(pn));
2643
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002644 chunk_printf(&msg,
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002645 " src=%s:%d fe=%s be=%s srv=%s",
2646 pn,
2647 ntohs(((struct sockaddr_in6 *)&curr_sess->cli_addr)->sin6_port),
2648 curr_sess->fe->id,
2649 curr_sess->be->id,
2650 curr_sess->srv ? curr_sess->srv->id : "<none>"
2651 );
2652
2653 break;
2654 case AF_UNIX:
2655 /* no more information to print right now */
2656 break;
2657 }
2658
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002659 chunk_printf(&msg,
Willy Tarreau65671ab2009-10-04 14:24:59 +02002660 " ts=%02x age=%s calls=%d",
2661 curr_sess->task->state,
Willy Tarreau3884cba2009-03-28 17:54:35 +01002662 human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1),
2663 curr_sess->task->calls);
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002664
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002665 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002666 " rq[f=%06xh,l=%d,an=%02xh,rx=%s",
2667 curr_sess->req->flags,
2668 curr_sess->req->l,
2669 curr_sess->req->analysers,
2670 curr_sess->req->rex ?
2671 human_time(TICKS_TO_MS(curr_sess->req->rex - now_ms),
2672 TICKS_TO_MS(1000)) : "");
2673
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002674 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002675 ",wx=%s",
2676 curr_sess->req->wex ?
2677 human_time(TICKS_TO_MS(curr_sess->req->wex - now_ms),
2678 TICKS_TO_MS(1000)) : "");
2679
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002680 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002681 ",ax=%s]",
2682 curr_sess->req->analyse_exp ?
2683 human_time(TICKS_TO_MS(curr_sess->req->analyse_exp - now_ms),
2684 TICKS_TO_MS(1000)) : "");
2685
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002686 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002687 " rp[f=%06xh,l=%d,an=%02xh,rx=%s",
2688 curr_sess->rep->flags,
2689 curr_sess->rep->l,
2690 curr_sess->rep->analysers,
2691 curr_sess->rep->rex ?
2692 human_time(TICKS_TO_MS(curr_sess->rep->rex - now_ms),
2693 TICKS_TO_MS(1000)) : "");
2694
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002695 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002696 ",wx=%s",
2697 curr_sess->rep->wex ?
2698 human_time(TICKS_TO_MS(curr_sess->rep->wex - now_ms),
2699 TICKS_TO_MS(1000)) : "");
2700
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002701 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002702 ",ax=%s]",
2703 curr_sess->rep->analyse_exp ?
2704 human_time(TICKS_TO_MS(curr_sess->rep->analyse_exp - now_ms),
2705 TICKS_TO_MS(1000)) : "");
2706
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002707 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002708 " s0=[%d,%1xh,fd=%d,ex=%s]",
2709 curr_sess->si[0].state,
2710 curr_sess->si[0].flags,
2711 curr_sess->si[0].fd,
2712 curr_sess->si[0].exp ?
2713 human_time(TICKS_TO_MS(curr_sess->si[0].exp - now_ms),
2714 TICKS_TO_MS(1000)) : "");
2715
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002716 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002717 " s1=[%d,%1xh,fd=%d,ex=%s]",
2718 curr_sess->si[1].state,
2719 curr_sess->si[1].flags,
2720 curr_sess->si[1].fd,
2721 curr_sess->si[1].exp ?
2722 human_time(TICKS_TO_MS(curr_sess->si[1].exp - now_ms),
2723 TICKS_TO_MS(1000)) : "");
2724
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002725 chunk_printf(&msg,
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002726 " exp=%s",
2727 curr_sess->task->expire ?
2728 human_time(TICKS_TO_MS(curr_sess->task->expire - now_ms),
2729 TICKS_TO_MS(1000)) : "");
Willy Tarreau4726f532009-03-07 17:25:21 +01002730 if (task_in_rq(curr_sess->task))
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002731 chunk_printf(&msg, " run(nice=%d)", curr_sess->task->nice);
Willy Tarreauc6dcad62009-03-29 00:18:14 +01002732
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002733 chunk_printf(&msg, "\n");
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002734
Willy Tarreau7e72a8f2009-10-03 23:55:14 +02002735 if (buffer_feed_chunk(rep, &msg) >= 0) {
Willy Tarreaufd3828e2009-02-22 15:17:24 +01002736 /* let's try again later from this session. We add ourselves into
2737 * this session's users so that it can remove us upon termination.
2738 */
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002739 LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
Willy Tarreau7e72a8f2009-10-03 23:55:14 +02002740 return 0;
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002741 }
2742
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002743 next_sess:
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002744 s->data_ctx.sess.bref.ref = curr_sess->list.n;
2745 }
Willy Tarreau66dc20a2010-03-05 17:53:32 +01002746
2747 if (s->data_ctx.sess.target) {
2748 /* specified session not found */
2749 if (s->data_ctx.sess.section > 0)
2750 chunk_printf(&msg, " *** session terminated while we were watching it ***\n");
2751 else
2752 chunk_printf(&msg, "Session not found.\n");
2753
2754 if (buffer_feed_chunk(rep, &msg) >= 0)
2755 return 0;
2756
2757 s->data_ctx.sess.target = NULL;
2758 s->data_ctx.sess.uid = 0;
2759 return 1;
2760 }
2761
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002762 s->data_state = DATA_ST_FIN;
2763 /* fall through */
2764
2765 default:
2766 s->data_state = DATA_ST_FIN;
Willy Tarreau7e72a8f2009-10-03 23:55:14 +02002767 return 1;
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002768 }
2769}
2770
Willy Tarreaud426a182010-03-05 14:58:26 +01002771/* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
Willy Tarreau74808cb2009-03-04 15:53:18 +01002772 * <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
2773 * which is 60 chars per line. Non-printable chars \t, \n, \r and \e are
2774 * encoded in C format. Other non-printable chars are encoded "\xHH". Original
2775 * lines are respected within the limit of 70 output chars. Lines that are
2776 * continuation of a previous truncated line begin with "+" instead of " "
2777 * after the offset. The new pointer is returned.
2778 */
Willy Tarreaud426a182010-03-05 14:58:26 +01002779static int dump_text_line(struct chunk *out, const char *buf, int bsize, int len,
2780 int *line, int ptr)
Willy Tarreau74808cb2009-03-04 15:53:18 +01002781{
2782 int end;
2783 unsigned char c;
2784
2785 end = out->len + 80;
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002786 if (end > out->size)
Willy Tarreau74808cb2009-03-04 15:53:18 +01002787 return ptr;
2788
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002789 chunk_printf(out, " %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
Willy Tarreau74808cb2009-03-04 15:53:18 +01002790
Willy Tarreaud426a182010-03-05 14:58:26 +01002791 while (ptr < len && ptr < bsize) {
2792 c = buf[ptr];
Willy Tarreau787bbd92009-03-12 08:18:33 +01002793 if (isprint(c) && isascii(c) && c != '\\') {
Willy Tarreau74808cb2009-03-04 15:53:18 +01002794 if (out->len > end - 2)
2795 break;
2796 out->str[out->len++] = c;
Willy Tarreau787bbd92009-03-12 08:18:33 +01002797 } else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\') {
Willy Tarreau74808cb2009-03-04 15:53:18 +01002798 if (out->len > end - 3)
2799 break;
2800 out->str[out->len++] = '\\';
2801 switch (c) {
2802 case '\t': c = 't'; break;
2803 case '\n': c = 'n'; break;
2804 case '\r': c = 'r'; break;
2805 case '\e': c = 'e'; break;
Willy Tarreau787bbd92009-03-12 08:18:33 +01002806 case '\\': c = '\\'; break;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002807 }
2808 out->str[out->len++] = c;
2809 } else {
2810 if (out->len > end - 5)
2811 break;
2812 out->str[out->len++] = '\\';
2813 out->str[out->len++] = 'x';
2814 out->str[out->len++] = hextab[(c >> 4) & 0xF];
2815 out->str[out->len++] = hextab[c & 0xF];
2816 }
Willy Tarreaud426a182010-03-05 14:58:26 +01002817 if (buf[ptr++] == '\n') {
Willy Tarreau74808cb2009-03-04 15:53:18 +01002818 /* we had a line break, let's return now */
2819 out->str[out->len++] = '\n';
2820 *line = ptr;
2821 return ptr;
2822 }
2823 }
2824 /* we have an incomplete line, we return it as-is */
2825 out->str[out->len++] = '\n';
2826 return ptr;
2827}
2828
2829/* This function is called to send output to the response buffer.
2830 * It dumps the errors logged in proxies onto the output buffer <rep>.
2831 * Expects to be called with client socket shut down on input.
2832 * s->data_ctx must have been zeroed first, and the flags properly set.
Willy Tarreau61b34732009-10-03 23:49:35 +02002833 * It returns 0 as long as it does not complete, non-zero upon completion.
Willy Tarreau74808cb2009-03-04 15:53:18 +01002834 */
Willy Tarreau61b34732009-10-03 23:49:35 +02002835int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep)
Willy Tarreau74808cb2009-03-04 15:53:18 +01002836{
2837 extern const char *monthname[12];
2838 struct chunk msg;
2839
Willy Tarreau61b34732009-10-03 23:49:35 +02002840 if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW)))
2841 return 1;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002842
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002843 chunk_init(&msg, trash, sizeof(trash));
Willy Tarreau74808cb2009-03-04 15:53:18 +01002844
2845 if (!s->data_ctx.errors.px) {
2846 /* the function had not been called yet, let's prepare the
2847 * buffer for a response.
2848 */
Willy Tarreau74808cb2009-03-04 15:53:18 +01002849 s->data_ctx.errors.px = proxy;
2850 s->data_ctx.errors.buf = 0;
2851 s->data_ctx.errors.bol = 0;
2852 s->data_ctx.errors.ptr = -1;
2853 }
2854
2855 /* we have two inner loops here, one for the proxy, the other one for
2856 * the buffer.
2857 */
2858 while (s->data_ctx.errors.px) {
2859 struct error_snapshot *es;
2860
2861 if (s->data_ctx.errors.buf == 0)
2862 es = &s->data_ctx.errors.px->invalid_req;
2863 else
2864 es = &s->data_ctx.errors.px->invalid_rep;
2865
2866 if (!es->when.tv_sec)
2867 goto next;
2868
2869 if (s->data_ctx.errors.iid >= 0 &&
2870 s->data_ctx.errors.px->uuid != s->data_ctx.errors.iid &&
2871 es->oe->uuid != s->data_ctx.errors.iid)
2872 goto next;
2873
2874 if (s->data_ctx.errors.ptr < 0) {
2875 /* just print headers now */
2876
2877 char pn[INET6_ADDRSTRLEN];
2878 struct tm tm;
2879
2880 get_localtime(es->when.tv_sec, &tm);
Willy Tarreau8811f8e2010-03-05 17:42:58 +01002881 chunk_printf(&msg, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
Willy Tarreau74808cb2009-03-04 15:53:18 +01002882 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
Willy Tarreau1772ece2009-04-03 14:49:12 +02002883 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
Willy Tarreau74808cb2009-03-04 15:53:18 +01002884
2885
2886 if (es->src.ss_family == AF_INET)
2887 inet_ntop(AF_INET,
2888 (const void *)&((struct sockaddr_in *)&es->src)->sin_addr,
2889 pn, sizeof(pn));
2890 else
2891 inet_ntop(AF_INET6,
2892 (const void *)&((struct sockaddr_in6 *)(&es->src))->sin6_addr,
2893 pn, sizeof(pn));
2894
2895 switch (s->data_ctx.errors.buf) {
2896 case 0:
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002897 chunk_printf(&msg,
Willy Tarreau74808cb2009-03-04 15:53:18 +01002898 " frontend %s (#%d): invalid request\n"
2899 " src %s, session #%d, backend %s (#%d), server %s (#%d)\n"
Willy Tarreau8811f8e2010-03-05 17:42:58 +01002900 " request length %d bytes, error at position %d:\n \n",
Willy Tarreau74808cb2009-03-04 15:53:18 +01002901 s->data_ctx.errors.px->id, s->data_ctx.errors.px->uuid,
Willy Tarreau3fd6cec2010-03-25 06:45:07 +01002902 pn, es->sid, (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
2903 (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1,
Willy Tarreau74808cb2009-03-04 15:53:18 +01002904 es->srv ? es->srv->id : "<NONE>",
2905 es->srv ? es->srv->puid : -1,
2906 es->len, es->pos);
2907 break;
2908 case 1:
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002909 chunk_printf(&msg,
Willy Tarreau74808cb2009-03-04 15:53:18 +01002910 " backend %s (#%d) : invalid response\n"
2911 " src %s, session #%d, frontend %s (#%d), server %s (#%d)\n"
Willy Tarreau8811f8e2010-03-05 17:42:58 +01002912 " response length %d bytes, error at position %d:\n \n",
Willy Tarreau74808cb2009-03-04 15:53:18 +01002913 s->data_ctx.errors.px->id, s->data_ctx.errors.px->uuid,
2914 pn, es->sid, es->oe->id, es->oe->uuid,
2915 es->srv ? es->srv->id : "<NONE>",
2916 es->srv ? es->srv->puid : -1,
2917 es->len, es->pos);
2918 break;
2919 }
2920
Willy Tarreau61b34732009-10-03 23:49:35 +02002921 if (buffer_feed_chunk(rep, &msg) >= 0) {
Willy Tarreau74808cb2009-03-04 15:53:18 +01002922 /* Socket buffer full. Let's try again later from the same point */
Willy Tarreau61b34732009-10-03 23:49:35 +02002923 return 0;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002924 }
2925 s->data_ctx.errors.ptr = 0;
2926 s->data_ctx.errors.sid = es->sid;
2927 }
2928
2929 if (s->data_ctx.errors.sid != es->sid) {
2930 /* the snapshot changed while we were dumping it */
Krzysztof Piotr Oledzki78abe612009-09-27 13:23:20 +02002931 chunk_printf(&msg,
Willy Tarreau74808cb2009-03-04 15:53:18 +01002932 " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
Willy Tarreau61b34732009-10-03 23:49:35 +02002933 if (buffer_feed_chunk(rep, &msg) >= 0)
2934 return 0;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002935 goto next;
2936 }
2937
2938 /* OK, ptr >= 0, so we have to dump the current line */
Willy Tarreau61b34732009-10-03 23:49:35 +02002939 while (s->data_ctx.errors.ptr < es->len && s->data_ctx.errors.ptr < sizeof(es->buf)) {
Willy Tarreau74808cb2009-03-04 15:53:18 +01002940 int newptr;
2941 int newline;
2942
2943 newline = s->data_ctx.errors.bol;
Willy Tarreaud426a182010-03-05 14:58:26 +01002944 newptr = dump_text_line(&msg, es->buf, sizeof(es->buf), es->len, &newline, s->data_ctx.errors.ptr);
Willy Tarreau74808cb2009-03-04 15:53:18 +01002945 if (newptr == s->data_ctx.errors.ptr)
Willy Tarreau61b34732009-10-03 23:49:35 +02002946 return 0;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002947
Willy Tarreau61b34732009-10-03 23:49:35 +02002948 if (buffer_feed_chunk(rep, &msg) >= 0) {
Willy Tarreau74808cb2009-03-04 15:53:18 +01002949 /* Socket buffer full. Let's try again later from the same point */
Willy Tarreau61b34732009-10-03 23:49:35 +02002950 return 0;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002951 }
2952 s->data_ctx.errors.ptr = newptr;
2953 s->data_ctx.errors.bol = newline;
2954 };
2955 next:
2956 s->data_ctx.errors.bol = 0;
2957 s->data_ctx.errors.ptr = -1;
2958 s->data_ctx.errors.buf++;
2959 if (s->data_ctx.errors.buf > 1) {
2960 s->data_ctx.errors.buf = 0;
2961 s->data_ctx.errors.px = s->data_ctx.errors.px->next;
2962 }
2963 }
2964
2965 /* dump complete */
Willy Tarreau61b34732009-10-03 23:49:35 +02002966 return 1;
Willy Tarreau74808cb2009-03-04 15:53:18 +01002967}
2968
Willy Tarreau3dfe6cd2008-12-07 22:29:48 +01002969
Willy Tarreau10522fd2008-07-09 20:12:41 +02002970static struct cfg_kw_list cfg_kws = {{ },{
2971 { CFG_GLOBAL, "stats", stats_parse_global },
2972 { 0, NULL, NULL },
2973}};
2974
2975__attribute__((constructor))
2976static void __dumpstats_module_init(void)
2977{
2978 cfg_register_keywords(&cfg_kws);
2979}
2980
Willy Tarreau91861262007-10-17 17:06:05 +02002981/*
2982 * Local variables:
2983 * c-indent-level: 8
2984 * c-basic-offset: 8
2985 * End:
2986 */