~ubuntu-branches/ubuntu/intrepid/haproxy/intrepid

« back to all changes in this revision

Viewing changes to src/dumpstats.c

  • Committer: Bazaar Package Importer
  • Author(s): Arnaud Cornet
  • Date: 2008-03-09 21:30:29 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20080309213029-8oupnrc607mg5uqw
Tags: 1.3.14.3-1
* New Upstream Version
* Add status argument support to init-script to conform to LSB.
* Cleanup pidfile after stop in init script. Init script return code fixups.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Functions dedicated to statistics output
 
3
 *
 
4
 * Copyright 2000-2007 Willy Tarreau <w@1wt.eu>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU General Public License
 
8
 * as published by the Free Software Foundation; either version
 
9
 * 2 of the License, or (at your option) any later version.
 
10
 *
 
11
 */
 
12
 
 
13
#include <ctype.h>
 
14
#include <errno.h>
 
15
#include <fcntl.h>
 
16
#include <stdio.h>
 
17
#include <stdlib.h>
 
18
#include <string.h>
 
19
#include <pwd.h>
 
20
#include <grp.h>
 
21
 
 
22
#include <sys/socket.h>
 
23
#include <sys/stat.h>
 
24
#include <sys/types.h>
 
25
 
 
26
#include <common/compat.h>
 
27
#include <common/config.h>
 
28
#include <common/debug.h>
 
29
#include <common/memory.h>
 
30
#include <common/mini-clist.h>
 
31
#include <common/standard.h>
 
32
#include <common/time.h>
 
33
#include <common/uri_auth.h>
 
34
#include <common/version.h>
 
35
 
 
36
#include <types/client.h>
 
37
#include <types/global.h>
 
38
#include <types/polling.h>
 
39
#include <types/proxy.h>
 
40
#include <types/server.h>
 
41
 
 
42
#include <proto/backend.h>
 
43
#include <proto/buffers.h>
 
44
#include <proto/dumpstats.h>
 
45
#include <proto/fd.h>
 
46
#include <proto/proto_uxst.h>
 
47
#include <proto/senddata.h>
 
48
#include <proto/session.h>
 
49
#include <proto/server.h>
 
50
 
 
51
/* This function parses a "stats" statement in the "global" section. It returns
 
52
 * -1 if there is any error, otherwise zero. If it returns -1, it may write an
 
53
 * error message into ther <err> buffer, for at most <errlen> bytes, trailing
 
54
 * zero included. The trailing '\n' must not be written. The function must be
 
55
 * called with <args> pointing to the first word after "stats".
 
56
 */
 
57
int stats_parse_global(const char **args, char *err, int errlen)
 
58
{
 
59
        if (!strcmp(args[0], "socket")) {
 
60
                struct sockaddr_un su;
 
61
                int cur_arg;
 
62
 
 
63
                if (*args[1] == 0) {
 
64
                        snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
 
65
                        return -1;
 
66
                }
 
67
 
 
68
                if (global.stats_sock.state != LI_NEW) {
 
69
                        snprintf(err, errlen, "'stats socket' already specified in global section");
 
70
                        return -1;
 
71
                }
 
72
 
 
73
                su.sun_family = AF_UNIX;
 
74
                strncpy(su.sun_path, args[1], sizeof(su.sun_path));
 
75
                su.sun_path[sizeof(su.sun_path) - 1] = 0;
 
76
                memcpy(&global.stats_sock.addr, &su, sizeof(su)); // guaranteed to fit
 
77
 
 
78
                global.stats_sock.state = LI_INIT;
 
79
                global.stats_sock.options = LI_O_NONE;
 
80
                global.stats_sock.accept = uxst_event_accept;
 
81
                global.stats_sock.handler = process_uxst_stats;
 
82
                global.stats_sock.private = NULL;
 
83
 
 
84
                cur_arg = 2;
 
85
                while (*args[cur_arg]) {
 
86
                        if (!strcmp(args[cur_arg], "uid")) {
 
87
                                global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]);
 
88
                                cur_arg += 2;
 
89
                        }
 
90
                        else if (!strcmp(args[cur_arg], "gid")) {
 
91
                                global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
 
92
                                cur_arg += 2;
 
93
                        }
 
94
                        else if (!strcmp(args[cur_arg], "mode")) {
 
95
                                global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
 
96
                                cur_arg += 2;
 
97
                        }
 
98
                        else if (!strcmp(args[cur_arg], "user")) {
 
99
                                struct passwd *user;
 
100
                                user = getpwnam(args[cur_arg + 1]);
 
101
                                if (!user) {
 
102
                                        snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
 
103
                                                 args[cur_arg + 1]);
 
104
                                        return -1;
 
105
                                }
 
106
                                global.stats_sock.perm.ux.uid = user->pw_uid;
 
107
                                cur_arg += 2;
 
108
                        }
 
109
                        else if (!strcmp(args[cur_arg], "group")) {
 
110
                                struct group *group;
 
111
                                group = getgrnam(args[cur_arg + 1]);
 
112
                                if (!group) {
 
113
                                        snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
 
114
                                                 args[cur_arg + 1]);
 
115
                                        return -1;
 
116
                                }
 
117
                                global.stats_sock.perm.ux.gid = group->gr_gid;
 
118
                                cur_arg += 2;
 
119
                        }
 
120
                        else {
 
121
                                snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', and 'mode'");
 
122
                                return -1;
 
123
                        }
 
124
                }
 
125
                        
 
126
                uxst_add_listener(&global.stats_sock);
 
127
                global.maxsock++;
 
128
        }
 
129
        else if (!strcmp(args[0], "timeout")) {
 
130
                unsigned timeout;
 
131
                const char *res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
 
132
 
 
133
                if (res) {
 
134
                        snprintf(err, errlen, "unexpected character '%c' in 'stats timeout' in 'global' section", *res);
 
135
                        return -1;
 
136
                }
 
137
 
 
138
                if (!timeout) {
 
139
                        snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'");
 
140
                        return -1;
 
141
                }
 
142
                __tv_from_ms(&global.stats_timeout, timeout);
 
143
        }
 
144
        else if (!strcmp(args[0], "maxconn")) {
 
145
                int maxconn = atol(args[1]);
 
146
 
 
147
                if (maxconn <= 0) {
 
148
                        snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'");
 
149
                        return -1;
 
150
                }
 
151
                global.maxsock -= global.stats_sock.maxconn;
 
152
                global.stats_sock.maxconn = maxconn;
 
153
                global.maxsock += global.stats_sock.maxconn;
 
154
        }
 
155
        else {
 
156
                snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section");
 
157
                return -1;
 
158
        }
 
159
        return 0;
 
160
}
 
161
 
 
162
int print_csv_header(struct chunk *msg, int size)
 
163
{
 
164
        return chunk_printf(msg, size,
 
165
                            "# pxname,svname,"
 
166
                            "qcur,qmax,"
 
167
                            "scur,smax,slim,stot,"
 
168
                            "bin,bout,"
 
169
                            "dreq,dresp,"
 
170
                            "ereq,econ,eresp,"
 
171
                            "wretr,wredis,"
 
172
                            "status,weight,act,bck,"
 
173
                            "chkfail,chkdown,lastchg,downtime,qlimit,"
 
174
                            "pid,iid,sid,throttle,lbtot,"
 
175
                            "\n");
 
176
}
 
177
 
 
178
/*
 
179
 * Produces statistics data for the session <s>. Expects to be called with
 
180
 * s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>
 
181
 * and <flags>.
 
182
 * It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
 
183
 * dump is finished and the session must be closed, or -1 in case of any error.
 
184
 */
 
185
int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags)
 
186
{
 
187
        struct buffer *rep = s->rep;
 
188
        struct proxy *px;
 
189
        struct chunk msg;
 
190
        unsigned int up;
 
191
 
 
192
        msg.len = 0;
 
193
        msg.str = trash;
 
194
 
 
195
        switch (s->data_state) {
 
196
        case DATA_ST_INIT:
 
197
                /* the function had not been called yet, let's prepare the
 
198
                 * buffer for a response.
 
199
                 */
 
200
                client_retnclose(s, &msg);
 
201
                s->data_state = DATA_ST_HEAD;
 
202
                /* fall through */
 
203
 
 
204
        case DATA_ST_HEAD:
 
205
                if (flags & STAT_SHOW_STAT) {
 
206
                        print_csv_header(&msg, sizeof(trash));
 
207
                        if (buffer_write_chunk(rep, &msg) != 0)
 
208
                                return 0;
 
209
                }
 
210
 
 
211
                s->data_state = DATA_ST_INFO;
 
212
                /* fall through */
 
213
 
 
214
        case DATA_ST_INFO:
 
215
                up = (now.tv_sec - start_date.tv_sec);
 
216
                memset(&s->data_ctx, 0, sizeof(s->data_ctx));
 
217
 
 
218
                if (flags & STAT_SHOW_INFO) {
 
219
                        chunk_printf(&msg, sizeof(trash),
 
220
                                     "Name: " PRODUCT_NAME "\n"
 
221
                                     "Version: " HAPROXY_VERSION "\n"
 
222
                                     "Release_date: " HAPROXY_DATE "\n"
 
223
                                     "Nbproc: %d\n"
 
224
                                     "Process_num: %d\n"
 
225
                                     "Pid: %d\n"
 
226
                                     "Uptime: %dd %dh%02dm%02ds\n"
 
227
                                     "Uptime_sec: %d\n"
 
228
                                     "Memmax_MB: %d\n"
 
229
                                     "Ulimit-n: %d\n"
 
230
                                     "Maxsock: %d\n"
 
231
                                     "Maxconn: %d\n"
 
232
                                     "CurrConns: %d\n"
 
233
                                     "",
 
234
                                     global.nbproc,
 
235
                                     relative_pid,
 
236
                                     pid,
 
237
                                     up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
 
238
                                     up,
 
239
                                     global.rlimit_memmax,
 
240
                                     global.rlimit_nofile,
 
241
                                     global.maxsock,
 
242
                                     global.maxconn,
 
243
                                     actconn
 
244
                                     );
 
245
                        if (buffer_write_chunk(rep, &msg) != 0)
 
246
                                return 0;
 
247
                }
 
248
 
 
249
                s->data_ctx.stats.px = proxy;
 
250
                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
 
251
                s->data_state = DATA_ST_LIST;
 
252
                /* fall through */
 
253
 
 
254
        case DATA_ST_LIST:
 
255
                /* dump proxies */
 
256
                if (flags & STAT_SHOW_STAT) {
 
257
                        while (s->data_ctx.stats.px) {
 
258
                                px = s->data_ctx.stats.px;
 
259
                                /* skip the disabled proxies and non-networked ones */
 
260
                                if (px->state != PR_STSTOPPED &&
 
261
                                    (px->cap & (PR_CAP_FE | PR_CAP_BE)))
 
262
                                        if (stats_dump_proxy(s, px, NULL, 0) == 0)
 
263
                                                return 0;
 
264
 
 
265
                                s->data_ctx.stats.px = px->next;
 
266
                                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
 
267
                        }
 
268
                        /* here, we just have reached the last proxy */
 
269
                }
 
270
 
 
271
                s->data_state = DATA_ST_END;
 
272
                /* fall through */
 
273
 
 
274
        case DATA_ST_END:
 
275
                s->data_state = DATA_ST_FIN;
 
276
                return 1;
 
277
 
 
278
        case DATA_ST_FIN:
 
279
                return 1;
 
280
 
 
281
        default:
 
282
                /* unknown state ! */
 
283
                return -1;
 
284
        }
 
285
}
 
286
 
 
287
 
 
288
/*
 
289
 * Produces statistics data for the session <s>. Expects to be called with
 
290
 * s->cli_state == CL_STSHUTR. It stops by itself by unsetting the SN_SELF_GEN
 
291
 * flag from the session, which it uses to keep on being called when there is
 
292
 * free space in the buffer, of simply by letting an empty buffer upon return.
 
293
 * It returns 0 if it had to stop writing data and an I/O is needed, 1 if the
 
294
 * dump is finished and the session must be closed, or -1 in case of any error.
 
295
 */
 
296
int stats_dump_http(struct session *s, struct uri_auth *uri, int flags)
 
297
{
 
298
        struct buffer *rep = s->rep;
 
299
        struct proxy *px;
 
300
        struct chunk msg;
 
301
        unsigned int up;
 
302
 
 
303
        msg.len = 0;
 
304
        msg.str = trash;
 
305
 
 
306
        switch (s->data_state) {
 
307
        case DATA_ST_INIT:
 
308
                /* the function had not been called yet */
 
309
                s->flags |= SN_SELF_GEN;  // more data will follow
 
310
 
 
311
                chunk_printf(&msg, sizeof(trash),
 
312
                             "HTTP/1.0 200 OK\r\n"
 
313
                             "Cache-Control: no-cache\r\n"
 
314
                             "Connection: close\r\n"
 
315
                             "Content-Type: %s\r\n",
 
316
                             (flags & STAT_FMT_HTML) ? "text/html" : "text/plain");
 
317
 
 
318
                if (uri->refresh > 0 && !(s->flags & SN_STAT_NORFRSH))
 
319
                        chunk_printf(&msg, sizeof(trash), "Refresh: %d\r\n",
 
320
                                     uri->refresh);
 
321
 
 
322
                chunk_printf(&msg, sizeof(trash), "\r\n");
 
323
 
 
324
                s->txn.status = 200;
 
325
                client_retnclose(s, &msg); // send the start of the response.
 
326
                msg.len = 0;
 
327
 
 
328
                if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
 
329
                        s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
 
330
                if (!(s->flags & SN_FINST_MASK))
 
331
                        s->flags |= SN_FINST_R;
 
332
 
 
333
                if (s->txn.meth == HTTP_METH_HEAD) {
 
334
                        /* that's all we return in case of HEAD request */
 
335
                        s->data_state = DATA_ST_FIN;
 
336
                        s->flags &= ~SN_SELF_GEN;
 
337
                        return 1;
 
338
                }
 
339
 
 
340
                s->data_state = DATA_ST_HEAD; /* let's start producing data */
 
341
                /* fall through */
 
342
 
 
343
        case DATA_ST_HEAD:
 
344
                if (flags & STAT_FMT_HTML) {
 
345
                        /* WARNING! This must fit in the first buffer !!! */        
 
346
                        chunk_printf(&msg, sizeof(trash),
 
347
                             "<html><head><title>Statistics Report for " PRODUCT_NAME "</title>\n"
 
348
                             "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
 
349
                             "<style type=\"text/css\"><!--\n"
 
350
                             "body {"
 
351
                             " font-family: helvetica, arial;"
 
352
                             " font-size: 12px;"
 
353
                             " font-weight: normal;"
 
354
                             " color: black;"
 
355
                             " background: white;"
 
356
                             "}\n"
 
357
                             "th,td {"
 
358
                             " font-size: 0.8em;"
 
359
                             " align: center;"
 
360
                             "}\n"
 
361
                             "h1 {"
 
362
                             " font-size: xx-large;"
 
363
                             " margin-bottom: 0.5em;"
 
364
                             "}\n"
 
365
                             "h2 {"
 
366
                             " font-family: helvetica, arial;"
 
367
                             " font-size: x-large;"
 
368
                             " font-weight: bold;"
 
369
                             " font-style: italic;"
 
370
                             " color: #6020a0;"
 
371
                             " margin-top: 0em;"
 
372
                             " margin-bottom: 0em;"
 
373
                             "}\n"
 
374
                             "h3 {"
 
375
                             " font-family: helvetica, arial;"
 
376
                             " font-size: 16px;"
 
377
                             " font-weight: bold;"
 
378
                             " color: #b00040;"
 
379
                             " background: #e8e8d0;"
 
380
                             " margin-top: 0em;"
 
381
                             " margin-bottom: 0em;"
 
382
                             "}\n"
 
383
                             "li {"
 
384
                             " margin-top: 0.25em;"
 
385
                             " margin-right: 2em;"
 
386
                             "}\n"
 
387
                             ".hr {margin-top: 0.25em;"
 
388
                             " border-color: black;"
 
389
                             " border-bottom-style: solid;"
 
390
                             "}\n"
 
391
                             ".pxname   {background: #b00040;color: #ffff40;font-weight: bold;}\n"
 
392
                             ".titre    {background: #20D0D0;color: #000000;font-weight: bold;}\n"
 
393
                             ".total    {background: #20D0D0;color: #ffff80;}\n"
 
394
                             ".frontend {background: #e8e8d0;}\n"
 
395
                             ".backend  {background: #e8e8d0;}\n"
 
396
                             ".active0  {background: #ff9090;}\n"
 
397
                             ".active1  {background: #ffd020;}\n"
 
398
                             ".active2  {background: #ffffa0;}\n"
 
399
                             ".active3  {background: #c0ffc0;}\n"
 
400
                             ".active4  {background: #ffffa0;}\n"  /* NOLB state shows same as going down */
 
401
                             ".active5  {background: #a0e0a0;}\n"  /* NOLB state shows darker than up */
 
402
                             ".active6  {background: #e0e0e0;}\n"
 
403
                             ".backup0  {background: #ff9090;}\n"
 
404
                             ".backup1  {background: #ff80ff;}\n"
 
405
                             ".backup2  {background: #c060ff;}\n"
 
406
                             ".backup3  {background: #b0d0ff;}\n"
 
407
                             ".backup4  {background: #c060ff;}\n"  /* NOLB state shows same as going down */
 
408
                             ".backup5  {background: #90b0e0;}\n"  /* NOLB state shows same as going down */
 
409
                             ".backup6  {background: #e0e0e0;}\n"
 
410
                             "table.tbl { border-collapse: collapse; border-style: none;}\n"
 
411
                             "table.tbl td { border-width: 1px 1px 1px 1px; border-style: solid solid solid solid; padding: 2px 3px; border-color: gray;}\n"
 
412
                             "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
 
413
                             "table.tbl th.empty { border-style: none; empty-cells: hide;}\n"
 
414
                             "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
 
415
                             "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
 
416
                             "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
 
417
                             "-->\n"
 
418
                             "</style></head>\n");
 
419
                } else {
 
420
                        print_csv_header(&msg, sizeof(trash));
 
421
                }
 
422
                if (buffer_write_chunk(rep, &msg) != 0)
 
423
                        return 0;
 
424
 
 
425
                s->data_state = DATA_ST_INFO;
 
426
                /* fall through */
 
427
 
 
428
        case DATA_ST_INFO:
 
429
                up = (now.tv_sec - start_date.tv_sec);
 
430
 
 
431
                /* WARNING! this has to fit the first packet too.
 
432
                         * We are around 3.5 kB, add adding entries will
 
433
                         * become tricky if we want to support 4kB buffers !
 
434
                         */
 
435
                if (flags & STAT_FMT_HTML) {
 
436
                        chunk_printf(&msg, sizeof(trash),
 
437
                             "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
 
438
                             PRODUCT_NAME "%s</a></h1>\n"
 
439
                             "<h2>Statistics Report for pid %d</h2>\n"
 
440
                             "<hr width=\"100%%\" class=\"hr\">\n"
 
441
                             "<h3>&gt; General process information</h3>\n"
 
442
                             "<table border=0 cols=4><tr><td align=\"left\" nowrap width=\"1%%\">\n"
 
443
                             "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
 
444
                             "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
 
445
                             "<b>system limits :</b> memmax = %s%s ; ulimit-n = %d<br>\n"
 
446
                             "<b>maxsock = </b> %d<br>\n"
 
447
                             "<b>maxconn = </b> %d (current conns = %d)<br>\n"
 
448
                             "</td><td align=\"center\" nowrap>\n"
 
449
                             "<table class=\"lgd\"><tr>\n"
 
450
                             "<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
 
451
                             "<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
 
452
                             "</tr><tr>\n"
 
453
                             "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
 
454
                             "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
 
455
                             "</tr><tr>\n"
 
456
                             "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
 
457
                             "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
 
458
                             "</tr><tr>\n"
 
459
                             "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
 
460
                             "<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
 
461
                             "</tr></table>\n"
 
462
                             "Note: UP with load-balancing disabled is reported as \"NOLB\"."
 
463
                             "</td>"
 
464
                             "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
 
465
                             "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
 
466
                             "",
 
467
                             (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
 
468
                             pid, pid,
 
469
                             relative_pid, global.nbproc,
 
470
                             up / 86400, (up % 86400) / 3600,
 
471
                             (up % 3600) / 60, (up % 60),
 
472
                             global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
 
473
                             global.rlimit_memmax ? " MB" : "",
 
474
                             global.rlimit_nofile,
 
475
                             global.maxsock,
 
476
                             global.maxconn,
 
477
                             actconn
 
478
                             );
 
479
            
 
480
                        if (s->flags & SN_STAT_HIDEDWN)
 
481
                                chunk_printf(&msg, sizeof(trash),
 
482
                                     "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
 
483
                                     uri->uri_prefix,
 
484
                                     "",
 
485
                                     (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
 
486
                        else
 
487
                                chunk_printf(&msg, sizeof(trash),
 
488
                                     "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
 
489
                                     uri->uri_prefix,
 
490
                                     ";up",
 
491
                                     (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
 
492
 
 
493
                        if (uri->refresh > 0) {
 
494
                                if (s->flags & SN_STAT_NORFRSH)
 
495
                                        chunk_printf(&msg, sizeof(trash),
 
496
                                             "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
 
497
                                             uri->uri_prefix,
 
498
                                             (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
 
499
                                             "");
 
500
                                else
 
501
                                        chunk_printf(&msg, sizeof(trash),
 
502
                                             "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
 
503
                                             uri->uri_prefix,
 
504
                                             (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
 
505
                                             ";norefresh");
 
506
                        }
 
507
 
 
508
                        chunk_printf(&msg, sizeof(trash),
 
509
                             "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
 
510
                             uri->uri_prefix,
 
511
                             (s->flags & SN_STAT_HIDEDWN) ? ";up" : "",
 
512
                             (s->flags & SN_STAT_NORFRSH) ? ";norefresh" : "");
 
513
 
 
514
                        chunk_printf(&msg, sizeof(trash),
 
515
                             "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
 
516
                             uri->uri_prefix,
 
517
                             (uri->refresh > 0) ? ";norefresh" : "");
 
518
 
 
519
                        chunk_printf(&msg, sizeof(trash),
 
520
                             "</td>"
 
521
                             "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
 
522
                             "<b>External ressources:</b><ul style=\"margin-top: 0.25em;\">\n"
 
523
                             "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
 
524
                             "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
 
525
                             "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
 
526
                             "</ul>"
 
527
                             "</td>"
 
528
                             "</tr></table>\n"
 
529
                             ""
 
530
                             );
 
531
            
 
532
                        if (buffer_write_chunk(rep, &msg) != 0)
 
533
                                return 0;
 
534
                }
 
535
 
 
536
                memset(&s->data_ctx, 0, sizeof(s->data_ctx));
 
537
 
 
538
                s->data_ctx.stats.px = proxy;
 
539
                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
 
540
                s->data_state = DATA_ST_LIST;
 
541
                /* fall through */
 
542
 
 
543
        case DATA_ST_LIST:
 
544
                /* dump proxies */
 
545
                while (s->data_ctx.stats.px) {
 
546
                        px = s->data_ctx.stats.px;
 
547
                        /* skip the disabled proxies and non-networked ones */
 
548
                        if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
 
549
                                if (stats_dump_proxy(s, px, uri, flags) == 0)
 
550
                                        return 0;
 
551
 
 
552
                        s->data_ctx.stats.px = px->next;
 
553
                        s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
 
554
                }
 
555
                /* here, we just have reached the last proxy */
 
556
 
 
557
                s->data_state = DATA_ST_END;
 
558
                /* fall through */
 
559
 
 
560
        case DATA_ST_END:
 
561
                if (flags & STAT_FMT_HTML) {
 
562
                        chunk_printf(&msg, sizeof(trash), "</body></html>\n");
 
563
                        if (buffer_write_chunk(rep, &msg) != 0)
 
564
                                return 0;
 
565
                }
 
566
 
 
567
                s->data_state = DATA_ST_FIN;
 
568
                /* fall through */
 
569
 
 
570
        case DATA_ST_FIN:
 
571
                s->flags &= ~SN_SELF_GEN;
 
572
                return 1;
 
573
 
 
574
        default:
 
575
                /* unknown state ! */
 
576
                s->flags &= ~SN_SELF_GEN;
 
577
                return -1;
 
578
        }
 
579
}
 
580
 
 
581
 
 
582
/*
 
583
 * Dumps statistics for a proxy.
 
584
 * Returns 0 if it had to stop dumping data because of lack of buffer space,
 
585
 * ot non-zero if everything completed.
 
586
 */
 
587
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags)
 
588
{
 
589
        struct buffer *rep = s->rep;
 
590
        struct server *sv;
 
591
        struct chunk msg;
 
592
 
 
593
        msg.len = 0;
 
594
        msg.str = trash;
 
595
 
 
596
        switch (s->data_ctx.stats.px_st) {
 
597
        case DATA_ST_PX_INIT:
 
598
                /* we are on a new proxy */
 
599
 
 
600
                if (uri && uri->scope) {
 
601
                        /* we have a limited scope, we have to check the proxy name */
 
602
                        struct stat_scope *scope;
 
603
                        int len;
 
604
 
 
605
                        len = strlen(px->id);
 
606
                        scope = uri->scope;
 
607
 
 
608
                        while (scope) {
 
609
                                /* match exact proxy name */
 
610
                                if (scope->px_len == len && !memcmp(px->id, scope->px_id, len))
 
611
                                        break;
 
612
 
 
613
                                /* match '.' which means 'self' proxy */
 
614
                                if (!strcmp(scope->px_id, ".") && px == s->be)
 
615
                                        break;
 
616
                                scope = scope->next;
 
617
                        }
 
618
 
 
619
                        /* proxy name not found : don't dump anything */
 
620
                        if (scope == NULL)
 
621
                                return 1;
 
622
                }
 
623
 
 
624
                s->data_ctx.stats.px_st = DATA_ST_PX_TH;
 
625
                /* fall through */
 
626
 
 
627
        case DATA_ST_PX_TH:
 
628
                if (flags & STAT_FMT_HTML) {
 
629
                        /* print a new table */
 
630
                        chunk_printf(&msg, sizeof(trash),
 
631
                                     "<table cols=\"26\" class=\"tbl\" width=\"100%%\">\n"
 
632
                                     "<tr align=\"center\" class=\"titre\">"
 
633
                                     "<th colspan=2 class=\"pxname\">%s</th>"
 
634
                                     "<th colspan=24 class=\"empty\"></th>"
 
635
                                     "</tr>\n"
 
636
                                     "<tr align=\"center\" class=\"titre\">"
 
637
                                     "<th rowspan=2></th>"
 
638
                                     "<th colspan=3>Queue</th><th colspan=5>Sessions</th>"
 
639
                                     "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
 
640
                                     "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
 
641
                                     "<th colspan=8>Server</th>"
 
642
                                     "</tr>\n"
 
643
                                     "<tr align=\"center\" class=\"titre\">"
 
644
                                     "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
 
645
                                     "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
 
646
                                     "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
 
647
                                     "<th>Resp</th><th>Retr</th><th>Redis</th>"
 
648
                                     "<th>Status</th><th>Wght</th><th>Act</th>"
 
649
                                     "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
 
650
                                     "<th>Thrtle</th>\n"
 
651
                                     "</tr>",
 
652
                                     px->id);
 
653
 
 
654
                        if (buffer_write_chunk(rep, &msg) != 0)
 
655
                                return 0;
 
656
                }
 
657
 
 
658
                s->data_ctx.stats.px_st = DATA_ST_PX_FE;
 
659
                /* fall through */
 
660
 
 
661
        case DATA_ST_PX_FE:
 
662
                /* print the frontend */
 
663
                if (px->cap & PR_CAP_FE) {
 
664
                        if (flags & STAT_FMT_HTML) {
 
665
                                chunk_printf(&msg, sizeof(trash),
 
666
                                     /* name, queue */
 
667
                                     "<tr align=center class=\"frontend\"><td>Frontend</td><td colspan=3></td>"
 
668
                                     /* sessions : current, max, limit, total, lbtot */
 
669
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
670
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
671
                                     "<td align=right></td>"
 
672
                                     /* bytes : in, out */
 
673
                                     "<td align=right>%lld</td><td align=right>%lld</td>"
 
674
                                     /* denied: req, resp */
 
675
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
676
                                     /* errors : request, connect, response */
 
677
                                     "<td align=right>%d</td><td align=right></td><td align=right></td>"
 
678
                                     /* warnings: retries, redispatches */
 
679
                                     "<td align=right></td><td align=right></td>"
 
680
                                     /* server status : reflect frontend status */
 
681
                                     "<td align=center>%s</td>"
 
682
                                     /* rest of server: nothing */
 
683
                                     "<td align=center colspan=7></td></tr>"
 
684
                                     "",
 
685
                                     px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
 
686
                                     px->bytes_in, px->bytes_out,
 
687
                                     px->denied_req, px->denied_resp,
 
688
                                     px->failed_req,
 
689
                                     px->state == PR_STRUN ? "OPEN" :
 
690
                                     px->state == PR_STIDLE ? "FULL" : "STOP");
 
691
                        } else {
 
692
                                chunk_printf(&msg, sizeof(trash),
 
693
                                     /* pxid, name, queue cur, queue max, */
 
694
                                     "%s,FRONTEND,,,"
 
695
                                     /* sessions : current, max, limit, total */
 
696
                                     "%d,%d,%d,%d,"
 
697
                                     /* bytes : in, out */
 
698
                                     "%lld,%lld,"
 
699
                                     /* denied: req, resp */
 
700
                                     "%d,%d,"
 
701
                                     /* errors : request, connect, response */
 
702
                                     "%d,,,"
 
703
                                     /* warnings: retries, redispatches */
 
704
                                     ",,"
 
705
                                     /* server status : reflect frontend status */
 
706
                                     "%s,"
 
707
                                     /* rest of server: nothing */
 
708
                                     ",,,,,,,,"
 
709
                                     /* pid, iid, sid, throttle, lbtot, */
 
710
                                     "%d,%d,0,,,"
 
711
                                     "\n",
 
712
                                     px->id,
 
713
                                     px->feconn, px->feconn_max, px->maxconn, px->cum_feconn,
 
714
                                     px->bytes_in, px->bytes_out,
 
715
                                     px->denied_req, px->denied_resp,
 
716
                                     px->failed_req,
 
717
                                     px->state == PR_STRUN ? "OPEN" :
 
718
                                     px->state == PR_STIDLE ? "FULL" : "STOP",
 
719
                                     relative_pid, px->uuid);
 
720
                        }
 
721
 
 
722
                        if (buffer_write_chunk(rep, &msg) != 0)
 
723
                                return 0;
 
724
                }
 
725
 
 
726
                s->data_ctx.stats.sv = px->srv; /* may be NULL */
 
727
                s->data_ctx.stats.px_st = DATA_ST_PX_SV;
 
728
                /* fall through */
 
729
 
 
730
        case DATA_ST_PX_SV:
 
731
                /* stats.sv has been initialized above */
 
732
                while (s->data_ctx.stats.sv != NULL) {
 
733
                        int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */
 
734
 
 
735
                        sv = s->data_ctx.stats.sv;
 
736
 
 
737
                        /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
 
738
                        if (!(sv->state & SRV_CHECKED))
 
739
                                sv_state = 6;
 
740
                        else if (sv->state & SRV_RUNNING) {
 
741
                                if (sv->health == sv->rise + sv->fall - 1)
 
742
                                        sv_state = 3; /* UP */
 
743
                                else
 
744
                                        sv_state = 2; /* going down */
 
745
 
 
746
                                if (sv->state & SRV_GOINGDOWN)
 
747
                                        sv_state += 2;
 
748
                        }
 
749
                        else
 
750
                                if (sv->health)
 
751
                                        sv_state = 1; /* going up */
 
752
                                else
 
753
                                        sv_state = 0; /* DOWN */
 
754
 
 
755
                        if ((sv_state == 0) && (s->flags & SN_STAT_HIDEDWN)) {
 
756
                                /* do not report servers which are DOWN */
 
757
                                s->data_ctx.stats.sv = sv->next;
 
758
                                continue;
 
759
                        }
 
760
 
 
761
                        if (flags & STAT_FMT_HTML) {
 
762
                                static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d &uarr;",
 
763
                                                               "UP %d/%d &darr;", "UP",
 
764
                                                               "NOLB %d/%d &darr;", "NOLB",
 
765
                                                               "<i>no check</i>" };
 
766
                                chunk_printf(&msg, sizeof(trash),
 
767
                                     /* name */
 
768
                                     "<tr align=\"center\" class=\"%s%d\"><td>%s</td>"
 
769
                                     /* queue : current, max, limit */
 
770
                                     "<td align=right>%d</td><td align=right>%d</td><td align=right>%s</td>"
 
771
                                     /* sessions : current, max, limit, total, lbtot */
 
772
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
773
                                     "<td align=right>%s</td><td align=right>%d</td>"
 
774
                                     "<td align=right>%d</td>"
 
775
                                     /* bytes : in, out */
 
776
                                     "<td align=right>%lld</td><td align=right>%lld</td>"
 
777
                                     /* denied: req, resp */
 
778
                                     "<td align=right></td><td align=right>%d</td>"
 
779
                                     /* errors : request, connect, response */
 
780
                                     "<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
 
781
                                     /* warnings: retries, redispatches */
 
782
                                     "<td align=right>%d</td><td align=right></td>"
 
783
                                     "",
 
784
                                     (sv->state & SRV_BACKUP) ? "backup" : "active",
 
785
                                     sv_state, sv->id,
 
786
                                     sv->nbpend, sv->nbpend_max, LIM2A0(sv->maxqueue, "-"),
 
787
                                     sv->cur_sess, sv->cur_sess_max, LIM2A1(sv->maxconn, "-"),
 
788
                                     sv->cum_sess, sv->cum_lbconn,
 
789
                                     sv->bytes_in, sv->bytes_out,
 
790
                                     sv->failed_secu,
 
791
                                     sv->failed_conns, sv->failed_resp,
 
792
                                     sv->retries);
 
793
                                     
 
794
                                /* status */
 
795
                                chunk_printf(&msg, sizeof(trash), "<td nowrap>");
 
796
 
 
797
                                if (sv->state & SRV_CHECKED)
 
798
                                        chunk_printf(&msg, sizeof(trash), "%s ",
 
799
                                                human_time(now.tv_sec - sv->last_change, 1));
 
800
 
 
801
                                chunk_printf(&msg, sizeof(trash),
 
802
                                     srv_hlt_st[sv_state],
 
803
                                     (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
 
804
                                     (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
 
805
 
 
806
                                chunk_printf(&msg, sizeof(trash),
 
807
                                     /* weight */
 
808
                                     "</td><td>%d</td>"
 
809
                                     /* act, bck */
 
810
                                     "<td>%s</td><td>%s</td>"
 
811
                                     "",
 
812
                                     (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
813
                                     (sv->state & SRV_BACKUP) ? "-" : "Y",
 
814
                                     (sv->state & SRV_BACKUP) ? "Y" : "-");
 
815
 
 
816
                                /* check failures: unique, fatal, down time */
 
817
                                if (sv->state & SRV_CHECKED)
 
818
                                        chunk_printf(&msg, sizeof(trash),
 
819
                                             "<td align=right>%d</td><td align=right>%d</td>"
 
820
                                             "<td nowrap align=right>%s</td>"
 
821
                                             "",
 
822
                                             sv->failed_checks, sv->down_trans,
 
823
                                             human_time(srv_downtime(sv), 1));
 
824
                                else
 
825
                                        chunk_printf(&msg, sizeof(trash),
 
826
                                             "<td colspan=3></td>");
 
827
 
 
828
                                /* throttle */
 
829
                                if ((sv->state & SRV_WARMINGUP) &&
 
830
                                    now.tv_sec < sv->last_change + sv->slowstart &&
 
831
                                    now.tv_sec >= sv->last_change) {
 
832
                                        unsigned int ratio;
 
833
                                        ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
 
834
                                        chunk_printf(&msg, sizeof(trash),
 
835
                                                     "<td>%d %%</td></tr>\n", ratio);
 
836
                                } else {
 
837
                                        chunk_printf(&msg, sizeof(trash),
 
838
                                                     "<td>-</td></tr>\n");
 
839
                                }
 
840
                        } else {
 
841
                                static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,",
 
842
                                                               "UP %d/%d,", "UP,",
 
843
                                                               "NOLB %d/%d,", "NOLB,",
 
844
                                                               "no check," };
 
845
                                chunk_printf(&msg, sizeof(trash),
 
846
                                     /* pxid, name */
 
847
                                     "%s,%s,"
 
848
                                     /* queue : current, max */
 
849
                                     "%d,%d,"
 
850
                                     /* sessions : current, max, limit, total */
 
851
                                     "%d,%d,%s,%d,"
 
852
                                     /* bytes : in, out */
 
853
                                     "%lld,%lld,"
 
854
                                     /* denied: req, resp */
 
855
                                     ",%d,"
 
856
                                     /* errors : request, connect, response */
 
857
                                     ",%d,%d,"
 
858
                                     /* warnings: retries, redispatches */
 
859
                                     "%d,,"
 
860
                                     "",
 
861
                                     px->id, sv->id,
 
862
                                     sv->nbpend, sv->nbpend_max,
 
863
                                     sv->cur_sess, sv->cur_sess_max, LIM2A0(sv->maxconn, ""), sv->cum_sess,
 
864
                                     sv->bytes_in, sv->bytes_out,
 
865
                                     sv->failed_secu,
 
866
                                     sv->failed_conns, sv->failed_resp,
 
867
                                     sv->retries);
 
868
                                     
 
869
                                /* status */
 
870
                                chunk_printf(&msg, sizeof(trash),
 
871
                                     srv_hlt_st[sv_state],
 
872
                                     (sv->state & SRV_RUNNING) ? (sv->health - sv->rise + 1) : (sv->health),
 
873
                                     (sv->state & SRV_RUNNING) ? (sv->fall) : (sv->rise));
 
874
 
 
875
                                chunk_printf(&msg, sizeof(trash),
 
876
                                     /* weight, active, backup */
 
877
                                     "%d,%d,%d,"
 
878
                                     "",
 
879
                                     (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
880
                                     (sv->state & SRV_BACKUP) ? 0 : 1,
 
881
                                     (sv->state & SRV_BACKUP) ? 1 : 0);
 
882
 
 
883
                                /* check failures: unique, fatal; last change, total downtime */
 
884
                                if (sv->state & SRV_CHECKED)
 
885
                                        chunk_printf(&msg, sizeof(trash),
 
886
                                             "%d,%d,%d,%d,",
 
887
                                             sv->failed_checks, sv->down_trans,
 
888
                                             now.tv_sec - sv->last_change, srv_downtime(sv));
 
889
                                else
 
890
                                        chunk_printf(&msg, sizeof(trash),
 
891
                                             ",,,,");
 
892
 
 
893
                                /* queue limit, pid, iid, sid, */
 
894
                                chunk_printf(&msg, sizeof(trash),
 
895
                                     "%s,"
 
896
                                     "%d,%d,%d,",
 
897
                                     LIM2A0(sv->maxqueue, ""),
 
898
                                     relative_pid, px->uuid, sv->puid);
 
899
 
 
900
                                /* throttle */
 
901
                                if ((sv->state & SRV_WARMINGUP) &&
 
902
                                    now.tv_sec < sv->last_change + sv->slowstart &&
 
903
                                    now.tv_sec >= sv->last_change) {
 
904
                                        unsigned int ratio;
 
905
                                        ratio = MAX(1, 100 * (now.tv_sec - sv->last_change) / sv->slowstart);
 
906
                                        chunk_printf(&msg, sizeof(trash), "%d", ratio);
 
907
                                }
 
908
 
 
909
                                /* sessions: lbtot */
 
910
                                chunk_printf(&msg, sizeof(trash), ",%d", sv->cum_lbconn);
 
911
                                /* ',' then EOL */
 
912
                                chunk_printf(&msg, sizeof(trash), ",\n");
 
913
                        }
 
914
                        if (buffer_write_chunk(rep, &msg) != 0)
 
915
                                return 0;
 
916
 
 
917
                        s->data_ctx.stats.sv = sv->next;
 
918
                } /* while sv */
 
919
 
 
920
                s->data_ctx.stats.px_st = DATA_ST_PX_BE;
 
921
                /* fall through */
 
922
 
 
923
        case DATA_ST_PX_BE:
 
924
                /* print the backend */
 
925
                if (px->cap & PR_CAP_BE) {
 
926
                        if (flags & STAT_FMT_HTML) {
 
927
                                chunk_printf(&msg, sizeof(trash),
 
928
                                     /* name */
 
929
                                     "<tr align=center class=\"backend\"><td>Backend</td>"
 
930
                                     /* queue : current, max */
 
931
                                     "<td align=right>%d</td><td align=right>%d</td><td></td>"
 
932
                                     /* sessions : current, max, limit, total, lbtot */
 
933
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
934
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
935
                                     "<td align=right>%d</td>"
 
936
                                     /* bytes : in, out */
 
937
                                     "<td align=right>%lld</td><td align=right>%lld</td>"
 
938
                                     /* denied: req, resp */
 
939
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
940
                                     /* errors : request, connect, response */
 
941
                                     "<td align=right></td><td align=right>%d</td><td align=right>%d</td>\n"
 
942
                                     /* warnings: retries, redispatches */
 
943
                                     "<td align=right>%d</td><td align=right>%d</td>"
 
944
                                     /* backend status: reflect backend status (up/down): we display UP
 
945
                                      * if the backend has known working servers or if it has no server at
 
946
                                      * all (eg: for stats). Then we display the total weight, number of
 
947
                                      * active and backups. */
 
948
                                     "<td align=center nowrap>%s %s</td><td align=center>%d</td>"
 
949
                                     "<td align=center>%d</td><td align=center>%d</td>",
 
950
                                     px->nbpend /* or px->totpend ? */, px->nbpend_max,
 
951
                                     px->beconn, px->beconn_max, px->fullconn, px->cum_beconn, px->cum_lbconn,
 
952
                                     px->bytes_in, px->bytes_out,
 
953
                                     px->denied_req, px->denied_resp,
 
954
                                     px->failed_conns, px->failed_resp,
 
955
                                     px->retries, px->redispatches,
 
956
                                     human_time(now.tv_sec - px->last_change, 1),
 
957
                                     (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" :
 
958
                                             "<font color=\"red\"><b>DOWN</b></font>",
 
959
                                     (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
960
                                     px->srv_act, px->srv_bck);
 
961
 
 
962
                                chunk_printf(&msg, sizeof(trash),
 
963
                                     /* rest of backend: nothing, down transitions, total downtime, throttle */
 
964
                                     "<td align=center>&nbsp;</td><td align=\"right\">%d</td>"
 
965
                                     "<td align=\"right\" nowrap>%s</td>"
 
966
                                     "<td></td>"
 
967
                                     "</tr>",
 
968
                                     px->down_trans,
 
969
                                     px->srv?human_time(be_downtime(px), 1):"&nbsp;");
 
970
                        } else {
 
971
                                chunk_printf(&msg, sizeof(trash),
 
972
                                     /* pxid, name */
 
973
                                     "%s,BACKEND,"
 
974
                                     /* queue : current, max */
 
975
                                     "%d,%d,"
 
976
                                     /* sessions : current, max, limit, total */
 
977
                                     "%d,%d,%d,%d,"
 
978
                                     /* bytes : in, out */
 
979
                                     "%lld,%lld,"
 
980
                                     /* denied: req, resp */
 
981
                                     "%d,%d,"
 
982
                                     /* errors : request, connect, response */
 
983
                                     ",%d,%d,"
 
984
                                     /* warnings: retries, redispatches */
 
985
                                     "%d,%d,"
 
986
                                     /* backend status: reflect backend status (up/down): we display UP
 
987
                                      * if the backend has known working servers or if it has no server at
 
988
                                      * all (eg: for stats). Then we display the total weight, number of
 
989
                                      * active and backups. */
 
990
                                     "%s,"
 
991
                                     "%d,%d,%d,"
 
992
                                     /* rest of backend: nothing, down transitions, last change, total downtime */
 
993
                                     ",%d,%d,%d,,"
 
994
                                     /* pid, iid, sid, throttle, lbtot, */
 
995
                                     "%d,%d,0,,%d,"
 
996
                                     "\n",
 
997
                                     px->id,
 
998
                                     px->nbpend /* or px->totpend ? */, px->nbpend_max,
 
999
                                     px->beconn, px->beconn_max, px->fullconn, px->cum_beconn,
 
1000
                                     px->bytes_in, px->bytes_out,
 
1001
                                     px->denied_req, px->denied_resp,
 
1002
                                     px->failed_conns, px->failed_resp,
 
1003
                                     px->retries, px->redispatches,
 
1004
                                     (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
 
1005
                                     (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
1006
                                     px->srv_act, px->srv_bck,
 
1007
                                     px->down_trans, now.tv_sec - px->last_change,
 
1008
                                     px->srv?be_downtime(px):0,
 
1009
                                     relative_pid, px->uuid,
 
1010
                                     px->cum_lbconn);
 
1011
                        }
 
1012
                        if (buffer_write_chunk(rep, &msg) != 0)
 
1013
                                return 0;
 
1014
                }
 
1015
                
 
1016
                s->data_ctx.stats.px_st = DATA_ST_PX_END;
 
1017
                /* fall through */
 
1018
 
 
1019
        case DATA_ST_PX_END:
 
1020
                if (flags & STAT_FMT_HTML) {
 
1021
                        chunk_printf(&msg, sizeof(trash), "</table><p>\n");
 
1022
 
 
1023
                        if (buffer_write_chunk(rep, &msg) != 0)
 
1024
                                return 0;
 
1025
                }
 
1026
 
 
1027
                s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
 
1028
                /* fall through */
 
1029
 
 
1030
        case DATA_ST_PX_FIN:
 
1031
                return 1;
 
1032
 
 
1033
        default:
 
1034
                /* unknown state, we should put an abort() here ! */
 
1035
                return 1;
 
1036
        }
 
1037
}
 
1038
 
 
1039
/*
 
1040
 * Local variables:
 
1041
 *  c-indent-level: 8
 
1042
 *  c-basic-offset: 8
 
1043
 * End:
 
1044
 */