~ubuntu-branches/debian/stretch/haproxy/stretch

« back to all changes in this revision

Viewing changes to src/dumpstats.c

  • Committer: Package Import Robot
  • Author(s): Apollon Oikonomopoulos
  • Date: 2014-06-20 11:05:17 UTC
  • mfrom: (1.1.15) (15.1.12 experimental)
  • Revision ID: package-import@ubuntu.com-20140620110517-u6q5p9kyy2f3ozw9
Tags: 1.5.0-1
* New upstream stable series. Notable changes since the 1.4 series:
  + Native SSL support on both sides with SNI/NPN/ALPN and OCSP stapling.
  + IPv6 and UNIX sockets are supported everywhere
  + End-to-end HTTP keep-alive for better support of NTLM and improved
    efficiency in static farms
  + HTTP/1.1 response compression (deflate, gzip) to save bandwidth
  + PROXY protocol versions 1 and 2 on both sides
  + Data sampling on everything in request or response, including payload
  + ACLs can use any matching method with any input sample
  + Maps and dynamic ACLs updatable from the CLI
  + Stick-tables support counters to track activity on any input sample
  + Custom format for logs, unique-id, header rewriting, and redirects
  + Improved health checks (SSL, scripted TCP, check agent, ...)
  + Much more scalable configuration supports hundreds of thousands of
    backends and certificates without sweating

* Upload to unstable, merge all 1.5 work from experimental. Most important
  packaging changes since 1.4.25-1 include:
  + systemd support.
  + A more sane default config file.
  + Zero-downtime upgrades between 1.5 releases by gracefully reloading
    HAProxy during upgrades.
  + HTML documentation shipped in the haproxy-doc package.
  + kqueue support for kfreebsd.

* Packaging changes since 1.5~dev26-2:
  + Drop patches merged upstream:
    o Fix-reference-location-in-manpage.patch
    o 0001-BUILD-stats-workaround-stupid-and-bogus-Werror-forma.patch
  + d/watch: look for stable 1.5 releases
  + systemd: respect CONFIG and EXTRAOPTS when specified in
    /etc/default/haproxy.
  + initscript: test the configuration before start or reload.
  + initscript: remove the ENABLED flag and logic.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Functions dedicated to statistics output
 
2
 * Functions dedicated to statistics output and the stats socket
3
3
 *
4
 
 * Copyright 2000-2009 Willy Tarreau <w@1wt.eu>
 
4
 * Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
5
5
 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
6
6
 *
7
7
 * This program is free software; you can redistribute it and/or
35
35
#include <common/time.h>
36
36
#include <common/uri_auth.h>
37
37
#include <common/version.h>
 
38
#include <common/base64.h>
38
39
 
39
40
#include <types/global.h>
40
41
 
41
42
#include <proto/backend.h>
42
 
#include <proto/buffers.h>
 
43
#include <proto/channel.h>
43
44
#include <proto/checks.h>
 
45
#include <proto/compression.h>
44
46
#include <proto/dumpstats.h>
45
47
#include <proto/fd.h>
46
48
#include <proto/freq_ctr.h>
 
49
#include <proto/log.h>
 
50
#include <proto/pattern.h>
47
51
#include <proto/pipe.h>
 
52
#include <proto/listener.h>
 
53
#include <proto/map.h>
 
54
#include <proto/proto_http.h>
48
55
#include <proto/proto_uxst.h>
49
56
#include <proto/proxy.h>
 
57
#include <proto/sample.h>
50
58
#include <proto/session.h>
51
59
#include <proto/server.h>
 
60
#include <proto/raw_sock.h>
52
61
#include <proto/stream_interface.h>
53
62
#include <proto/task.h>
54
63
 
55
 
const char stats_sock_usage_msg[] =
 
64
#ifdef USE_OPENSSL
 
65
#include <proto/ssl_sock.h>
 
66
#endif
 
67
 
 
68
/* stats socket states */
 
69
enum {
 
70
        STAT_CLI_INIT = 0,   /* initial state, must leave to zero ! */
 
71
        STAT_CLI_END,        /* final state, let's close */
 
72
        STAT_CLI_GETREQ,     /* wait for a request */
 
73
        STAT_CLI_OUTPUT,     /* all states after this one are responses */
 
74
        STAT_CLI_PROMPT,     /* display the prompt (first output, same code) */
 
75
        STAT_CLI_PRINT,      /* display message in cli->msg */
 
76
        STAT_CLI_PRINT_FREE, /* display message in cli->msg. After the display, free the pointer */
 
77
        STAT_CLI_O_INFO,     /* dump info */
 
78
        STAT_CLI_O_SESS,     /* dump sessions */
 
79
        STAT_CLI_O_ERR,      /* dump errors */
 
80
        STAT_CLI_O_TAB,      /* dump tables */
 
81
        STAT_CLI_O_CLR,      /* clear tables */
 
82
        STAT_CLI_O_SET,      /* set entries in tables */
 
83
        STAT_CLI_O_STAT,     /* dump stats */
 
84
        STAT_CLI_O_PATS,     /* list all pattern reference avalaible */
 
85
        STAT_CLI_O_PAT,      /* list all entries of a pattern */
 
86
        STAT_CLI_O_MLOOK,    /* lookup a map entry */
 
87
        STAT_CLI_O_POOLS,    /* dump memory pools */
 
88
};
 
89
 
 
90
/* Actions available for the stats admin forms */
 
91
enum {
 
92
        ST_ADM_ACTION_NONE = 0,
 
93
 
 
94
        /* enable/disable health checks */
 
95
        ST_ADM_ACTION_DHLTH,
 
96
        ST_ADM_ACTION_EHLTH,
 
97
 
 
98
        /* force health check status */
 
99
        ST_ADM_ACTION_HRUNN,
 
100
        ST_ADM_ACTION_HNOLB,
 
101
        ST_ADM_ACTION_HDOWN,
 
102
 
 
103
        /* enable/disable agent checks */
 
104
        ST_ADM_ACTION_DAGENT,
 
105
        ST_ADM_ACTION_EAGENT,
 
106
 
 
107
        /* force agent check status */
 
108
        ST_ADM_ACTION_ARUNN,
 
109
        ST_ADM_ACTION_ADOWN,
 
110
 
 
111
        /* set admin state */
 
112
        ST_ADM_ACTION_READY,
 
113
        ST_ADM_ACTION_DRAIN,
 
114
        ST_ADM_ACTION_MAINT,
 
115
        ST_ADM_ACTION_SHUTDOWN,
 
116
        /* these are the ancient actions, still available for compatibility */
 
117
        ST_ADM_ACTION_DISABLE,
 
118
        ST_ADM_ACTION_ENABLE,
 
119
        ST_ADM_ACTION_STOP,
 
120
        ST_ADM_ACTION_START,
 
121
};
 
122
 
 
123
static int stats_dump_info_to_buffer(struct stream_interface *si);
 
124
static int stats_dump_pools_to_buffer(struct stream_interface *si);
 
125
static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct session *sess);
 
126
static int stats_dump_sess_to_buffer(struct stream_interface *si);
 
127
static int stats_dump_errors_to_buffer(struct stream_interface *si);
 
128
static int stats_table_request(struct stream_interface *si, int show);
 
129
static int stats_dump_proxy_to_buffer(struct stream_interface *si, struct proxy *px, struct uri_auth *uri);
 
130
static int stats_dump_stat_to_buffer(struct stream_interface *si, struct uri_auth *uri);
 
131
static int stats_pats_list(struct stream_interface *si);
 
132
static int stats_pat_list(struct stream_interface *si);
 
133
static int stats_map_lookup(struct stream_interface *si);
 
134
 
 
135
/*
 
136
 * cli_io_handler()
 
137
 *     -> stats_dump_sess_to_buffer()     // "show sess"
 
138
 *     -> stats_dump_errors_to_buffer()   // "show errors"
 
139
 *     -> stats_dump_info_to_buffer()     // "show info"
 
140
 *     -> stats_dump_stat_to_buffer()     // "show stat"
 
141
 *        -> stats_dump_csv_header()
 
142
 *        -> stats_dump_proxy_to_buffer()
 
143
 *           -> stats_dump_fe_stats()
 
144
 *           -> stats_dump_li_stats()
 
145
 *           -> stats_dump_sv_stats()
 
146
 *           -> stats_dump_be_stats()
 
147
 *
 
148
 * http_stats_io_handler()
 
149
 *     -> stats_dump_stat_to_buffer()     // same as above, but used for CSV or HTML
 
150
 *        -> stats_dump_csv_header()      // emits the CSV headers (same as above)
 
151
 *        -> stats_dump_html_head()       // emits the HTML headers
 
152
 *        -> stats_dump_html_info()       // emits the equivalent of "show info" at the top
 
153
 *        -> stats_dump_proxy_to_buffer() // same as above, valid for CSV and HTML
 
154
 *           -> stats_dump_html_px_hdr()
 
155
 *           -> stats_dump_fe_stats()
 
156
 *           -> stats_dump_li_stats()
 
157
 *           -> stats_dump_sv_stats()
 
158
 *           -> stats_dump_be_stats()
 
159
 *           -> stats_dump_html_px_end()
 
160
 *        -> stats_dump_html_end()       // emits HTML trailer
 
161
 */
 
162
 
 
163
static struct si_applet cli_applet;
 
164
 
 
165
static const char stats_sock_usage_msg[] =
56
166
        "Unknown command. Please enter one of the following commands only :\n"
57
167
        "  clear counters : clear max statistics counters (add 'all' for all counters)\n"
 
168
        "  clear table    : remove an entry from a table\n"
58
169
        "  help           : this message\n"
59
170
        "  prompt         : toggle interactive mode with prompt\n"
60
171
        "  quit           : disconnect\n"
61
172
        "  show info      : report information about the running process\n"
 
173
        "  show pools     : report information about the memory pools usage\n"
62
174
        "  show stat      : report counters for each proxy and server\n"
63
175
        "  show errors    : report last request and response errors for each proxy\n"
64
176
        "  show sess [id] : report the list of current sessions or dump this session\n"
 
177
        "  show table [id]: report table usage stats or dump this table's contents\n"
65
178
        "  get weight     : report a server's current weight\n"
66
179
        "  set weight     : change a server's weight\n"
 
180
        "  set server     : change a server's state or weight\n"
 
181
        "  set table [id] : update or create a table entry's data\n"
67
182
        "  set timeout    : change a timeout setting\n"
68
 
        "  disable server : set a server in maintenance mode\n"
69
 
        "  enable server  : re-enable a server that was previously in maintenance mode\n"
 
183
        "  set maxconn    : change a maxconn setting\n"
 
184
        "  set rate-limit : change a rate limiting value\n"
 
185
        "  disable        : put a server or frontend in maintenance mode\n"
 
186
        "  enable         : re-enable a server or frontend which is in maintenance mode\n"
 
187
        "  shutdown       : kill a session or a frontend (eg:to release listening ports)\n"
 
188
        "  show acl [id]  : report avalaible acls or dump an acl's contents\n"
 
189
        "  get acl        : reports the patterns matching a sample for an ACL\n"
 
190
        "  add acl        : add acl entry\n"
 
191
        "  del acl        : delete acl entry\n"
 
192
        "  clear acl <id> : clear the content of this acl\n"
 
193
        "  show map [id]  : report avalaible maps or dump a map's contents\n"
 
194
        "  get map        : reports the keys and values matching a sample for a map\n"
 
195
        "  set map        : modify map entry\n"
 
196
        "  add map        : add map entry\n"
 
197
        "  del map        : delete map entry\n"
 
198
        "  clear map <id> : clear the content of this map\n"
 
199
        "  set ssl <stmt> : set statement for ssl\n"
70
200
        "";
71
201
 
72
 
const char stats_permission_denied_msg[] =
 
202
static const char stats_permission_denied_msg[] =
73
203
        "Permission denied\n"
74
204
        "";
75
205
 
 
206
/* data transmission states for the stats responses */
 
207
enum {
 
208
        STAT_ST_INIT = 0,
 
209
        STAT_ST_HEAD,
 
210
        STAT_ST_INFO,
 
211
        STAT_ST_LIST,
 
212
        STAT_ST_END,
 
213
        STAT_ST_FIN,
 
214
};
 
215
 
 
216
/* data transmission states for the stats responses inside a proxy */
 
217
enum {
 
218
        STAT_PX_ST_INIT = 0,
 
219
        STAT_PX_ST_TH,
 
220
        STAT_PX_ST_FE,
 
221
        STAT_PX_ST_LI,
 
222
        STAT_PX_ST_SV,
 
223
        STAT_PX_ST_BE,
 
224
        STAT_PX_ST_END,
 
225
        STAT_PX_ST_FIN,
 
226
};
 
227
 
 
228
extern const char *stat_status_codes[];
 
229
 
 
230
/* This function is called from the session-level accept() in order to instanciate
 
231
 * a new stats socket. It returns a positive value upon success, 0 if the session
 
232
 * needs to be closed and ignored, or a negative value upon critical failure.
 
233
 */
 
234
static int stats_accept(struct session *s)
 
235
{
 
236
        s->target = &cli_applet.obj_type;
 
237
        /* no need to initialize the applet, it will start with st0=st1 = 0 */
 
238
 
 
239
        tv_zero(&s->logs.tv_request);
 
240
        s->logs.t_queue = 0;
 
241
        s->logs.t_connect = 0;
 
242
        s->logs.t_data = 0;
 
243
        s->logs.t_close = 0;
 
244
        s->logs.bytes_in = s->logs.bytes_out = 0;
 
245
        s->logs.prx_queue_size = 0;  /* we get the number of pending conns before us */
 
246
        s->logs.srv_queue_size = 0; /* we will get this number soon */
 
247
 
 
248
        s->req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
 
249
 
 
250
        if (s->listener->timeout) {
 
251
                s->req->rto = *s->listener->timeout;
 
252
                s->rep->wto = *s->listener->timeout;
 
253
        }
 
254
        return 1;
 
255
}
 
256
 
76
257
/* allocate a new stats frontend named <name>, and return it
77
258
 * (or NULL in case of lack of memory).
78
259
 */
79
 
static struct proxy *alloc_stats_fe(const char *name)
 
260
static struct proxy *alloc_stats_fe(const char *name, const char *file, int line)
80
261
{
81
262
        struct proxy *fe;
82
263
 
84
265
        if (!fe)
85
266
                return NULL;
86
267
 
87
 
        LIST_INIT(&fe->pendconns);
88
 
        LIST_INIT(&fe->acl);
89
 
        LIST_INIT(&fe->block_cond);
90
 
        LIST_INIT(&fe->redirect_rules);
91
 
        LIST_INIT(&fe->mon_fail_cond);
92
 
        LIST_INIT(&fe->switching_rules);
93
 
        LIST_INIT(&fe->tcp_req.inspect_rules);
94
 
 
95
 
        /* Timeouts are defined as -1, so we cannot use the zeroed area
96
 
         * as a default value.
97
 
         */
98
 
        proxy_reset_timeouts(fe);
99
 
 
 
268
        init_new_proxy(fe);
 
269
        fe->next = proxy;
 
270
        proxy = fe;
100
271
        fe->last_change = now.tv_sec;
101
272
        fe->id = strdup("GLOBAL");
102
273
        fe->cap = PR_CAP_FE;
 
274
        fe->maxconn = 10;                 /* default to 10 concurrent connections */
 
275
        fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
 
276
        fe->conf.file = strdup(file);
 
277
        fe->conf.line = line;
 
278
        fe->accept = stats_accept;
 
279
 
 
280
        /* the stats frontend is the only one able to assign ID #0 */
 
281
        fe->conf.id.key = fe->uuid = 0;
 
282
        eb32_insert(&used_proxy_id, &fe->conf.id);
103
283
        return fe;
104
284
}
105
285
 
106
286
/* This function parses a "stats" statement in the "global" section. It returns
107
 
 * -1 if there is any error, otherwise zero. If it returns -1, it may write an
108
 
 * error message into ther <err> buffer, for at most <errlen> bytes, trailing
109
 
 * zero included. The trailing '\n' must not be written. The function must be
110
 
 * called with <args> pointing to the first word after "stats".
 
287
 * -1 if there is any error, otherwise zero. If it returns -1, it will write an
 
288
 * error message into the <err> buffer which will be preallocated. The trailing
 
289
 * '\n' must not be written. The function must be called with <args> pointing to
 
290
 * the first word after "stats".
111
291
 */
112
292
static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
113
 
                              struct proxy *defpx, char *err, int errlen)
 
293
                              struct proxy *defpx, const char *file, int line,
 
294
                              char **err)
114
295
{
115
 
        args++;
116
 
        if (!strcmp(args[0], "socket")) {
117
 
                struct sockaddr_un *su;
 
296
        struct bind_conf *bind_conf;
 
297
        struct listener *l;
 
298
 
 
299
        if (!strcmp(args[1], "socket")) {
118
300
                int cur_arg;
119
301
 
120
 
                if (*args[1] == 0) {
121
 
                        snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
122
 
                        return -1;
123
 
                }
124
 
 
125
 
                if (global.stats_sock.state != LI_NEW) {
126
 
                        snprintf(err, errlen, "'stats socket' already specified in global section");
127
 
                        return -1;
128
 
                }
129
 
 
130
 
                su = str2sun(args[1]);
131
 
                if (!su) {
132
 
                        snprintf(err, errlen, "'stats socket' path would require truncation");
133
 
                        return -1;
134
 
                }
135
 
                memcpy(&global.stats_sock.addr, su, sizeof(struct sockaddr_un)); // guaranteed to fit
 
302
                if (*args[2] == 0) {
 
303
                        memprintf(err, "'%s %s' in global section expects an address or a path to a UNIX socket", args[0], args[1]);
 
304
                        return -1;
 
305
                }
136
306
 
137
307
                if (!global.stats_fe) {
138
 
                        if ((global.stats_fe = alloc_stats_fe("GLOBAL")) == NULL) {
139
 
                                snprintf(err, errlen, "out of memory");
 
308
                        if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
 
309
                                memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
140
310
                                return -1;
141
311
                        }
142
 
                        global.stats_fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
143
 
                }
144
 
 
145
 
                global.stats_sock.state = LI_INIT;
146
 
                global.stats_sock.options = LI_O_NONE;
147
 
                global.stats_sock.accept = uxst_event_accept;
148
 
                global.stats_sock.handler = process_session;
149
 
                global.stats_sock.analysers = 0;
150
 
                global.stats_sock.nice = -64;  /* we want to boost priority for local stats */
151
 
                global.stats_sock.private = global.stats_fe; /* must point to the frontend */
152
 
                global.stats_sock.perm.ux.level = ACCESS_LVL_OPER; /* default access level */
153
 
                global.stats_sock.timeout = &global.stats_fe->timeout.client;
154
 
 
155
 
                global.stats_sock.next  = global.stats_fe->listen;
156
 
                global.stats_fe->listen = &global.stats_sock;
157
 
 
158
 
                cur_arg = 2;
 
312
                }
 
313
 
 
314
                bind_conf = bind_conf_alloc(&global.stats_fe->conf.bind, file, line, args[2]);
 
315
                bind_conf->level = ACCESS_LVL_OPER; /* default access level */
 
316
 
 
317
                if (!str2listener(args[2], global.stats_fe, bind_conf, file, line, err)) {
 
318
                        memprintf(err, "parsing [%s:%d] : '%s %s' : %s\n",
 
319
                                  file, line, args[0], args[1], err && *err ? *err : "error");
 
320
                        return -1;
 
321
                }
 
322
 
 
323
                cur_arg = 3;
159
324
                while (*args[cur_arg]) {
160
 
                        if (!strcmp(args[cur_arg], "uid")) {
161
 
                                global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]);
162
 
                                cur_arg += 2;
163
 
                        }
164
 
                        else if (!strcmp(args[cur_arg], "gid")) {
165
 
                                global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
166
 
                                cur_arg += 2;
167
 
                        }
168
 
                        else if (!strcmp(args[cur_arg], "mode")) {
169
 
                                global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
170
 
                                cur_arg += 2;
171
 
                        }
172
 
                        else if (!strcmp(args[cur_arg], "user")) {
173
 
                                struct passwd *user;
174
 
                                user = getpwnam(args[cur_arg + 1]);
175
 
                                if (!user) {
176
 
                                        snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
177
 
                                                 args[cur_arg + 1]);
178
 
                                        return -1;
179
 
                                }
180
 
                                global.stats_sock.perm.ux.uid = user->pw_uid;
181
 
                                cur_arg += 2;
182
 
                        }
183
 
                        else if (!strcmp(args[cur_arg], "group")) {
184
 
                                struct group *group;
185
 
                                group = getgrnam(args[cur_arg + 1]);
186
 
                                if (!group) {
187
 
                                        snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
188
 
                                                 args[cur_arg + 1]);
189
 
                                        return -1;
190
 
                                }
191
 
                                global.stats_sock.perm.ux.gid = group->gr_gid;
192
 
                                cur_arg += 2;
193
 
                        }
194
 
                        else if (!strcmp(args[cur_arg], "level")) {
195
 
                                if (!strcmp(args[cur_arg+1], "user"))
196
 
                                        global.stats_sock.perm.ux.level = ACCESS_LVL_USER;
197
 
                                else if (!strcmp(args[cur_arg+1], "operator"))
198
 
                                        global.stats_sock.perm.ux.level = ACCESS_LVL_OPER;
199
 
                                else if (!strcmp(args[cur_arg+1], "admin"))
200
 
                                        global.stats_sock.perm.ux.level = ACCESS_LVL_ADMIN;
201
 
                                else {
202
 
                                        snprintf(err, errlen, "'stats socket level' only supports 'user', 'operator', and 'admin'");
203
 
                                        return -1;
204
 
                                }
205
 
                                cur_arg += 2;
206
 
                        }
207
 
                        else {
208
 
                                snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', 'level', and 'mode'");
209
 
                                return -1;
210
 
                        }
211
 
                }
212
 
 
213
 
                uxst_add_listener(&global.stats_sock);
214
 
                global.maxsock++;
 
325
                        static int bind_dumped;
 
326
                        struct bind_kw *kw;
 
327
 
 
328
                        kw = bind_find_kw(args[cur_arg]);
 
329
                        if (kw) {
 
330
                                if (!kw->parse) {
 
331
                                        memprintf(err, "'%s %s' : '%s' option is not implemented in this version (check build options).",
 
332
                                                  args[0], args[1], args[cur_arg]);
 
333
                                        return -1;
 
334
                                }
 
335
 
 
336
                                if (kw->parse(args, cur_arg, curpx, bind_conf, err) != 0) {
 
337
                                        if (err && *err)
 
338
                                                memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
 
339
                                        else
 
340
                                                memprintf(err, "'%s %s' : error encountered while processing '%s'",
 
341
                                                          args[0], args[1], args[cur_arg]);
 
342
                                        return -1;
 
343
                                }
 
344
 
 
345
                                cur_arg += 1 + kw->skip;
 
346
                                continue;
 
347
                        }
 
348
 
 
349
                        if (!bind_dumped) {
 
350
                                bind_dump_kws(err);
 
351
                                indent_msg(err, 4);
 
352
                                bind_dumped = 1;
 
353
                        }
 
354
 
 
355
                        memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s",
 
356
                                  args[0], args[1], args[cur_arg],
 
357
                                  err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
 
358
                        return -1;
 
359
                }
 
360
 
 
361
                list_for_each_entry(l, &bind_conf->listeners, by_bind) {
 
362
                        l->maxconn = global.stats_fe->maxconn;
 
363
                        l->backlog = global.stats_fe->backlog;
 
364
                        l->timeout = &global.stats_fe->timeout.client;
 
365
                        l->accept = session_accept;
 
366
                        l->handler = process_session;
 
367
                        l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
 
368
                        l->nice = -64;  /* we want to boost priority for local stats */
 
369
                        global.maxsock += l->maxconn;
 
370
                }
215
371
        }
216
 
        else if (!strcmp(args[0], "timeout")) {
 
372
        else if (!strcmp(args[1], "timeout")) {
217
373
                unsigned timeout;
218
 
                const char *res = parse_time_err(args[1], &timeout, TIME_UNIT_MS);
 
374
                const char *res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
219
375
 
220
376
                if (res) {
221
 
                        snprintf(err, errlen, "unexpected character '%c' in 'stats timeout' in 'global' section", *res);
 
377
                        memprintf(err, "'%s %s' : unexpected character '%c'", args[0], args[1], *res);
222
378
                        return -1;
223
379
                }
224
380
 
225
381
                if (!timeout) {
226
 
                        snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'");
 
382
                        memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
227
383
                        return -1;
228
384
                }
229
385
                if (!global.stats_fe) {
230
 
                        if ((global.stats_fe = alloc_stats_fe("GLOBAL")) == NULL) {
231
 
                                snprintf(err, errlen, "out of memory");
 
386
                        if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
 
387
                                memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
232
388
                                return -1;
233
389
                        }
234
390
                }
235
391
                global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
236
392
        }
237
 
        else if (!strcmp(args[0], "maxconn")) {
238
 
                int maxconn = atol(args[1]);
 
393
        else if (!strcmp(args[1], "maxconn")) {
 
394
                int maxconn = atol(args[2]);
239
395
 
240
396
                if (maxconn <= 0) {
241
 
                        snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'");
 
397
                        memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
242
398
                        return -1;
243
399
                }
244
 
                global.maxsock -= global.stats_sock.maxconn;
245
 
                global.stats_sock.maxconn = maxconn;
246
 
                global.maxsock += global.stats_sock.maxconn;
 
400
 
 
401
                if (!global.stats_fe) {
 
402
                        if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
 
403
                                memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
 
404
                                return -1;
 
405
                        }
 
406
                }
 
407
                global.stats_fe->maxconn = maxconn;
 
408
        }
 
409
        else if (!strcmp(args[1], "bind-process")) {  /* enable the socket only on some processes */
 
410
                int cur_arg = 2;
 
411
                unsigned long set = 0;
 
412
 
 
413
                if (!global.stats_fe) {
 
414
                        if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
 
415
                                memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
 
416
                                return -1;
 
417
                        }
 
418
                }
 
419
 
 
420
                while (*args[cur_arg]) {
 
421
                        unsigned int low, high;
 
422
 
 
423
                        if (strcmp(args[cur_arg], "all") == 0) {
 
424
                                set = 0;
 
425
                                break;
 
426
                        }
 
427
                        else if (strcmp(args[cur_arg], "odd") == 0) {
 
428
                                set |= ~0UL/3UL; /* 0x555....555 */
 
429
                        }
 
430
                        else if (strcmp(args[cur_arg], "even") == 0) {
 
431
                                set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
 
432
                        }
 
433
                        else if (isdigit((int)*args[cur_arg])) {
 
434
                                char *dash = strchr(args[cur_arg], '-');
 
435
 
 
436
                                low = high = str2uic(args[cur_arg]);
 
437
                                if (dash)
 
438
                                        high = str2uic(dash + 1);
 
439
 
 
440
                                if (high < low) {
 
441
                                        unsigned int swap = low;
 
442
                                        low = high;
 
443
                                        high = swap;
 
444
                                }
 
445
 
 
446
                                if (low < 1 || high > LONGBITS) {
 
447
                                        memprintf(err, "'%s %s' supports process numbers from 1 to %d.\n",
 
448
                                                  args[0], args[1], LONGBITS);
 
449
                                        return -1;
 
450
                                }
 
451
                                while (low <= high)
 
452
                                        set |= 1UL << (low++ - 1);
 
453
                        }
 
454
                        else {
 
455
                                memprintf(err,
 
456
                                          "'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
 
457
                                          args[0], args[1], LONGBITS);
 
458
                                return -1;
 
459
                        }
 
460
                        cur_arg++;
 
461
                }
 
462
                global.stats_fe->bind_proc = set;
247
463
        }
248
464
        else {
249
 
                snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section");
 
465
                memprintf(err, "'%s' only supports 'socket', 'maxconn', 'bind-process' and 'timeout' (got '%s')", args[0], args[1]);
250
466
                return -1;
251
467
        }
252
468
        return 0;
253
469
}
254
470
 
255
 
int print_csv_header(struct chunk *msg)
256
 
{
257
 
        return chunk_printf(msg,
258
 
                            "# pxname,svname,"
259
 
                            "qcur,qmax,"
260
 
                            "scur,smax,slim,stot,"
261
 
                            "bin,bout,"
262
 
                            "dreq,dresp,"
263
 
                            "ereq,econ,eresp,"
264
 
                            "wretr,wredis,"
265
 
                            "status,weight,act,bck,"
266
 
                            "chkfail,chkdown,lastchg,downtime,qlimit,"
267
 
                            "pid,iid,sid,throttle,lbtot,tracked,type,"
268
 
                            "rate,rate_lim,rate_max,"
269
 
                            "check_status,check_code,check_duration,"
270
 
                            "hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,"
271
 
                            "req_rate,req_rate_max,req_tot,"
272
 
                            "cli_abrt,srv_abrt,"
273
 
                            "\n");
 
471
/* Dumps the stats CSV header to the trash buffer which. The caller is responsible
 
472
 * for clearing it if needed.
 
473
 * NOTE: Some tools happen to rely on the field position instead of its name,
 
474
 *       so please only append new fields at the end, never in the middle.
 
475
 */
 
476
static void stats_dump_csv_header()
 
477
{
 
478
        chunk_appendf(&trash,
 
479
                      "# pxname,svname,"
 
480
                      "qcur,qmax,"
 
481
                      "scur,smax,slim,stot,"
 
482
                      "bin,bout,"
 
483
                      "dreq,dresp,"
 
484
                      "ereq,econ,eresp,"
 
485
                      "wretr,wredis,"
 
486
                      "status,weight,act,bck,"
 
487
                      "chkfail,chkdown,lastchg,downtime,qlimit,"
 
488
                      "pid,iid,sid,throttle,lbtot,tracked,type,"
 
489
                      "rate,rate_lim,rate_max,"
 
490
                      "check_status,check_code,check_duration,"
 
491
                      "hrsp_1xx,hrsp_2xx,hrsp_3xx,hrsp_4xx,hrsp_5xx,hrsp_other,hanafail,"
 
492
                      "req_rate,req_rate_max,req_tot,"
 
493
                      "cli_abrt,srv_abrt,"
 
494
                      "comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk,last_agt,qtime,ctime,rtime,ttime,"
 
495
                      "\n");
 
496
}
 
497
 
 
498
/* print a string of text buffer to <out>. The format is :
 
499
 * Non-printable chars \t, \n, \r and \e are * encoded in C format.
 
500
 * Other non-printable chars are encoded "\xHH". Space and '\' are also escaped.
 
501
 * Print stopped if null char or <bsize> is reached, or if no more place in the chunk.
 
502
 */
 
503
static int dump_text(struct chunk *out, const char *buf, int bsize)
 
504
{
 
505
        unsigned char c;
 
506
        int ptr = 0;
 
507
 
 
508
        while (buf[ptr] && ptr < bsize) {
 
509
                c = buf[ptr];
 
510
                if (isprint(c) && isascii(c) && c != '\\' && c != ' ') {
 
511
                        if (out->len > out->size - 1)
 
512
                                break;
 
513
                        out->str[out->len++] = c;
 
514
                }
 
515
                else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\' || c == ' ') {
 
516
                        if (out->len > out->size - 2)
 
517
                                break;
 
518
                        out->str[out->len++] = '\\';
 
519
                        switch (c) {
 
520
                        case ' ': c = ' '; break;
 
521
                        case '\t': c = 't'; break;
 
522
                        case '\n': c = 'n'; break;
 
523
                        case '\r': c = 'r'; break;
 
524
                        case '\e': c = 'e'; break;
 
525
                        case '\\': c = '\\'; break;
 
526
                        }
 
527
                        out->str[out->len++] = c;
 
528
                }
 
529
                else {
 
530
                        if (out->len > out->size - 4)
 
531
                                break;
 
532
                        out->str[out->len++] = '\\';
 
533
                        out->str[out->len++] = 'x';
 
534
                        out->str[out->len++] = hextab[(c >> 4) & 0xF];
 
535
                        out->str[out->len++] = hextab[c & 0xF];
 
536
                }
 
537
                ptr++;
 
538
        }
 
539
 
 
540
        return ptr;
 
541
}
 
542
 
 
543
/* print a buffer in hexa.
 
544
 * Print stopped if <bsize> is reached, or if no more place in the chunk.
 
545
 */
 
546
static int dump_binary(struct chunk *out, const char *buf, int bsize)
 
547
{
 
548
        unsigned char c;
 
549
        int ptr = 0;
 
550
 
 
551
        while (ptr < bsize) {
 
552
                c = buf[ptr];
 
553
 
 
554
                if (out->len > out->size - 2)
 
555
                        break;
 
556
                out->str[out->len++] = hextab[(c >> 4) & 0xF];
 
557
                out->str[out->len++] = hextab[c & 0xF];
 
558
 
 
559
                ptr++;
 
560
        }
 
561
        return ptr;
 
562
}
 
563
 
 
564
/* Dump the status of a table to a stream interface's
 
565
 * read buffer. It returns 0 if the output buffer is full
 
566
 * and needs to be called again, otherwise non-zero.
 
567
 */
 
568
static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
 
569
                                           struct proxy *proxy, struct proxy *target)
 
570
{
 
571
        struct session *s = session_from_task(si->owner);
 
572
 
 
573
        chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
 
574
                     proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
 
575
 
 
576
        /* any other information should be dumped here */
 
577
 
 
578
        if (target && s->listener->bind_conf->level < ACCESS_LVL_OPER)
 
579
                chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
 
580
 
 
581
        if (bi_putchk(si->ib, msg) == -1)
 
582
                return 0;
 
583
 
 
584
        return 1;
 
585
}
 
586
 
 
587
/* Dump the a table entry to a stream interface's
 
588
 * read buffer. It returns 0 if the output buffer is full
 
589
 * and needs to be called again, otherwise non-zero.
 
590
 */
 
591
static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
 
592
                                            struct proxy *proxy, struct stksess *entry)
 
593
{
 
594
        int dt;
 
595
 
 
596
        chunk_appendf(msg, "%p:", entry);
 
597
 
 
598
        if (proxy->table.type == STKTABLE_TYPE_IP) {
 
599
                char addr[INET_ADDRSTRLEN];
 
600
                inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
 
601
                chunk_appendf(msg, " key=%s", addr);
 
602
        }
 
603
        else if (proxy->table.type == STKTABLE_TYPE_IPV6) {
 
604
                char addr[INET6_ADDRSTRLEN];
 
605
                inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
 
606
                chunk_appendf(msg, " key=%s", addr);
 
607
        }
 
608
        else if (proxy->table.type == STKTABLE_TYPE_INTEGER) {
 
609
                chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
 
610
        }
 
611
        else if (proxy->table.type == STKTABLE_TYPE_STRING) {
 
612
                chunk_appendf(msg, " key=");
 
613
                dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
 
614
        }
 
615
        else {
 
616
                chunk_appendf(msg, " key=");
 
617
                dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
 
618
        }
 
619
 
 
620
        chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
 
621
 
 
622
        for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
 
623
                void *ptr;
 
624
 
 
625
                if (proxy->table.data_ofs[dt] == 0)
 
626
                        continue;
 
627
                if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
 
628
                        chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
 
629
                else
 
630
                        chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
 
631
 
 
632
                ptr = stktable_data_ptr(&proxy->table, entry, dt);
 
633
                switch (stktable_data_types[dt].std_type) {
 
634
                case STD_T_SINT:
 
635
                        chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
 
636
                        break;
 
637
                case STD_T_UINT:
 
638
                        chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
 
639
                        break;
 
640
                case STD_T_ULL:
 
641
                        chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
 
642
                        break;
 
643
                case STD_T_FRQP:
 
644
                        chunk_appendf(msg, "%d",
 
645
                                     read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
 
646
                                                          proxy->table.data_arg[dt].u));
 
647
                        break;
 
648
                }
 
649
        }
 
650
        chunk_appendf(msg, "\n");
 
651
 
 
652
        if (bi_putchk(si->ib, msg) == -1)
 
653
                return 0;
 
654
 
 
655
        return 1;
 
656
}
 
657
 
 
658
static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
 
659
{
 
660
        struct session *s = session_from_task(si->owner);
 
661
        struct appctx *appctx = __objt_appctx(si->end);
 
662
        struct proxy *px = appctx->ctx.table.target;
 
663
        struct stksess *ts;
 
664
        uint32_t uint32_key;
 
665
        unsigned char ip6_key[sizeof(struct in6_addr)];
 
666
        long long value;
 
667
        int data_type;
 
668
        int cur_arg;
 
669
        void *ptr;
 
670
        struct freq_ctr_period *frqp;
 
671
 
 
672
        appctx->st0 = STAT_CLI_OUTPUT;
 
673
 
 
674
        if (!*args[4]) {
 
675
                appctx->ctx.cli.msg = "Key value expected\n";
 
676
                appctx->st0 = STAT_CLI_PRINT;
 
677
                return;
 
678
        }
 
679
 
 
680
        switch (px->table.type) {
 
681
        case STKTABLE_TYPE_IP:
 
682
                uint32_key = htonl(inetaddr_host(args[4]));
 
683
                static_table_key->key = &uint32_key;
 
684
                break;
 
685
        case STKTABLE_TYPE_IPV6:
 
686
                inet_pton(AF_INET6, args[4], ip6_key);
 
687
                static_table_key->key = &ip6_key;
 
688
                break;
 
689
        case STKTABLE_TYPE_INTEGER:
 
690
                {
 
691
                        char *endptr;
 
692
                        unsigned long val;
 
693
                        errno = 0;
 
694
                        val = strtoul(args[4], &endptr, 10);
 
695
                        if ((errno == ERANGE && val == ULONG_MAX) ||
 
696
                            (errno != 0 && val == 0) || endptr == args[4] ||
 
697
                            val > 0xffffffff) {
 
698
                                appctx->ctx.cli.msg = "Invalid key\n";
 
699
                                appctx->st0 = STAT_CLI_PRINT;
 
700
                                return;
 
701
                        }
 
702
                        uint32_key = (uint32_t) val;
 
703
                        static_table_key->key = &uint32_key;
 
704
                        break;
 
705
                }
 
706
                break;
 
707
        case STKTABLE_TYPE_STRING:
 
708
                static_table_key->key = args[4];
 
709
                static_table_key->key_len = strlen(args[4]);
 
710
                break;
 
711
        default:
 
712
                switch (action) {
 
713
                case STAT_CLI_O_TAB:
 
714
                        appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
 
715
                        break;
 
716
                case STAT_CLI_O_CLR:
 
717
                        appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
 
718
                        break;
 
719
                default:
 
720
                        appctx->ctx.cli.msg = "Unknown action\n";
 
721
                        break;
 
722
                }
 
723
                appctx->st0 = STAT_CLI_PRINT;
 
724
                return;
 
725
        }
 
726
 
 
727
        /* check permissions */
 
728
        if (s->listener->bind_conf->level < ACCESS_LVL_OPER) {
 
729
                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
730
                appctx->st0 = STAT_CLI_PRINT;
 
731
                return;
 
732
        }
 
733
 
 
734
        ts = stktable_lookup_key(&px->table, static_table_key);
 
735
 
 
736
        switch (action) {
 
737
        case STAT_CLI_O_TAB:
 
738
                if (!ts)
 
739
                        return;
 
740
                chunk_reset(&trash);
 
741
                if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
 
742
                        return;
 
743
                stats_dump_table_entry_to_buffer(&trash, si, px, ts);
 
744
                return;
 
745
 
 
746
        case STAT_CLI_O_CLR:
 
747
                if (!ts)
 
748
                        return;
 
749
                if (ts->ref_cnt) {
 
750
                        /* don't delete an entry which is currently referenced */
 
751
                        appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
 
752
                        appctx->st0 = STAT_CLI_PRINT;
 
753
                        return;
 
754
                }
 
755
                stksess_kill(&px->table, ts);
 
756
                break;
 
757
 
 
758
        case STAT_CLI_O_SET:
 
759
                if (ts)
 
760
                        stktable_touch(&px->table, ts, 1);
 
761
                else {
 
762
                        ts = stksess_new(&px->table, static_table_key);
 
763
                        if (!ts) {
 
764
                                /* don't delete an entry which is currently referenced */
 
765
                                appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
 
766
                                appctx->st0 = STAT_CLI_PRINT;
 
767
                                return;
 
768
                        }
 
769
                        stktable_store(&px->table, ts, 1);
 
770
                }
 
771
 
 
772
                for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
 
773
                        if (strncmp(args[cur_arg], "data.", 5) != 0) {
 
774
                                appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
 
775
                                appctx->st0 = STAT_CLI_PRINT;
 
776
                                return;
 
777
                        }
 
778
 
 
779
                        data_type = stktable_get_data_type(args[cur_arg] + 5);
 
780
                        if (data_type < 0) {
 
781
                                appctx->ctx.cli.msg = "Unknown data type\n";
 
782
                                appctx->st0 = STAT_CLI_PRINT;
 
783
                                return;
 
784
                        }
 
785
 
 
786
                        if (!px->table.data_ofs[data_type]) {
 
787
                                appctx->ctx.cli.msg = "Data type not stored in this table\n";
 
788
                                appctx->st0 = STAT_CLI_PRINT;
 
789
                                return;
 
790
                        }
 
791
 
 
792
                        if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
 
793
                                appctx->ctx.cli.msg = "Require a valid integer value to store\n";
 
794
                                appctx->st0 = STAT_CLI_PRINT;
 
795
                                return;
 
796
                        }
 
797
 
 
798
                        ptr = stktable_data_ptr(&px->table, ts, data_type);
 
799
 
 
800
                        switch (stktable_data_types[data_type].std_type) {
 
801
                        case STD_T_SINT:
 
802
                                stktable_data_cast(ptr, std_t_sint) = value;
 
803
                                break;
 
804
                        case STD_T_UINT:
 
805
                                stktable_data_cast(ptr, std_t_uint) = value;
 
806
                                break;
 
807
                        case STD_T_ULL:
 
808
                                stktable_data_cast(ptr, std_t_ull) = value;
 
809
                                break;
 
810
                        case STD_T_FRQP:
 
811
                                /* We set both the current and previous values. That way
 
812
                                 * the reported frequency is stable during all the period
 
813
                                 * then slowly fades out. This allows external tools to
 
814
                                 * push measures without having to update them too often.
 
815
                                 */
 
816
                                frqp = &stktable_data_cast(ptr, std_t_frqp);
 
817
                                frqp->curr_tick = now_ms;
 
818
                                frqp->prev_ctr = 0;
 
819
                                frqp->curr_ctr = value;
 
820
                                break;
 
821
                        }
 
822
                }
 
823
                break;
 
824
 
 
825
        default:
 
826
                appctx->ctx.cli.msg = "Unknown action\n";
 
827
                appctx->st0 = STAT_CLI_PRINT;
 
828
                break;
 
829
        }
 
830
}
 
831
 
 
832
static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
 
833
{
 
834
        struct appctx *appctx = __objt_appctx(si->end);
 
835
 
 
836
        if (action != STAT_CLI_O_TAB && action != STAT_CLI_O_CLR) {
 
837
                appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
 
838
                appctx->st0 = STAT_CLI_PRINT;
 
839
                return;
 
840
        }
 
841
 
 
842
        /* condition on stored data value */
 
843
        appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
 
844
        if (appctx->ctx.table.data_type < 0) {
 
845
                appctx->ctx.cli.msg = "Unknown data type\n";
 
846
                appctx->st0 = STAT_CLI_PRINT;
 
847
                return;
 
848
        }
 
849
 
 
850
        if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
 
851
                appctx->ctx.cli.msg = "Data type not stored in this table\n";
 
852
                appctx->st0 = STAT_CLI_PRINT;
 
853
                return;
 
854
        }
 
855
 
 
856
        appctx->ctx.table.data_op = get_std_op(args[4]);
 
857
        if (appctx->ctx.table.data_op < 0) {
 
858
                appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
 
859
                appctx->st0 = STAT_CLI_PRINT;
 
860
                return;
 
861
        }
 
862
 
 
863
        if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
 
864
                appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
 
865
                appctx->st0 = STAT_CLI_PRINT;
 
866
                return;
 
867
        }
 
868
}
 
869
 
 
870
static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
 
871
{
 
872
        struct appctx *appctx = __objt_appctx(si->end);
 
873
 
 
874
        appctx->ctx.table.data_type = -1;
 
875
        appctx->st2 = STAT_ST_INIT;
 
876
        appctx->ctx.table.target = NULL;
 
877
        appctx->ctx.table.proxy = NULL;
 
878
        appctx->ctx.table.entry = NULL;
 
879
        appctx->st0 = action;
 
880
 
 
881
        if (*args[2]) {
 
882
                appctx->ctx.table.target = find_stktable(args[2]);
 
883
                if (!appctx->ctx.table.target) {
 
884
                        appctx->ctx.cli.msg = "No such table\n";
 
885
                        appctx->st0 = STAT_CLI_PRINT;
 
886
                        return;
 
887
                }
 
888
        }
 
889
        else {
 
890
                if (action != STAT_CLI_O_TAB)
 
891
                        goto err_args;
 
892
                return;
 
893
        }
 
894
 
 
895
        if (strcmp(args[3], "key") == 0)
 
896
                stats_sock_table_key_request(si, args, action);
 
897
        else if (strncmp(args[3], "data.", 5) == 0)
 
898
                stats_sock_table_data_request(si, args, action);
 
899
        else if (*args[3])
 
900
                goto err_args;
 
901
 
 
902
        return;
 
903
 
 
904
err_args:
 
905
        switch (action) {
 
906
        case STAT_CLI_O_TAB:
 
907
                appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
 
908
                break;
 
909
        case STAT_CLI_O_CLR:
 
910
                appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
 
911
                break;
 
912
        default:
 
913
                appctx->ctx.cli.msg = "Unknown action\n";
 
914
                break;
 
915
        }
 
916
        appctx->st0 = STAT_CLI_PRINT;
 
917
}
 
918
 
 
919
/* Expects to find a frontend named <arg> and returns it, otherwise displays various
 
920
 * adequate error messages and returns NULL. This function also expects the session
 
921
 * level to be admin.
 
922
 */
 
923
static struct proxy *expect_frontend_admin(struct session *s, struct stream_interface *si, const char *arg)
 
924
{
 
925
        struct appctx *appctx = __objt_appctx(si->end);
 
926
        struct proxy *px;
 
927
 
 
928
        if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
929
                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
930
                appctx->st0 = STAT_CLI_PRINT;
 
931
                return NULL;
 
932
        }
 
933
 
 
934
        if (!*arg) {
 
935
                appctx->ctx.cli.msg = "A frontend name is expected.\n";
 
936
                appctx->st0 = STAT_CLI_PRINT;
 
937
                return NULL;
 
938
        }
 
939
 
 
940
        px = findproxy(arg, PR_CAP_FE);
 
941
        if (!px) {
 
942
                appctx->ctx.cli.msg = "No such frontend.\n";
 
943
                appctx->st0 = STAT_CLI_PRINT;
 
944
                return NULL;
 
945
        }
 
946
        return px;
 
947
}
 
948
 
 
949
/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
 
950
 * and returns the pointer to the server. Otherwise, display adequate error messages
 
951
 * and returns NULL. This function also expects the session level to be admin. Note:
 
952
 * the <arg> is modified to remove the '/'.
 
953
 */
 
954
static struct server *expect_server_admin(struct session *s, struct stream_interface *si, char *arg)
 
955
{
 
956
        struct appctx *appctx = __objt_appctx(si->end);
 
957
        struct proxy *px;
 
958
        struct server *sv;
 
959
        char *line;
 
960
 
 
961
        if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
962
                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
963
                appctx->st0 = STAT_CLI_PRINT;
 
964
                return NULL;
 
965
        }
 
966
 
 
967
        /* split "backend/server" and make <line> point to server */
 
968
        for (line = arg; *line; line++)
 
969
                if (*line == '/') {
 
970
                        *line++ = '\0';
 
971
                        break;
 
972
                }
 
973
 
 
974
        if (!*line || !*arg) {
 
975
                appctx->ctx.cli.msg = "Require 'backend/server'.\n";
 
976
                appctx->st0 = STAT_CLI_PRINT;
 
977
                return NULL;
 
978
        }
 
979
 
 
980
        if (!get_backend_server(arg, line, &px, &sv)) {
 
981
                appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
 
982
                appctx->st0 = STAT_CLI_PRINT;
 
983
                return NULL;
 
984
        }
 
985
 
 
986
        if (px->state == PR_STSTOPPED) {
 
987
                appctx->ctx.cli.msg = "Proxy is disabled.\n";
 
988
                appctx->st0 = STAT_CLI_PRINT;
 
989
                return NULL;
 
990
        }
 
991
 
 
992
        return sv;
 
993
}
 
994
 
 
995
/* This function is used with map and acl management. It permits to browse
 
996
 * each reference. The variable <getnext> must contain the current node,
 
997
 * <end> point to the root node and the <flags> permit to filter required
 
998
 * nodes.
 
999
 */
 
1000
static inline
 
1001
struct pat_ref *pat_list_get_next(struct pat_ref *getnext, struct list *end,
 
1002
                                  unsigned int flags)
 
1003
{
 
1004
        struct pat_ref *ref = getnext;
 
1005
 
 
1006
        while (1) {
 
1007
 
 
1008
                /* Get next list entry. */
 
1009
                ref = LIST_NEXT(&ref->list, struct pat_ref *, list);
 
1010
 
 
1011
                /* If the entry is the last of the list, return NULL. */
 
1012
                if (&ref->list == end)
 
1013
                        return NULL;
 
1014
 
 
1015
                /* If the entry match the flag, return it. */
 
1016
                if (ref->flags & flags)
 
1017
                        return ref;
 
1018
        }
 
1019
}
 
1020
 
 
1021
static inline
 
1022
struct pat_ref *pat_ref_lookup_ref(const char *reference)
 
1023
{
 
1024
        int id;
 
1025
        char *error;
 
1026
 
 
1027
        /* If the reference starts by a '#', this is numeric id. */
 
1028
        if (reference[0] == '#') {
 
1029
                /* Try to convert the numeric id. If the conversion fails, the lookup fails. */
 
1030
                id = strtol(reference + 1, &error, 10);
 
1031
                if (*error != '\0')
 
1032
                        return NULL;
 
1033
 
 
1034
                /* Perform the unique id lookup. */
 
1035
                return pat_ref_lookupid(id);
 
1036
        }
 
1037
 
 
1038
        /* Perform the string lookup. */
 
1039
        return pat_ref_lookup(reference);
 
1040
}
 
1041
 
 
1042
/* This function is used with map and acl management. It permits to browse
 
1043
 * each reference.
 
1044
 */
 
1045
static inline
 
1046
struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
 
1047
{
 
1048
        struct pattern_expr *expr;
 
1049
        expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
 
1050
        if (&expr->list == end)
 
1051
                return NULL;
 
1052
        return expr;
274
1053
}
275
1054
 
276
1055
/* Processes the stats interpreter on the statistics socket. This function is
277
1056
 * called from an applet running in a stream interface. The function returns 1
278
 
 * if the request was understood, otherwise zero. It sets si->st0 to a value
 
1057
 * if the request was understood, otherwise zero. It sets appctx->st0 to a value
279
1058
 * designating the function which will have to process the request, which can
280
1059
 * also be the print function to display the return message set into cli.msg.
281
1060
 */
282
 
int stats_sock_parse_request(struct stream_interface *si, char *line)
 
1061
static int stats_sock_parse_request(struct stream_interface *si, char *line)
283
1062
{
284
 
        struct session *s = si->private;
 
1063
        struct session *s = session_from_task(si->owner);
 
1064
        struct appctx *appctx = __objt_appctx(si->end);
285
1065
        char *args[MAX_STATS_ARGS + 1];
286
1066
        int arg;
 
1067
        int i, j;
287
1068
 
288
1069
        while (isspace((unsigned char)*line))
289
1070
                line++;
292
1073
        args[arg] = line;
293
1074
 
294
1075
        while (*line && arg < MAX_STATS_ARGS) {
295
 
                if (isspace((unsigned char)*line)) {
 
1076
                if (*line == '\\') {
 
1077
                        line++;
 
1078
                        if (*line == '\0')
 
1079
                                break;
 
1080
                }
 
1081
                else if (isspace((unsigned char)*line)) {
296
1082
                        *line++ = '\0';
297
1083
 
298
1084
                        while (isspace((unsigned char)*line))
308
1094
        while (++arg <= MAX_STATS_ARGS)
309
1095
                args[arg] = line;
310
1096
 
311
 
        s->data_ctx.stats.flags = 0;
 
1097
        /* remove \ */
 
1098
        arg = 0;
 
1099
        while (*args[arg] != '\0') {
 
1100
                j = 0;
 
1101
                for (i=0; args[arg][i] != '\0'; i++) {
 
1102
                        if (args[arg][i] == '\\')
 
1103
                                continue;
 
1104
                        args[arg][j] = args[arg][i];
 
1105
                        j++;
 
1106
                }
 
1107
                args[arg][j] = '\0';
 
1108
                arg++;
 
1109
        }
 
1110
 
 
1111
        appctx->ctx.stats.flags = 0;
312
1112
        if (strcmp(args[0], "show") == 0) {
313
1113
                if (strcmp(args[1], "stat") == 0) {
314
1114
                        if (*args[2] && *args[3] && *args[4]) {
315
 
                                s->data_ctx.stats.flags |= STAT_BOUND;
316
 
                                s->data_ctx.stats.iid   = atoi(args[2]);
317
 
                                s->data_ctx.stats.type  = atoi(args[3]);
318
 
                                s->data_ctx.stats.sid   = atoi(args[4]);
 
1115
                                appctx->ctx.stats.flags |= STAT_BOUND;
 
1116
                                appctx->ctx.stats.iid = atoi(args[2]);
 
1117
                                appctx->ctx.stats.type = atoi(args[3]);
 
1118
                                appctx->ctx.stats.sid = atoi(args[4]);
319
1119
                        }
320
1120
 
321
 
                        s->data_ctx.stats.flags |= STAT_SHOW_STAT;
322
 
                        s->data_ctx.stats.flags |= STAT_FMT_CSV;
323
 
                        s->data_state = DATA_ST_INIT;
324
 
                        si->st0 = STAT_CLI_O_INFO; // stats_dump_raw_to_buffer
 
1121
                        appctx->st2 = STAT_ST_INIT;
 
1122
                        appctx->st0 = STAT_CLI_O_STAT; // stats_dump_stat_to_buffer
325
1123
                }
326
1124
                else if (strcmp(args[1], "info") == 0) {
327
 
                        s->data_ctx.stats.flags |= STAT_SHOW_INFO;
328
 
                        s->data_ctx.stats.flags |= STAT_FMT_CSV;
329
 
                        s->data_state = DATA_ST_INIT;
330
 
                        si->st0 = STAT_CLI_O_INFO; // stats_dump_raw_to_buffer
 
1125
                        appctx->st2 = STAT_ST_INIT;
 
1126
                        appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer
 
1127
                }
 
1128
                else if (strcmp(args[1], "pools") == 0) {
 
1129
                        appctx->st2 = STAT_ST_INIT;
 
1130
                        appctx->st0 = STAT_CLI_O_POOLS; // stats_dump_pools_to_buffer
331
1131
                }
332
1132
                else if (strcmp(args[1], "sess") == 0) {
333
 
                        s->data_state = DATA_ST_INIT;
334
 
                        if (s->listener->perm.ux.level < ACCESS_LVL_OPER) {
335
 
                                s->data_ctx.cli.msg = stats_permission_denied_msg;
336
 
                                si->st0 = STAT_CLI_PRINT;
 
1133
                        appctx->st2 = STAT_ST_INIT;
 
1134
                        if (s->listener->bind_conf->level < ACCESS_LVL_OPER) {
 
1135
                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1136
                                appctx->st0 = STAT_CLI_PRINT;
337
1137
                                return 1;
338
1138
                        }
339
 
                        if (*args[2])
340
 
                                s->data_ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
 
1139
                        if (*args[2] && strcmp(args[2], "all") == 0)
 
1140
                                appctx->ctx.sess.target = (void *)-1;
 
1141
                        else if (*args[2])
 
1142
                                appctx->ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
341
1143
                        else
342
 
                                s->data_ctx.sess.target = NULL;
343
 
                        s->data_ctx.sess.section = 0; /* start with session status */
344
 
                        s->data_ctx.sess.pos = 0;
345
 
                        si->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
 
1144
                                appctx->ctx.sess.target = NULL;
 
1145
                        appctx->ctx.sess.section = 0; /* start with session status */
 
1146
                        appctx->ctx.sess.pos = 0;
 
1147
                        appctx->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
346
1148
                }
347
1149
                else if (strcmp(args[1], "errors") == 0) {
348
 
                        if (s->listener->perm.ux.level < ACCESS_LVL_OPER) {
349
 
                                s->data_ctx.cli.msg = stats_permission_denied_msg;
350
 
                                si->st0 = STAT_CLI_PRINT;
 
1150
                        if (s->listener->bind_conf->level < ACCESS_LVL_OPER) {
 
1151
                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1152
                                appctx->st0 = STAT_CLI_PRINT;
351
1153
                                return 1;
352
1154
                        }
353
1155
                        if (*args[2])
354
 
                                s->data_ctx.errors.iid  = atoi(args[2]);
355
 
                        else
356
 
                                s->data_ctx.errors.iid  = -1;
357
 
                        s->data_ctx.errors.px = NULL;
358
 
                        s->data_state = DATA_ST_INIT;
359
 
                        si->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
360
 
                }
361
 
                else { /* neither "stat" nor "info" nor "sess" nor "errors"*/
 
1156
                                appctx->ctx.errors.iid  = atoi(args[2]);
 
1157
                        else
 
1158
                                appctx->ctx.errors.iid  = -1;
 
1159
                        appctx->ctx.errors.px = NULL;
 
1160
                        appctx->st2 = STAT_ST_INIT;
 
1161
                        appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
 
1162
                }
 
1163
                else if (strcmp(args[1], "table") == 0) {
 
1164
                        stats_sock_table_request(si, args, STAT_CLI_O_TAB);
 
1165
                }
 
1166
                else if (strcmp(args[1], "map") == 0 ||
 
1167
                         strcmp(args[1], "acl") == 0) {
 
1168
 
 
1169
                        /* Set ACL or MAP flags. */
 
1170
                        if (args[1][0] == 'm')
 
1171
                                appctx->ctx.map.display_flags = PAT_REF_MAP;
 
1172
                        else
 
1173
                                appctx->ctx.map.display_flags = PAT_REF_ACL;
 
1174
 
 
1175
                        /* no parameter: display all map avalaible */
 
1176
                        if (!*args[2]) {
 
1177
                                appctx->st2 = STAT_ST_INIT;
 
1178
                                appctx->st0 = STAT_CLI_O_PATS;
 
1179
                                return 1;
 
1180
                        }
 
1181
 
 
1182
                        /* lookup into the refs and check the map flag */
 
1183
                        appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 
1184
                        if (!appctx->ctx.map.ref ||
 
1185
                            !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
 
1186
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
1187
                                        appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 
1188
                                else
 
1189
                                        appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
 
1190
                                appctx->st0 = STAT_CLI_PRINT;
 
1191
                                return 1;
 
1192
                        }
 
1193
                        appctx->st2 = STAT_ST_INIT;
 
1194
                        appctx->st0 = STAT_CLI_O_PAT;
 
1195
                }
 
1196
                else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
362
1197
                        return 0;
363
1198
                }
364
1199
        }
373
1208
                                clrall = 1;
374
1209
 
375
1210
                        /* check permissions */
376
 
                        if (s->listener->perm.ux.level < ACCESS_LVL_OPER ||
377
 
                            (clrall && s->listener->perm.ux.level < ACCESS_LVL_ADMIN)) {
378
 
                                s->data_ctx.cli.msg = stats_permission_denied_msg;
379
 
                                si->st0 = STAT_CLI_PRINT;
 
1211
                        if (s->listener->bind_conf->level < ACCESS_LVL_OPER ||
 
1212
                            (clrall && s->listener->bind_conf->level < ACCESS_LVL_ADMIN)) {
 
1213
                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1214
                                appctx->st0 = STAT_CLI_PRINT;
380
1215
                                return 1;
381
1216
                        }
382
1217
 
383
1218
                        for (px = proxy; px; px = px->next) {
384
 
                                if (clrall)
385
 
                                        memset(&px->counters, 0, sizeof(px->counters));
 
1219
                                if (clrall) {
 
1220
                                        memset(&px->be_counters, 0, sizeof(px->be_counters));
 
1221
                                        memset(&px->fe_counters, 0, sizeof(px->fe_counters));
 
1222
                                }
386
1223
                                else {
387
 
                                        px->counters.feconn_max = 0;
388
 
                                        px->counters.beconn_max = 0;
389
 
                                        px->counters.fe_rps_max = 0;
390
 
                                        px->counters.fe_sps_max = 0;
391
 
                                        px->counters.be_sps_max = 0;
392
 
                                        px->counters.nbpend_max = 0;
 
1224
                                        px->be_counters.conn_max = 0;
 
1225
                                        px->be_counters.p.http.rps_max = 0;
 
1226
                                        px->be_counters.sps_max = 0;
 
1227
                                        px->be_counters.cps_max = 0;
 
1228
                                        px->be_counters.nbpend_max = 0;
 
1229
 
 
1230
                                        px->fe_counters.conn_max = 0;
 
1231
                                        px->fe_counters.p.http.rps_max = 0;
 
1232
                                        px->fe_counters.sps_max = 0;
 
1233
                                        px->fe_counters.cps_max = 0;
 
1234
                                        px->fe_counters.nbpend_max = 0;
393
1235
                                }
394
1236
 
395
1237
                                for (sv = px->srv; sv; sv = sv->next)
401
1243
                                                sv->counters.sps_max = 0;
402
1244
                                        }
403
1245
 
404
 
                                for (li = px->listen; li; li = li->next)
 
1246
                                list_for_each_entry(li, &px->conf.listeners, by_fe)
405
1247
                                        if (li->counters) {
406
1248
                                                if (clrall)
407
1249
                                                        memset(li->counters, 0, sizeof(*li->counters));
410
1252
                                        }
411
1253
                        }
412
1254
 
 
1255
                        global.cps_max = 0;
 
1256
                        global.sps_max = 0;
 
1257
                        return 1;
 
1258
                }
 
1259
                else if (strcmp(args[1], "table") == 0) {
 
1260
                        stats_sock_table_request(si, args, STAT_CLI_O_CLR);
 
1261
                        /* end of processing */
 
1262
                        return 1;
 
1263
                }
 
1264
                else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
 
1265
                        /* Set ACL or MAP flags. */
 
1266
                        if (args[1][0] == 'm')
 
1267
                                appctx->ctx.map.display_flags = PAT_REF_MAP;
 
1268
                        else
 
1269
                                appctx->ctx.map.display_flags = PAT_REF_ACL;
 
1270
 
 
1271
                        /* no parameter */
 
1272
                        if (!*args[2]) {
 
1273
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
1274
                                        appctx->ctx.cli.msg = "Missing map identifier.\n";
 
1275
                                else
 
1276
                                        appctx->ctx.cli.msg = "Missing ACL identifier.\n";
 
1277
                                appctx->st0 = STAT_CLI_PRINT;
 
1278
                                return 1;
 
1279
                        }
 
1280
 
 
1281
                        /* lookup into the refs and check the map flag */
 
1282
                        appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 
1283
                        if (!appctx->ctx.map.ref ||
 
1284
                            !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
 
1285
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
1286
                                        appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 
1287
                                else
 
1288
                                        appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
 
1289
                                appctx->st0 = STAT_CLI_PRINT;
 
1290
                                return 1;
 
1291
                        }
 
1292
 
 
1293
                        /* Clear all. */
 
1294
                        pat_ref_prune(appctx->ctx.map.ref);
 
1295
 
 
1296
                        /* return response */
 
1297
                        appctx->ctx.cli.msg = "Done.\n";
 
1298
                        appctx->st0 = STAT_CLI_PRINT;
413
1299
                        return 1;
414
1300
                }
415
1301
                else {
 
1302
                        /* unknown "clear" argument */
416
1303
                        return 0;
417
1304
                }
418
1305
        }
429
1316
                                }
430
1317
 
431
1318
                        if (!*line) {
432
 
                                s->data_ctx.cli.msg = "Require 'backend/server'.\n";
433
 
                                si->st0 = STAT_CLI_PRINT;
 
1319
                                appctx->ctx.cli.msg = "Require 'backend/server'.\n";
 
1320
                                appctx->st0 = STAT_CLI_PRINT;
434
1321
                                return 1;
435
1322
                        }
436
1323
 
437
1324
                        if (!get_backend_server(args[2], line, &px, &sv)) {
438
 
                                s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
439
 
                                si->st0 = STAT_CLI_PRINT;
 
1325
                                appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
 
1326
                                appctx->st0 = STAT_CLI_PRINT;
440
1327
                                return 1;
441
1328
                        }
442
1329
 
443
1330
                        /* return server's effective weight at the moment */
444
 
                        snprintf(trash, trashlen, "%d (initial %d)\n", sv->uweight, sv->iweight);
445
 
                        buffer_feed(si->ib, trash);
 
1331
                        snprintf(trash.str, trash.size, "%d (initial %d)\n", sv->uweight, sv->iweight);
 
1332
                        bi_putstr(si->ib, trash.str);
446
1333
                        return 1;
447
1334
                }
 
1335
                else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
 
1336
                        /* Set flags. */
 
1337
                        if (args[1][0] == 'm')
 
1338
                                appctx->ctx.map.display_flags = PAT_REF_MAP;
 
1339
                        else
 
1340
                                appctx->ctx.map.display_flags = PAT_REF_ACL;
 
1341
 
 
1342
                        /* No parameter. */
 
1343
                        if (!*args[2] || !*args[3]) {
 
1344
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
1345
                                        appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
 
1346
                                else
 
1347
                                        appctx->ctx.cli.msg = "Missing ACL identifier and/or key.\n";
 
1348
                                appctx->st0 = STAT_CLI_PRINT;
 
1349
                                return 1;
 
1350
                        }
 
1351
 
 
1352
                        /* lookup into the maps */
 
1353
                        appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 
1354
                        if (!appctx->ctx.map.ref) {
 
1355
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
1356
                                        appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 
1357
                                else
 
1358
                                        appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
 
1359
                                appctx->st0 = STAT_CLI_PRINT;
 
1360
                                return 1;
 
1361
                        }
 
1362
 
 
1363
                        /* copy input string. The string must be allocated because
 
1364
                         * it may be used over multiple iterations. It's released
 
1365
                         * at the end and upon abort anyway.
 
1366
                         */
 
1367
                        appctx->ctx.map.chunk.len = strlen(args[3]);
 
1368
                        appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
 
1369
                        appctx->ctx.map.chunk.str = strdup(args[3]);
 
1370
                        if (!appctx->ctx.map.chunk.str) {
 
1371
                                appctx->ctx.cli.msg = "Out of memory error.\n";
 
1372
                                appctx->st0 = STAT_CLI_PRINT;
 
1373
                                return 1;
 
1374
                        }
 
1375
 
 
1376
                        /* prepare response */
 
1377
                        appctx->st2 = STAT_ST_INIT;
 
1378
                        appctx->st0 = STAT_CLI_O_MLOOK;
 
1379
                }
448
1380
                else { /* not "get weight" */
449
1381
                        return 0;
450
1382
                }
451
1383
        }
452
1384
        else if (strcmp(args[0], "set") == 0) {
453
1385
                if (strcmp(args[1], "weight") == 0) {
454
 
                        struct proxy *px;
455
 
                        struct server *sv;
456
 
                        int w;
457
 
 
458
 
                        if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
459
 
                                s->data_ctx.cli.msg = stats_permission_denied_msg;
460
 
                                si->st0 = STAT_CLI_PRINT;
461
 
                                return 1;
462
 
                        }
463
 
 
464
 
                        /* split "backend/server" and make <line> point to server */
465
 
                        for (line = args[2]; *line; line++)
466
 
                                if (*line == '/') {
467
 
                                        *line++ = '\0';
468
 
                                        break;
469
 
                                }
470
 
 
471
 
                        if (!*line || !*args[3]) {
472
 
                                s->data_ctx.cli.msg = "Require 'backend/server' and 'weight' or 'weight%'.\n";
473
 
                                si->st0 = STAT_CLI_PRINT;
474
 
                                return 1;
475
 
                        }
476
 
 
477
 
                        if (!get_backend_server(args[2], line, &px, &sv)) {
478
 
                                s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
479
 
                                si->st0 = STAT_CLI_PRINT;
480
 
                                return 1;
481
 
                        }
482
 
 
483
 
                        if (px->state == PR_STSTOPPED) {
484
 
                                s->data_ctx.cli.msg = "Proxy is disabled.\n";
485
 
                                si->st0 = STAT_CLI_PRINT;
486
 
                                return 1;
487
 
                        }
488
 
 
489
 
                        /* if the weight is terminated with '%', it is set relative to
490
 
                         * the initial weight, otherwise it is absolute.
491
 
                         */
492
 
                        w = atoi(args[3]);
493
 
                        if (strchr(args[3], '%') != NULL) {
494
 
                                if (w < 0 || w > 100) {
495
 
                                        s->data_ctx.cli.msg = "Relative weight can only be set between 0 and 100% inclusive.\n";
496
 
                                        si->st0 = STAT_CLI_PRINT;
497
 
                                        return 1;
498
 
                                }
499
 
                                w = sv->iweight * w / 100;
 
1386
                        struct server *sv;
 
1387
                        const char *warning;
 
1388
 
 
1389
                        sv = expect_server_admin(s, si, args[2]);
 
1390
                        if (!sv)
 
1391
                                return 1;
 
1392
 
 
1393
                        warning = server_parse_weight_change_request(sv, args[3]);
 
1394
                        if (warning) {
 
1395
                                appctx->ctx.cli.msg = warning;
 
1396
                                appctx->st0 = STAT_CLI_PRINT;
 
1397
                        }
 
1398
                        return 1;
 
1399
                }
 
1400
                else if (strcmp(args[1], "server") == 0) {
 
1401
                        struct server *sv;
 
1402
                        const char *warning;
 
1403
 
 
1404
                        sv = expect_server_admin(s, si, args[2]);
 
1405
                        if (!sv)
 
1406
                                return 1;
 
1407
 
 
1408
                        if (strcmp(args[3], "weight") == 0) {
 
1409
                                warning = server_parse_weight_change_request(sv, args[4]);
 
1410
                                if (warning) {
 
1411
                                        appctx->ctx.cli.msg = warning;
 
1412
                                        appctx->st0 = STAT_CLI_PRINT;
 
1413
                                }
 
1414
                        }
 
1415
                        else if (strcmp(args[3], "state") == 0) {
 
1416
                                if (strcmp(args[4], "ready") == 0)
 
1417
                                        srv_adm_set_ready(sv);
 
1418
                                else if (strcmp(args[4], "drain") == 0)
 
1419
                                        srv_adm_set_drain(sv);
 
1420
                                else if (strcmp(args[4], "maint") == 0)
 
1421
                                        srv_adm_set_maint(sv);
 
1422
                                else {
 
1423
                                        appctx->ctx.cli.msg = "'set server <srv> state' expects 'ready', 'drain' and 'maint'.\n";
 
1424
                                        appctx->st0 = STAT_CLI_PRINT;
 
1425
                                }
 
1426
                        }
 
1427
                        else if (strcmp(args[3], "health") == 0) {
 
1428
                                if (sv->track) {
 
1429
                                        appctx->ctx.cli.msg = "cannot change health on a tracking server.\n";
 
1430
                                        appctx->st0 = STAT_CLI_PRINT;
 
1431
                                }
 
1432
                                else if (strcmp(args[4], "up") == 0) {
 
1433
                                        sv->check.health = sv->check.rise + sv->check.fall - 1;
 
1434
                                        srv_set_running(sv, "changed from CLI");
 
1435
                                }
 
1436
                                else if (strcmp(args[4], "stopping") == 0) {
 
1437
                                        sv->check.health = sv->check.rise + sv->check.fall - 1;
 
1438
                                        srv_set_stopping(sv, "changed from CLI");
 
1439
                                }
 
1440
                                else if (strcmp(args[4], "down") == 0) {
 
1441
                                        sv->check.health = 0;
 
1442
                                        srv_set_stopped(sv, "changed from CLI");
 
1443
                                }
 
1444
                                else {
 
1445
                                        appctx->ctx.cli.msg = "'set server <srv> health' expects 'up', 'stopping', or 'down'.\n";
 
1446
                                        appctx->st0 = STAT_CLI_PRINT;
 
1447
                                }
 
1448
                        }
 
1449
                        else if (strcmp(args[3], "agent") == 0) {
 
1450
                                if (!(sv->agent.state & CHK_ST_ENABLED)) {
 
1451
                                        appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
 
1452
                                        appctx->st0 = STAT_CLI_PRINT;
 
1453
                                }
 
1454
                                else if (strcmp(args[4], "up") == 0) {
 
1455
                                        sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
 
1456
                                        srv_set_running(sv, "changed from CLI");
 
1457
                                }
 
1458
                                else if (strcmp(args[4], "down") == 0) {
 
1459
                                        sv->agent.health = 0;
 
1460
                                        srv_set_stopped(sv, "changed from CLI");
 
1461
                                }
 
1462
                                else {
 
1463
                                        appctx->ctx.cli.msg = "'set server <srv> agent' expects 'up' or 'down'.\n";
 
1464
                                        appctx->st0 = STAT_CLI_PRINT;
 
1465
                                }
500
1466
                        }
501
1467
                        else {
502
 
                                if (w < 0 || w > 256) {
503
 
                                        s->data_ctx.cli.msg = "Absolute weight can only be between 0 and 256 inclusive.\n";
504
 
                                        si->st0 = STAT_CLI_PRINT;
505
 
                                        return 1;
506
 
                                }
507
 
                        }
508
 
 
509
 
                        if (w && w != sv->iweight && !(px->lbprm.algo & BE_LB_PROP_DYN)) {
510
 
                                s->data_ctx.cli.msg = "Backend is using a static LB algorithm and only accepts weights '0%' and '100%'.\n";
511
 
                                si->st0 = STAT_CLI_PRINT;
512
 
                                return 1;
513
 
                        }
514
 
 
515
 
                        sv->uweight = w;
516
 
                        server_recalc_eweight(sv);
 
1468
                                appctx->ctx.cli.msg = "'set server <srv>' only supports 'agent', 'health', 'state' and 'weight'.\n";
 
1469
                                appctx->st0 = STAT_CLI_PRINT;
 
1470
                        }
517
1471
                        return 1;
518
1472
                }
519
1473
                else if (strcmp(args[1], "timeout") == 0) {
522
1476
                                const char *res;
523
1477
 
524
1478
                                if (!*args[3]) {
525
 
                                        s->data_ctx.cli.msg = "Expects an integer value.\n";
526
 
                                        si->st0 = STAT_CLI_PRINT;
 
1479
                                        appctx->ctx.cli.msg = "Expects an integer value.\n";
 
1480
                                        appctx->st0 = STAT_CLI_PRINT;
527
1481
                                        return 1;
528
1482
                                }
529
1483
 
530
1484
                                res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
531
1485
                                if (res || timeout < 1) {
532
 
                                        s->data_ctx.cli.msg = "Invalid timeout value.\n";
533
 
                                        si->st0 = STAT_CLI_PRINT;
 
1486
                                        appctx->ctx.cli.msg = "Invalid timeout value.\n";
 
1487
                                        appctx->st0 = STAT_CLI_PRINT;
534
1488
                                        return 1;
535
1489
                                }
536
1490
 
538
1492
                                return 1;
539
1493
                        }
540
1494
                        else {
541
 
                                s->data_ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
542
 
                                si->st0 = STAT_CLI_PRINT;
543
 
                                return 1;
544
 
                        }
545
 
                }
 
1495
                                appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
 
1496
                                appctx->st0 = STAT_CLI_PRINT;
 
1497
                                return 1;
 
1498
                        }
 
1499
                }
 
1500
                else if (strcmp(args[1], "maxconn") == 0) {
 
1501
                        if (strcmp(args[2], "frontend") == 0) {
 
1502
                                struct proxy *px;
 
1503
                                struct listener *l;
 
1504
                                int v;
 
1505
 
 
1506
                                px = expect_frontend_admin(s, si, args[3]);
 
1507
                                if (!px)
 
1508
                                        return 1;
 
1509
 
 
1510
                                if (!*args[4]) {
 
1511
                                        appctx->ctx.cli.msg = "Integer value expected.\n";
 
1512
                                        appctx->st0 = STAT_CLI_PRINT;
 
1513
                                        return 1;
 
1514
                                }
 
1515
 
 
1516
                                v = atoi(args[4]);
 
1517
                                if (v < 0) {
 
1518
                                        appctx->ctx.cli.msg = "Value out of range.\n";
 
1519
                                        appctx->st0 = STAT_CLI_PRINT;
 
1520
                                        return 1;
 
1521
                                }
 
1522
 
 
1523
                                /* OK, the value is fine, so we assign it to the proxy and to all of
 
1524
                                 * its listeners. The blocked ones will be dequeued.
 
1525
                                 */
 
1526
                                px->maxconn = v;
 
1527
                                list_for_each_entry(l, &px->conf.listeners, by_fe) {
 
1528
                                        l->maxconn = v;
 
1529
                                        if (l->state == LI_FULL)
 
1530
                                                resume_listener(l);
 
1531
                                }
 
1532
 
 
1533
                                if (px->maxconn > px->feconn && !LIST_ISEMPTY(&s->fe->listener_queue))
 
1534
                                        dequeue_all_listeners(&s->fe->listener_queue);
 
1535
 
 
1536
                                return 1;
 
1537
                        }
 
1538
                        else if (strcmp(args[2], "global") == 0) {
 
1539
                                int v;
 
1540
 
 
1541
                                if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
1542
                                        appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1543
                                        appctx->st0 = STAT_CLI_PRINT;
 
1544
                                        return 1;
 
1545
                                }
 
1546
 
 
1547
                                if (!*args[3]) {
 
1548
                                        appctx->ctx.cli.msg = "Expects an integer value.\n";
 
1549
                                        appctx->st0 = STAT_CLI_PRINT;
 
1550
                                        return 1;
 
1551
                                }
 
1552
 
 
1553
                                v = atoi(args[3]);
 
1554
                                if (v > global.hardmaxconn) {
 
1555
                                        appctx->ctx.cli.msg = "Value out of range.\n";
 
1556
                                        appctx->st0 = STAT_CLI_PRINT;
 
1557
                                        return 1;
 
1558
                                }
 
1559
 
 
1560
                                /* check for unlimited values */
 
1561
                                if (v <= 0)
 
1562
                                        v = global.hardmaxconn;
 
1563
 
 
1564
                                global.maxconn = v;
 
1565
 
 
1566
                                /* Dequeues all of the listeners waiting for a resource */
 
1567
                                if (!LIST_ISEMPTY(&global_listener_queue))
 
1568
                                        dequeue_all_listeners(&global_listener_queue);
 
1569
 
 
1570
                                return 1;
 
1571
                        }
 
1572
                        else {
 
1573
                                appctx->ctx.cli.msg = "'set maxconn' only supports 'frontend' and 'global'.\n";
 
1574
                                appctx->st0 = STAT_CLI_PRINT;
 
1575
                                return 1;
 
1576
                        }
 
1577
                }
 
1578
                else if (strcmp(args[1], "rate-limit") == 0) {
 
1579
                        if (strcmp(args[2], "connections") == 0) {
 
1580
                                if (strcmp(args[3], "global") == 0) {
 
1581
                                        int v;
 
1582
 
 
1583
                                        if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
1584
                                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1585
                                                appctx->st0 = STAT_CLI_PRINT;
 
1586
                                                return 1;
 
1587
                                        }
 
1588
 
 
1589
                                        if (!*args[4]) {
 
1590
                                                appctx->ctx.cli.msg = "Expects an integer value.\n";
 
1591
                                                appctx->st0 = STAT_CLI_PRINT;
 
1592
                                                return 1;
 
1593
                                        }
 
1594
 
 
1595
                                        v = atoi(args[4]);
 
1596
                                        if (v < 0) {
 
1597
                                                appctx->ctx.cli.msg = "Value out of range.\n";
 
1598
                                                appctx->st0 = STAT_CLI_PRINT;
 
1599
                                                return 1;
 
1600
                                        }
 
1601
 
 
1602
                                        global.cps_lim = v;
 
1603
 
 
1604
                                        /* Dequeues all of the listeners waiting for a resource */
 
1605
                                        if (!LIST_ISEMPTY(&global_listener_queue))
 
1606
                                                dequeue_all_listeners(&global_listener_queue);
 
1607
 
 
1608
                                        return 1;
 
1609
                                }
 
1610
                                else {
 
1611
                                        appctx->ctx.cli.msg = "'set rate-limit connections' only supports 'global'.\n";
 
1612
                                        appctx->st0 = STAT_CLI_PRINT;
 
1613
                                        return 1;
 
1614
                                }
 
1615
                        }
 
1616
                        else if (strcmp(args[2], "sessions") == 0) {
 
1617
                                if (strcmp(args[3], "global") == 0) {
 
1618
                                        int v;
 
1619
 
 
1620
                                        if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
1621
                                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1622
                                                appctx->st0 = STAT_CLI_PRINT;
 
1623
                                                return 1;
 
1624
                                        }
 
1625
 
 
1626
                                        if (!*args[4]) {
 
1627
                                                appctx->ctx.cli.msg = "Expects an integer value.\n";
 
1628
                                                appctx->st0 = STAT_CLI_PRINT;
 
1629
                                                return 1;
 
1630
                                        }
 
1631
 
 
1632
                                        v = atoi(args[4]);
 
1633
                                        if (v < 0) {
 
1634
                                                appctx->ctx.cli.msg = "Value out of range.\n";
 
1635
                                                appctx->st0 = STAT_CLI_PRINT;
 
1636
                                                return 1;
 
1637
                                        }
 
1638
 
 
1639
                                        global.sps_lim = v;
 
1640
 
 
1641
                                        /* Dequeues all of the listeners waiting for a resource */
 
1642
                                        if (!LIST_ISEMPTY(&global_listener_queue))
 
1643
                                                dequeue_all_listeners(&global_listener_queue);
 
1644
 
 
1645
                                        return 1;
 
1646
                                }
 
1647
                                else {
 
1648
                                        appctx->ctx.cli.msg = "'set rate-limit sessions' only supports 'global'.\n";
 
1649
                                        appctx->st0 = STAT_CLI_PRINT;
 
1650
                                        return 1;
 
1651
                                }
 
1652
                        }
 
1653
#ifdef USE_OPENSSL
 
1654
                        else if (strcmp(args[2], "ssl-sessions") == 0) {
 
1655
                                if (strcmp(args[3], "global") == 0) {
 
1656
                                        int v;
 
1657
 
 
1658
                                        if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
1659
                                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
1660
                                                appctx->st0 = STAT_CLI_PRINT;
 
1661
                                                return 1;
 
1662
                                        }
 
1663
 
 
1664
                                        if (!*args[4]) {
 
1665
                                                appctx->ctx.cli.msg = "Expects an integer value.\n";
 
1666
                                                appctx->st0 = STAT_CLI_PRINT;
 
1667
                                                return 1;
 
1668
                                        }
 
1669
 
 
1670
                                        v = atoi(args[4]);
 
1671
                                        if (v < 0) {
 
1672
                                                appctx->ctx.cli.msg = "Value out of range.\n";
 
1673
                                                appctx->st0 = STAT_CLI_PRINT;
 
1674
                                                return 1;
 
1675
                                        }
 
1676
 
 
1677
                                        global.ssl_lim = v;
 
1678
 
 
1679
                                        /* Dequeues all of the listeners waiting for a resource */
 
1680
                                        if (!LIST_ISEMPTY(&global_listener_queue))
 
1681
                                                dequeue_all_listeners(&global_listener_queue);
 
1682
 
 
1683
                                        return 1;
 
1684
                                }
 
1685
                                else {
 
1686
                                        appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
 
1687
                                        appctx->st0 = STAT_CLI_PRINT;
 
1688
                                        return 1;
 
1689
                                }
 
1690
                        }
 
1691
#endif
 
1692
                        else if (strcmp(args[2], "http-compression") == 0) {
 
1693
                                if (strcmp(args[3], "global") == 0) {
 
1694
                                        int v;
 
1695
 
 
1696
                                        if (!*args[4]) {
 
1697
                                                appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
 
1698
                                                appctx->st0 = STAT_CLI_PRINT;
 
1699
                                                return 1;
 
1700
                                        }
 
1701
 
 
1702
                                        v = atoi(args[4]);
 
1703
                                        global.comp_rate_lim = v * 1024; /* Kilo to bytes. */
 
1704
                                }
 
1705
                                else {
 
1706
                                        appctx->ctx.cli.msg = "'set rate-limit http-compression' only supports 'global'.\n";
 
1707
                                        appctx->st0 = STAT_CLI_PRINT;
 
1708
                                        return 1;
 
1709
                                }
 
1710
                        }
 
1711
                        else {
 
1712
                                appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
 
1713
                                appctx->st0 = STAT_CLI_PRINT;
 
1714
                                return 1;
 
1715
                        }
 
1716
                }
 
1717
                else if (strcmp(args[1], "table") == 0) {
 
1718
                        stats_sock_table_request(si, args, STAT_CLI_O_SET);
 
1719
                }
 
1720
                else if (strcmp(args[1], "map") == 0) {
 
1721
                        char *err;
 
1722
 
 
1723
                        /* Set flags. */
 
1724
                        appctx->ctx.map.display_flags = PAT_REF_MAP;
 
1725
 
 
1726
                        /* Expect three parameters: map name, key and new value. */
 
1727
                        if (!*args[2] || !*args[3] || !*args[4]) {
 
1728
                                appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
 
1729
                                appctx->st0 = STAT_CLI_PRINT;
 
1730
                                return 1;
 
1731
                        }
 
1732
 
 
1733
                        /* Lookup the reference in the maps. */
 
1734
                        appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 
1735
                        if (!appctx->ctx.map.ref) {
 
1736
                                appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 
1737
                                appctx->st0 = STAT_CLI_PRINT;
 
1738
                                return 1;
 
1739
                        }
 
1740
 
 
1741
                        /* If the entry identifier start with a '#', it is considered as
 
1742
                         * pointer id
 
1743
                         */
 
1744
                        if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
 
1745
                                struct pat_ref_elt *ref;
 
1746
                                long long int conv;
 
1747
                                char *error;
 
1748
 
 
1749
                                /* Convert argument to integer value. */
 
1750
                                conv = strtoll(&args[3][1], &error, 16);
 
1751
                                if (*error != '\0') {
 
1752
                                        appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 
1753
                                        appctx->st0 = STAT_CLI_PRINT;
 
1754
                                        return 1;
 
1755
                                }
 
1756
 
 
1757
                                /* Convert and check integer to pointer. */
 
1758
                                ref = (struct pat_ref_elt *)(long)conv;
 
1759
                                if ((long long int)(long)ref != conv) {
 
1760
                                        appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 
1761
                                        appctx->st0 = STAT_CLI_PRINT;
 
1762
                                        return 1;
 
1763
                                }
 
1764
 
 
1765
                                /* Try to delete the entry. */
 
1766
                                err = NULL;
 
1767
                                if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
 
1768
                                        if (err)
 
1769
                                                memprintf(&err, "%s.\n", err);
 
1770
                                        appctx->ctx.cli.err = err;
 
1771
                                        appctx->st0 = STAT_CLI_PRINT_FREE;
 
1772
                                        return 1;
 
1773
                                }
 
1774
                        }
 
1775
                        else {
 
1776
                                /* Else, use the entry identifier as pattern
 
1777
                                 * string, and update the value.
 
1778
                                 */
 
1779
                                err = NULL;
 
1780
                                if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
 
1781
                                        if (err)
 
1782
                                                memprintf(&err, "%s.\n", err);
 
1783
                                        appctx->ctx.cli.err = err;
 
1784
                                        appctx->st0 = STAT_CLI_PRINT_FREE;
 
1785
                                        return 1;
 
1786
                                }
 
1787
                        }
 
1788
 
 
1789
                        /* The set is done, send message. */
 
1790
                        appctx->ctx.cli.msg = "Done.\n";
 
1791
                        appctx->st0 = STAT_CLI_PRINT;
 
1792
                        return 1;
 
1793
                }
 
1794
#ifdef USE_OPENSSL
 
1795
                else if (strcmp(args[1], "ssl") == 0) {
 
1796
                        if (strcmp(args[2], "ocsp-response") == 0) {
 
1797
#ifdef SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB
 
1798
                                char *err = NULL;
 
1799
 
 
1800
                                /* Expect one parameter: the new response in base64 encoding */
 
1801
                                if (!*args[3]) {
 
1802
                                        appctx->ctx.cli.msg = "'set ssl ocsp-response' expects response in base64 encoding.\n";
 
1803
                                        appctx->st0 = STAT_CLI_PRINT;
 
1804
                                        return 1;
 
1805
                                }
 
1806
 
 
1807
                                trash.len = base64dec(args[3], strlen(args[3]), trash.str, trash.size);
 
1808
                                if (trash.len < 0) {
 
1809
                                        appctx->ctx.cli.msg = "'set ssl ocsp-response' received invalid base64 encoded response.\n";
 
1810
                                        appctx->st0 = STAT_CLI_PRINT;
 
1811
                                        return 1;
 
1812
                                }
 
1813
 
 
1814
                                if (ssl_sock_update_ocsp_response(&trash, &err)) {
 
1815
                                        if (err) {
 
1816
                                                memprintf(&err, "%s.\n", err);
 
1817
                                                appctx->ctx.cli.err = err;
 
1818
                                                appctx->st0 = STAT_CLI_PRINT_FREE;
 
1819
                                        }
 
1820
                                        return 1;
 
1821
                                }
 
1822
                                appctx->ctx.cli.msg = "OCSP Response updated!";
 
1823
                                appctx->st0 = STAT_CLI_PRINT;
 
1824
                                return 1;
 
1825
#else
 
1826
                                appctx->ctx.cli.msg = "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n";
 
1827
                                appctx->st0 = STAT_CLI_PRINT;
 
1828
                                return 1;
 
1829
#endif
 
1830
                        }
 
1831
                        else {
 
1832
                                appctx->ctx.cli.msg = "'set ssl' only supports 'ocsp-response'.\n";
 
1833
                                appctx->st0 = STAT_CLI_PRINT;
 
1834
                                return 1;
 
1835
                        }
 
1836
                }
 
1837
#endif
546
1838
                else { /* unknown "set" parameter */
547
1839
                        return 0;
548
1840
                }
549
1841
        }
550
1842
        else if (strcmp(args[0], "enable") == 0) {
551
 
                if (strcmp(args[1], "server") == 0) {
 
1843
                if (strcmp(args[1], "agent") == 0) {
 
1844
                        struct server *sv;
 
1845
 
 
1846
                        sv = expect_server_admin(s, si, args[2]);
 
1847
                        if (!sv)
 
1848
                                return 1;
 
1849
 
 
1850
                        if (!(sv->agent.state & CHK_ST_CONFIGURED)) {
 
1851
                                appctx->ctx.cli.msg = "Agent was not configured on this server, cannot enable.\n";
 
1852
                                appctx->st0 = STAT_CLI_PRINT;
 
1853
                                return 1;
 
1854
                        }
 
1855
 
 
1856
                        sv->agent.state |= CHK_ST_ENABLED;
 
1857
                        return 1;
 
1858
                }
 
1859
                else if (strcmp(args[1], "health") == 0) {
 
1860
                        struct server *sv;
 
1861
 
 
1862
                        sv = expect_server_admin(s, si, args[2]);
 
1863
                        if (!sv)
 
1864
                                return 1;
 
1865
 
 
1866
                        if (!(sv->check.state & CHK_ST_CONFIGURED)) {
 
1867
                                appctx->ctx.cli.msg = "Health checks are not configured on this server, cannot enable.\n";
 
1868
                                appctx->st0 = STAT_CLI_PRINT;
 
1869
                                return 1;
 
1870
                        }
 
1871
 
 
1872
                        sv->check.state |= CHK_ST_ENABLED;
 
1873
                        return 1;
 
1874
                }
 
1875
                else if (strcmp(args[1], "server") == 0) {
 
1876
                        struct server *sv;
 
1877
 
 
1878
                        sv = expect_server_admin(s, si, args[2]);
 
1879
                        if (!sv)
 
1880
                                return 1;
 
1881
 
 
1882
                        srv_adm_set_ready(sv);
 
1883
                        return 1;
 
1884
                }
 
1885
                else if (strcmp(args[1], "frontend") == 0) {
552
1886
                        struct proxy *px;
553
 
                        struct server *sv;
554
 
 
555
 
                        if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
556
 
                                s->data_ctx.cli.msg = stats_permission_denied_msg;
557
 
                                si->st0 = STAT_CLI_PRINT;
558
 
                                return 1;
559
 
                        }
560
 
 
561
 
                        /* split "backend/server" and make <line> point to server */
562
 
                        for (line = args[2]; *line; line++)
563
 
                                if (*line == '/') {
564
 
                                        *line++ = '\0';
565
 
                                        break;
566
 
                                }
567
 
 
568
 
                        if (!*line || !*args[2]) {
569
 
                                s->data_ctx.cli.msg = "Require 'backend/server'.\n";
570
 
                                si->st0 = STAT_CLI_PRINT;
571
 
                                return 1;
572
 
                        }
573
 
 
574
 
                        if (!get_backend_server(args[2], line, &px, &sv)) {
575
 
                                s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
576
 
                                si->st0 = STAT_CLI_PRINT;
577
 
                                return 1;
578
 
                        }
 
1887
 
 
1888
                        px = expect_frontend_admin(s, si, args[2]);
 
1889
                        if (!px)
 
1890
                                return 1;
579
1891
 
580
1892
                        if (px->state == PR_STSTOPPED) {
581
 
                                s->data_ctx.cli.msg = "Proxy is disabled.\n";
582
 
                                si->st0 = STAT_CLI_PRINT;
583
 
                                return 1;
584
 
                        }
585
 
 
586
 
                        if (sv->state & SRV_MAINTAIN) {
587
 
                                /* The server is really in maintenance, we can change the server state */
588
 
                                if (sv->tracked) {
589
 
                                        /* If this server tracks the status of another one,
590
 
                                        * we must restore the good status.
591
 
                                        */
592
 
                                        if (sv->tracked->state & SRV_RUNNING) {
593
 
                                                set_server_up(sv);
594
 
                                                sv->health = sv->rise;  /* up, but will fall down at first failure */
595
 
                                        } else {
596
 
                                                sv->state &= ~SRV_MAINTAIN;
597
 
                                                set_server_down(sv);
598
 
                                        }
599
 
                                } else {
600
 
                                        set_server_up(sv);
601
 
                                        sv->health = sv->rise;  /* up, but will fall down at first failure */
602
 
                                }
603
 
                        }
604
 
 
 
1893
                                appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
 
1894
                                appctx->st0 = STAT_CLI_PRINT;
 
1895
                                return 1;
 
1896
                        }
 
1897
 
 
1898
                        if (px->state != PR_STPAUSED) {
 
1899
                                appctx->ctx.cli.msg = "Frontend is already enabled.\n";
 
1900
                                appctx->st0 = STAT_CLI_PRINT;
 
1901
                                return 1;
 
1902
                        }
 
1903
 
 
1904
                        if (!resume_proxy(px)) {
 
1905
                                appctx->ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
 
1906
                                appctx->st0 = STAT_CLI_PRINT;
 
1907
                                return 1;
 
1908
                        }
605
1909
                        return 1;
606
1910
                }
607
1911
                else { /* unknown "enable" parameter */
608
 
                        return 0;
 
1912
                        appctx->ctx.cli.msg = "'enable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
 
1913
                        appctx->st0 = STAT_CLI_PRINT;
 
1914
                        return 1;
609
1915
                }
610
1916
        }
611
1917
        else if (strcmp(args[0], "disable") == 0) {
612
 
                if (strcmp(args[1], "server") == 0) {
613
 
                        struct proxy *px;
614
 
                        struct server *sv;
615
 
 
616
 
                        if (s->listener->perm.ux.level < ACCESS_LVL_ADMIN) {
617
 
                                s->data_ctx.cli.msg = stats_permission_denied_msg;
618
 
                                si->st0 = STAT_CLI_PRINT;
619
 
                                return 1;
620
 
                        }
621
 
 
622
 
                        /* split "backend/server" and make <line> point to server */
623
 
                        for (line = args[2]; *line; line++)
624
 
                                if (*line == '/') {
625
 
                                        *line++ = '\0';
 
1918
                if (strcmp(args[1], "agent") == 0) {
 
1919
                        struct server *sv;
 
1920
 
 
1921
                        sv = expect_server_admin(s, si, args[2]);
 
1922
                        if (!sv)
 
1923
                                return 1;
 
1924
 
 
1925
                        sv->agent.state &= ~CHK_ST_ENABLED;
 
1926
                        return 1;
 
1927
                }
 
1928
                else if (strcmp(args[1], "health") == 0) {
 
1929
                        struct server *sv;
 
1930
 
 
1931
                        sv = expect_server_admin(s, si, args[2]);
 
1932
                        if (!sv)
 
1933
                                return 1;
 
1934
 
 
1935
                        sv->check.state &= ~CHK_ST_ENABLED;
 
1936
                        return 1;
 
1937
                }
 
1938
                else if (strcmp(args[1], "server") == 0) {
 
1939
                        struct server *sv;
 
1940
 
 
1941
                        sv = expect_server_admin(s, si, args[2]);
 
1942
                        if (!sv)
 
1943
                                return 1;
 
1944
 
 
1945
                        srv_adm_set_maint(sv);
 
1946
                        return 1;
 
1947
                }
 
1948
                else if (strcmp(args[1], "frontend") == 0) {
 
1949
                        struct proxy *px;
 
1950
 
 
1951
                        px = expect_frontend_admin(s, si, args[2]);
 
1952
                        if (!px)
 
1953
                                return 1;
 
1954
 
 
1955
                        if (px->state == PR_STSTOPPED) {
 
1956
                                appctx->ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
 
1957
                                appctx->st0 = STAT_CLI_PRINT;
 
1958
                                return 1;
 
1959
                        }
 
1960
 
 
1961
                        if (px->state == PR_STPAUSED) {
 
1962
                                appctx->ctx.cli.msg = "Frontend is already disabled.\n";
 
1963
                                appctx->st0 = STAT_CLI_PRINT;
 
1964
                                return 1;
 
1965
                        }
 
1966
 
 
1967
                        if (!pause_proxy(px)) {
 
1968
                                appctx->ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
 
1969
                                appctx->st0 = STAT_CLI_PRINT;
 
1970
                                return 1;
 
1971
                        }
 
1972
                        return 1;
 
1973
                }
 
1974
                else { /* unknown "disable" parameter */
 
1975
                        appctx->ctx.cli.msg = "'disable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
 
1976
                        appctx->st0 = STAT_CLI_PRINT;
 
1977
                        return 1;
 
1978
                }
 
1979
        }
 
1980
        else if (strcmp(args[0], "shutdown") == 0) {
 
1981
                if (strcmp(args[1], "frontend") == 0) {
 
1982
                        struct proxy *px;
 
1983
 
 
1984
                        px = expect_frontend_admin(s, si, args[2]);
 
1985
                        if (!px)
 
1986
                                return 1;
 
1987
 
 
1988
                        if (px->state == PR_STSTOPPED) {
 
1989
                                appctx->ctx.cli.msg = "Frontend was already shut down.\n";
 
1990
                                appctx->st0 = STAT_CLI_PRINT;
 
1991
                                return 1;
 
1992
                        }
 
1993
 
 
1994
                        Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
 
1995
                                px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
 
1996
                        send_log(px, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
 
1997
                                 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
 
1998
                        stop_proxy(px);
 
1999
                        return 1;
 
2000
                }
 
2001
                else if (strcmp(args[1], "session") == 0) {
 
2002
                        struct session *sess, *ptr;
 
2003
 
 
2004
                        if (s->listener->bind_conf->level < ACCESS_LVL_ADMIN) {
 
2005
                                appctx->ctx.cli.msg = stats_permission_denied_msg;
 
2006
                                appctx->st0 = STAT_CLI_PRINT;
 
2007
                                return 1;
 
2008
                        }
 
2009
 
 
2010
                        if (!*args[2]) {
 
2011
                                appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
 
2012
                                appctx->st0 = STAT_CLI_PRINT;
 
2013
                                return 1;
 
2014
                        }
 
2015
 
 
2016
                        ptr = (void *)strtoul(args[2], NULL, 0);
 
2017
 
 
2018
                        /* first, look for the requested session in the session table */
 
2019
                        list_for_each_entry(sess, &sessions, list) {
 
2020
                                if (sess == ptr)
626
2021
                                        break;
627
 
                                }
628
 
 
629
 
                        if (!*line || !*args[2]) {
630
 
                                s->data_ctx.cli.msg = "Require 'backend/server'.\n";
631
 
                                si->st0 = STAT_CLI_PRINT;
632
 
                                return 1;
633
 
                        }
634
 
 
635
 
                        if (!get_backend_server(args[2], line, &px, &sv)) {
636
 
                                s->data_ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
637
 
                                si->st0 = STAT_CLI_PRINT;
638
 
                                return 1;
639
 
                        }
640
 
 
641
 
                        if (px->state == PR_STSTOPPED) {
642
 
                                s->data_ctx.cli.msg = "Proxy is disabled.\n";
643
 
                                si->st0 = STAT_CLI_PRINT;
644
 
                                return 1;
645
 
                        }
646
 
 
647
 
                        if (! (sv->state & SRV_MAINTAIN)) {
648
 
                                /* Not already in maintenance, we can change the server state */
649
 
                                sv->state |= SRV_MAINTAIN;
650
 
                                set_server_down(sv);
651
 
                        }
652
 
 
 
2022
                        }
 
2023
 
 
2024
                        /* do we have the session ? */
 
2025
                        if (sess != ptr) {
 
2026
                                appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
 
2027
                                appctx->st0 = STAT_CLI_PRINT;
 
2028
                                return 1;
 
2029
                        }
 
2030
 
 
2031
                        session_shutdown(sess, SN_ERR_KILLED);
653
2032
                        return 1;
654
2033
                }
 
2034
                else if (strcmp(args[1], "sessions") == 0) {
 
2035
                        if (strcmp(args[2], "server") == 0) {
 
2036
                                struct server *sv;
 
2037
                                struct session *sess, *sess_bck;
 
2038
 
 
2039
                                sv = expect_server_admin(s, si, args[3]);
 
2040
                                if (!sv)
 
2041
                                        return 1;
 
2042
 
 
2043
                                /* kill all the session that are on this server */
 
2044
                                list_for_each_entry_safe(sess, sess_bck, &sv->actconns, by_srv)
 
2045
                                        if (sess->srv_conn == sv)
 
2046
                                                session_shutdown(sess, SN_ERR_KILLED);
 
2047
 
 
2048
                                return 1;
 
2049
                        }
 
2050
                        else {
 
2051
                                appctx->ctx.cli.msg = "'shutdown sessions' only supports 'server'.\n";
 
2052
                                appctx->st0 = STAT_CLI_PRINT;
 
2053
                                return 1;
 
2054
                        }
 
2055
                }
655
2056
                else { /* unknown "disable" parameter */
656
 
                        return 0;
 
2057
                        appctx->ctx.cli.msg = "'shutdown' only supports 'frontend', 'session' and 'sessions'.\n";
 
2058
                        appctx->st0 = STAT_CLI_PRINT;
 
2059
                        return 1;
 
2060
                }
 
2061
        }
 
2062
        else if (strcmp(args[0], "del") == 0) {
 
2063
                if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
 
2064
                        if (args[1][0] == 'm')
 
2065
                                appctx->ctx.map.display_flags = PAT_REF_MAP;
 
2066
                        else
 
2067
                                appctx->ctx.map.display_flags = PAT_REF_ACL;
 
2068
 
 
2069
                        /* Expect two parameters: map name and key. */
 
2070
                        if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
 
2071
                                if (!*args[2] || !*args[3]) {
 
2072
                                        appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
 
2073
                                        appctx->st0 = STAT_CLI_PRINT;
 
2074
                                        return 1;
 
2075
                                }
 
2076
                        }
 
2077
 
 
2078
                        else {
 
2079
                                if (!*args[2] || !*args[3]) {
 
2080
                                        appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
 
2081
                                        appctx->st0 = STAT_CLI_PRINT;
 
2082
                                        return 1;
 
2083
                                }
 
2084
                        }
 
2085
 
 
2086
                        /* Lookup the reference in the maps. */
 
2087
                        appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 
2088
                        if (!appctx->ctx.map.ref ||
 
2089
                            !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
 
2090
                                appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 
2091
                                appctx->st0 = STAT_CLI_PRINT;
 
2092
                                return 1;
 
2093
                        }
 
2094
 
 
2095
                        /* If the entry identifier start with a '#', it is considered as
 
2096
                         * pointer id
 
2097
                         */
 
2098
                        if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
 
2099
                                struct pat_ref_elt *ref;
 
2100
                                long long int conv;
 
2101
                                char *error;
 
2102
 
 
2103
                                /* Convert argument to integer value. */
 
2104
                                conv = strtoll(&args[3][1], &error, 16);
 
2105
                                if (*error != '\0') {
 
2106
                                        appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 
2107
                                        appctx->st0 = STAT_CLI_PRINT;
 
2108
                                        return 1;
 
2109
                                }
 
2110
 
 
2111
                                /* Convert and check integer to pointer. */
 
2112
                                ref = (struct pat_ref_elt *)(long)conv;
 
2113
                                if ((long long int)(long)ref != conv) {
 
2114
                                        appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
 
2115
                                        appctx->st0 = STAT_CLI_PRINT;
 
2116
                                        return 1;
 
2117
                                }
 
2118
 
 
2119
                                /* Try to delete the entry. */
 
2120
                                if (!pat_ref_delete_by_id(appctx->ctx.map.ref, ref)) {
 
2121
                                        /* The entry is not found, send message. */
 
2122
                                        appctx->ctx.cli.msg = "Key not found.\n";
 
2123
                                        appctx->st0 = STAT_CLI_PRINT;
 
2124
                                        return 1;
 
2125
                                }
 
2126
                        }
 
2127
                        else {
 
2128
                                /* Else, use the entry identifier as pattern
 
2129
                                 * string and try to delete the entry.
 
2130
                                 */
 
2131
                                if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
 
2132
                                        /* The entry is not found, send message. */
 
2133
                                        appctx->ctx.cli.msg = "Key not found.\n";
 
2134
                                        appctx->st0 = STAT_CLI_PRINT;
 
2135
                                        return 1;
 
2136
                                }
 
2137
                        }
 
2138
 
 
2139
                        /* The deletion is done, send message. */
 
2140
                        appctx->ctx.cli.msg = "Done.\n";
 
2141
                        appctx->st0 = STAT_CLI_PRINT;
 
2142
                        return 1;
 
2143
                }
 
2144
                else { /* unknown "del" parameter */
 
2145
                        appctx->ctx.cli.msg = "'del' only supports 'map' or 'acl'.\n";
 
2146
                        appctx->st0 = STAT_CLI_PRINT;
 
2147
                        return 1;
 
2148
                }
 
2149
        }
 
2150
        else if (strcmp(args[0], "add") == 0) {
 
2151
                if (strcmp(args[1], "map") == 0 ||
 
2152
                    strcmp(args[1], "acl") == 0) {
 
2153
                        int ret;
 
2154
                        char *err;
 
2155
 
 
2156
                        /* Set flags. */
 
2157
                        if (args[1][0] == 'm')
 
2158
                                appctx->ctx.map.display_flags = PAT_REF_MAP;
 
2159
                        else
 
2160
                                appctx->ctx.map.display_flags = PAT_REF_ACL;
 
2161
 
 
2162
                        /* If the keywork is "map", we expect three parameters, if it
 
2163
                         * is "acl", we expect only two parameters
 
2164
                         */
 
2165
                        if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
 
2166
                                if (!*args[2] || !*args[3] || !*args[4]) {
 
2167
                                        appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
 
2168
                                        appctx->st0 = STAT_CLI_PRINT;
 
2169
                                        return 1;
 
2170
                                }
 
2171
                        }
 
2172
                        else {
 
2173
                                if (!*args[2] || !*args[3]) {
 
2174
                                        appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
 
2175
                                        appctx->st0 = STAT_CLI_PRINT;
 
2176
                                        return 1;
 
2177
                                }
 
2178
                        }
 
2179
 
 
2180
                        /* Lookup for the reference. */
 
2181
                        appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
 
2182
                        if (!appctx->ctx.map.ref) {
 
2183
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
2184
                                        appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
 
2185
                                else
 
2186
                                        appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
 
2187
                                appctx->st0 = STAT_CLI_PRINT;
 
2188
                                return 1;
 
2189
                        }
 
2190
 
 
2191
                        /* The command "add acl" is prohibited if the reference
 
2192
                         * use samples.
 
2193
                         */
 
2194
                        if ((appctx->ctx.map.display_flags & PAT_REF_ACL) &&
 
2195
                            (appctx->ctx.map.ref->flags & PAT_REF_SMP)) {
 
2196
                                appctx->ctx.cli.msg = "This ACL is shared with a map containing samples. "
 
2197
                                                      "You must use the command 'add map' to add values.\n";
 
2198
                                appctx->st0 = STAT_CLI_PRINT;
 
2199
                                return 1;
 
2200
                        }
 
2201
 
 
2202
                        /* Add value. */
 
2203
                        err = NULL;
 
2204
                        if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
2205
                                ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
 
2206
                        else
 
2207
                                ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
 
2208
                        if (!ret) {
 
2209
                                if (err)
 
2210
                                        memprintf(&err, "%s.\n", err);
 
2211
                                appctx->ctx.cli.err = err;
 
2212
                                appctx->st0 = STAT_CLI_PRINT_FREE;
 
2213
                                return 1;
 
2214
                        }
 
2215
 
 
2216
                        /* The add is done, send message. */
 
2217
                        appctx->ctx.cli.msg = "Done.\n";
 
2218
                        appctx->st0 = STAT_CLI_PRINT;
 
2219
                        return 1;
 
2220
                }
 
2221
                else { /* unknown "del" parameter */
 
2222
                        appctx->ctx.cli.msg = "'add' only supports 'map'.\n";
 
2223
                        appctx->st0 = STAT_CLI_PRINT;
 
2224
                        return 1;
657
2225
                }
658
2226
        }
659
2227
        else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
666
2234
 * used to processes I/O from/to the stats unix socket. The system relies on a
667
2235
 * state machine handling requests and various responses. We read a request,
668
2236
 * then we process it and send the response, and we possibly display a prompt.
669
 
 * Then we can read again. The state is stored in si->st0 and is one of the
670
 
 * STAT_CLI_* constants. si->st1 is used to indicate whether prompt is enabled
 
2237
 * Then we can read again. The state is stored in appctx->st0 and is one of the
 
2238
 * STAT_CLI_* constants. appctx->st1 is used to indicate whether prompt is enabled
671
2239
 * or not.
672
2240
 */
673
 
void stats_io_handler(struct stream_interface *si)
 
2241
static void cli_io_handler(struct stream_interface *si)
674
2242
{
675
 
        struct session *s = si->private;
676
 
        struct buffer *req = si->ob;
677
 
        struct buffer *res = si->ib;
 
2243
        struct appctx *appctx = __objt_appctx(si->end);
 
2244
        struct channel *req = si->ob;
 
2245
        struct channel *res = si->ib;
678
2246
        int reql;
679
2247
        int len;
680
2248
 
682
2250
                goto out;
683
2251
 
684
2252
        while (1) {
685
 
                if (si->st0 == STAT_CLI_INIT) {
 
2253
                if (appctx->st0 == STAT_CLI_INIT) {
686
2254
                        /* Stats output not initialized yet */
687
 
                        memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats));
688
 
                        s->data_source = DATA_SRC_STATS;
689
 
                        si->st0 = STAT_CLI_GETREQ;
 
2255
                        memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
 
2256
                        appctx->st0 = STAT_CLI_GETREQ;
690
2257
                }
691
 
                else if (si->st0 == STAT_CLI_END) {
 
2258
                else if (appctx->st0 == STAT_CLI_END) {
692
2259
                        /* Let's close for real now. We just close the request
693
2260
                         * side, the conditions below will complete if needed.
694
2261
                         */
695
 
                        si->shutw(si);
 
2262
                        si_shutw(si);
696
2263
                        break;
697
2264
                }
698
 
                else if (si->st0 == STAT_CLI_GETREQ) {
 
2265
                else if (appctx->st0 == STAT_CLI_GETREQ) {
699
2266
                        /* ensure we have some output room left in the event we
700
2267
                         * would want to return some info right after parsing.
701
2268
                         */
702
 
                        if (buffer_almost_full(si->ib))
 
2269
                        if (buffer_almost_full(si->ib->buf)) {
 
2270
                                si->ib->flags |= CF_WAKE_WRITE;
703
2271
                                break;
 
2272
                        }
704
2273
 
705
 
                        reql = buffer_si_peekline(si->ob, trash, trashlen);
 
2274
                        reql = bo_getline(si->ob, trash.str, trash.size);
706
2275
                        if (reql <= 0) { /* closed or EOL not found */
707
2276
                                if (reql == 0)
708
2277
                                        break;
709
 
                                si->st0 = STAT_CLI_END;
 
2278
                                appctx->st0 = STAT_CLI_END;
710
2279
                                continue;
711
2280
                        }
712
2281
 
714
2283
                         * replace it with an LF and skip only this part.
715
2284
                         */
716
2285
                        for (len = 0; len < reql; len++)
717
 
                                if (trash[len] == ';') {
718
 
                                        trash[len] = '\n';
 
2286
                                if (trash.str[len] == ';') {
 
2287
                                        trash.str[len] = '\n';
719
2288
                                        reql = len + 1;
720
2289
                                        break;
721
2290
                                }
725
2294
                         * line.
726
2295
                         */
727
2296
                        len = reql - 1;
728
 
                        if (trash[len] != '\n') {
729
 
                                si->st0 = STAT_CLI_END;
 
2297
                        if (trash.str[len] != '\n') {
 
2298
                                appctx->st0 = STAT_CLI_END;
730
2299
                                continue;
731
2300
                        }
732
2301
 
733
 
                        if (len && trash[len-1] == '\r')
 
2302
                        if (len && trash.str[len-1] == '\r')
734
2303
                                len--;
735
2304
 
736
 
                        trash[len] = '\0';
 
2305
                        trash.str[len] = '\0';
737
2306
 
738
 
                        si->st0 = STAT_CLI_PROMPT;
 
2307
                        appctx->st0 = STAT_CLI_PROMPT;
739
2308
                        if (len) {
740
 
                                if (strcmp(trash, "quit") == 0) {
741
 
                                        si->st0 = STAT_CLI_END;
 
2309
                                if (strcmp(trash.str, "quit") == 0) {
 
2310
                                        appctx->st0 = STAT_CLI_END;
742
2311
                                        continue;
743
2312
                                }
744
 
                                else if (strcmp(trash, "prompt") == 0)
745
 
                                        si->st1 = !si->st1;
746
 
                                else if (strcmp(trash, "help") == 0 ||
747
 
                                         !stats_sock_parse_request(si, trash)) {
748
 
                                        s->data_ctx.cli.msg = stats_sock_usage_msg;
749
 
                                        si->st0 = STAT_CLI_PRINT;
 
2313
                                else if (strcmp(trash.str, "prompt") == 0)
 
2314
                                        appctx->st1 = !appctx->st1;
 
2315
                                else if (strcmp(trash.str, "help") == 0 ||
 
2316
                                         !stats_sock_parse_request(si, trash.str)) {
 
2317
                                        appctx->ctx.cli.msg = stats_sock_usage_msg;
 
2318
                                        appctx->st0 = STAT_CLI_PRINT;
750
2319
                                }
751
2320
                                /* NB: stats_sock_parse_request() may have put
752
 
                                 * another STAT_CLI_O_* into si->st0.
 
2321
                                 * another STAT_CLI_O_* into appctx->st0.
753
2322
                                 */
754
2323
                        }
755
 
                        else if (!si->st1) {
 
2324
                        else if (!appctx->st1) {
756
2325
                                /* if prompt is disabled, print help on empty lines,
757
2326
                                 * so that the user at least knows how to enable
758
2327
                                 * prompt and find help.
759
2328
                                 */
760
 
                                s->data_ctx.cli.msg = stats_sock_usage_msg;
761
 
                                si->st0 = STAT_CLI_PRINT;
 
2329
                                appctx->ctx.cli.msg = stats_sock_usage_msg;
 
2330
                                appctx->st0 = STAT_CLI_PRINT;
762
2331
                        }
763
2332
 
764
2333
                        /* re-adjust req buffer */
765
 
                        buffer_skip(si->ob, reql);
766
 
                        req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */
 
2334
                        bo_skip(si->ob, reql);
 
2335
                        req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
767
2336
                }
768
2337
                else {  /* output functions: first check if the output buffer is closed then abort */
769
 
                        if (res->flags & (BF_SHUTR_NOW|BF_SHUTR)) {
770
 
                                si->st0 = STAT_CLI_END;
 
2338
                        if (res->flags & (CF_SHUTR_NOW|CF_SHUTR)) {
 
2339
                                appctx->st0 = STAT_CLI_END;
771
2340
                                continue;
772
2341
                        }
773
2342
 
774
 
                        switch (si->st0) {
 
2343
                        switch (appctx->st0) {
775
2344
                        case STAT_CLI_PRINT:
776
 
                                if (buffer_feed(si->ib, s->data_ctx.cli.msg) < 0)
777
 
                                        si->st0 = STAT_CLI_PROMPT;
 
2345
                                if (bi_putstr(si->ib, appctx->ctx.cli.msg) != -1)
 
2346
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2347
                                break;
 
2348
                        case STAT_CLI_PRINT_FREE:
 
2349
                                if (bi_putstr(si->ib, appctx->ctx.cli.err) != -1) {
 
2350
                                        free(appctx->ctx.cli.err);
 
2351
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2352
                                }
778
2353
                                break;
779
2354
                        case STAT_CLI_O_INFO:
780
 
                                if (stats_dump_raw_to_buffer(s, res))
781
 
                                        si->st0 = STAT_CLI_PROMPT;
 
2355
                                if (stats_dump_info_to_buffer(si))
 
2356
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2357
                                break;
 
2358
                        case STAT_CLI_O_STAT:
 
2359
                                if (stats_dump_stat_to_buffer(si, NULL))
 
2360
                                        appctx->st0 = STAT_CLI_PROMPT;
782
2361
                                break;
783
2362
                        case STAT_CLI_O_SESS:
784
 
                                if (stats_dump_sess_to_buffer(s, res))
785
 
                                        si->st0 = STAT_CLI_PROMPT;
 
2363
                                if (stats_dump_sess_to_buffer(si))
 
2364
                                        appctx->st0 = STAT_CLI_PROMPT;
786
2365
                                break;
787
2366
                        case STAT_CLI_O_ERR:    /* errors dump */
788
 
                                if (stats_dump_errors_to_buffer(s, res))
789
 
                                        si->st0 = STAT_CLI_PROMPT;
 
2367
                                if (stats_dump_errors_to_buffer(si))
 
2368
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2369
                                break;
 
2370
                        case STAT_CLI_O_TAB:
 
2371
                        case STAT_CLI_O_CLR:
 
2372
                                if (stats_table_request(si, appctx->st0))
 
2373
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2374
                                break;
 
2375
                        case STAT_CLI_O_PATS:
 
2376
                                if (stats_pats_list(si))
 
2377
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2378
                                break;
 
2379
                        case STAT_CLI_O_PAT:
 
2380
                                if (stats_pat_list(si))
 
2381
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2382
                                break;
 
2383
                        case STAT_CLI_O_MLOOK:
 
2384
                                if (stats_map_lookup(si))
 
2385
                                        appctx->st0 = STAT_CLI_PROMPT;
 
2386
                                break;
 
2387
                        case STAT_CLI_O_POOLS:
 
2388
                                if (stats_dump_pools_to_buffer(si))
 
2389
                                        appctx->st0 = STAT_CLI_PROMPT;
790
2390
                                break;
791
2391
                        default: /* abnormal state */
792
 
                                si->st0 = STAT_CLI_PROMPT;
 
2392
                                appctx->st0 = STAT_CLI_PROMPT;
793
2393
                                break;
794
2394
                        }
795
2395
 
796
2396
                        /* The post-command prompt is either LF alone or LF + '> ' in interactive mode */
797
 
                        if (si->st0 == STAT_CLI_PROMPT) {
798
 
                                if (buffer_feed(si->ib, si->st1 ? "\n> " : "\n") < 0)
799
 
                                        si->st0 = STAT_CLI_GETREQ;
 
2397
                        if (appctx->st0 == STAT_CLI_PROMPT) {
 
2398
                                if (bi_putstr(si->ib, appctx->st1 ? "\n> " : "\n") != -1)
 
2399
                                        appctx->st0 = STAT_CLI_GETREQ;
800
2400
                        }
801
2401
 
802
2402
                        /* If the output functions are still there, it means they require more room. */
803
 
                        if (si->st0 >= STAT_CLI_OUTPUT)
 
2403
                        if (appctx->st0 >= STAT_CLI_OUTPUT)
804
2404
                                break;
805
2405
 
806
2406
                        /* Now we close the output if one of the writers did so,
808
2408
                         * buffer is empty. This still allows pipelined requests
809
2409
                         * to be sent in non-interactive mode.
810
2410
                         */
811
 
                        if ((res->flags & (BF_SHUTW|BF_SHUTW_NOW)) || (!si->st1 && !req->send_max)) {
812
 
                                si->st0 = STAT_CLI_END;
 
2411
                        if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!appctx->st1 && !req->buf->o)) {
 
2412
                                appctx->st0 = STAT_CLI_END;
813
2413
                                continue;
814
2414
                        }
815
2415
 
816
2416
                        /* switch state back to GETREQ to read next requests */
817
 
                        si->st0 = STAT_CLI_GETREQ;
 
2417
                        appctx->st0 = STAT_CLI_GETREQ;
818
2418
                }
819
2419
        }
820
2420
 
821
 
        if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST) && (si->st0 != STAT_CLI_GETREQ)) {
 
2421
        if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST) && (appctx->st0 != STAT_CLI_GETREQ)) {
822
2422
                DPRINTF(stderr, "%s@%d: si to buf closed. req=%08x, res=%08x, st=%d\n",
823
2423
                        __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
824
 
                /* Other size has closed, let's abort if we have no more processing to do
 
2424
                /* Other side has closed, let's abort if we have no more processing to do
825
2425
                 * and nothing more to consume. This is comparable to a broken pipe, so
826
2426
                 * we forward the close to the request side so that it flows upstream to
827
2427
                 * the client.
828
2428
                 */
829
 
                si->shutw(si);
 
2429
                si_shutw(si);
830
2430
        }
831
2431
 
832
 
        if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && (si->st0 < STAT_CLI_OUTPUT)) {
 
2432
        if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && (appctx->st0 < STAT_CLI_OUTPUT)) {
833
2433
                DPRINTF(stderr, "%s@%d: buf to si closed. req=%08x, res=%08x, st=%d\n",
834
2434
                        __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
835
2435
                /* We have no more processing to do, and nothing more to send, and
836
2436
                 * the client side has closed. So we'll forward this state downstream
837
2437
                 * on the response buffer.
838
2438
                 */
839
 
                si->shutr(si);
840
 
                res->flags |= BF_READ_NULL;
 
2439
                si_shutr(si);
 
2440
                res->flags |= CF_READ_NULL;
841
2441
        }
842
2442
 
843
2443
        /* update all other flags and resync with the other side */
844
 
        si->update(si);
 
2444
        si_update(si);
845
2445
 
846
2446
        /* we don't want to expire timeouts while we're processing requests */
847
2447
        si->ib->rex = TICK_ETERNITY;
848
2448
        si->ob->wex = TICK_ETERNITY;
849
2449
 
850
2450
 out:
851
 
        DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rql=%d, rqs=%d, rl=%d, rs=%d\n",
 
2451
        DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rqh=%d, rqs=%d, rh=%d, rs=%d\n",
852
2452
                __FUNCTION__, __LINE__,
853
 
                si->state, req->flags, res->flags, req->l, req->send_max, res->l, res->send_max);
 
2453
                si->state, req->flags, res->flags, req->buf->i, req->buf->o, res->buf->i, res->buf->o);
854
2454
 
855
2455
        if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
856
2456
                /* check that we have released everything then unregister */
858
2458
        }
859
2459
}
860
2460
 
861
 
/* This function is called to send output to the response buffer.
862
 
 * It dumps statistics onto the output buffer <rep> owned by session <s>.
863
 
 * s->data_ctx must have been zeroed first, and the flags properly set.
 
2461
/* This function dumps information onto the stream interface's read buffer.
864
2462
 * It returns 0 as long as it does not complete, non-zero upon completion.
865
 
 * Some states are not used but it makes the code more similar to other
866
 
 * functions which handle stats too.
867
 
 */
868
 
int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep)
869
 
{
870
 
        struct proxy *px;
871
 
        struct chunk msg;
872
 
        unsigned int up;
873
 
 
874
 
        chunk_init(&msg, trash, trashlen);
875
 
 
876
 
        switch (s->data_state) {
877
 
        case DATA_ST_INIT:
878
 
                /* the function had not been called yet */
879
 
                s->data_state = DATA_ST_HEAD;
880
 
                /* fall through */
881
 
 
882
 
        case DATA_ST_HEAD:
883
 
                if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
884
 
                        print_csv_header(&msg);
885
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
886
 
                                return 0;
887
 
                }
888
 
 
889
 
                s->data_state = DATA_ST_INFO;
890
 
                /* fall through */
891
 
 
892
 
        case DATA_ST_INFO:
893
 
                up = (now.tv_sec - start_date.tv_sec);
894
 
                if (s->data_ctx.stats.flags & STAT_SHOW_INFO) {
895
 
                        chunk_printf(&msg,
896
 
                                     "Name: " PRODUCT_NAME "\n"
897
 
                                     "Version: " HAPROXY_VERSION "\n"
898
 
                                     "Release_date: " HAPROXY_DATE "\n"
899
 
                                     "Nbproc: %d\n"
900
 
                                     "Process_num: %d\n"
901
 
                                     "Pid: %d\n"
902
 
                                     "Uptime: %dd %dh%02dm%02ds\n"
903
 
                                     "Uptime_sec: %d\n"
904
 
                                     "Memmax_MB: %d\n"
905
 
                                     "Ulimit-n: %d\n"
906
 
                                     "Maxsock: %d\n"
907
 
                                     "Maxconn: %d\n"
908
 
                                     "Maxpipes: %d\n"
909
 
                                     "CurrConns: %d\n"
910
 
                                     "PipesUsed: %d\n"
911
 
                                     "PipesFree: %d\n"
912
 
                                     "Tasks: %d\n"
913
 
                                     "Run_queue: %d\n"
914
 
                                     "node: %s\n"
915
 
                                     "description: %s\n"
916
 
                                     "",
917
 
                                     global.nbproc,
918
 
                                     relative_pid,
919
 
                                     pid,
920
 
                                     up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
921
 
                                     up,
922
 
                                     global.rlimit_memmax,
923
 
                                     global.rlimit_nofile,
924
 
                                     global.maxsock, global.maxconn, global.maxpipes,
925
 
                                     actconn, pipes_used, pipes_free,
926
 
                                     nb_tasks_cur, run_queue_cur,
927
 
                                     global.node, global.desc?global.desc:""
928
 
                                     );
929
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
930
 
                                return 0;
931
 
                }
932
 
 
933
 
                s->data_ctx.stats.px = proxy;
934
 
                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
935
 
 
936
 
                s->data_ctx.stats.sv = NULL;
937
 
                s->data_ctx.stats.sv_st = 0;
938
 
 
939
 
                s->data_state = DATA_ST_LIST;
940
 
                /* fall through */
941
 
 
942
 
        case DATA_ST_LIST:
943
 
                /* dump proxies */
944
 
                if (s->data_ctx.stats.flags & STAT_SHOW_STAT) {
945
 
                        while (s->data_ctx.stats.px) {
946
 
                                px = s->data_ctx.stats.px;
947
 
                                /* skip the disabled proxies and non-networked ones */
948
 
                                if (px->state != PR_STSTOPPED &&
949
 
                                    (px->cap & (PR_CAP_FE | PR_CAP_BE))) {
950
 
                                        if (stats_dump_proxy(s, px, NULL) == 0)
951
 
                                                return 0;
952
 
                                }
953
 
 
954
 
                                s->data_ctx.stats.px = px->next;
955
 
                                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
956
 
                        }
957
 
                        /* here, we just have reached the last proxy */
958
 
                }
959
 
 
960
 
                s->data_state = DATA_ST_END;
961
 
                /* fall through */
962
 
 
963
 
        case DATA_ST_END:
964
 
                s->data_state = DATA_ST_FIN;
965
 
                /* fall through */
966
 
 
967
 
        case DATA_ST_FIN:
968
 
                return 1;
969
 
 
970
 
        default:
971
 
                /* unknown state ! */
972
 
                s->data_state = DATA_ST_FIN;
973
 
                return 1;
974
 
        }
975
 
}
976
 
 
977
 
 
978
 
/* We don't want to land on the posted stats page because a refresh will
979
 
 * repost the data.  We don't want this to happen on accident so we redirect
980
 
 * the browse to the stats page with a GET.
981
 
 */
982
 
int stats_http_redir(struct session *s, struct buffer *rep, struct uri_auth *uri)
983
 
{
984
 
        struct chunk msg;
985
 
 
986
 
        chunk_init(&msg, trash, trashlen);
987
 
 
988
 
        switch (s->data_state) {
989
 
        case DATA_ST_INIT:
990
 
                chunk_printf(&msg,
991
 
                        "HTTP/1.0 303 See Other\r\n"
992
 
                        "Cache-Control: no-cache\r\n"
993
 
                        "Content-Type: text/plain\r\n"
994
 
                        "Connection: close\r\n"
995
 
                        "Location: %s;st=%s",
996
 
                        uri->uri_prefix, s->data_ctx.stats.st_code);
997
 
                chunk_printf(&msg, "\r\n\r\n");
998
 
 
999
 
                if (buffer_feed_chunk(rep, &msg) >= 0)
1000
 
                        return 0;
1001
 
 
1002
 
                s->txn.status = 303;
1003
 
 
1004
 
                if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
1005
 
                        s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
1006
 
                if (!(s->flags & SN_FINST_MASK))
1007
 
                        s->flags |= SN_FINST_R;
1008
 
 
1009
 
                s->data_state = DATA_ST_FIN;
1010
 
                return 1;
1011
 
        }
1012
 
        return 1;
1013
 
}
1014
 
 
1015
 
 
1016
 
/* This I/O handler runs as an applet embedded in a stream interface. It is
1017
 
 * used to send HTTP stats over a TCP socket. The mechanism is very simple.
1018
 
 * si->st0 becomes non-zero once the transfer is finished. The handler
1019
 
 * automatically unregisters itself once transfer is complete.
1020
 
 */
1021
 
void http_stats_io_handler(struct stream_interface *si)
1022
 
{
1023
 
        struct session *s = si->private;
1024
 
        struct buffer *req = si->ob;
1025
 
        struct buffer *res = si->ib;
1026
 
 
1027
 
        if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
1028
 
                goto out;
1029
 
 
1030
 
        /* check that the output is not closed */
1031
 
        if (res->flags & (BF_SHUTW|BF_SHUTW_NOW))
1032
 
                si->st0 = 1;
1033
 
 
1034
 
        if (!si->st0) {
1035
 
                if (s->txn.meth == HTTP_METH_POST) {
1036
 
                        if (stats_http_redir(s, res, s->be->uri_auth)) {
1037
 
                                si->st0 = 1;
1038
 
                                si->shutw(si);
1039
 
                        }
1040
 
                } else {
1041
 
                        if (stats_dump_http(s, res, s->be->uri_auth)) {
1042
 
                                si->st0 = 1;
1043
 
                                si->shutw(si);
1044
 
                        }
1045
 
                }
1046
 
        }
1047
 
 
1048
 
        if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST))
1049
 
                si->shutw(si);
1050
 
 
1051
 
        if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && si->st0) {
1052
 
                si->shutr(si);
1053
 
                res->flags |= BF_READ_NULL;
1054
 
        }
1055
 
 
1056
 
        /* update all other flags and resync with the other side */
1057
 
        si->update(si);
1058
 
 
1059
 
        /* we don't want to expire timeouts while we're processing requests */
1060
 
        si->ib->rex = TICK_ETERNITY;
1061
 
        si->ob->wex = TICK_ETERNITY;
1062
 
 
1063
 
 out:
1064
 
        if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
1065
 
                /* check that we have released everything then unregister */
1066
 
                stream_int_unregister_handler(si);
1067
 
        }
1068
 
}
1069
 
 
1070
 
 
1071
 
/*
1072
 
 * Produces statistics data for the session <s>. Expects to be called with
1073
 
 * client socket shut down on input. It stops by itself by unsetting the
1074
 
 * BF_HIJACK flag from the buffer, which it uses to keep on being called
1075
 
 * when there is free space in the buffer, of simply by letting an empty buffer
1076
 
 * upon return.s->data_ctx must have been zeroed before the first call, and the
1077
 
 * flags set. It returns 0 if it had to stop writing data and an I/O is needed,
1078
 
 * 1 if the dump is finished and the session must be closed, or -1 in case of
1079
 
 * any error.
1080
 
 */
1081
 
int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
1082
 
{
1083
 
        struct proxy *px;
1084
 
        struct chunk msg;
1085
 
        unsigned int up;
1086
 
 
1087
 
        chunk_init(&msg, trash, trashlen);
1088
 
 
1089
 
        switch (s->data_state) {
1090
 
        case DATA_ST_INIT:
1091
 
                chunk_printf(&msg,
1092
 
                             "HTTP/1.0 200 OK\r\n"
1093
 
                             "Cache-Control: no-cache\r\n"
1094
 
                             "Connection: close\r\n"
1095
 
                             "Content-Type: %s\r\n",
1096
 
                             (s->data_ctx.stats.flags & STAT_FMT_CSV) ? "text/plain" : "text/html");
1097
 
 
1098
 
                if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH))
1099
 
                        chunk_printf(&msg, "Refresh: %d\r\n",
1100
 
                                     uri->refresh);
1101
 
 
1102
 
                chunk_printf(&msg, "\r\n");
1103
 
 
1104
 
                s->txn.status = 200;
1105
 
                if (buffer_feed_chunk(rep, &msg) >= 0)
1106
 
                        return 0;
1107
 
 
1108
 
                if (!(s->flags & SN_ERR_MASK))  // this is not really an error but it is
1109
 
                        s->flags |= SN_ERR_PRXCOND; // to mark that it comes from the proxy
1110
 
                if (!(s->flags & SN_FINST_MASK))
1111
 
                        s->flags |= SN_FINST_R;
1112
 
 
1113
 
                if (s->txn.meth == HTTP_METH_HEAD) {
1114
 
                        /* that's all we return in case of HEAD request */
1115
 
                        s->data_state = DATA_ST_FIN;
1116
 
                        return 1;
1117
 
                }
1118
 
 
1119
 
                s->data_state = DATA_ST_HEAD; /* let's start producing data */
1120
 
                /* fall through */
1121
 
 
1122
 
        case DATA_ST_HEAD:
1123
 
                if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1124
 
                        /* WARNING! This must fit in the first buffer !!! */
1125
 
                        chunk_printf(&msg,
1126
 
                             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
1127
 
                             "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
1128
 
                             "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
1129
 
                             "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
1130
 
                             "<style type=\"text/css\"><!--\n"
1131
 
                             "body {"
1132
 
                             " font-family: arial, helvetica, sans-serif;"
1133
 
                             " font-size: 12px;"
1134
 
                             " font-weight: normal;"
1135
 
                             " color: black;"
1136
 
                             " background: white;"
1137
 
                             "}\n"
1138
 
                             "th,td {"
1139
 
                             " font-size: 10px;"
1140
 
                             "}\n"
1141
 
                             "h1 {"
1142
 
                             " font-size: x-large;"
1143
 
                             " margin-bottom: 0.5em;"
1144
 
                             "}\n"
1145
 
                             "h2 {"
1146
 
                             " font-family: helvetica, arial;"
1147
 
                             " font-size: x-large;"
1148
 
                             " font-weight: bold;"
1149
 
                             " font-style: italic;"
1150
 
                             " color: #6020a0;"
1151
 
                             " margin-top: 0em;"
1152
 
                             " margin-bottom: 0em;"
1153
 
                             "}\n"
1154
 
                             "h3 {"
1155
 
                             " font-family: helvetica, arial;"
1156
 
                             " font-size: 16px;"
1157
 
                             " font-weight: bold;"
1158
 
                             " color: #b00040;"
1159
 
                             " background: #e8e8d0;"
1160
 
                             " margin-top: 0em;"
1161
 
                             " margin-bottom: 0em;"
1162
 
                             "}\n"
1163
 
                             "li {"
1164
 
                             " margin-top: 0.25em;"
1165
 
                             " margin-right: 2em;"
1166
 
                             "}\n"
1167
 
                             ".hr {margin-top: 0.25em;"
1168
 
                             " border-color: black;"
1169
 
                             " border-bottom-style: solid;"
1170
 
                             "}\n"
1171
 
                             ".titre    {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
1172
 
                             ".total    {background: #20D0D0;color: #ffff80;}\n"
1173
 
                             ".frontend {background: #e8e8d0;}\n"
1174
 
                             ".socket   {background: #d0d0d0;}\n"
1175
 
                             ".backend  {background: #e8e8d0;}\n"
1176
 
                             ".active0  {background: #ff9090;}\n"
1177
 
                             ".active1  {background: #ffd020;}\n"
1178
 
                             ".active2  {background: #ffffa0;}\n"
1179
 
                             ".active3  {background: #c0ffc0;}\n"
1180
 
                             ".active4  {background: #ffffa0;}\n"  /* NOLB state shows same as going down */
1181
 
                             ".active5  {background: #a0e0a0;}\n"  /* NOLB state shows darker than up */
1182
 
                             ".active6  {background: #e0e0e0;}\n"
1183
 
                             ".backup0  {background: #ff9090;}\n"
1184
 
                             ".backup1  {background: #ff80ff;}\n"
1185
 
                             ".backup2  {background: #c060ff;}\n"
1186
 
                             ".backup3  {background: #b0d0ff;}\n"
1187
 
                             ".backup4  {background: #c060ff;}\n"  /* NOLB state shows same as going down */
1188
 
                             ".backup5  {background: #90b0e0;}\n"  /* NOLB state shows same as going down */
1189
 
                             ".backup6  {background: #e0e0e0;}\n"
1190
 
                             ".maintain {background: #c07820;}\n"
1191
 
                             ".rls      {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
1192
 
                             "\n"
1193
 
                             "a.px:link {color: #ffff40; text-decoration: none;}"
1194
 
                             "a.px:visited {color: #ffff40; text-decoration: none;}"
1195
 
                             "a.px:hover {color: #ffffff; text-decoration: none;}"
1196
 
                             "a.lfsb:link {color: #000000; text-decoration: none;}"
1197
 
                             "a.lfsb:visited {color: #000000; text-decoration: none;}"
1198
 
                             "a.lfsb:hover {color: #505050; text-decoration: none;}"
1199
 
                             "\n"
1200
 
                             "table.tbl { border-collapse: collapse; border-style: none;}\n"
1201
 
                             "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"
1202
 
                             "table.tbl td.ac { text-align: center;}\n"
1203
 
                             "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
1204
 
                             "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
1205
 
                             "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
1206
 
                             "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
1207
 
                             "\n"
1208
 
                             "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
1209
 
                             "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
1210
 
                             "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
1211
 
                             "u {text-decoration:none; border-bottom: 1px dotted black;}\n"
1212
 
                             "-->\n"
1213
 
                             "</style></head>\n",
1214
 
                             (uri->flags&ST_SHNODE) ? " on " : "",
1215
 
                             (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
1216
 
                             );
1217
 
                } else {
1218
 
                        print_csv_header(&msg);
1219
 
                }
1220
 
                if (buffer_feed_chunk(rep, &msg) >= 0)
1221
 
                        return 0;
1222
 
 
1223
 
                s->data_state = DATA_ST_INFO;
1224
 
                /* fall through */
1225
 
 
1226
 
        case DATA_ST_INFO:
1227
 
                up = (now.tv_sec - start_date.tv_sec);
1228
 
 
1229
 
                /* WARNING! this has to fit the first packet too.
1230
 
                         * We are around 3.5 kB, add adding entries will
1231
 
                         * become tricky if we want to support 4kB buffers !
1232
 
                         */
1233
 
                if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1234
 
                        chunk_printf(&msg,
1235
 
                             "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
1236
 
                             PRODUCT_NAME "%s</a></h1>\n"
1237
 
                             "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
1238
 
                             "<hr width=\"100%%\" class=\"hr\">\n"
1239
 
                             "<h3>&gt; General process information</h3>\n"
1240
 
                             "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
1241
 
                             "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
1242
 
                             "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
1243
 
                             "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
1244
 
                             "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>maxpipes = </b> %d<br>\n"
1245
 
                             "current conns = %d; current pipes = %d/%d<br>\n"
1246
 
                             "Running tasks: %d/%d<br>\n"
1247
 
                             "</td><td align=\"center\" nowrap>\n"
1248
 
                             "<table class=\"lgd\"><tr>\n"
1249
 
                             "<td class=\"active3\">&nbsp;</td><td class=\"noborder\">active UP </td>"
1250
 
                             "<td class=\"backup3\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
1251
 
                             "</tr><tr>\n"
1252
 
                             "<td class=\"active2\"></td><td class=\"noborder\">active UP, going down </td>"
1253
 
                             "<td class=\"backup2\"></td><td class=\"noborder\">backup UP, going down </td>"
1254
 
                             "</tr><tr>\n"
1255
 
                             "<td class=\"active1\"></td><td class=\"noborder\">active DOWN, going up </td>"
1256
 
                             "<td class=\"backup1\"></td><td class=\"noborder\">backup DOWN, going up </td>"
1257
 
                             "</tr><tr>\n"
1258
 
                             "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
1259
 
                             "<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
1260
 
                             "</tr><tr>\n"
1261
 
                             "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) &nbsp;</td>"
1262
 
                             "</tr></table>\n"
1263
 
                             "Note: UP with load-balancing disabled is reported as \"NOLB\"."
1264
 
                             "</td>"
1265
 
                             "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
1266
 
                             "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
1267
 
                             "",
1268
 
                             (uri->flags&ST_HIDEVER)?"":(STATS_VERSION_STRING),
1269
 
                             pid, (uri->flags&ST_SHNODE) ? " on " : "", (uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : "",
1270
 
                             (uri->flags&ST_SHDESC)? ": " : "", (uri->flags&ST_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
1271
 
                             pid, relative_pid, global.nbproc,
1272
 
                             up / 86400, (up % 86400) / 3600,
1273
 
                             (up % 3600) / 60, (up % 60),
1274
 
                             global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
1275
 
                             global.rlimit_memmax ? " MB" : "",
1276
 
                             global.rlimit_nofile,
1277
 
                             global.maxsock, global.maxconn, global.maxpipes,
1278
 
                             actconn, pipes_used, pipes_used+pipes_free,
1279
 
                             run_queue_cur, nb_tasks_cur
1280
 
                             );
1281
 
 
1282
 
                        if (s->data_ctx.stats.flags & STAT_HIDE_DOWN)
1283
 
                                chunk_printf(&msg,
1284
 
                                     "<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
1285
 
                                     uri->uri_prefix,
1286
 
                                     "",
1287
 
                                     (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
1288
 
                        else
1289
 
                                chunk_printf(&msg,
1290
 
                                     "<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
1291
 
                                     uri->uri_prefix,
1292
 
                                     ";up",
1293
 
                                     (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
1294
 
 
1295
 
                        if (uri->refresh > 0) {
1296
 
                                if (s->data_ctx.stats.flags & STAT_NO_REFRESH)
1297
 
                                        chunk_printf(&msg,
1298
 
                                             "<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
1299
 
                                             uri->uri_prefix,
1300
 
                                             (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1301
 
                                             "");
1302
 
                                else
1303
 
                                        chunk_printf(&msg,
1304
 
                                             "<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
1305
 
                                             uri->uri_prefix,
1306
 
                                             (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1307
 
                                             ";norefresh");
1308
 
                        }
1309
 
 
1310
 
                        chunk_printf(&msg,
1311
 
                             "<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
1312
 
                             uri->uri_prefix,
1313
 
                             (s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1314
 
                             (s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
1315
 
 
1316
 
                        chunk_printf(&msg,
1317
 
                             "<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
1318
 
                             uri->uri_prefix,
1319
 
                             (uri->refresh > 0) ? ";norefresh" : "");
1320
 
 
1321
 
                        chunk_printf(&msg,
1322
 
                             "</ul></td>"
1323
 
                             "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
1324
 
                             "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
1325
 
                             "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
1326
 
                             "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
1327
 
                             "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
1328
 
                             "</ul>"
1329
 
                             "</td>"
1330
 
                             "</tr></table>\n"
1331
 
                             ""
1332
 
                             );
1333
 
 
1334
 
                        if (s->data_ctx.stats.st_code) {
1335
 
                                if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_DONE) == 0) {
1336
 
                                        chunk_printf(&msg,
1337
 
                                                     "<p><div class=active3>"
1338
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1339
 
                                                     "Action processed successfully."
1340
 
                                                     "</div>\n", uri->uri_prefix);
1341
 
                                }
1342
 
                                else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_NONE) == 0) {
1343
 
                                        chunk_printf(&msg,
1344
 
                                                     "<p><div class=active2>"
1345
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1346
 
                                                     "Nothing has changed."
1347
 
                                                     "</div>\n", uri->uri_prefix);
1348
 
                                }
1349
 
                                else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_PART) == 0) {
1350
 
                                        chunk_printf(&msg,
1351
 
                                                     "<p><div class=active2>"
1352
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1353
 
                                                     "Action partially processed.<br>"
1354
 
                                                     "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
1355
 
                                                     "</div>\n", uri->uri_prefix);
1356
 
                                }
1357
 
                                else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_ERRP) == 0) {
1358
 
                                        chunk_printf(&msg,
1359
 
                                                     "<p><div class=active0>"
1360
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1361
 
                                                     "Action not processed because of invalid parameters."
1362
 
                                                     "<ul>"
1363
 
                                                     "<li>The action is maybe unknown.</li>"
1364
 
                                                     "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
1365
 
                                                     "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
1366
 
                                                     "</ul>"
1367
 
                                                     "</div>\n", uri->uri_prefix);
1368
 
                                }
1369
 
                                else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_EXCD) == 0) {
1370
 
                                        chunk_printf(&msg,
1371
 
                                                     "<p><div class=active0>"
1372
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1373
 
                                                     "<b>Action not processed : the buffer couldn't store all the data.<br>"
1374
 
                                                     "You should retry with less servers at a time.</b>"
1375
 
                                                     "</div>\n", uri->uri_prefix);
1376
 
                                }
1377
 
                                else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_DENY) == 0) {
1378
 
                                        chunk_printf(&msg,
1379
 
                                                     "<p><div class=active0>"
1380
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1381
 
                                                     "<b>Action denied.</b>"
1382
 
                                                     "</div>\n", uri->uri_prefix);
1383
 
                                }
1384
 
                                else {
1385
 
                                        chunk_printf(&msg,
1386
 
                                                     "<p><div class=active6>"
1387
 
                                                     "<a class=lfsb href=\"%s\" title=\"Remove this message\">[X]</a> "
1388
 
                                                     "Unexpected result."
1389
 
                                                     "</div>\n", uri->uri_prefix);
1390
 
                                }
1391
 
                                chunk_printf(&msg,"<p>\n");
1392
 
                        }
1393
 
 
1394
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
1395
 
                                return 0;
1396
 
                }
1397
 
 
1398
 
                s->data_ctx.stats.px = proxy;
1399
 
                s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
1400
 
                s->data_state = DATA_ST_LIST;
1401
 
                /* fall through */
1402
 
 
1403
 
        case DATA_ST_LIST:
1404
 
                /* dump proxies */
1405
 
                while (s->data_ctx.stats.px) {
1406
 
                        if (buffer_almost_full(rep))
1407
 
                                return 0;
1408
 
                        px = s->data_ctx.stats.px;
1409
 
                        /* skip the disabled proxies and non-networked ones */
1410
 
                        if (px->state != PR_STSTOPPED && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
1411
 
                                if (stats_dump_proxy(s, px, uri) == 0)
1412
 
                                        return 0;
1413
 
 
1414
 
                        s->data_ctx.stats.px = px->next;
1415
 
                        s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
1416
 
                }
1417
 
                /* here, we just have reached the last proxy */
1418
 
 
1419
 
                s->data_state = DATA_ST_END;
1420
 
                /* fall through */
1421
 
 
1422
 
        case DATA_ST_END:
1423
 
                if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1424
 
                        chunk_printf(&msg, "</body></html>\n");
1425
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
1426
 
                                return 0;
1427
 
                }
1428
 
 
1429
 
                s->data_state = DATA_ST_FIN;
1430
 
                /* fall through */
1431
 
 
1432
 
        case DATA_ST_FIN:
1433
 
                return 1;
1434
 
 
1435
 
        default:
1436
 
                /* unknown state ! */
1437
 
                s->data_state = DATA_ST_FIN;
1438
 
                return -1;
1439
 
        }
1440
 
}
1441
 
 
1442
 
 
1443
 
/*
1444
 
 * Dumps statistics for a proxy.
1445
 
 * Returns 0 if it had to stop dumping data because of lack of buffer space,
1446
 
 * ot non-zero if everything completed.
1447
 
 */
1448
 
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
1449
 
{
1450
 
        struct buffer *rep = s->rep;
1451
 
        struct server *sv, *svs;        /* server and server-state, server-state=server or server->tracked */
 
2463
 * No state is used.
 
2464
 */
 
2465
static int stats_dump_info_to_buffer(struct stream_interface *si)
 
2466
{
 
2467
        unsigned int up = (now.tv_sec - start_date.tv_sec);
 
2468
 
 
2469
#ifdef USE_OPENSSL
 
2470
        int ssl_sess_rate = read_freq_ctr(&global.ssl_per_sec);
 
2471
        int ssl_key_rate = read_freq_ctr(&global.ssl_fe_keys_per_sec);
 
2472
        int ssl_reuse = 0;
 
2473
 
 
2474
        if (ssl_key_rate < ssl_sess_rate) {
 
2475
                /* count the ssl reuse ratio and avoid overflows in both directions */
 
2476
                ssl_reuse = 100 - (100 * ssl_key_rate + (ssl_sess_rate - 1) / 2) / ssl_sess_rate;
 
2477
        }
 
2478
#endif
 
2479
 
 
2480
        chunk_printf(&trash,
 
2481
                     "Name: " PRODUCT_NAME "\n"
 
2482
                     "Version: " HAPROXY_VERSION "\n"
 
2483
                     "Release_date: " HAPROXY_DATE "\n"
 
2484
                     "Nbproc: %d\n"
 
2485
                     "Process_num: %d\n"
 
2486
                     "Pid: %d\n"
 
2487
                     "Uptime: %dd %dh%02dm%02ds\n"
 
2488
                     "Uptime_sec: %d\n"
 
2489
                     "Memmax_MB: %d\n"
 
2490
                     "Ulimit-n: %d\n"
 
2491
                     "Maxsock: %d\n"
 
2492
                     "Maxconn: %d\n"
 
2493
                     "Hard_maxconn: %d\n"
 
2494
                     "CurrConns: %d\n"
 
2495
                     "CumConns: %d\n"
 
2496
                     "CumReq: %d\n"
 
2497
#ifdef USE_OPENSSL
 
2498
                     "MaxSslConns: %d\n"
 
2499
                     "CurrSslConns: %d\n"
 
2500
                     "CumSslConns: %d\n"
 
2501
#endif
 
2502
                     "Maxpipes: %d\n"
 
2503
                     "PipesUsed: %d\n"
 
2504
                     "PipesFree: %d\n"
 
2505
                     "ConnRate: %d\n"
 
2506
                     "ConnRateLimit: %d\n"
 
2507
                     "MaxConnRate: %d\n"
 
2508
                     "SessRate: %d\n"
 
2509
                     "SessRateLimit: %d\n"
 
2510
                     "MaxSessRate: %d\n"
 
2511
#ifdef USE_OPENSSL
 
2512
                     "SslRate: %d\n"
 
2513
                     "SslRateLimit: %d\n"
 
2514
                     "MaxSslRate: %d\n"
 
2515
                     "SslFrontendKeyRate: %d\n"
 
2516
                     "SslFrontendMaxKeyRate: %d\n"
 
2517
                     "SslFrontendSessionReuse_pct: %d\n"
 
2518
                     "SslBackendKeyRate: %d\n"
 
2519
                     "SslBackendMaxKeyRate: %d\n"
 
2520
                     "SslCacheLookups: %u\n"
 
2521
                     "SslCacheMisses: %u\n"
 
2522
#endif
 
2523
                     "CompressBpsIn: %u\n"
 
2524
                     "CompressBpsOut: %u\n"
 
2525
                     "CompressBpsRateLim: %u\n"
 
2526
#ifdef USE_ZLIB
 
2527
                     "ZlibMemUsage: %ld\n"
 
2528
                     "MaxZlibMemUsage: %ld\n"
 
2529
#endif
 
2530
                     "Tasks: %d\n"
 
2531
                     "Run_queue: %d\n"
 
2532
                     "Idle_pct: %d\n"
 
2533
                     "node: %s\n"
 
2534
                     "description: %s\n"
 
2535
                     "",
 
2536
                     global.nbproc,
 
2537
                     relative_pid,
 
2538
                     pid,
 
2539
                     up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
 
2540
                     up,
 
2541
                     global.rlimit_memmax,
 
2542
                     global.rlimit_nofile,
 
2543
                     global.maxsock, global.maxconn, global.hardmaxconn,
 
2544
                     actconn, totalconn, global.req_count,
 
2545
#ifdef USE_OPENSSL
 
2546
                     global.maxsslconn, sslconns, totalsslconns,
 
2547
#endif
 
2548
                     global.maxpipes, pipes_used, pipes_free,
 
2549
                     read_freq_ctr(&global.conn_per_sec), global.cps_lim, global.cps_max,
 
2550
                     read_freq_ctr(&global.sess_per_sec), global.sps_lim, global.sps_max,
 
2551
#ifdef USE_OPENSSL
 
2552
                     ssl_sess_rate, global.ssl_lim, global.ssl_max,
 
2553
                     ssl_key_rate, global.ssl_fe_keys_max,
 
2554
                     ssl_reuse,
 
2555
                     read_freq_ctr(&global.ssl_be_keys_per_sec), global.ssl_be_keys_max,
 
2556
                     global.shctx_lookups, global.shctx_misses,
 
2557
#endif
 
2558
                     read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
 
2559
                     global.comp_rate_lim,
 
2560
#ifdef USE_ZLIB
 
2561
                     zlib_used_memory, global.maxzlibmem,
 
2562
#endif
 
2563
                     nb_tasks_cur, run_queue_cur, idle_pct,
 
2564
                     global.node, global.desc ? global.desc : ""
 
2565
                     );
 
2566
 
 
2567
        if (bi_putchk(si->ib, &trash) == -1)
 
2568
                return 0;
 
2569
 
 
2570
        return 1;
 
2571
}
 
2572
 
 
2573
/* This function dumps memory usage information onto the stream interface's
 
2574
 * read buffer. It returns 0 as long as it does not complete, non-zero upon
 
2575
 * completion. No state is used.
 
2576
 */
 
2577
static int stats_dump_pools_to_buffer(struct stream_interface *si)
 
2578
{
 
2579
        dump_pools_to_trash();
 
2580
        if (bi_putchk(si->ib, &trash) == -1)
 
2581
                return 0;
 
2582
        return 1;
 
2583
}
 
2584
 
 
2585
/* Dumps a frontend's line to the trash for the current proxy <px> and uses
 
2586
 * the state from stream interface <si>. The caller is responsible for clearing
 
2587
 * the trash if needed. Returns non-zero if it emits anything, zero otherwise.
 
2588
 */
 
2589
static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
 
2590
{
 
2591
        struct appctx *appctx = __objt_appctx(si->end);
 
2592
        int i;
 
2593
 
 
2594
        if (!(px->cap & PR_CAP_FE))
 
2595
                return 0;
 
2596
 
 
2597
        if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_FE)))
 
2598
                return 0;
 
2599
 
 
2600
        if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
2601
                chunk_appendf(&trash,
 
2602
                              /* name, queue */
 
2603
                              "<tr class=\"frontend\">");
 
2604
 
 
2605
                if (px->cap & PR_CAP_BE && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
 
2606
                        /* Column sub-heading for Enable or Disable server */
 
2607
                        chunk_appendf(&trash, "<td></td>");
 
2608
                }
 
2609
 
 
2610
                chunk_appendf(&trash,
 
2611
                              "<td class=ac>"
 
2612
                              "<a name=\"%s/Frontend\"></a>"
 
2613
                              "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
 
2614
                              "<td colspan=3></td>"
 
2615
                              "",
 
2616
                              px->id, px->id);
 
2617
 
 
2618
                chunk_appendf(&trash,
 
2619
                              /* sessions rate : current */
 
2620
                              "<td><u>%s<div class=tips><table class=det>"
 
2621
                              "<tr><th>Current connection rate:</th><td>%s/s</td></tr>"
 
2622
                              "<tr><th>Current session rate:</th><td>%s/s</td></tr>"
 
2623
                              "",
 
2624
                              U2H(read_freq_ctr(&px->fe_sess_per_sec)),
 
2625
                              U2H(read_freq_ctr(&px->fe_conn_per_sec)),
 
2626
                              U2H(read_freq_ctr(&px->fe_sess_per_sec)));
 
2627
 
 
2628
                if (px->mode == PR_MODE_HTTP)
 
2629
                        chunk_appendf(&trash,
 
2630
                                      "<tr><th>Current request rate:</th><td>%s/s</td></tr>",
 
2631
                                      U2H(read_freq_ctr(&px->fe_req_per_sec)));
 
2632
 
 
2633
                chunk_appendf(&trash,
 
2634
                              "</table></div></u></td>"
 
2635
                              /* sessions rate : max */
 
2636
                              "<td><u>%s<div class=tips><table class=det>"
 
2637
                              "<tr><th>Max connection rate:</th><td>%s/s</td></tr>"
 
2638
                              "<tr><th>Max session rate:</th><td>%s/s</td></tr>"
 
2639
                              "",
 
2640
                              U2H(px->fe_counters.sps_max),
 
2641
                              U2H(px->fe_counters.cps_max),
 
2642
                              U2H(px->fe_counters.sps_max));
 
2643
 
 
2644
                if (px->mode == PR_MODE_HTTP)
 
2645
                        chunk_appendf(&trash,
 
2646
                                      "<tr><th>Max request rate:</th><td>%s/s</td></tr>",
 
2647
                                      U2H(px->fe_counters.p.http.rps_max));
 
2648
 
 
2649
                chunk_appendf(&trash,
 
2650
                              "</table></div></u></td>"
 
2651
                              /* sessions rate : limit */
 
2652
                              "<td>%s</td>",
 
2653
                              LIM2A(px->fe_sps_lim, "-"));
 
2654
 
 
2655
                chunk_appendf(&trash,
 
2656
                              /* sessions: current, max, limit, total */
 
2657
                              "<td>%s</td><td>%s</td><td>%s</td>"
 
2658
                              "<td><u>%s<div class=tips><table class=det>"
 
2659
                              "<tr><th>Cum. connections:</th><td>%s</td></tr>"
 
2660
                              "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
 
2661
                              "",
 
2662
                              U2H(px->feconn), U2H(px->fe_counters.conn_max), U2H(px->maxconn),
 
2663
                              U2H(px->fe_counters.cum_sess),
 
2664
                              U2H(px->fe_counters.cum_conn),
 
2665
                              U2H(px->fe_counters.cum_sess));
 
2666
 
 
2667
                /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
2668
                if (px->mode == PR_MODE_HTTP) {
 
2669
                        chunk_appendf(&trash,
 
2670
                                      "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
 
2671
                                      "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
 
2672
                                      "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
 
2673
                                      "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
 
2674
                                      "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
 
2675
                                      "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
 
2676
                                      "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
 
2677
                                      "<tr><th>- other responses:</th><td>%s</td></tr>"
 
2678
                                      "<tr><th>Intercepted requests:</th><td>%s</td></tr>"
 
2679
                                      "",
 
2680
                                      U2H(px->fe_counters.p.http.cum_req),
 
2681
                                      U2H(px->fe_counters.p.http.rsp[1]),
 
2682
                                      U2H(px->fe_counters.p.http.rsp[2]),
 
2683
                                      U2H(px->fe_counters.p.http.comp_rsp),
 
2684
                                      px->fe_counters.p.http.rsp[2] ?
 
2685
                                      (int)(100*px->fe_counters.p.http.comp_rsp/px->fe_counters.p.http.rsp[2]) : 0,
 
2686
                                      U2H(px->fe_counters.p.http.rsp[3]),
 
2687
                                      U2H(px->fe_counters.p.http.rsp[4]),
 
2688
                                      U2H(px->fe_counters.p.http.rsp[5]),
 
2689
                                      U2H(px->fe_counters.p.http.rsp[0]),
 
2690
                                      U2H(px->fe_counters.intercepted_req));
 
2691
                }
 
2692
 
 
2693
                chunk_appendf(&trash,
 
2694
                              "</table></div></u></td>"
 
2695
                              /* sessions: lbtot, lastsess */
 
2696
                              "<td></td><td></td>"
 
2697
                              /* bytes : in */
 
2698
                              "<td>%s</td>"
 
2699
                              "",
 
2700
                              U2H(px->fe_counters.bytes_in));
 
2701
 
 
2702
                chunk_appendf(&trash,
 
2703
                              /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
 
2704
                              "<td>%s%s<div class=tips>compression: in=%lld out=%lld bypassed=%lld savings=%d%%</div>%s</td>",
 
2705
                              (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "<u>":"",
 
2706
                              U2H(px->fe_counters.bytes_out),
 
2707
                              px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp,
 
2708
                              px->fe_counters.comp_in ?
 
2709
                              (int)((px->fe_counters.comp_in - px->fe_counters.comp_out)*100/px->fe_counters.comp_in) : 0,
 
2710
                              (px->fe_counters.comp_in || px->fe_counters.comp_byp) ? "</u>":"");
 
2711
 
 
2712
                chunk_appendf(&trash,
 
2713
                              /* denied: req, resp */
 
2714
                              "<td>%s</td><td>%s</td>"
 
2715
                              /* errors : request, connect, response */
 
2716
                              "<td>%s</td><td></td><td></td>"
 
2717
                              /* warnings: retries, redispatches */
 
2718
                              "<td></td><td></td>"
 
2719
                              /* server status : reflect frontend status */
 
2720
                              "<td class=ac>%s</td>"
 
2721
                              /* rest of server: nothing */
 
2722
                              "<td class=ac colspan=8></td></tr>"
 
2723
                              "",
 
2724
                              U2H(px->fe_counters.denied_req), U2H(px->fe_counters.denied_resp),
 
2725
                              U2H(px->fe_counters.failed_req),
 
2726
                              px->state == PR_STREADY ? "OPEN" :
 
2727
                              px->state == PR_STFULL ? "FULL" : "STOP");
 
2728
        }
 
2729
        else { /* CSV mode */
 
2730
                chunk_appendf(&trash,
 
2731
                              /* pxid, name, queue cur, queue max, */
 
2732
                              "%s,FRONTEND,,,"
 
2733
                              /* sessions : current, max, limit, total */
 
2734
                              "%d,%d,%d,%lld,"
 
2735
                              /* bytes : in, out */
 
2736
                              "%lld,%lld,"
 
2737
                              /* denied: req, resp */
 
2738
                              "%lld,%lld,"
 
2739
                              /* errors : request, connect, response */
 
2740
                              "%lld,,,"
 
2741
                              /* warnings: retries, redispatches */
 
2742
                              ",,"
 
2743
                              /* server status : reflect frontend status */
 
2744
                              "%s,"
 
2745
                              /* rest of server: nothing */
 
2746
                              ",,,,,,,,"
 
2747
                              /* pid, iid, sid, throttle, lbtot, tracked, type */
 
2748
                              "%d,%d,0,,,,%d,"
 
2749
                              /* rate, rate_lim, rate_max */
 
2750
                              "%u,%u,%u,"
 
2751
                              /* check_status, check_code, check_duration */
 
2752
                              ",,,",
 
2753
                              px->id,
 
2754
                              px->feconn, px->fe_counters.conn_max, px->maxconn, px->fe_counters.cum_sess,
 
2755
                              px->fe_counters.bytes_in, px->fe_counters.bytes_out,
 
2756
                              px->fe_counters.denied_req, px->fe_counters.denied_resp,
 
2757
                              px->fe_counters.failed_req,
 
2758
                              px->state == PR_STREADY ? "OPEN" :
 
2759
                              px->state == PR_STFULL ? "FULL" : "STOP",
 
2760
                              relative_pid, px->uuid, STATS_TYPE_FE,
 
2761
                              read_freq_ctr(&px->fe_sess_per_sec),
 
2762
                              px->fe_sps_lim, px->fe_counters.sps_max);
 
2763
 
 
2764
                /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
2765
                if (px->mode == PR_MODE_HTTP) {
 
2766
                        for (i=1; i<6; i++)
 
2767
                                chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[i]);
 
2768
                        chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[0]);
 
2769
                }
 
2770
                else
 
2771
                        chunk_appendf(&trash, ",,,,,,");
 
2772
 
 
2773
                /* failed health analyses */
 
2774
                chunk_appendf(&trash, ",");
 
2775
 
 
2776
                /* requests : req_rate, req_rate_max, req_tot, */
 
2777
                chunk_appendf(&trash, "%u,%u,%lld,",
 
2778
                              read_freq_ctr(&px->fe_req_per_sec),
 
2779
                              px->fe_counters.p.http.rps_max, px->fe_counters.p.http.cum_req);
 
2780
 
 
2781
                /* errors: cli_aborts, srv_aborts */
 
2782
                chunk_appendf(&trash, ",,");
 
2783
 
 
2784
                /* compression: in, out, bypassed */
 
2785
                chunk_appendf(&trash, "%lld,%lld,%lld,",
 
2786
                              px->fe_counters.comp_in, px->fe_counters.comp_out, px->fe_counters.comp_byp);
 
2787
 
 
2788
                /* compression: comp_rsp */
 
2789
                chunk_appendf(&trash, "%lld,",
 
2790
                              px->fe_counters.p.http.comp_rsp);
 
2791
 
 
2792
                /* lastsess, last_chk, last_agt, qtime, ctime, rtime, ttime, */
 
2793
                chunk_appendf(&trash, ",,,,,,,");
 
2794
 
 
2795
                /* finish with EOL */
 
2796
                chunk_appendf(&trash, "\n");
 
2797
        }
 
2798
        return 1;
 
2799
}
 
2800
 
 
2801
/* Dumps a line for listener <l> and proxy <px> to the trash and uses the state
 
2802
 * from stream interface <si>, and stats flags <flags>. The caller is responsible
 
2803
 * for clearing the trash if needed. Returns non-zero if it emits anything, zero
 
2804
 * otherwise.
 
2805
 */
 
2806
static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, struct listener *l, int flags)
 
2807
{
 
2808
        struct appctx *appctx = __objt_appctx(si->end);
 
2809
 
 
2810
        if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
2811
                chunk_appendf(&trash, "<tr class=socket>");
 
2812
                if (px->cap & PR_CAP_BE && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
 
2813
                        /* Column sub-heading for Enable or Disable server */
 
2814
                        chunk_appendf(&trash, "<td></td>");
 
2815
                }
 
2816
                chunk_appendf(&trash,
 
2817
                              /* frontend name, listener name */
 
2818
                              "<td class=ac><a name=\"%s/+%s\"></a>%s"
 
2819
                              "<a class=lfsb href=\"#%s/+%s\">%s</a>"
 
2820
                              "",
 
2821
                              px->id, l->name,
 
2822
                              (flags & ST_SHLGNDS)?"<u>":"",
 
2823
                              px->id, l->name, l->name);
 
2824
 
 
2825
                if (flags & ST_SHLGNDS) {
 
2826
                        char str[INET6_ADDRSTRLEN];
 
2827
                        int port;
 
2828
 
 
2829
                        chunk_appendf(&trash, "<div class=tips>");
 
2830
 
 
2831
                        port = get_host_port(&l->addr);
 
2832
                        switch (addr_to_str(&l->addr, str, sizeof(str))) {
 
2833
                        case AF_INET:
 
2834
                                chunk_appendf(&trash, "IPv4: %s:%d, ", str, port);
 
2835
                                break;
 
2836
                        case AF_INET6:
 
2837
                                chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, port);
 
2838
                                break;
 
2839
                        case AF_UNIX:
 
2840
                                chunk_appendf(&trash, "unix, ");
 
2841
                                break;
 
2842
                        case -1:
 
2843
                                chunk_appendf(&trash, "(%s), ", strerror(errno));
 
2844
                                break;
 
2845
                        }
 
2846
 
 
2847
                        /* id */
 
2848
                        chunk_appendf(&trash, "id: %d</div>", l->luid);
 
2849
                }
 
2850
 
 
2851
                chunk_appendf(&trash,
 
2852
                              /* queue */
 
2853
                              "%s</td><td colspan=3></td>"
 
2854
                              /* sessions rate: current, max, limit */
 
2855
                              "<td colspan=3>&nbsp;</td>"
 
2856
                              /* sessions: current, max, limit, total, lbtot, lastsess */
 
2857
                              "<td>%s</td><td>%s</td><td>%s</td>"
 
2858
                              "<td>%s</td><td>&nbsp;</td><td>&nbsp;</td>"
 
2859
                              /* bytes: in, out */
 
2860
                              "<td>%s</td><td>%s</td>"
 
2861
                              "",
 
2862
                              (flags & ST_SHLGNDS)?"</u>":"",
 
2863
                              U2H(l->nbconn), U2H(l->counters->conn_max), U2H(l->maxconn),
 
2864
                              U2H(l->counters->cum_conn), U2H(l->counters->bytes_in), U2H(l->counters->bytes_out));
 
2865
 
 
2866
                chunk_appendf(&trash,
 
2867
                              /* denied: req, resp */
 
2868
                              "<td>%s</td><td>%s</td>"
 
2869
                              /* errors: request, connect, response */
 
2870
                              "<td>%s</td><td></td><td></td>"
 
2871
                              /* warnings: retries, redispatches */
 
2872
                              "<td></td><td></td>"
 
2873
                              /* server status: reflect listener status */
 
2874
                              "<td class=ac>%s</td>"
 
2875
                              /* rest of server: nothing */
 
2876
                              "<td class=ac colspan=8></td></tr>"
 
2877
                              "",
 
2878
                              U2H(l->counters->denied_req), U2H(l->counters->denied_resp),
 
2879
                              U2H(l->counters->failed_req),
 
2880
                              (l->nbconn < l->maxconn) ? (l->state == LI_LIMITED) ? "WAITING" : "OPEN" : "FULL");
 
2881
        }
 
2882
        else { /* CSV mode */
 
2883
                chunk_appendf(&trash,
 
2884
                              /* pxid, name, queue cur, queue max, */
 
2885
                              "%s,%s,,,"
 
2886
                              /* sessions: current, max, limit, total */
 
2887
                              "%d,%d,%d,%lld,"
 
2888
                              /* bytes: in, out */
 
2889
                              "%lld,%lld,"
 
2890
                              /* denied: req, resp */
 
2891
                              "%lld,%lld,"
 
2892
                              /* errors: request, connect, response */
 
2893
                              "%lld,,,"
 
2894
                              /* warnings: retries, redispatches */
 
2895
                              ",,"
 
2896
                              /* server status: reflect listener status */
 
2897
                              "%s,"
 
2898
                              /* rest of server: nothing */
 
2899
                              ",,,,,,,,"
 
2900
                              /* pid, iid, sid, throttle, lbtot, tracked, type */
 
2901
                              "%d,%d,%d,,,,%d,"
 
2902
                              /* rate, rate_lim, rate_max */
 
2903
                              ",,,"
 
2904
                              /* check_status, check_code, check_duration */
 
2905
                              ",,,"
 
2906
                              /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
2907
                              ",,,,,,"
 
2908
                              /* failed health analyses */
 
2909
                              ","
 
2910
                              /* requests : req_rate, req_rate_max, req_tot, */
 
2911
                              ",,,"
 
2912
                              /* errors: cli_aborts, srv_aborts */
 
2913
                              ",,"
 
2914
                              /* compression: in, out, bypassed, comp_rsp */
 
2915
                              ",,,,"
 
2916
                              /* lastsess, last_chk, last_agt, qtime, ctime, rtime, ttime, */
 
2917
                              ",,,,,,,"
 
2918
                              "\n",
 
2919
                              px->id, l->name,
 
2920
                              l->nbconn, l->counters->conn_max,
 
2921
                              l->maxconn, l->counters->cum_conn,
 
2922
                              l->counters->bytes_in, l->counters->bytes_out,
 
2923
                              l->counters->denied_req, l->counters->denied_resp,
 
2924
                              l->counters->failed_req,
 
2925
                              (l->nbconn < l->maxconn) ? "OPEN" : "FULL",
 
2926
                              relative_pid, px->uuid, l->luid, STATS_TYPE_SO);
 
2927
        }
 
2928
        return 1;
 
2929
}
 
2930
 
 
2931
/* Dumps a line for server <sv> and proxy <px> to the trash and uses the state
 
2932
 * from stream interface <si>, stats flags <flags>, and server state <state>.
 
2933
 * The caller is responsible for clearing the trash if needed. Returns non-zero
 
2934
 * if it emits anything, zero otherwise. The <state> parameter can take the
 
2935
 * following values : 0=DOWN, 1=DOWN(agent) 2=going up, 3=going down, 4=UP, 5,6=NOLB,
 
2936
 * 7,8=DRAIN, 9=unchecked.
 
2937
 */
 
2938
static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, int flags, struct server *sv, int state)
 
2939
{
 
2940
        struct appctx *appctx = __objt_appctx(si->end);
 
2941
        struct server *via, *ref;
 
2942
        char str[INET6_ADDRSTRLEN];
 
2943
        struct chunk src;
 
2944
        int i;
 
2945
 
 
2946
        /* we have "via" which is the tracked server as described in the configuration,
 
2947
         * and "ref" which is the checked server and the end of the chain.
 
2948
         */
 
2949
        via = sv->track ? sv->track : sv;
 
2950
        ref = via;
 
2951
        while (ref->track)
 
2952
                ref = ref->track;
 
2953
 
 
2954
        if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
2955
                static char *srv_hlt_st[10] = {
 
2956
                        "DOWN",
 
2957
                        "DOWN (agent)",
 
2958
                        "DN %d/%d &uarr;",
 
2959
                        "UP %d/%d &darr;",
 
2960
                        "UP",
 
2961
                        "NOLB %d/%d &darr;",
 
2962
                        "NOLB",
 
2963
                        "DRAIN %d/%d &darr;",
 
2964
                        "DRAIN",
 
2965
                        "<i>no check</i>"
 
2966
                };
 
2967
 
 
2968
                if (sv->admin & SRV_ADMF_MAINT)
 
2969
                        chunk_appendf(&trash, "<tr class=\"maintain\">");
 
2970
                else
 
2971
                        chunk_appendf(&trash,
 
2972
                                      "<tr class=\"%s%d\">",
 
2973
                                      (sv->flags & SRV_F_BACKUP) ? "backup" : "active", state);
 
2974
 
 
2975
                if ((px->cap & PR_CAP_BE) && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN))
 
2976
                        chunk_appendf(&trash,
 
2977
                                      "<td><input type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
 
2978
                                      sv->id);
 
2979
 
 
2980
                chunk_appendf(&trash,
 
2981
                              "<td class=ac><a name=\"%s/%s\"></a>%s"
 
2982
                              "<a class=lfsb href=\"#%s/%s\">%s</a>"
 
2983
                              "",
 
2984
                              px->id, sv->id,
 
2985
                              (flags & ST_SHLGNDS) ? "<u>" : "",
 
2986
                              px->id, sv->id, sv->id);
 
2987
 
 
2988
                if (flags & ST_SHLGNDS) {
 
2989
                        chunk_appendf(&trash, "<div class=tips>");
 
2990
 
 
2991
                        switch (addr_to_str(&sv->addr, str, sizeof(str))) {
 
2992
                        case AF_INET:
 
2993
                                chunk_appendf(&trash, "IPv4: %s:%d, ", str, get_host_port(&sv->addr));
 
2994
                                break;
 
2995
                        case AF_INET6:
 
2996
                                chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, get_host_port(&sv->addr));
 
2997
                                break;
 
2998
                        case AF_UNIX:
 
2999
                                chunk_appendf(&trash, "unix, ");
 
3000
                                break;
 
3001
                        case -1:
 
3002
                                chunk_appendf(&trash, "(%s), ", strerror(errno));
 
3003
                                break;
 
3004
                        default: /* address family not supported */
 
3005
                                break;
 
3006
                        }
 
3007
 
 
3008
                        /* id */
 
3009
                        chunk_appendf(&trash, "id: %d", sv->puid);
 
3010
 
 
3011
                        /* cookie */
 
3012
                        if (sv->cookie) {
 
3013
                                chunk_appendf(&trash, ", cookie: '");
 
3014
 
 
3015
                                chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
 
3016
                                chunk_htmlencode(&trash, &src);
 
3017
 
 
3018
                                chunk_appendf(&trash, "'");
 
3019
                        }
 
3020
 
 
3021
                        chunk_appendf(&trash, "</div>");
 
3022
                }
 
3023
 
 
3024
                chunk_appendf(&trash,
 
3025
                              /* queue : current, max, limit */
 
3026
                              "%s</td><td>%s</td><td>%s</td><td>%s</td>"
 
3027
                              /* sessions rate : current, max, limit */
 
3028
                              "<td>%s</td><td>%s</td><td></td>"
 
3029
                              "",
 
3030
                              (flags & ST_SHLGNDS) ? "</u>" : "",
 
3031
                              U2H(sv->nbpend), U2H(sv->counters.nbpend_max), LIM2A(sv->maxqueue, "-"),
 
3032
                              U2H(read_freq_ctr(&sv->sess_per_sec)), U2H(sv->counters.sps_max));
 
3033
 
 
3034
 
 
3035
                chunk_appendf(&trash,
 
3036
                              /* sessions: current, max, limit, total */
 
3037
                              "<td>%s</td><td>%s</td><td>%s</td>"
 
3038
                              "<td><u>%s<div class=tips><table class=det>"
 
3039
                              "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
 
3040
                              "",
 
3041
                              U2H(sv->cur_sess), U2H(sv->counters.cur_sess_max), LIM2A(sv->maxconn, "-"),
 
3042
                              U2H(sv->counters.cum_sess),
 
3043
                              U2H(sv->counters.cum_sess));
 
3044
 
 
3045
                /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
3046
                if (px->mode == PR_MODE_HTTP) {
 
3047
                        unsigned long long tot;
 
3048
                        for (tot = i = 0; i < 6; i++)
 
3049
                                tot += sv->counters.p.http.rsp[i];
 
3050
 
 
3051
                        chunk_appendf(&trash,
 
3052
                                      "<tr><th>Cum. HTTP responses:</th><td>%s</td></tr>"
 
3053
                                      "<tr><th>- HTTP 1xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3054
                                      "<tr><th>- HTTP 2xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3055
                                      "<tr><th>- HTTP 3xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3056
                                      "<tr><th>- HTTP 4xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3057
                                      "<tr><th>- HTTP 5xx responses:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3058
                                      "<tr><th>- other responses:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3059
                                      "",
 
3060
                                      U2H(tot),
 
3061
                                      U2H(sv->counters.p.http.rsp[1]), tot ? (int)(100*sv->counters.p.http.rsp[1] / tot) : 0,
 
3062
                                      U2H(sv->counters.p.http.rsp[2]), tot ? (int)(100*sv->counters.p.http.rsp[2] / tot) : 0,
 
3063
                                      U2H(sv->counters.p.http.rsp[3]), tot ? (int)(100*sv->counters.p.http.rsp[3] / tot) : 0,
 
3064
                                      U2H(sv->counters.p.http.rsp[4]), tot ? (int)(100*sv->counters.p.http.rsp[4] / tot) : 0,
 
3065
                                      U2H(sv->counters.p.http.rsp[5]), tot ? (int)(100*sv->counters.p.http.rsp[5] / tot) : 0,
 
3066
                                      U2H(sv->counters.p.http.rsp[0]), tot ? (int)(100*sv->counters.p.http.rsp[0] / tot) : 0);
 
3067
                }
 
3068
 
 
3069
                chunk_appendf(&trash, "<tr><th colspan=3>Avg over last 1024 success. conn.</th></tr>");
 
3070
                chunk_appendf(&trash, "<tr><th>- Queue time:</th><td>%s</td><td>ms</td></tr>",   U2H(swrate_avg(sv->counters.q_time, TIME_STATS_SAMPLES)));
 
3071
                chunk_appendf(&trash, "<tr><th>- Connect time:</th><td>%s</td><td>ms</td></tr>", U2H(swrate_avg(sv->counters.c_time, TIME_STATS_SAMPLES)));
 
3072
                if (px->mode == PR_MODE_HTTP)
 
3073
                        chunk_appendf(&trash, "<tr><th>- Response time:</th><td>%s</td><td>ms</td></tr>", U2H(swrate_avg(sv->counters.d_time, TIME_STATS_SAMPLES)));
 
3074
                chunk_appendf(&trash, "<tr><th>- Total time:</th><td>%s</td><td>ms</td></tr>",   U2H(swrate_avg(sv->counters.t_time, TIME_STATS_SAMPLES)));
 
3075
 
 
3076
                chunk_appendf(&trash,
 
3077
                              "</table></div></u></td>"
 
3078
                              /* sessions: lbtot, last */
 
3079
                              "<td>%s</td><td>%s</td>",
 
3080
                              U2H(sv->counters.cum_lbconn),
 
3081
                              human_time(srv_lastsession(sv), 1));
 
3082
 
 
3083
                chunk_appendf(&trash,
 
3084
                              /* bytes : in, out */
 
3085
                              "<td>%s</td><td>%s</td>"
 
3086
                              /* denied: req, resp */
 
3087
                              "<td></td><td>%s</td>"
 
3088
                              /* errors : request, connect */
 
3089
                              "<td></td><td>%s</td>"
 
3090
                              /* errors : response */
 
3091
                              "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
 
3092
                              /* warnings: retries, redispatches */
 
3093
                              "<td>%lld</td><td>%lld</td>"
 
3094
                              "",
 
3095
                              U2H(sv->counters.bytes_in), U2H(sv->counters.bytes_out),
 
3096
                              U2H(sv->counters.failed_secu),
 
3097
                              U2H(sv->counters.failed_conns),
 
3098
                              U2H(sv->counters.failed_resp),
 
3099
                              sv->counters.cli_aborts,
 
3100
                              sv->counters.srv_aborts,
 
3101
                              sv->counters.retries, sv->counters.redispatches);
 
3102
 
 
3103
                /* status, lest check */
 
3104
                chunk_appendf(&trash, "<td class=ac>");
 
3105
 
 
3106
                if (sv->admin & SRV_ADMF_MAINT) {
 
3107
                        chunk_appendf(&trash, "%s ", human_time(now.tv_sec - sv->last_change, 1));
 
3108
                        chunk_appendf(&trash, "MAINT");
 
3109
                }
 
3110
                else if ((ref->agent.state & CHK_ST_ENABLED) && (ref->state == SRV_ST_STOPPED)) {
 
3111
                        chunk_appendf(&trash, "%s ", human_time(now.tv_sec - ref->last_change, 1));
 
3112
                        /* DOWN (agent) */
 
3113
                        chunk_appendf(&trash, srv_hlt_st[1], "GCC: your -Werror=format-security is bogus, annoying, and hides real bugs, I don't thank you, really!");
 
3114
                }
 
3115
                else if (ref->check.state & CHK_ST_ENABLED) {
 
3116
                        chunk_appendf(&trash, "%s ", human_time(now.tv_sec - ref->last_change, 1));
 
3117
                        chunk_appendf(&trash,
 
3118
                                      srv_hlt_st[state],
 
3119
                                      (ref->state != SRV_ST_STOPPED) ? (ref->check.health - ref->check.rise + 1) : (ref->check.health),
 
3120
                                      (ref->state != SRV_ST_STOPPED) ? (ref->check.fall) : (ref->check.rise));
 
3121
                }
 
3122
 
 
3123
                if ((sv->state == SRV_ST_STOPPED) &&
 
3124
                    ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) && !(sv->agent.health)) {
 
3125
                        chunk_appendf(&trash,
 
3126
                                      "</td><td class=ac><u> %s%s",
 
3127
                                      (sv->agent.state & CHK_ST_INPROGRESS) ? "* " : "",
 
3128
                                      get_check_status_info(sv->agent.status));
 
3129
 
 
3130
                        if (sv->agent.status >= HCHK_STATUS_L57DATA)
 
3131
                                chunk_appendf(&trash, "/%d", sv->agent.code);
 
3132
 
 
3133
                        if (sv->agent.status >= HCHK_STATUS_CHECKED && sv->agent.duration >= 0)
 
3134
                                chunk_appendf(&trash, " in %lums", sv->agent.duration);
 
3135
 
 
3136
                        chunk_appendf(&trash, "<div class=tips>%s",
 
3137
                                      get_check_status_description(sv->agent.status));
 
3138
                        if (*sv->agent.desc) {
 
3139
                                chunk_appendf(&trash, ": ");
 
3140
                                chunk_initlen(&src, sv->agent.desc, 0, strlen(sv->agent.desc));
 
3141
                                chunk_htmlencode(&trash, &src);
 
3142
                        }
 
3143
                        chunk_appendf(&trash, "</div></u>");
 
3144
                }
 
3145
                else if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) {
 
3146
                        chunk_appendf(&trash,
 
3147
                                      "</td><td class=ac><u> %s%s",
 
3148
                                      (sv->check.state & CHK_ST_INPROGRESS) ? "* " : "",
 
3149
                                      get_check_status_info(sv->check.status));
 
3150
 
 
3151
                        if (sv->check.status >= HCHK_STATUS_L57DATA)
 
3152
                                chunk_appendf(&trash, "/%d", sv->check.code);
 
3153
 
 
3154
                        if (sv->check.status >= HCHK_STATUS_CHECKED && sv->check.duration >= 0)
 
3155
                                chunk_appendf(&trash, " in %lums", sv->check.duration);
 
3156
 
 
3157
                        chunk_appendf(&trash, "<div class=tips>%s",
 
3158
                                      get_check_status_description(sv->check.status));
 
3159
                        if (*sv->check.desc) {
 
3160
                                chunk_appendf(&trash, ": ");
 
3161
                                chunk_initlen(&src, sv->check.desc, 0, strlen(sv->check.desc));
 
3162
                                chunk_htmlencode(&trash, &src);
 
3163
                        }
 
3164
                        chunk_appendf(&trash, "</div></u>");
 
3165
                }
 
3166
                else
 
3167
                        chunk_appendf(&trash, "</td><td>");
 
3168
 
 
3169
                chunk_appendf(&trash,
 
3170
                              /* weight */
 
3171
                              "</td><td class=ac>%d</td>"
 
3172
                              /* act, bck */
 
3173
                              "<td class=ac>%s</td><td class=ac>%s</td>"
 
3174
                              "",
 
3175
                              (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
3176
                              (sv->flags & SRV_F_BACKUP) ? "-" : "Y",
 
3177
                              (sv->flags & SRV_F_BACKUP) ? "Y" : "-");
 
3178
 
 
3179
                /* check failures: unique, fatal, down time */
 
3180
                if (sv->check.state & CHK_ST_ENABLED) {
 
3181
                        chunk_appendf(&trash, "<td><u>%lld", ref->counters.failed_checks);
 
3182
 
 
3183
                        if (ref->observe)
 
3184
                                chunk_appendf(&trash, "/%lld", ref->counters.failed_hana);
 
3185
 
 
3186
                        chunk_appendf(&trash,
 
3187
                                      "<div class=tips>Failed Health Checks%s</div></u></td>"
 
3188
                                      "<td>%lld</td><td>%s</td>"
 
3189
                                      "",
 
3190
                                      ref->observe ? "/Health Analyses" : "",
 
3191
                                      ref->counters.down_trans, human_time(srv_downtime(sv), 1));
 
3192
                }
 
3193
                else if (!(sv->admin & SRV_ADMF_FMAINT) && sv != ref) {
 
3194
                        /* tracking a server */
 
3195
                        chunk_appendf(&trash,
 
3196
                                      "<td class=ac colspan=3><a class=lfsb href=\"#%s/%s\">via %s/%s</a></td>",
 
3197
                                      via->proxy->id, via->id, via->proxy->id, via->id);
 
3198
                }
 
3199
                else
 
3200
                        chunk_appendf(&trash, "<td colspan=3></td>");
 
3201
 
 
3202
                /* throttle */
 
3203
                if (sv->state == SRV_ST_STARTING && !server_is_draining(sv))
 
3204
                        chunk_appendf(&trash, "<td class=ac>%d %%</td></tr>\n", server_throttle_rate(sv));
 
3205
                else
 
3206
                        chunk_appendf(&trash, "<td class=ac>-</td></tr>\n");
 
3207
        }
 
3208
        else { /* CSV mode */
 
3209
                static char *srv_hlt_st[10] = {
 
3210
                        "DOWN,",
 
3211
                        "DOWN (agent),",
 
3212
                        "DOWN %d/%d,",
 
3213
                        "UP %d/%d,",
 
3214
                        "UP,",
 
3215
                        "NOLB %d/%d,",
 
3216
                        "NOLB,",
 
3217
                        "DRAIN %d/%d,",
 
3218
                        "DRAIN,",
 
3219
                        "no check,"
 
3220
                };
 
3221
 
 
3222
                chunk_appendf(&trash,
 
3223
                              /* pxid, name */
 
3224
                              "%s,%s,"
 
3225
                              /* queue : current, max */
 
3226
                              "%d,%d,"
 
3227
                              /* sessions : current, max, limit, total */
 
3228
                              "%d,%d,%s,%lld,"
 
3229
                              /* bytes : in, out */
 
3230
                              "%lld,%lld,"
 
3231
                              /* denied: req, resp */
 
3232
                              ",%lld,"
 
3233
                              /* errors : request, connect, response */
 
3234
                              ",%lld,%lld,"
 
3235
                              /* warnings: retries, redispatches */
 
3236
                              "%lld,%lld,"
 
3237
                              "",
 
3238
                              px->id, sv->id,
 
3239
                              sv->nbpend, sv->counters.nbpend_max,
 
3240
                              sv->cur_sess, sv->counters.cur_sess_max, LIM2A(sv->maxconn, ""), sv->counters.cum_sess,
 
3241
                              sv->counters.bytes_in, sv->counters.bytes_out,
 
3242
                              sv->counters.failed_secu,
 
3243
                              sv->counters.failed_conns, sv->counters.failed_resp,
 
3244
                              sv->counters.retries, sv->counters.redispatches);
 
3245
 
 
3246
                /* status */
 
3247
                if (sv->admin & SRV_ADMF_IMAINT)
 
3248
                        chunk_appendf(&trash, "MAINT (via %s/%s),", via->proxy->id, via->id);
 
3249
                else if (sv->admin & SRV_ADMF_MAINT)
 
3250
                        chunk_appendf(&trash, "MAINT,");
 
3251
                else
 
3252
                        chunk_appendf(&trash,
 
3253
                                      srv_hlt_st[state],
 
3254
                                      (ref->state != SRV_ST_STOPPED) ? (ref->check.health - ref->check.rise + 1) : (ref->check.health),
 
3255
                                      (ref->state != SRV_ST_STOPPED) ? (ref->check.fall) : (ref->check.rise));
 
3256
 
 
3257
                chunk_appendf(&trash,
 
3258
                              /* weight, active, backup */
 
3259
                              "%d,%d,%d,"
 
3260
                              "",
 
3261
                              (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
3262
                              (sv->flags & SRV_F_BACKUP) ? 0 : 1,
 
3263
                              (sv->flags & SRV_F_BACKUP) ? 1 : 0);
 
3264
 
 
3265
                /* check failures: unique, fatal; last change, total downtime */
 
3266
                if (sv->check.state & CHK_ST_ENABLED)
 
3267
                        chunk_appendf(&trash,
 
3268
                                      "%lld,%lld,%d,%d,",
 
3269
                                      sv->counters.failed_checks, sv->counters.down_trans,
 
3270
                                      (int)(now.tv_sec - sv->last_change), srv_downtime(sv));
 
3271
                else
 
3272
                        chunk_appendf(&trash, ",,,,");
 
3273
 
 
3274
                /* queue limit, pid, iid, sid, */
 
3275
                chunk_appendf(&trash,
 
3276
                              "%s,"
 
3277
                              "%d,%d,%d,",
 
3278
                              LIM2A(sv->maxqueue, ""),
 
3279
                              relative_pid, px->uuid, sv->puid);
 
3280
 
 
3281
                /* throttle */
 
3282
                if (sv->state == SRV_ST_STARTING && !server_is_draining(sv))
 
3283
                        chunk_appendf(&trash, "%d", server_throttle_rate(sv));
 
3284
 
 
3285
                /* sessions: lbtot */
 
3286
                chunk_appendf(&trash, ",%lld,", sv->counters.cum_lbconn);
 
3287
 
 
3288
                /* tracked */
 
3289
                if (sv->track)
 
3290
                        chunk_appendf(&trash, "%s/%s,",
 
3291
                                      sv->track->proxy->id, sv->track->id);
 
3292
                else
 
3293
                        chunk_appendf(&trash, ",");
 
3294
 
 
3295
                /* type */
 
3296
                chunk_appendf(&trash, "%d,", STATS_TYPE_SV);
 
3297
 
 
3298
                /* rate */
 
3299
                chunk_appendf(&trash, "%u,,%u,",
 
3300
                              read_freq_ctr(&sv->sess_per_sec),
 
3301
                              sv->counters.sps_max);
 
3302
 
 
3303
                if (sv->check.state & CHK_ST_ENABLED) {
 
3304
                        /* check_status */
 
3305
                        chunk_appendf(&trash, "%s,", get_check_status_info(sv->check.status));
 
3306
 
 
3307
                        /* check_code */
 
3308
                        if (sv->check.status >= HCHK_STATUS_L57DATA)
 
3309
                                chunk_appendf(&trash, "%u,", sv->check.code);
 
3310
                        else
 
3311
                                chunk_appendf(&trash, ",");
 
3312
 
 
3313
                        /* check_duration */
 
3314
                        if (sv->check.status >= HCHK_STATUS_CHECKED)
 
3315
                                chunk_appendf(&trash, "%lu,", sv->check.duration);
 
3316
                        else
 
3317
                                chunk_appendf(&trash, ",");
 
3318
 
 
3319
                }
 
3320
                else
 
3321
                        chunk_appendf(&trash, ",,,");
 
3322
 
 
3323
                /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
3324
                if (px->mode == PR_MODE_HTTP) {
 
3325
                        for (i=1; i<6; i++)
 
3326
                                chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[i]);
 
3327
 
 
3328
                        chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[0]);
 
3329
                }
 
3330
                else
 
3331
                        chunk_appendf(&trash, ",,,,,,");
 
3332
 
 
3333
                /* failed health analyses */
 
3334
                chunk_appendf(&trash, "%lld,",  sv->counters.failed_hana);
 
3335
 
 
3336
                /* requests : req_rate, req_rate_max, req_tot, */
 
3337
                chunk_appendf(&trash, ",,,");
 
3338
 
 
3339
                /* errors: cli_aborts, srv_aborts */
 
3340
                chunk_appendf(&trash, "%lld,%lld,",
 
3341
                              sv->counters.cli_aborts, sv->counters.srv_aborts);
 
3342
 
 
3343
                /* compression: in, out, bypassed, comp_rsp */
 
3344
                chunk_appendf(&trash, ",,,,");
 
3345
 
 
3346
                /* lastsess */
 
3347
                chunk_appendf(&trash, "%d,", srv_lastsession(sv));
 
3348
 
 
3349
                /* capture of last check and agent statuses */
 
3350
                chunk_appendf(&trash, "%s,", ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) ? cstr(sv->check.desc) : "");
 
3351
                chunk_appendf(&trash, "%s,", ((sv->agent.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) ? cstr(sv->agent.desc) : "");
 
3352
 
 
3353
                /* qtime, ctime, rtime, ttime, */
 
3354
                chunk_appendf(&trash, "%u,%u,%u,%u,",
 
3355
                              swrate_avg(sv->counters.q_time, TIME_STATS_SAMPLES),
 
3356
                              swrate_avg(sv->counters.c_time, TIME_STATS_SAMPLES),
 
3357
                              swrate_avg(sv->counters.d_time, TIME_STATS_SAMPLES),
 
3358
                              swrate_avg(sv->counters.t_time, TIME_STATS_SAMPLES));
 
3359
 
 
3360
                /* finish with EOL */
 
3361
                chunk_appendf(&trash, "\n");
 
3362
        }
 
3363
        return 1;
 
3364
}
 
3365
 
 
3366
/* Dumps a line for backend <px> to the trash for and uses the state from stream
 
3367
 * interface <si> and stats flags <flags>. The caller is responsible for clearing
 
3368
 * the trash if needed. Returns non-zero if it emits anything, zero otherwise.
 
3369
 */
 
3370
static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, int flags)
 
3371
{
 
3372
        struct appctx *appctx = __objt_appctx(si->end);
 
3373
        struct chunk src;
 
3374
        int i;
 
3375
 
 
3376
        if (!(px->cap & PR_CAP_BE))
 
3377
                return 0;
 
3378
 
 
3379
        if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_BE)))
 
3380
                return 0;
 
3381
 
 
3382
        if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
3383
                chunk_appendf(&trash, "<tr class=\"backend\">");
 
3384
                if (px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
 
3385
                        /* Column sub-heading for Enable or Disable server */
 
3386
                        chunk_appendf(&trash, "<td></td>");
 
3387
                }
 
3388
                chunk_appendf(&trash,
 
3389
                              "<td class=ac>"
 
3390
                              /* name */
 
3391
                              "%s<a name=\"%s/Backend\"></a>"
 
3392
                              "<a class=lfsb href=\"#%s/Backend\">Backend</a>"
 
3393
                              "",
 
3394
                              (flags & ST_SHLGNDS)?"<u>":"",
 
3395
                              px->id, px->id);
 
3396
 
 
3397
                if (flags & ST_SHLGNDS) {
 
3398
                        /* balancing */
 
3399
                        chunk_appendf(&trash, "<div class=tips>balancing: %s",
 
3400
                                      backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
 
3401
 
 
3402
                        /* cookie */
 
3403
                        if (px->cookie_name) {
 
3404
                                chunk_appendf(&trash, ", cookie: '");
 
3405
                                chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
 
3406
                                chunk_htmlencode(&trash, &src);
 
3407
                                chunk_appendf(&trash, "'");
 
3408
                        }
 
3409
                        chunk_appendf(&trash, "</div>");
 
3410
                }
 
3411
 
 
3412
                chunk_appendf(&trash,
 
3413
                              "%s</td>"
 
3414
                              /* queue : current, max */
 
3415
                              "<td>%s</td><td>%s</td><td></td>"
 
3416
                              /* sessions rate : current, max, limit */
 
3417
                              "<td>%s</td><td>%s</td><td></td>"
 
3418
                              "",
 
3419
                              (flags & ST_SHLGNDS)?"</u>":"",
 
3420
                              U2H(px->nbpend) /* or px->totpend ? */, U2H(px->be_counters.nbpend_max),
 
3421
                              U2H(read_freq_ctr(&px->be_sess_per_sec)), U2H(px->be_counters.sps_max));
 
3422
 
 
3423
                chunk_appendf(&trash,
 
3424
                              /* sessions: current, max, limit, total */
 
3425
                              "<td>%s</td><td>%s</td><td>%s</td>"
 
3426
                              "<td><u>%s<div class=tips><table class=det>"
 
3427
                              "<tr><th>Cum. sessions:</th><td>%s</td></tr>"
 
3428
                              "",
 
3429
                              U2H(px->beconn), U2H(px->be_counters.conn_max), U2H(px->fullconn),
 
3430
                              U2H(px->be_counters.cum_conn),
 
3431
                              U2H(px->be_counters.cum_conn));
 
3432
 
 
3433
                /* http response (via hover): 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
3434
                if (px->mode == PR_MODE_HTTP) {
 
3435
                        chunk_appendf(&trash,
 
3436
                                      "<tr><th>Cum. HTTP requests:</th><td>%s</td></tr>"
 
3437
                                      "<tr><th>- HTTP 1xx responses:</th><td>%s</td></tr>"
 
3438
                                      "<tr><th>- HTTP 2xx responses:</th><td>%s</td></tr>"
 
3439
                                      "<tr><th>&nbsp;&nbsp;Compressed 2xx:</th><td>%s</td><td>(%d%%)</td></tr>"
 
3440
                                      "<tr><th>- HTTP 3xx responses:</th><td>%s</td></tr>"
 
3441
                                      "<tr><th>- HTTP 4xx responses:</th><td>%s</td></tr>"
 
3442
                                      "<tr><th>- HTTP 5xx responses:</th><td>%s</td></tr>"
 
3443
                                      "<tr><th>- other responses:</th><td>%s</td></tr>"
 
3444
                                      "<tr><th>Intercepted requests:</th><td>%s</td></tr>"
 
3445
                                      "<tr><th colspan=3>Avg over last 1024 success. conn.</th></tr>"
 
3446
                                      "",
 
3447
                                      U2H(px->be_counters.p.http.cum_req),
 
3448
                                      U2H(px->be_counters.p.http.rsp[1]),
 
3449
                                      U2H(px->be_counters.p.http.rsp[2]),
 
3450
                                      U2H(px->be_counters.p.http.comp_rsp),
 
3451
                                      px->be_counters.p.http.rsp[2] ?
 
3452
                                      (int)(100*px->be_counters.p.http.comp_rsp/px->be_counters.p.http.rsp[2]) : 0,
 
3453
                                      U2H(px->be_counters.p.http.rsp[3]),
 
3454
                                      U2H(px->be_counters.p.http.rsp[4]),
 
3455
                                      U2H(px->be_counters.p.http.rsp[5]),
 
3456
                                      U2H(px->be_counters.p.http.rsp[0]),
 
3457
                                      U2H(px->be_counters.intercepted_req));
 
3458
                }
 
3459
 
 
3460
                chunk_appendf(&trash, "<tr><th>- Queue time:</th><td>%s</td><td>ms</td></tr>",   U2H(swrate_avg(px->be_counters.q_time, TIME_STATS_SAMPLES)));
 
3461
                chunk_appendf(&trash, "<tr><th>- Connect time:</th><td>%s</td><td>ms</td></tr>", U2H(swrate_avg(px->be_counters.c_time, TIME_STATS_SAMPLES)));
 
3462
                if (px->mode == PR_MODE_HTTP)
 
3463
                        chunk_appendf(&trash, "<tr><th>- Response time:</th><td>%s</td><td>ms</td></tr>", U2H(swrate_avg(px->be_counters.d_time, TIME_STATS_SAMPLES)));
 
3464
                chunk_appendf(&trash, "<tr><th>- Total time:</th><td>%s</td><td>ms</td></tr>",   U2H(swrate_avg(px->be_counters.t_time, TIME_STATS_SAMPLES)));
 
3465
 
 
3466
                chunk_appendf(&trash,
 
3467
                              "</table></div></u></td>"
 
3468
                              /* sessions: lbtot, last */
 
3469
                              "<td>%s</td><td>%s</td>"
 
3470
                              /* bytes: in */
 
3471
                              "<td>%s</td>"
 
3472
                              "",
 
3473
                              U2H(px->be_counters.cum_lbconn),
 
3474
                              human_time(be_lastsession(px), 1),
 
3475
                              U2H(px->be_counters.bytes_in));
 
3476
 
 
3477
                chunk_appendf(&trash,
 
3478
                              /* bytes:out + compression stats (via hover): comp_in, comp_out, comp_byp */
 
3479
                              "<td>%s%s<div class=tips>compression: in=%lld out=%lld bypassed=%lld savings=%d%%</div>%s</td>",
 
3480
                              (px->be_counters.comp_in || px->be_counters.comp_byp) ? "<u>":"",
 
3481
                              U2H(px->be_counters.bytes_out),
 
3482
                              px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp,
 
3483
                              px->be_counters.comp_in ?
 
3484
                              (int)((px->be_counters.comp_in - px->be_counters.comp_out)*100/px->be_counters.comp_in) : 0,
 
3485
                              (px->be_counters.comp_in || px->be_counters.comp_byp) ? "</u>":"");
 
3486
 
 
3487
                chunk_appendf(&trash,
 
3488
                              /* denied: req, resp */
 
3489
                              "<td>%s</td><td>%s</td>"
 
3490
                              /* errors : request, connect */
 
3491
                              "<td></td><td>%s</td>"
 
3492
                              /* errors : response */
 
3493
                              "<td><u>%s<div class=tips>Connection resets during transfers: %lld client, %lld server</div></u></td>"
 
3494
                              /* warnings: retries, redispatches */
 
3495
                              "<td>%lld</td><td>%lld</td>"
 
3496
                              /* backend status: reflect backend status (up/down): we display UP
 
3497
                               * if the backend has known working servers or if it has no server at
 
3498
                               * all (eg: for stats). Then we display the total weight, number of
 
3499
                               * active and backups. */
 
3500
                              "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d</td>"
 
3501
                              "<td class=ac>%d</td><td class=ac>%d</td>"
 
3502
                              "",
 
3503
                              U2H(px->be_counters.denied_req), U2H(px->be_counters.denied_resp),
 
3504
                              U2H(px->be_counters.failed_conns),
 
3505
                              U2H(px->be_counters.failed_resp),
 
3506
                              px->be_counters.cli_aborts,
 
3507
                              px->be_counters.srv_aborts,
 
3508
                              px->be_counters.retries, px->be_counters.redispatches,
 
3509
                              human_time(now.tv_sec - px->last_change, 1),
 
3510
                              (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" :
 
3511
                              "<font color=\"red\"><b>DOWN</b></font>",
 
3512
                              (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
3513
                              px->srv_act, px->srv_bck);
 
3514
 
 
3515
                chunk_appendf(&trash,
 
3516
                              /* rest of backend: nothing, down transitions, total downtime, throttle */
 
3517
                              "<td class=ac>&nbsp;</td><td>%d</td>"
 
3518
                              "<td>%s</td>"
 
3519
                              "<td></td>"
 
3520
                              "</tr>",
 
3521
                              px->down_trans,
 
3522
                              px->srv?human_time(be_downtime(px), 1):"&nbsp;");
 
3523
        }
 
3524
        else { /* CSV mode */
 
3525
                chunk_appendf(&trash,
 
3526
                              /* pxid, name */
 
3527
                              "%s,BACKEND,"
 
3528
                              /* queue : current, max */
 
3529
                              "%d,%d,"
 
3530
                              /* sessions : current, max, limit, total */
 
3531
                              "%d,%d,%d,%lld,"
 
3532
                              /* bytes : in, out */
 
3533
                              "%lld,%lld,"
 
3534
                              /* denied: req, resp */
 
3535
                              "%lld,%lld,"
 
3536
                              /* errors : request, connect, response */
 
3537
                              ",%lld,%lld,"
 
3538
                              /* warnings: retries, redispatches */
 
3539
                              "%lld,%lld,"
 
3540
                              /* backend status: reflect backend status (up/down): we display UP
 
3541
                               * if the backend has known working servers or if it has no server at
 
3542
                               * all (eg: for stats). Then we display the total weight, number of
 
3543
                               * active and backups. */
 
3544
                              "%s,"
 
3545
                              "%d,%d,%d,"
 
3546
                              /* rest of backend: nothing, down transitions, last change, total downtime */
 
3547
                              ",%d,%d,%d,,"
 
3548
                              /* pid, iid, sid, throttle, lbtot, tracked, type */
 
3549
                              "%d,%d,0,,%lld,,%d,"
 
3550
                              /* rate, rate_lim, rate_max, */
 
3551
                              "%u,,%u,"
 
3552
                              /* check_status, check_code, check_duration */
 
3553
                              ",,,",
 
3554
                              px->id,
 
3555
                              px->nbpend /* or px->totpend ? */, px->be_counters.nbpend_max,
 
3556
                              px->beconn, px->be_counters.conn_max, px->fullconn, px->be_counters.cum_conn,
 
3557
                              px->be_counters.bytes_in, px->be_counters.bytes_out,
 
3558
                              px->be_counters.denied_req, px->be_counters.denied_resp,
 
3559
                              px->be_counters.failed_conns, px->be_counters.failed_resp,
 
3560
                              px->be_counters.retries, px->be_counters.redispatches,
 
3561
                              (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
 
3562
                              (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
 
3563
                              px->srv_act, px->srv_bck,
 
3564
                              px->down_trans, (int)(now.tv_sec - px->last_change),
 
3565
                              px->srv?be_downtime(px):0,
 
3566
                              relative_pid, px->uuid,
 
3567
                              px->be_counters.cum_lbconn, STATS_TYPE_BE,
 
3568
                              read_freq_ctr(&px->be_sess_per_sec),
 
3569
                              px->be_counters.sps_max);
 
3570
 
 
3571
                /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
 
3572
                if (px->mode == PR_MODE_HTTP) {
 
3573
                        for (i=1; i<6; i++)
 
3574
                                chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[i]);
 
3575
                        chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[0]);
 
3576
                }
 
3577
                else
 
3578
                        chunk_appendf(&trash, ",,,,,,");
 
3579
 
 
3580
                /* failed health analyses */
 
3581
                chunk_appendf(&trash, ",");
 
3582
 
 
3583
                /* requests : req_rate, req_rate_max, req_tot, */
 
3584
                chunk_appendf(&trash, ",,,");
 
3585
 
 
3586
                /* errors: cli_aborts, srv_aborts */
 
3587
                chunk_appendf(&trash, "%lld,%lld,",
 
3588
                              px->be_counters.cli_aborts, px->be_counters.srv_aborts);
 
3589
 
 
3590
                /* compression: in, out, bypassed */
 
3591
                chunk_appendf(&trash, "%lld,%lld,%lld,",
 
3592
                              px->be_counters.comp_in, px->be_counters.comp_out, px->be_counters.comp_byp);
 
3593
 
 
3594
                /* compression: comp_rsp */
 
3595
                chunk_appendf(&trash, "%lld,", px->be_counters.p.http.comp_rsp);
 
3596
 
 
3597
                /* lastsess, last_chk, last_agt, */
 
3598
                chunk_appendf(&trash, "%d,,,", be_lastsession(px));
 
3599
 
 
3600
                /* qtime, ctime, rtime, ttime, */
 
3601
                chunk_appendf(&trash, "%u,%u,%u,%u,",
 
3602
                              swrate_avg(px->be_counters.q_time, TIME_STATS_SAMPLES),
 
3603
                              swrate_avg(px->be_counters.c_time, TIME_STATS_SAMPLES),
 
3604
                              swrate_avg(px->be_counters.d_time, TIME_STATS_SAMPLES),
 
3605
                              swrate_avg(px->be_counters.t_time, TIME_STATS_SAMPLES));
 
3606
 
 
3607
                /* finish with EOL */
 
3608
                chunk_appendf(&trash, "\n");
 
3609
        }
 
3610
        return 1;
 
3611
}
 
3612
 
 
3613
/* Dumps the HTML table header for proxy <px> to the trash for and uses the state from
 
3614
 * stream interface <si> and per-uri parameters <uri>. The caller is responsible
 
3615
 * for clearing the trash if needed.
 
3616
 */
 
3617
static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
 
3618
{
 
3619
        struct appctx *appctx = __objt_appctx(si->end);
 
3620
        char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
 
3621
 
 
3622
        if (px->cap & PR_CAP_BE && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
 
3623
                /* A form to enable/disable this proxy servers */
 
3624
 
 
3625
                /* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
 
3626
                scope_txt[0] = 0;
 
3627
                if (appctx->ctx.stats.scope_len) {
 
3628
                        strcpy(scope_txt, STAT_SCOPE_PATTERN);
 
3629
                        memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), bo_ptr(si->ob->buf) + appctx->ctx.stats.scope_str, appctx->ctx.stats.scope_len);
 
3630
                        scope_txt[strlen(STAT_SCOPE_PATTERN) + appctx->ctx.stats.scope_len] = 0;
 
3631
                }
 
3632
 
 
3633
                chunk_appendf(&trash,
 
3634
                              "<form action=\"%s%s%s%s\" method=\"post\">",
 
3635
                              uri->uri_prefix,
 
3636
                              (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
3637
                              (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
3638
                              scope_txt);
 
3639
        }
 
3640
 
 
3641
        /* print a new table */
 
3642
        chunk_appendf(&trash,
 
3643
                      "<table class=\"tbl\" width=\"100%%\">\n"
 
3644
                      "<tr class=\"titre\">"
 
3645
                      "<th class=\"pxname\" width=\"10%%\">");
 
3646
 
 
3647
        chunk_appendf(&trash,
 
3648
                      "<a name=\"%s\"></a>%s"
 
3649
                      "<a class=px href=\"#%s\">%s</a>",
 
3650
                      px->id,
 
3651
                      (uri->flags & ST_SHLGNDS) ? "<u>":"",
 
3652
                      px->id, px->id);
 
3653
 
 
3654
        if (uri->flags & ST_SHLGNDS) {
 
3655
                /* cap, mode, id */
 
3656
                chunk_appendf(&trash, "<div class=tips>cap: %s, mode: %s, id: %d",
 
3657
                              proxy_cap_str(px->cap), proxy_mode_str(px->mode),
 
3658
                              px->uuid);
 
3659
                chunk_appendf(&trash, "</div>");
 
3660
        }
 
3661
 
 
3662
        chunk_appendf(&trash,
 
3663
                      "%s</th>"
 
3664
                      "<th class=\"%s\" width=\"90%%\">%s</th>"
 
3665
                      "</tr>\n"
 
3666
                      "</table>\n"
 
3667
                      "<table class=\"tbl\" width=\"100%%\">\n"
 
3668
                      "<tr class=\"titre\">",
 
3669
                      (uri->flags & ST_SHLGNDS) ? "</u>":"",
 
3670
                      px->desc ? "desc" : "empty", px->desc ? px->desc : "");
 
3671
 
 
3672
        if ((px->cap & PR_CAP_BE) && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
 
3673
                /* Column heading for Enable or Disable server */
 
3674
                chunk_appendf(&trash, "<th rowspan=2 width=1></th>");
 
3675
        }
 
3676
 
 
3677
        chunk_appendf(&trash,
 
3678
                      "<th rowspan=2></th>"
 
3679
                      "<th colspan=3>Queue</th>"
 
3680
                      "<th colspan=3>Session rate</th><th colspan=6>Sessions</th>"
 
3681
                      "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
 
3682
                      "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
 
3683
                      "<th colspan=9>Server</th>"
 
3684
                      "</tr>\n"
 
3685
                      "<tr class=\"titre\">"
 
3686
                      "<th>Cur</th><th>Max</th><th>Limit</th>"
 
3687
                      "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
 
3688
                      "<th>Limit</th><th>Total</th><th>LbTot</th><th>Last</th><th>In</th><th>Out</th>"
 
3689
                      "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
 
3690
                      "<th>Resp</th><th>Retr</th><th>Redis</th>"
 
3691
                      "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
 
3692
                      "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
 
3693
                      "<th>Thrtle</th>\n"
 
3694
                      "</tr>");
 
3695
}
 
3696
 
 
3697
/* Dumps the HTML table trailer for proxy <px> to the trash for and uses the state from
 
3698
 * stream interface <si>. The caller is responsible for clearing the trash if needed.
 
3699
 */
 
3700
static void stats_dump_html_px_end(struct stream_interface *si, struct proxy *px)
 
3701
{
 
3702
        struct appctx *appctx = __objt_appctx(si->end);
 
3703
        chunk_appendf(&trash, "</table>");
 
3704
 
 
3705
        if ((px->cap & PR_CAP_BE) && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
 
3706
                /* close the form used to enable/disable this proxy servers */
 
3707
                chunk_appendf(&trash,
 
3708
                              "Choose the action to perform on the checked servers : "
 
3709
                              "<select name=action>"
 
3710
                              "<option value=\"\"></option>"
 
3711
                              "<option value=\"ready\">Set state to READY</option>"
 
3712
                              "<option value=\"drain\">Set state to DRAIN</option>"
 
3713
                              "<option value=\"maint\">set state to MAINT</option>"
 
3714
                              "<option value=\"dhlth\">Health: disable checks</option>"
 
3715
                              "<option value=\"ehlth\">Health: enable checks</option>"
 
3716
                              "<option value=\"hrunn\">Health: force UP</option>"
 
3717
                              "<option value=\"hnolb\">Health: force NOLB</option>"
 
3718
                              "<option value=\"hdown\">Health: force DOWN</option>"
 
3719
                              "<option value=\"dagent\">Agent: disable checks</option>"
 
3720
                              "<option value=\"eagent\">Agent: enable checks</option>"
 
3721
                              "<option value=\"arunn\">Agent: force UP</option>"
 
3722
                              "<option value=\"adown\">Agent: force DOWN</option>"
 
3723
                              "<option value=\"shutdown\">Kill Sessions</option>"
 
3724
                              "</select>"
 
3725
                              "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
 
3726
                              "&nbsp;<input type=\"submit\" value=\"Apply\">"
 
3727
                              "</form>",
 
3728
                              px->uuid);
 
3729
        }
 
3730
 
 
3731
        chunk_appendf(&trash, "<p>\n");
 
3732
}
 
3733
 
 
3734
/*
 
3735
 * Dumps statistics for a proxy. The output is sent to the stream interface's
 
3736
 * input buffer. Returns 0 if it had to stop dumping data because of lack of
 
3737
 * buffer space, or non-zero if everything completed. This function is used
 
3738
 * both by the CLI and the HTTP entry points, and is able to dump the output
 
3739
 * in HTML or CSV formats. If the later, <uri> must be NULL.
 
3740
 */
 
3741
static int stats_dump_proxy_to_buffer(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
 
3742
{
 
3743
        struct appctx *appctx = __objt_appctx(si->end);
 
3744
        struct session *s = session_from_task(si->owner);
 
3745
        struct channel *rep = si->ib;
 
3746
        struct server *sv, *svs;        /* server and server-state, server-state=server or server->track */
1452
3747
        struct listener *l;
1453
 
        struct chunk msg;
1454
 
 
1455
 
        chunk_init(&msg, trash, trashlen);
1456
 
 
1457
 
        switch (s->data_ctx.stats.px_st) {
1458
 
        case DATA_ST_PX_INIT:
 
3748
 
 
3749
        chunk_reset(&trash);
 
3750
 
 
3751
        switch (appctx->ctx.stats.px_st) {
 
3752
        case STAT_PX_ST_INIT:
1459
3753
                /* we are on a new proxy */
1460
 
 
1461
3754
                if (uri && uri->scope) {
1462
3755
                        /* we have a limited scope, we have to check the proxy name */
1463
3756
                        struct stat_scope *scope;
1482
3775
                                return 1;
1483
3776
                }
1484
3777
 
1485
 
                if ((s->data_ctx.stats.flags & STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
1486
 
                        (px->uuid != s->data_ctx.stats.iid))
1487
 
                        return 1;
1488
 
 
1489
 
                s->data_ctx.stats.px_st = DATA_ST_PX_TH;
 
3778
                /* if the user has requested a limited output and the proxy
 
3779
                 * name does not match, skip it.
 
3780
                 */
 
3781
                if (appctx->ctx.stats.scope_len &&
 
3782
                    strnistr(px->id, strlen(px->id), bo_ptr(si->ob->buf) + appctx->ctx.stats.scope_str, appctx->ctx.stats.scope_len) == NULL)
 
3783
                        return 1;
 
3784
 
 
3785
                if ((appctx->ctx.stats.flags & STAT_BOUND) &&
 
3786
                    (appctx->ctx.stats.iid != -1) &&
 
3787
                    (px->uuid != appctx->ctx.stats.iid))
 
3788
                        return 1;
 
3789
 
 
3790
                appctx->ctx.stats.px_st = STAT_PX_ST_TH;
1490
3791
                /* fall through */
1491
3792
 
1492
 
        case DATA_ST_PX_TH:
1493
 
                if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1494
 
                        if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
1495
 
                                /* A form to enable/disable this proxy servers */
1496
 
                                chunk_printf(&msg,
1497
 
                                        "<form action=\"%s\" method=\"post\">",
1498
 
                                        uri->uri_prefix);
1499
 
                        }
1500
 
 
1501
 
                        /* print a new table */
1502
 
                        chunk_printf(&msg,
1503
 
                                     "<table class=\"tbl\" width=\"100%%\">\n"
1504
 
                                     "<tr class=\"titre\">"
1505
 
                                     "<th class=\"pxname\" width=\"10%%\"");
1506
 
 
1507
 
                        if (uri->flags&ST_SHLGNDS) {
1508
 
                                /* cap, mode, id */
1509
 
                                chunk_printf(&msg, " title=\"cap: %s, mode: %s, id: %d",
1510
 
                                        proxy_cap_str(px->cap), proxy_mode_str(px->mode),
1511
 
                                        px->uuid);
1512
 
 
1513
 
                                chunk_printf(&msg, "\"");
1514
 
                        }
1515
 
 
1516
 
                        chunk_printf(&msg,
1517
 
                                     ">%s<a name=\"%s\"></a>"
1518
 
                                     "<a class=px href=\"#%s\">%s</a>%s</th>"
1519
 
                                     "<th class=\"%s\" width=\"90%%\">%s</th>"
1520
 
                                     "</tr>\n"
1521
 
                                     "</table>\n"
1522
 
                                     "<table class=\"tbl\" width=\"100%%\">\n"
1523
 
                                     "<tr class=\"titre\">",
1524
 
                                     (uri->flags & ST_SHLGNDS)?"<u>":"",
1525
 
                                     px->id, px->id, px->id,
1526
 
                                     (uri->flags & ST_SHLGNDS)?"</u>":"",
1527
 
                                     px->desc ? "desc" : "empty", px->desc ? px->desc : "");
1528
 
 
1529
 
                        if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
1530
 
                                 /* Column heading for Enable or Disable server */
1531
 
                                chunk_printf(&msg, "<th rowspan=2 width=1></th>");
1532
 
                        }
1533
 
 
1534
 
                        chunk_printf(&msg,
1535
 
                                     "<th rowspan=2></th>"
1536
 
                                     "<th colspan=3>Queue</th>"
1537
 
                                     "<th colspan=3>Session rate</th><th colspan=5>Sessions</th>"
1538
 
                                     "<th colspan=2>Bytes</th><th colspan=2>Denied</th>"
1539
 
                                     "<th colspan=3>Errors</th><th colspan=2>Warnings</th>"
1540
 
                                     "<th colspan=9>Server</th>"
1541
 
                                     "</tr>\n"
1542
 
                                     "<tr class=\"titre\">"
1543
 
                                     "<th>Cur</th><th>Max</th><th>Limit</th>"
1544
 
                                     "<th>Cur</th><th>Max</th><th>Limit</th><th>Cur</th><th>Max</th>"
1545
 
                                     "<th>Limit</th><th>Total</th><th>LbTot</th><th>In</th><th>Out</th>"
1546
 
                                     "<th>Req</th><th>Resp</th><th>Req</th><th>Conn</th>"
1547
 
                                     "<th>Resp</th><th>Retr</th><th>Redis</th>"
1548
 
                                     "<th>Status</th><th>LastChk</th><th>Wght</th><th>Act</th>"
1549
 
                                     "<th>Bck</th><th>Chk</th><th>Dwn</th><th>Dwntme</th>"
1550
 
                                     "<th>Thrtle</th>\n"
1551
 
                                     "</tr>");
1552
 
 
1553
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
 
3793
        case STAT_PX_ST_TH:
 
3794
                if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
3795
                        stats_dump_html_px_hdr(si, px, uri);
 
3796
                        if (bi_putchk(rep, &trash) == -1)
1554
3797
                                return 0;
1555
3798
                }
1556
3799
 
1557
 
                s->data_ctx.stats.px_st = DATA_ST_PX_FE;
 
3800
                appctx->ctx.stats.px_st = STAT_PX_ST_FE;
1558
3801
                /* fall through */
1559
3802
 
1560
 
        case DATA_ST_PX_FE:
 
3803
        case STAT_PX_ST_FE:
1561
3804
                /* print the frontend */
1562
 
                if ((px->cap & PR_CAP_FE) &&
1563
 
                    (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_FE)))) {
1564
 
                        if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1565
 
                                chunk_printf(&msg,
1566
 
                                     /* name, queue */
1567
 
                                     "<tr class=\"frontend\">");
1568
 
 
1569
 
                                if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
1570
 
                                        /* Column sub-heading for Enable or Disable server */
1571
 
                                        chunk_printf(&msg, "<td></td>");
1572
 
                                }
1573
 
 
1574
 
                                chunk_printf(&msg,
1575
 
                                     "<td class=ac>"
1576
 
                                     "<a name=\"%s/Frontend\"></a>"
1577
 
                                     "<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
1578
 
                                     "<td colspan=3></td>"
1579
 
                                     "",
1580
 
                                     px->id, px->id);
1581
 
 
1582
 
                                if (px->mode == PR_MODE_HTTP) {
1583
 
                                        chunk_printf(&msg,
1584
 
                                                     /* sessions rate : current, max, limit */
1585
 
                                                     "<td title=\"Cur: %u req/s\"><u>%s</u></td><td title=\"Max: %u req/s\"><u>%s</u></td><td>%s</td>"
1586
 
                                                     "",
1587
 
                                                     read_freq_ctr(&px->fe_req_per_sec),
1588
 
                                                     U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
1589
 
                                                     px->counters.fe_rps_max,
1590
 
                                                     U2H1(px->counters.fe_sps_max),
1591
 
                                                     LIM2A2(px->fe_sps_lim, "-"));
1592
 
                                } else {
1593
 
                                        chunk_printf(&msg,
1594
 
                                                     /* sessions rate : current, max, limit */
1595
 
                                                     "<td>%s</td><td>%s</td><td>%s</td>"
1596
 
                                                     "",
1597
 
                                                     U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
1598
 
                                                     U2H1(px->counters.fe_sps_max), LIM2A2(px->fe_sps_lim, "-"));
1599
 
                                }
1600
 
 
1601
 
                                chunk_printf(&msg,
1602
 
                                     /* sessions: current, max, limit */
1603
 
                                     "<td>%s</td><td>%s</td><td>%s</td>"
1604
 
                                     "<td"
1605
 
                                     "",
1606
 
                                     U2H3(px->feconn), U2H4(px->counters.feconn_max), U2H5(px->maxconn));
1607
 
 
1608
 
                                /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1609
 
                                if (px->mode == PR_MODE_HTTP) {
1610
 
                                        int i;
1611
 
 
1612
 
                                        chunk_printf(&msg, " title=\"%lld requests:", px->counters.cum_fe_req);
1613
 
 
1614
 
                                        for (i = 1; i < 6; i++)
1615
 
                                                chunk_printf(&msg, " %dxx=%lld,", i, px->counters.fe.http.rsp[i]);
1616
 
 
1617
 
                                        chunk_printf(&msg, " other=%lld\"", px->counters.fe.http.rsp[0]);
1618
 
                                }
1619
 
 
1620
 
                                chunk_printf(&msg,
1621
 
                                     /* sessions: total, lbtot */
1622
 
                                     ">%s%s%s</td><td></td>"
1623
 
                                     /* bytes : in, out */
1624
 
                                     "<td>%s</td><td>%s</td>"
1625
 
                                     "",
1626
 
                                     (px->mode == PR_MODE_HTTP)?"<u>":"",
1627
 
                                     U2H6(px->counters.cum_feconn),
1628
 
                                     (px->mode == PR_MODE_HTTP)?"</u>":"",
1629
 
                                     U2H7(px->counters.bytes_in), U2H8(px->counters.bytes_out));
1630
 
 
1631
 
                                chunk_printf(&msg,
1632
 
                                     /* denied: req, resp */
1633
 
                                     "<td>%s</td><td>%s</td>"
1634
 
                                     /* errors : request, connect, response */
1635
 
                                     "<td>%s</td><td></td><td></td>"
1636
 
                                     /* warnings: retries, redispatches */
1637
 
                                     "<td></td><td></td>"
1638
 
                                     /* server status : reflect frontend status */
1639
 
                                     "<td class=ac>%s</td>"
1640
 
                                     /* rest of server: nothing */
1641
 
                                     "<td class=ac colspan=8></td></tr>"
1642
 
                                     "",
1643
 
                                     U2H0(px->counters.denied_req), U2H1(px->counters.denied_resp),
1644
 
                                     U2H2(px->counters.failed_req),
1645
 
                                     px->state == PR_STRUN ? "OPEN" :
1646
 
                                     px->state == PR_STIDLE ? "FULL" : "STOP");
1647
 
                        } else {
1648
 
                                chunk_printf(&msg,
1649
 
                                     /* pxid, name, queue cur, queue max, */
1650
 
                                     "%s,FRONTEND,,,"
1651
 
                                     /* sessions : current, max, limit, total */
1652
 
                                     "%d,%d,%d,%lld,"
1653
 
                                     /* bytes : in, out */
1654
 
                                     "%lld,%lld,"
1655
 
                                     /* denied: req, resp */
1656
 
                                     "%lld,%lld,"
1657
 
                                     /* errors : request, connect, response */
1658
 
                                     "%lld,,,"
1659
 
                                     /* warnings: retries, redispatches */
1660
 
                                     ",,"
1661
 
                                     /* server status : reflect frontend status */
1662
 
                                     "%s,"
1663
 
                                     /* rest of server: nothing */
1664
 
                                     ",,,,,,,,"
1665
 
                                     /* pid, iid, sid, throttle, lbtot, tracked, type */
1666
 
                                     "%d,%d,0,,,,%d,"
1667
 
                                     /* rate, rate_lim, rate_max */
1668
 
                                     "%u,%u,%u,"
1669
 
                                     /* check_status, check_code, check_duration */
1670
 
                                     ",,,",
1671
 
                                     px->id,
1672
 
                                     px->feconn, px->counters.feconn_max, px->maxconn, px->counters.cum_feconn,
1673
 
                                     px->counters.bytes_in, px->counters.bytes_out,
1674
 
                                     px->counters.denied_req, px->counters.denied_resp,
1675
 
                                     px->counters.failed_req,
1676
 
                                     px->state == PR_STRUN ? "OPEN" :
1677
 
                                     px->state == PR_STIDLE ? "FULL" : "STOP",
1678
 
                                     relative_pid, px->uuid, STATS_TYPE_FE,
1679
 
                                     read_freq_ctr(&px->fe_sess_per_sec),
1680
 
                                     px->fe_sps_lim, px->counters.fe_sps_max);
1681
 
 
1682
 
                                /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
1683
 
                                if (px->mode == PR_MODE_HTTP) {
1684
 
                                        int i;
1685
 
 
1686
 
                                        for (i=1; i<6; i++)
1687
 
                                                chunk_printf(&msg, "%lld,", px->counters.fe.http.rsp[i]);
1688
 
 
1689
 
                                        chunk_printf(&msg, "%lld,", px->counters.fe.http.rsp[0]);
1690
 
                                } else {
1691
 
                                        chunk_printf(&msg, ",,,,,,");
1692
 
                                }
1693
 
 
1694
 
                                /* failed health analyses */
1695
 
                                chunk_printf(&msg, ",");
1696
 
 
1697
 
                                /* requests : req_rate, req_rate_max, req_tot, */
1698
 
                                chunk_printf(&msg, "%u,%u,%lld,",
1699
 
                                             read_freq_ctr(&px->fe_req_per_sec),
1700
 
                                             px->counters.fe_rps_max, px->counters.cum_fe_req);
1701
 
 
1702
 
                                /* errors: cli_aborts, srv_aborts */
1703
 
                                chunk_printf(&msg, ",,");
1704
 
 
1705
 
                                /* finish with EOL */
1706
 
                                chunk_printf(&msg, "\n");
1707
 
                        }
1708
 
 
1709
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
 
3805
                if (stats_dump_fe_stats(si, px))
 
3806
                        if (bi_putchk(rep, &trash) == -1)
1710
3807
                                return 0;
1711
 
                }
1712
3808
 
1713
 
                s->data_ctx.stats.l = px->listen; /* may be NULL */
1714
 
                s->data_ctx.stats.px_st = DATA_ST_PX_LI;
 
3809
                appctx->ctx.stats.l = px->conf.listeners.n;
 
3810
                appctx->ctx.stats.px_st = STAT_PX_ST_LI;
1715
3811
                /* fall through */
1716
3812
 
1717
 
        case DATA_ST_PX_LI:
 
3813
        case STAT_PX_ST_LI:
1718
3814
                /* stats.l has been initialized above */
1719
 
                for (; s->data_ctx.stats.l != NULL; s->data_ctx.stats.l = l->next) {
1720
 
                        if (buffer_almost_full(rep))
 
3815
                for (; appctx->ctx.stats.l != &px->conf.listeners; appctx->ctx.stats.l = l->by_fe.n) {
 
3816
                        if (buffer_almost_full(rep->buf)) {
 
3817
                                rep->flags |= CF_WAKE_WRITE;
1721
3818
                                return 0;
 
3819
                        }
1722
3820
 
1723
 
                        l = s->data_ctx.stats.l;
 
3821
                        l = LIST_ELEM(appctx->ctx.stats.l, struct listener *, by_fe);
1724
3822
                        if (!l->counters)
1725
3823
                                continue;
1726
3824
 
1727
 
                        if (s->data_ctx.stats.flags & STAT_BOUND) {
1728
 
                                if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SO)))
 
3825
                        if (appctx->ctx.stats.flags & STAT_BOUND) {
 
3826
                                if (!(appctx->ctx.stats.type & (1 << STATS_TYPE_SO)))
1729
3827
                                        break;
1730
3828
 
1731
 
                                if (s->data_ctx.stats.sid != -1 && l->luid != s->data_ctx.stats.sid)
 
3829
                                if (appctx->ctx.stats.sid != -1 && l->luid != appctx->ctx.stats.sid)
1732
3830
                                        continue;
1733
3831
                        }
1734
3832
 
1735
 
                        if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1736
 
                                chunk_printf(&msg, "<tr class=socket>");
1737
 
                                if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
1738
 
                                         /* Column sub-heading for Enable or Disable server */
1739
 
                                        chunk_printf(&msg, "<td></td>");
1740
 
                                }
1741
 
                                chunk_printf(&msg, "<td class=ac");
1742
 
 
1743
 
                                        if (uri->flags&ST_SHLGNDS) {
1744
 
                                                char str[INET6_ADDRSTRLEN], *fmt = NULL;
1745
 
                                                int port;
1746
 
 
1747
 
                                                chunk_printf(&msg, " title=\"IP: ");
1748
 
 
1749
 
                                                port = (l->addr.ss_family == AF_INET6)
1750
 
                                                        ? ntohs(((struct sockaddr_in6 *)(&l->addr))->sin6_port)
1751
 
                                                        : ntohs(((struct sockaddr_in *)(&l->addr))->sin_port);
1752
 
 
1753
 
                                                if (l->addr.ss_family == AF_INET) {
1754
 
                                                        if (inet_ntop(AF_INET,
1755
 
                                                            (const void *)&((struct sockaddr_in *)&l->addr)->sin_addr,
1756
 
                                                            str, sizeof(str)))
1757
 
                                                                fmt = "%s:%d";
1758
 
                                                } else {
1759
 
                                                        if (inet_ntop(AF_INET6,
1760
 
                                                            (const void *)&((struct sockaddr_in6 *)(&l->addr))->sin6_addr,
1761
 
                                                            str, sizeof(str)))
1762
 
                                                                fmt = "[%s]:%d";
1763
 
                                                }
1764
 
 
1765
 
                                                if (fmt)
1766
 
                                                        chunk_printf(&msg, fmt, str, port);
1767
 
                                                else
1768
 
                                                        chunk_printf(&msg, "(%s)", strerror(errno));
1769
 
 
1770
 
                                                /* id */
1771
 
                                                chunk_printf(&msg, ", id: %d", l->luid);
1772
 
 
1773
 
                                                chunk_printf(&msg, "\"");
1774
 
                                        }
1775
 
 
1776
 
                                chunk_printf(&msg,
1777
 
                                     /* name, queue */
1778
 
                                     ">%s<a name=\"%s/+%s\"></a>"
1779
 
                                     "<a class=lfsb href=\"#%s/+%s\">%s</a></td><td colspan=3>%s</td>"
1780
 
                                     /* sessions rate: current, max, limit */
1781
 
                                     "<td colspan=3>&nbsp;</td>"
1782
 
                                     /* sessions: current, max, limit, total, lbtot */
1783
 
                                     "<td>%s</td><td>%s</td><td>%s</td>"
1784
 
                                     "<td>%s</td><td>&nbsp;</td>"
1785
 
                                     /* bytes: in, out */
1786
 
                                     "<td>%s</td><td>%s</td>"
1787
 
                                     "",
1788
 
                                     (uri->flags & ST_SHLGNDS)?"<u>":"",
1789
 
                                     px->id, l->name, px->id, l->name, l->name,
1790
 
                                     (uri->flags & ST_SHLGNDS)?"</u>":"",
1791
 
                                     U2H3(l->nbconn), U2H4(l->counters->conn_max), U2H5(l->maxconn),
1792
 
                                     U2H6(l->counters->cum_conn), U2H7(l->counters->bytes_in), U2H8(l->counters->bytes_out));
1793
 
 
1794
 
                                chunk_printf(&msg,
1795
 
                                     /* denied: req, resp */
1796
 
                                     "<td>%s</td><td>%s</td>"
1797
 
                                     /* errors: request, connect, response */
1798
 
                                     "<td>%s</td><td></td><td></td>"
1799
 
                                     /* warnings: retries, redispatches */
1800
 
                                     "<td></td><td></td>"
1801
 
                                     /* server status: reflect listener status */
1802
 
                                     "<td class=ac>%s</td>"
1803
 
                                     /* rest of server: nothing */
1804
 
                                     "<td class=ac colspan=8></td></tr>"
1805
 
                                     "",
1806
 
                                     U2H0(l->counters->denied_req), U2H1(l->counters->denied_resp),
1807
 
                                     U2H2(l->counters->failed_req),
1808
 
                                     (l->nbconn < l->maxconn) ? "OPEN" : "FULL");
1809
 
                        } else {
1810
 
                                chunk_printf(&msg,
1811
 
                                     /* pxid, name, queue cur, queue max, */
1812
 
                                     "%s,%s,,,"
1813
 
                                     /* sessions: current, max, limit, total */
1814
 
                                     "%d,%d,%d,%lld,"
1815
 
                                     /* bytes: in, out */
1816
 
                                     "%lld,%lld,"
1817
 
                                     /* denied: req, resp */
1818
 
                                     "%lld,%lld,"
1819
 
                                     /* errors: request, connect, response */
1820
 
                                     "%lld,,,"
1821
 
                                     /* warnings: retries, redispatches */
1822
 
                                     ",,"
1823
 
                                     /* server status: reflect listener status */
1824
 
                                     "%s,"
1825
 
                                     /* rest of server: nothing */
1826
 
                                     ",,,,,,,,"
1827
 
                                     /* pid, iid, sid, throttle, lbtot, tracked, type */
1828
 
                                     "%d,%d,%d,,,,%d,"
1829
 
                                     /* rate, rate_lim, rate_max */
1830
 
                                     ",,,"
1831
 
                                     /* check_status, check_code, check_duration */
1832
 
                                     ",,,"
1833
 
                                     /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
1834
 
                                     ",,,,,,"
1835
 
                                     /* failed health analyses */
1836
 
                                     ","
1837
 
                                     /* requests : req_rate, req_rate_max, req_tot, */
1838
 
                                     ",,,"
1839
 
                                     /* errors: cli_aborts, srv_aborts */
1840
 
                                     ",,"
1841
 
                                     "\n",
1842
 
                                     px->id, l->name,
1843
 
                                     l->nbconn, l->counters->conn_max,
1844
 
                                     l->maxconn, l->counters->cum_conn,
1845
 
                                     l->counters->bytes_in, l->counters->bytes_out,
1846
 
                                     l->counters->denied_req, l->counters->denied_resp,
1847
 
                                     l->counters->failed_req,
1848
 
                                     (l->nbconn < l->maxconn) ? "OPEN" : "FULL",
1849
 
                                     relative_pid, px->uuid, l->luid, STATS_TYPE_SO);
1850
 
                        }
1851
 
 
1852
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
1853
 
                                return 0;
 
3833
                        /* print the frontend */
 
3834
                        if (stats_dump_li_stats(si, px, l, uri ? uri->flags : 0))
 
3835
                                if (bi_putchk(rep, &trash) == -1)
 
3836
                                        return 0;
1854
3837
                }
1855
3838
 
1856
 
                s->data_ctx.stats.sv = px->srv; /* may be NULL */
1857
 
                s->data_ctx.stats.px_st = DATA_ST_PX_SV;
 
3839
                appctx->ctx.stats.sv = px->srv; /* may be NULL */
 
3840
                appctx->ctx.stats.px_st = STAT_PX_ST_SV;
1858
3841
                /* fall through */
1859
3842
 
1860
 
        case DATA_ST_PX_SV:
 
3843
        case STAT_PX_ST_SV:
1861
3844
                /* stats.sv has been initialized above */
1862
 
                for (; s->data_ctx.stats.sv != NULL; s->data_ctx.stats.sv = sv->next) {
1863
 
                        int sv_state; /* 0=DOWN, 1=going up, 2=going down, 3=UP, 4,5=NOLB, 6=unchecked */
 
3845
                for (; appctx->ctx.stats.sv != NULL; appctx->ctx.stats.sv = sv->next) {
 
3846
                        int sv_state;
1864
3847
 
1865
 
                        if (buffer_almost_full(rep))
 
3848
                        if (buffer_almost_full(rep->buf)) {
 
3849
                                rep->flags |= CF_WAKE_WRITE;
1866
3850
                                return 0;
1867
 
 
1868
 
                        sv = s->data_ctx.stats.sv;
1869
 
 
1870
 
                        if (s->data_ctx.stats.flags & STAT_BOUND) {
1871
 
                                if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV)))
 
3851
                        }
 
3852
 
 
3853
                        sv = appctx->ctx.stats.sv;
 
3854
 
 
3855
                        if (appctx->ctx.stats.flags & STAT_BOUND) {
 
3856
                                if (!(appctx->ctx.stats.type & (1 << STATS_TYPE_SV)))
1872
3857
                                        break;
1873
3858
 
1874
 
                                if (s->data_ctx.stats.sid != -1 && sv->puid != s->data_ctx.stats.sid)
 
3859
                                if (appctx->ctx.stats.sid != -1 && sv->puid != appctx->ctx.stats.sid)
1875
3860
                                        continue;
1876
3861
                        }
1877
3862
 
1878
 
                        if (sv->tracked)
1879
 
                                svs = sv->tracked;
1880
 
                        else
1881
 
                                svs = sv;
1882
 
 
1883
 
                        /* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
1884
 
                        if (!(svs->state & SRV_CHECKED))
1885
 
                                sv_state = 6;
1886
 
                        else if (svs->state & SRV_RUNNING) {
1887
 
                                if (svs->health == svs->rise + svs->fall - 1)
1888
 
                                        sv_state = 3; /* UP */
1889
 
                                else
1890
 
                                        sv_state = 2; /* going down */
1891
 
 
1892
 
                                if (svs->state & SRV_GOINGDOWN)
1893
 
                                        sv_state += 2;
1894
 
                        }
1895
 
                        else
1896
 
                                if (svs->health)
1897
 
                                        sv_state = 1; /* going up */
1898
 
                                else
 
3863
                        svs = sv;
 
3864
                        while (svs->track)
 
3865
                                svs = svs->track;
 
3866
 
 
3867
                        if (sv->state == SRV_ST_RUNNING || sv->state == SRV_ST_STARTING) {
 
3868
                                /* server is UP. The possibilities are :
 
3869
                                 *   - UP, draining, going down    => state = 7
 
3870
                                 *   - UP, going down              => state = 3
 
3871
                                 *   - UP, draining                => state = 8
 
3872
                                 *   - UP, checked                 => state = 4
 
3873
                                 *   - UP, not checked nor tracked => state = 9
 
3874
                                 */
 
3875
 
 
3876
                                if ((svs->check.state & CHK_ST_ENABLED) &&
 
3877
                                    (svs->check.health < svs->check.rise + svs->check.fall - 1))
 
3878
                                        sv_state = 3;
 
3879
                                else
 
3880
                                        sv_state = 4;
 
3881
 
 
3882
                                if (server_is_draining(sv))
 
3883
                                        sv_state += 4;
 
3884
 
 
3885
                                if (sv_state == 4 && !(svs->check.state & CHK_ST_ENABLED))
 
3886
                                        sv_state = 9; /* unchecked UP */
 
3887
                        }
 
3888
                        else if (sv->state == SRV_ST_STOPPING) {
 
3889
                                if ((!(sv->check.state & CHK_ST_ENABLED) && !sv->track) ||
 
3890
                                    (svs->check.health == svs->check.rise + svs->check.fall - 1))
 
3891
                                        sv_state = 6; /* NOLB */
 
3892
                                else
 
3893
                                        sv_state = 5; /* NOLB going down */
 
3894
                        }
 
3895
                        else {  /* stopped */
 
3896
                                if ((svs->agent.state & CHK_ST_ENABLED) && !svs->agent.health)
 
3897
                                        sv_state = 1; /* DOWN (agent) */
 
3898
                                else if ((svs->check.state & CHK_ST_ENABLED) && !svs->check.health)
1899
3899
                                        sv_state = 0; /* DOWN */
 
3900
                                else if ((svs->agent.state & CHK_ST_ENABLED) || (svs->check.state & CHK_ST_ENABLED))
 
3901
                                        sv_state = 2; /* going up */
 
3902
                                else
 
3903
                                        sv_state = 0; /* DOWN, unchecked */
 
3904
                        }
1900
3905
 
1901
 
                        if (((sv_state == 0) || (sv->state & SRV_MAINTAIN)) && (s->data_ctx.stats.flags & STAT_HIDE_DOWN)) {
 
3906
                        if (((sv_state <= 1) || (sv->admin & SRV_ADMF_MAINT)) && (appctx->ctx.stats.flags & STAT_HIDE_DOWN)) {
1902
3907
                                /* do not report servers which are DOWN */
1903
 
                                s->data_ctx.stats.sv = sv->next;
 
3908
                                appctx->ctx.stats.sv = sv->next;
1904
3909
                                continue;
1905
3910
                        }
1906
3911
 
1907
 
                        if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1908
 
                                static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d &uarr;",
1909
 
                                                               "UP %d/%d &darr;", "UP",
1910
 
                                                               "NOLB %d/%d &darr;", "NOLB",
1911
 
                                                               "<i>no check</i>" };
1912
 
                                if ((sv->state & SRV_MAINTAIN) || (svs->state & SRV_MAINTAIN)) {
1913
 
                                        chunk_printf(&msg,
1914
 
                                            /* name */
1915
 
                                            "<tr class=\"maintain\">"
1916
 
                                        );
1917
 
                                }
1918
 
                                else {
1919
 
                                        chunk_printf(&msg,
1920
 
                                            /* name */
1921
 
                                            "<tr class=\"%s%d\">",
1922
 
                                            (sv->state & SRV_BACKUP) ? "backup" : "active", sv_state);
1923
 
                                }
1924
 
 
1925
 
                                if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
1926
 
                                        chunk_printf(&msg,
1927
 
                                                "<td><input type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
1928
 
                                                sv->id);
1929
 
                                }
1930
 
 
1931
 
                                chunk_printf(&msg, "<td class=ac");
1932
 
 
1933
 
                                if (uri->flags&ST_SHLGNDS) {
1934
 
                                        char str[INET6_ADDRSTRLEN];
1935
 
 
1936
 
                                        chunk_printf(&msg, " title=\"IP: ");
1937
 
 
1938
 
                                        /* IP */
1939
 
                                        if (inet_ntop(sv->addr.sin_family, &sv->addr.sin_addr, str, sizeof(str)))
1940
 
                                                chunk_printf(&msg, "%s:%d", str, htons(sv->addr.sin_port));
1941
 
                                        else
1942
 
                                                chunk_printf(&msg, "(%s)", strerror(errno));
1943
 
 
1944
 
                                        /* id */
1945
 
                                        chunk_printf(&msg, ", id: %d", sv->puid);
1946
 
 
1947
 
                                        /* cookie */
1948
 
                                        if (sv->cookie) {
1949
 
                                                struct chunk src;
1950
 
 
1951
 
                                                chunk_printf(&msg, ", cookie: '");
1952
 
 
1953
 
                                                chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
1954
 
                                                chunk_htmlencode(&msg, &src);
1955
 
 
1956
 
                                                chunk_printf(&msg, "'");
1957
 
                                        }
1958
 
 
1959
 
                                        chunk_printf(&msg, "\"");
1960
 
                                }
1961
 
 
1962
 
                                chunk_printf(&msg,
1963
 
                                     ">%s<a name=\"%s/%s\"></a>"
1964
 
                                     "<a class=lfsb href=\"#%s/%s\">%s</a>%s</td>"
1965
 
                                     /* queue : current, max, limit */
1966
 
                                     "<td>%s</td><td>%s</td><td>%s</td>"
1967
 
                                     /* sessions rate : current, max, limit */
1968
 
                                     "<td>%s</td><td>%s</td><td></td>"
1969
 
                                     /* sessions: current, max, limit */
1970
 
                                     "<td>%s</td><td>%s</td><td>%s</td>"
1971
 
                                     "<td"
1972
 
                                     "",
1973
 
                                     (uri->flags & ST_SHLGNDS)?"<u>":"",
1974
 
                                     px->id, sv->id, px->id, sv->id, sv->id,
1975
 
                                     (uri->flags & ST_SHLGNDS)?"</u>":"",
1976
 
                                     U2H0(sv->nbpend), U2H1(sv->counters.nbpend_max), LIM2A2(sv->maxqueue, "-"),
1977
 
                                     U2H3(read_freq_ctr(&sv->sess_per_sec)), U2H4(sv->counters.sps_max),
1978
 
                                     U2H5(sv->cur_sess), U2H6(sv->counters.cur_sess_max), LIM2A7(sv->maxconn, "-"));
1979
 
 
1980
 
                                /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1981
 
                                if (px->mode == PR_MODE_HTTP) {
1982
 
                                        int i;
1983
 
 
1984
 
                                        chunk_printf(&msg, " title=\"rsp codes:");
1985
 
 
1986
 
                                        for (i = 1; i < 6; i++)
1987
 
                                                chunk_printf(&msg, " %dxx=%lld,", i, sv->counters.p.http.rsp[i]);
1988
 
 
1989
 
                                        chunk_printf(&msg, " other=%lld\"", sv->counters.p.http.rsp[0]);
1990
 
                                }
1991
 
 
1992
 
                                chunk_printf(&msg,
1993
 
                                     /* sessions: total, lbtot */
1994
 
                                     ">%s%s%s</td><td>%s</td>",
1995
 
                                     (px->mode == PR_MODE_HTTP)?"<u>":"",
1996
 
                                     U2H0(sv->counters.cum_sess),
1997
 
                                     (px->mode == PR_MODE_HTTP)?"</u>":"",
1998
 
                                     U2H1(sv->counters.cum_lbconn));
1999
 
 
2000
 
                                chunk_printf(&msg,
2001
 
                                     /* bytes : in, out */
2002
 
                                     "<td>%s</td><td>%s</td>"
2003
 
                                     /* denied: req, resp */
2004
 
                                     "<td></td><td>%s</td>"
2005
 
                                     /* errors : request, connect */
2006
 
                                     "<td></td><td>%s</td>"
2007
 
                                     /* errors : response */
2008
 
                                     "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
2009
 
                                     /* warnings: retries, redispatches */
2010
 
                                     "<td>%lld</td><td>%lld</td>"
2011
 
                                     "",
2012
 
                                     U2H0(sv->counters.bytes_in), U2H1(sv->counters.bytes_out),
2013
 
                                     U2H2(sv->counters.failed_secu),
2014
 
                                     U2H3(sv->counters.failed_conns),
2015
 
                                     sv->counters.cli_aborts,
2016
 
                                     sv->counters.srv_aborts,
2017
 
                                     U2H6(sv->counters.failed_resp),
2018
 
                                     sv->counters.retries, sv->counters.redispatches);
2019
 
 
2020
 
                                /* status, lest check */
2021
 
                                chunk_printf(&msg, "<td class=ac>");
2022
 
 
2023
 
                                if (sv->state & SRV_MAINTAIN) {
2024
 
                                        chunk_printf(&msg, "%s ",
2025
 
                                                human_time(now.tv_sec - sv->last_change, 1));
2026
 
                                        chunk_printf(&msg, "MAINT");
2027
 
                                }
2028
 
                                else if (svs != sv && svs->state & SRV_MAINTAIN) {
2029
 
                                        chunk_printf(&msg, "%s ",
2030
 
                                                human_time(now.tv_sec - svs->last_change, 1));
2031
 
                                        chunk_printf(&msg, "MAINT(via)");
2032
 
                                }
2033
 
                                else if (svs->state & SRV_CHECKED) {
2034
 
                                        chunk_printf(&msg, "%s ",
2035
 
                                                human_time(now.tv_sec - svs->last_change, 1));
2036
 
 
2037
 
                                        chunk_printf(&msg,
2038
 
                                             srv_hlt_st[sv_state],
2039
 
                                             (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
2040
 
                                             (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
2041
 
                                }
2042
 
 
2043
 
                                if (sv->state & SRV_CHECKED) {
2044
 
                                        chunk_printf(&msg, "</td><td class=ac title=\"%s",
2045
 
                                                get_check_status_description(sv->check_status));
2046
 
 
2047
 
                                        if (*sv->check_desc) {
2048
 
                                                struct chunk src;
2049
 
 
2050
 
                                                chunk_printf(&msg, ": ");
2051
 
 
2052
 
                                                chunk_initlen(&src, sv->check_desc, 0, strlen(sv->check_desc));
2053
 
                                                chunk_htmlencode(&msg, &src);
2054
 
                                        }
2055
 
 
2056
 
                                        chunk_printf(&msg, "\"><u> %s%s",
2057
 
                                                tv_iszero(&sv->check_start)?"":"* ",
2058
 
                                                get_check_status_info(sv->check_status));
2059
 
 
2060
 
                                        if (sv->check_status >= HCHK_STATUS_L57DATA)
2061
 
                                                chunk_printf(&msg, "/%d", sv->check_code);
2062
 
 
2063
 
                                        if (sv->check_status >= HCHK_STATUS_CHECKED && sv->check_duration >= 0)
2064
 
                                        chunk_printf(&msg, " in %lums</u>", sv->check_duration);
2065
 
                                } else
2066
 
                                        chunk_printf(&msg, "</td><td>");
2067
 
 
2068
 
                                chunk_printf(&msg,
2069
 
                                     /* weight */
2070
 
                                     "</td><td class=ac>%d</td>"
2071
 
                                     /* act, bck */
2072
 
                                     "<td class=ac>%s</td><td class=ac>%s</td>"
2073
 
                                     "",
2074
 
                                     (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
2075
 
                                     (sv->state & SRV_BACKUP) ? "-" : "Y",
2076
 
                                     (sv->state & SRV_BACKUP) ? "Y" : "-");
2077
 
 
2078
 
                                /* check failures: unique, fatal, down time */
2079
 
                                if (sv->state & SRV_CHECKED) {
2080
 
                                        chunk_printf(&msg, "<td title=\"Failed Health Checks%s\"><u>%lld",
2081
 
                                             svs->observe?"/Health Analyses":"", svs->counters.failed_checks);
2082
 
 
2083
 
                                        if (svs->observe)
2084
 
                                                chunk_printf(&msg, "/%lld", svs->counters.failed_hana);
2085
 
 
2086
 
                                        chunk_printf(&msg,
2087
 
                                             "</u></td>"
2088
 
                                             "<td>%lld</td><td>%s</td>"
2089
 
                                             "",
2090
 
                                             svs->counters.down_trans, human_time(srv_downtime(sv), 1));
2091
 
                                } else if (sv != svs)
2092
 
                                        chunk_printf(&msg,
2093
 
                                             "<td class=ac colspan=3><a class=lfsb href=\"#%s/%s\">via %s/%s<a></td>",
2094
 
                                                        svs->proxy->id, svs->id, svs->proxy->id, svs->id);
2095
 
                                else
2096
 
                                        chunk_printf(&msg,
2097
 
                                             "<td colspan=3></td>");
2098
 
 
2099
 
                                /* throttle */
2100
 
                                if (sv->state & SRV_WARMINGUP)
2101
 
                                        chunk_printf(&msg, "<td class=ac>%d %%</td></tr>\n", server_throttle_rate(sv));
2102
 
                                else
2103
 
                                        chunk_printf(&msg, "<td class=ac>-</td></tr>\n");
2104
 
                        } else {
2105
 
                                static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,",
2106
 
                                                               "UP %d/%d,", "UP,",
2107
 
                                                               "NOLB %d/%d,", "NOLB,",
2108
 
                                                               "no check," };
2109
 
                                chunk_printf(&msg,
2110
 
                                     /* pxid, name */
2111
 
                                     "%s,%s,"
2112
 
                                     /* queue : current, max */
2113
 
                                     "%d,%d,"
2114
 
                                     /* sessions : current, max, limit, total */
2115
 
                                     "%d,%d,%s,%lld,"
2116
 
                                     /* bytes : in, out */
2117
 
                                     "%lld,%lld,"
2118
 
                                     /* denied: req, resp */
2119
 
                                     ",%lld,"
2120
 
                                     /* errors : request, connect, response */
2121
 
                                     ",%lld,%lld,"
2122
 
                                     /* warnings: retries, redispatches */
2123
 
                                     "%lld,%lld,"
2124
 
                                     "",
2125
 
                                     px->id, sv->id,
2126
 
                                     sv->nbpend, sv->counters.nbpend_max,
2127
 
                                     sv->cur_sess, sv->counters.cur_sess_max, LIM2A0(sv->maxconn, ""), sv->counters.cum_sess,
2128
 
                                     sv->counters.bytes_in, sv->counters.bytes_out,
2129
 
                                     sv->counters.failed_secu,
2130
 
                                     sv->counters.failed_conns, sv->counters.failed_resp,
2131
 
                                     sv->counters.retries, sv->counters.redispatches);
2132
 
 
2133
 
                                /* status */
2134
 
                                if (sv->state & SRV_MAINTAIN) {
2135
 
                                        chunk_printf(&msg, "MAINT,");
2136
 
                                }
2137
 
                                else if (svs != sv && svs->state & SRV_MAINTAIN) {
2138
 
                                        chunk_printf(&msg, "MAINT(via),");
2139
 
                                }
2140
 
                                else {
2141
 
                                        chunk_printf(&msg,
2142
 
                                            srv_hlt_st[sv_state],
2143
 
                                            (svs->state & SRV_RUNNING) ? (svs->health - svs->rise + 1) : (svs->health),
2144
 
                                            (svs->state & SRV_RUNNING) ? (svs->fall) : (svs->rise));
2145
 
                                }
2146
 
 
2147
 
                                chunk_printf(&msg,
2148
 
                                     /* weight, active, backup */
2149
 
                                     "%d,%d,%d,"
2150
 
                                     "",
2151
 
                                     (sv->eweight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
2152
 
                                     (sv->state & SRV_BACKUP) ? 0 : 1,
2153
 
                                     (sv->state & SRV_BACKUP) ? 1 : 0);
2154
 
 
2155
 
                                /* check failures: unique, fatal; last change, total downtime */
2156
 
                                if (sv->state & SRV_CHECKED)
2157
 
                                        chunk_printf(&msg,
2158
 
                                             "%lld,%lld,%d,%d,",
2159
 
                                             sv->counters.failed_checks, sv->counters.down_trans,
2160
 
                                             (int)(now.tv_sec - sv->last_change), srv_downtime(sv));
2161
 
                                else
2162
 
                                        chunk_printf(&msg,
2163
 
                                             ",,,,");
2164
 
 
2165
 
                                /* queue limit, pid, iid, sid, */
2166
 
                                chunk_printf(&msg,
2167
 
                                     "%s,"
2168
 
                                     "%d,%d,%d,",
2169
 
                                     LIM2A0(sv->maxqueue, ""),
2170
 
                                     relative_pid, px->uuid, sv->puid);
2171
 
 
2172
 
                                /* throttle */
2173
 
                                if (sv->state & SRV_WARMINGUP)
2174
 
                                        chunk_printf(&msg, "%d", server_throttle_rate(sv));
2175
 
 
2176
 
                                /* sessions: lbtot */
2177
 
                                chunk_printf(&msg, ",%lld,", sv->counters.cum_lbconn);
2178
 
 
2179
 
                                /* tracked */
2180
 
                                if (sv->tracked)
2181
 
                                        chunk_printf(&msg, "%s/%s,",
2182
 
                                                sv->tracked->proxy->id, sv->tracked->id);
2183
 
                                else
2184
 
                                        chunk_printf(&msg, ",");
2185
 
 
2186
 
                                /* type */
2187
 
                                chunk_printf(&msg, "%d,", STATS_TYPE_SV);
2188
 
 
2189
 
                                /* rate */
2190
 
                                chunk_printf(&msg, "%u,,%u,",
2191
 
                                             read_freq_ctr(&sv->sess_per_sec),
2192
 
                                             sv->counters.sps_max);
2193
 
 
2194
 
                                if (sv->state & SRV_CHECKED) {
2195
 
                                        /* check_status */
2196
 
                                        chunk_printf(&msg, "%s,", get_check_status_info(sv->check_status));
2197
 
 
2198
 
                                        /* check_code */
2199
 
                                        if (sv->check_status >= HCHK_STATUS_L57DATA)
2200
 
                                                chunk_printf(&msg, "%u,", sv->check_code);
2201
 
                                        else
2202
 
                                                chunk_printf(&msg, ",");
2203
 
 
2204
 
                                        /* check_duration */
2205
 
                                        if (sv->check_status >= HCHK_STATUS_CHECKED)
2206
 
                                                chunk_printf(&msg, "%lu,", sv->check_duration);
2207
 
                                        else
2208
 
                                                chunk_printf(&msg, ",");
2209
 
 
2210
 
                                } else {
2211
 
                                        chunk_printf(&msg, ",,,");
2212
 
                                }
2213
 
 
2214
 
                                /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2215
 
                                if (px->mode == PR_MODE_HTTP) {
2216
 
                                        int i;
2217
 
 
2218
 
                                        for (i=1; i<6; i++)
2219
 
                                                chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[i]);
2220
 
 
2221
 
                                        chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[0]);
2222
 
                                } else {
2223
 
                                        chunk_printf(&msg, ",,,,,,");
2224
 
                                }
2225
 
 
2226
 
                                /* failed health analyses */
2227
 
                                chunk_printf(&msg, "%lld,",  sv->counters.failed_hana);
2228
 
 
2229
 
                                /* requests : req_rate, req_rate_max, req_tot, */
2230
 
                                chunk_printf(&msg, ",,,");
2231
 
 
2232
 
                                /* errors: cli_aborts, srv_aborts */
2233
 
                                chunk_printf(&msg, "%lld,%lld,",
2234
 
                                             sv->counters.cli_aborts, sv->counters.srv_aborts);
2235
 
 
2236
 
                                /* finish with EOL */
2237
 
                                chunk_printf(&msg, "\n");
2238
 
                        }
2239
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
2240
 
                                return 0;
 
3912
                        if (stats_dump_sv_stats(si, px, uri ? uri->flags : 0, sv, sv_state))
 
3913
                                if (bi_putchk(rep, &trash) == -1)
 
3914
                                        return 0;
2241
3915
                } /* for sv */
2242
3916
 
2243
 
                s->data_ctx.stats.px_st = DATA_ST_PX_BE;
 
3917
                appctx->ctx.stats.px_st = STAT_PX_ST_BE;
2244
3918
                /* fall through */
2245
3919
 
2246
 
        case DATA_ST_PX_BE:
 
3920
        case STAT_PX_ST_BE:
2247
3921
                /* print the backend */
2248
 
                if ((px->cap & PR_CAP_BE) &&
2249
 
                    (!(s->data_ctx.stats.flags & STAT_BOUND) || (s->data_ctx.stats.type & (1 << STATS_TYPE_BE)))) {
2250
 
                        if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
2251
 
                                chunk_printf(&msg, "<tr class=\"backend\">");
2252
 
                                if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
2253
 
                                        /* Column sub-heading for Enable or Disable server */
2254
 
                                        chunk_printf(&msg, "<td></td>");
2255
 
                                }
2256
 
                                chunk_printf(&msg, "<td class=ac");
2257
 
 
2258
 
                                if (uri->flags&ST_SHLGNDS) {
2259
 
                                        /* balancing */
2260
 
                                         chunk_printf(&msg, " title=\"balancing: %s",
2261
 
                                                 backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
2262
 
 
2263
 
                                        /* cookie */
2264
 
                                        if (px->cookie_name) {
2265
 
                                                struct chunk src;
2266
 
 
2267
 
                                                chunk_printf(&msg, ", cookie: '");
2268
 
 
2269
 
                                                chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
2270
 
                                                chunk_htmlencode(&msg, &src);
2271
 
 
2272
 
                                                chunk_printf(&msg, "'");
2273
 
                                        }
2274
 
 
2275
 
                                        chunk_printf(&msg, "\"");
2276
 
 
2277
 
                                }
2278
 
 
2279
 
                                chunk_printf(&msg,
2280
 
                                     /* name */
2281
 
                                     ">%s<a name=\"%s/Backend\"></a>"
2282
 
                                     "<a class=lfsb href=\"#%s/Backend\">Backend</a>%s</td>"
2283
 
                                     /* queue : current, max */
2284
 
                                     "<td>%s</td><td>%s</td><td></td>"
2285
 
                                     /* sessions rate : current, max, limit */
2286
 
                                     "<td>%s</td><td>%s</td><td></td>"
2287
 
                                     "",
2288
 
                                     (uri->flags & ST_SHLGNDS)?"<u>":"",
2289
 
                                     px->id, px->id,
2290
 
                                     (uri->flags & ST_SHLGNDS)?"</u>":"",
2291
 
                                     U2H0(px->nbpend) /* or px->totpend ? */, U2H1(px->counters.nbpend_max),
2292
 
                                     U2H2(read_freq_ctr(&px->be_sess_per_sec)), U2H3(px->counters.be_sps_max));
2293
 
 
2294
 
                                chunk_printf(&msg,
2295
 
                                     /* sessions: current, max, limit */
2296
 
                                     "<td>%s</td><td>%s</td><td>%s</td>"
2297
 
                                     "<td"
2298
 
                                     "",
2299
 
                                     U2H2(px->beconn), U2H3(px->counters.beconn_max), U2H4(px->fullconn));
2300
 
 
2301
 
                                /* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
2302
 
                                if (px->mode == PR_MODE_HTTP) {
2303
 
                                        int i;
2304
 
 
2305
 
                                        chunk_printf(&msg, " title=\"rsp codes:");
2306
 
 
2307
 
                                        for (i = 1; i < 6; i++)
2308
 
                                                chunk_printf(&msg, " %dxx=%lld", i, px->counters.be.http.rsp[i]);
2309
 
 
2310
 
                                        chunk_printf(&msg, " other=%lld\"", px->counters.be.http.rsp[0]);
2311
 
                                }
2312
 
 
2313
 
                                chunk_printf(&msg,
2314
 
                                     /* sessions: total, lbtot */
2315
 
                                     ">%s%s%s</td><td>%s</td>"
2316
 
                                     /* bytes: in, out */
2317
 
                                     "<td>%s</td><td>%s</td>"
2318
 
                                     "",
2319
 
                                     (px->mode == PR_MODE_HTTP)?"<u>":"",
2320
 
                                     U2H6(px->counters.cum_beconn),
2321
 
                                     (px->mode == PR_MODE_HTTP)?"</u>":"",
2322
 
                                     U2H7(px->counters.cum_lbconn),
2323
 
                                     U2H8(px->counters.bytes_in), U2H9(px->counters.bytes_out));
2324
 
 
2325
 
                                chunk_printf(&msg,
2326
 
                                     /* denied: req, resp */
2327
 
                                     "<td>%s</td><td>%s</td>"
2328
 
                                     /* errors : request, connect */
2329
 
                                     "<td></td><td>%s</td>"
2330
 
                                     /* errors : response */
2331
 
                                     "<td title=\"Connection resets during transfers: %lld client, %lld server\"><u>%s</u></td>"
2332
 
                                     /* warnings: retries, redispatches */
2333
 
                                     "<td>%lld</td><td>%lld</td>"
2334
 
                                     /* backend status: reflect backend status (up/down): we display UP
2335
 
                                      * if the backend has known working servers or if it has no server at
2336
 
                                      * all (eg: for stats). Then we display the total weight, number of
2337
 
                                      * active and backups. */
2338
 
                                     "<td class=ac>%s %s</td><td class=ac>&nbsp;</td><td class=ac>%d</td>"
2339
 
                                     "<td class=ac>%d</td><td class=ac>%d</td>"
2340
 
                                     "",
2341
 
                                     U2H0(px->counters.denied_req), U2H1(px->counters.denied_resp),
2342
 
                                     U2H2(px->counters.failed_conns),
2343
 
                                     px->counters.cli_aborts,
2344
 
                                     px->counters.srv_aborts,
2345
 
                                     U2H5(px->counters.failed_resp),
2346
 
                                     px->counters.retries, px->counters.redispatches,
2347
 
                                     human_time(now.tv_sec - px->last_change, 1),
2348
 
                                     (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" :
2349
 
                                             "<font color=\"red\"><b>DOWN</b></font>",
2350
 
                                     (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
2351
 
                                     px->srv_act, px->srv_bck);
2352
 
 
2353
 
                                chunk_printf(&msg,
2354
 
                                     /* rest of backend: nothing, down transitions, total downtime, throttle */
2355
 
                                     "<td class=ac>&nbsp;</td><td>%d</td>"
2356
 
                                     "<td>%s</td>"
2357
 
                                     "<td></td>"
2358
 
                                     "</tr>",
2359
 
                                     px->down_trans,
2360
 
                                     px->srv?human_time(be_downtime(px), 1):"&nbsp;");
2361
 
                        } else {
2362
 
                                chunk_printf(&msg,
2363
 
                                     /* pxid, name */
2364
 
                                     "%s,BACKEND,"
2365
 
                                     /* queue : current, max */
2366
 
                                     "%d,%d,"
2367
 
                                     /* sessions : current, max, limit, total */
2368
 
                                     "%d,%d,%d,%lld,"
2369
 
                                     /* bytes : in, out */
2370
 
                                     "%lld,%lld,"
2371
 
                                     /* denied: req, resp */
2372
 
                                     "%lld,%lld,"
2373
 
                                     /* errors : request, connect, response */
2374
 
                                     ",%lld,%lld,"
2375
 
                                     /* warnings: retries, redispatches */
2376
 
                                     "%lld,%lld,"
2377
 
                                     /* backend status: reflect backend status (up/down): we display UP
2378
 
                                      * if the backend has known working servers or if it has no server at
2379
 
                                      * all (eg: for stats). Then we display the total weight, number of
2380
 
                                      * active and backups. */
2381
 
                                     "%s,"
2382
 
                                     "%d,%d,%d,"
2383
 
                                     /* rest of backend: nothing, down transitions, last change, total downtime */
2384
 
                                     ",%d,%d,%d,,"
2385
 
                                     /* pid, iid, sid, throttle, lbtot, tracked, type */
2386
 
                                     "%d,%d,0,,%lld,,%d,"
2387
 
                                     /* rate, rate_lim, rate_max, */
2388
 
                                     "%u,,%u,"
2389
 
                                     /* check_status, check_code, check_duration */
2390
 
                                     ",,,",
2391
 
                                     px->id,
2392
 
                                     px->nbpend /* or px->totpend ? */, px->counters.nbpend_max,
2393
 
                                     px->beconn, px->counters.beconn_max, px->fullconn, px->counters.cum_beconn,
2394
 
                                     px->counters.bytes_in, px->counters.bytes_out,
2395
 
                                     px->counters.denied_req, px->counters.denied_resp,
2396
 
                                     px->counters.failed_conns, px->counters.failed_resp,
2397
 
                                     px->counters.retries, px->counters.redispatches,
2398
 
                                     (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN",
2399
 
                                     (px->lbprm.tot_weight * px->lbprm.wmult + px->lbprm.wdiv - 1) / px->lbprm.wdiv,
2400
 
                                     px->srv_act, px->srv_bck,
2401
 
                                     px->down_trans, (int)(now.tv_sec - px->last_change),
2402
 
                                     px->srv?be_downtime(px):0,
2403
 
                                     relative_pid, px->uuid,
2404
 
                                     px->counters.cum_lbconn, STATS_TYPE_BE,
2405
 
                                     read_freq_ctr(&px->be_sess_per_sec),
2406
 
                                     px->counters.be_sps_max);
2407
 
 
2408
 
                                /* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2409
 
                                if (px->mode == PR_MODE_HTTP) {
2410
 
                                        int i;
2411
 
 
2412
 
                                        for (i=1; i<6; i++)
2413
 
                                                chunk_printf(&msg, "%lld,", px->counters.be.http.rsp[i]);
2414
 
 
2415
 
                                        chunk_printf(&msg, "%lld,", px->counters.be.http.rsp[0]);
2416
 
                                } else {
2417
 
                                        chunk_printf(&msg, ",,,,,,");
2418
 
                                }
2419
 
 
2420
 
                                /* failed health analyses */
2421
 
                                chunk_printf(&msg, ",");
2422
 
 
2423
 
                                /* requests : req_rate, req_rate_max, req_tot, */
2424
 
                                chunk_printf(&msg, ",,,");
2425
 
 
2426
 
                                /* errors: cli_aborts, srv_aborts */
2427
 
                                chunk_printf(&msg, "%lld,%lld,",
2428
 
                                             px->counters.cli_aborts, px->counters.srv_aborts);
2429
 
 
2430
 
                                /* finish with EOL */
2431
 
                                chunk_printf(&msg, "\n");
2432
 
 
2433
 
                        }
2434
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
2435
 
                                return 0;
2436
 
                }
2437
 
 
2438
 
                s->data_ctx.stats.px_st = DATA_ST_PX_END;
2439
 
                /* fall through */
2440
 
 
2441
 
        case DATA_ST_PX_END:
2442
 
                if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
2443
 
                        chunk_printf(&msg, "</table>");
2444
 
 
2445
 
                        if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
2446
 
                                /* close the form used to enable/disable this proxy servers */
2447
 
                                chunk_printf(&msg,
2448
 
                                        "Choose the action to perform on the checked servers : "
2449
 
                                        "<select name=action>"
2450
 
                                        "<option value=\"\"></option>"
2451
 
                                        "<option value=\"disable\">Disable</option>"
2452
 
                                        "<option value=\"enable\">Enable</option>"
2453
 
                                        "<option value=\"stop\">Soft Stop</option>"
2454
 
                                        "<option value=\"start\">Soft Start</option>"
2455
 
                                        "</select>"
2456
 
                                        "<input type=\"hidden\" name=\"b\" value=\"#%d\">"
2457
 
                                        "&nbsp;<input type=\"submit\" value=\"Apply\">"
2458
 
                                        "</form>",
2459
 
                                        px->uuid);
2460
 
                        }
2461
 
 
2462
 
                        chunk_printf(&msg, "<p>\n");
2463
 
 
2464
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
2465
 
                                return 0;
2466
 
                }
2467
 
 
2468
 
                s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
2469
 
                /* fall through */
2470
 
 
2471
 
        case DATA_ST_PX_FIN:
 
3922
                if (stats_dump_be_stats(si, px, uri ? uri->flags : 0))
 
3923
                        if (bi_putchk(rep, &trash) == -1)
 
3924
                                return 0;
 
3925
 
 
3926
                appctx->ctx.stats.px_st = STAT_PX_ST_END;
 
3927
                /* fall through */
 
3928
 
 
3929
        case STAT_PX_ST_END:
 
3930
                if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
3931
                        stats_dump_html_px_end(si, px);
 
3932
                        if (bi_putchk(rep, &trash) == -1)
 
3933
                                return 0;
 
3934
                }
 
3935
 
 
3936
                appctx->ctx.stats.px_st = STAT_PX_ST_FIN;
 
3937
                /* fall through */
 
3938
 
 
3939
        case STAT_PX_ST_FIN:
2472
3940
                return 1;
2473
3941
 
2474
3942
        default:
2477
3945
        }
2478
3946
}
2479
3947
 
2480
 
/* This function is called to send output to the response buffer. It dumps a
2481
 
 * complete session state onto the output buffer <rep>. The session has to be
2482
 
 * set in data_ctx.sess.target. It returns 0 if the output buffer is full and
2483
 
 * it needs to be called again, otherwise non-zero. It is designed to be called
2484
 
 * from stats_dump_sess_to_buffer() below.
2485
 
 */
2486
 
 
2487
 
/* returns 1 if dump is not complete */
2488
 
int stats_dump_full_sess_to_buffer(struct session *s, struct buffer *rep)
2489
 
{
 
3948
/* Dumps the HTTP stats head block to the trash for and uses the per-uri
 
3949
 * parameters <uri>. The caller is responsible for clearing the trash if needed.
 
3950
 */
 
3951
static void stats_dump_html_head(struct uri_auth *uri)
 
3952
{
 
3953
        /* WARNING! This must fit in the first buffer !!! */
 
3954
        chunk_appendf(&trash,
 
3955
                      "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n"
 
3956
                      "\"http://www.w3.org/TR/html4/loose.dtd\">\n"
 
3957
                      "<html><head><title>Statistics Report for " PRODUCT_NAME "%s%s</title>\n"
 
3958
                      "<meta http-equiv=\"content-type\" content=\"text/html; charset=iso-8859-1\">\n"
 
3959
                      "<style type=\"text/css\"><!--\n"
 
3960
                      "body {"
 
3961
                      " font-family: arial, helvetica, sans-serif;"
 
3962
                      " font-size: 12px;"
 
3963
                      " font-weight: normal;"
 
3964
                      " color: black;"
 
3965
                      " background: white;"
 
3966
                      "}\n"
 
3967
                      "th,td {"
 
3968
                      " font-size: 10px;"
 
3969
                      "}\n"
 
3970
                      "h1 {"
 
3971
                      " font-size: x-large;"
 
3972
                      " margin-bottom: 0.5em;"
 
3973
                      "}\n"
 
3974
                      "h2 {"
 
3975
                      " font-family: helvetica, arial;"
 
3976
                      " font-size: x-large;"
 
3977
                      " font-weight: bold;"
 
3978
                      " font-style: italic;"
 
3979
                      " color: #6020a0;"
 
3980
                      " margin-top: 0em;"
 
3981
                      " margin-bottom: 0em;"
 
3982
                      "}\n"
 
3983
                      "h3 {"
 
3984
                      " font-family: helvetica, arial;"
 
3985
                      " font-size: 16px;"
 
3986
                      " font-weight: bold;"
 
3987
                      " color: #b00040;"
 
3988
                      " background: #e8e8d0;"
 
3989
                      " margin-top: 0em;"
 
3990
                      " margin-bottom: 0em;"
 
3991
                      "}\n"
 
3992
                      "li {"
 
3993
                      " margin-top: 0.25em;"
 
3994
                      " margin-right: 2em;"
 
3995
                      "}\n"
 
3996
                      ".hr {margin-top: 0.25em;"
 
3997
                      " border-color: black;"
 
3998
                      " border-bottom-style: solid;"
 
3999
                      "}\n"
 
4000
                      ".titre   {background: #20D0D0;color: #000000; font-weight: bold; text-align: center;}\n"
 
4001
                      ".total   {background: #20D0D0;color: #ffff80;}\n"
 
4002
                      ".frontend        {background: #e8e8d0;}\n"
 
4003
                      ".socket  {background: #d0d0d0;}\n"
 
4004
                      ".backend {background: #e8e8d0;}\n"
 
4005
                      ".active0 {background: #ff9090;}\n"
 
4006
                      ".active1 {background: #ff9090;}\n"
 
4007
                      ".active2 {background: #ffd020;}\n"
 
4008
                      ".active3 {background: #ffffa0;}\n"
 
4009
                      ".active4 {background: #c0ffc0;}\n"
 
4010
                      ".active5 {background: #ffffa0;}\n"  /* NOLB state shows same as going down */
 
4011
                      ".active6 {background: #20a0ff;}\n"  /* NOLB state shows different to be detected */
 
4012
                      ".active7 {background: #ffffa0;}\n"  /* DRAIN going down = same as going down */
 
4013
                      ".active8 {background: #20a0FF;}\n"  /* DRAIN must be detected (weight=0) */
 
4014
                      ".active9 {background: #e0e0e0;}\n"
 
4015
                      ".backup0 {background: #ff9090;}\n"
 
4016
                      ".backup1 {background: #ff9090;}\n"
 
4017
                      ".backup2 {background: #ff80ff;}\n"
 
4018
                      ".backup3 {background: #c060ff;}\n"
 
4019
                      ".backup4 {background: #b0d0ff;}\n"
 
4020
                      ".backup5 {background: #c060ff;}\n"  /* NOLB state shows same as going down */
 
4021
                      ".backup6 {background: #90b0e0;}\n"  /* NOLB state shows same as going down */
 
4022
                      ".backup7 {background: #c060ff;}\n"
 
4023
                      ".backup8 {background: #cc9900;}\n"
 
4024
                      ".backup9 {background: #e0e0e0;}\n"
 
4025
                      ".maintain        {background: #c07820;}\n"
 
4026
                      ".rls      {letter-spacing: 0.2em; margin-right: 1px;}\n" /* right letter spacing (used for grouping digits) */
 
4027
                      "\n"
 
4028
                      "a.px:link {color: #ffff40; text-decoration: none;}"
 
4029
                      "a.px:visited {color: #ffff40; text-decoration: none;}"
 
4030
                      "a.px:hover {color: #ffffff; text-decoration: none;}"
 
4031
                      "a.lfsb:link {color: #000000; text-decoration: none;}"
 
4032
                      "a.lfsb:visited {color: #000000; text-decoration: none;}"
 
4033
                      "a.lfsb:hover {color: #505050; text-decoration: none;}"
 
4034
                      "\n"
 
4035
                      "table.tbl { border-collapse: collapse; border-style: none;}\n"
 
4036
                      "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"
 
4037
                      "table.tbl td.ac { text-align: center;}\n"
 
4038
                      "table.tbl th { border-width: 1px; border-style: solid solid solid solid; border-color: gray;}\n"
 
4039
                      "table.tbl th.pxname { background: #b00040; color: #ffff40; font-weight: bold; border-style: solid solid none solid; padding: 2px 3px; white-space: nowrap;}\n"
 
4040
                      "table.tbl th.empty { border-style: none; empty-cells: hide; background: white;}\n"
 
4041
                      "table.tbl th.desc { background: white; border-style: solid solid none solid; text-align: left; padding: 2px 3px;}\n"
 
4042
                      "\n"
 
4043
                      "table.lgd { border-collapse: collapse; border-width: 1px; border-style: none none none solid; border-color: black;}\n"
 
4044
                      "table.lgd td { border-width: 1px; border-style: solid solid solid solid; border-color: gray; padding: 2px;}\n"
 
4045
                      "table.lgd td.noborder { border-style: none; padding: 2px; white-space: nowrap;}\n"
 
4046
                      "table.det { border-collapse: collapse; border-style: none; }\n"
 
4047
                      "table.det th { text-align: left; border-width: 0px; padding: 0px 1px 0px 0px; font-style:normal;font-size:11px;font-weight:bold;font-family: sans-serif;}\n"
 
4048
                      "table.det td { text-align: right; border-width: 0px; padding: 0px 0px 0px 4px; white-space: nowrap; font-style:normal;font-size:11px;font-weight:normal;}\n"
 
4049
                      "u {text-decoration:none; border-bottom: 1px dotted black;}\n"
 
4050
                      "div.tips {\n"
 
4051
                      " display:block;\n"
 
4052
                      " visibility:hidden;\n"
 
4053
                      " z-index:2147483647;\n"
 
4054
                      " position:absolute;\n"
 
4055
                      " padding:2px 4px 3px;\n"
 
4056
                      " background:#f0f060; color:#000000;\n"
 
4057
                      " border:1px solid #7040c0;\n"
 
4058
                      " white-space:nowrap;\n"
 
4059
                      " font-style:normal;font-size:11px;font-weight:normal;\n"
 
4060
                      " -moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;\n"
 
4061
                      " -moz-box-shadow:gray 2px 2px 3px;-webkit-box-shadow:gray 2px 2px 3px;box-shadow:gray 2px 2px 3px;\n"
 
4062
                      "}\n"
 
4063
                      "u:hover div.tips {visibility:visible;}\n"
 
4064
                      "-->\n"
 
4065
                      "</style></head>\n",
 
4066
                      (uri->flags & ST_SHNODE) ? " on " : "",
 
4067
                      (uri->flags & ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
 
4068
                      );
 
4069
}
 
4070
 
 
4071
/* Dumps the HTML stats information block to the trash for and uses the state from
 
4072
 * stream interface <si> and per-uri parameters <uri>. The caller is responsible
 
4073
 * for clearing the trash if needed.
 
4074
 */
 
4075
static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *uri)
 
4076
{
 
4077
        struct appctx *appctx = __objt_appctx(si->end);
 
4078
        unsigned int up = (now.tv_sec - start_date.tv_sec);
 
4079
        char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
 
4080
 
 
4081
        /* WARNING! this has to fit the first packet too.
 
4082
         * We are around 3.5 kB, add adding entries will
 
4083
         * become tricky if we want to support 4kB buffers !
 
4084
         */
 
4085
        chunk_appendf(&trash,
 
4086
                      "<body><h1><a href=\"" PRODUCT_URL "\" style=\"text-decoration: none;\">"
 
4087
                      PRODUCT_NAME "%s</a></h1>\n"
 
4088
                      "<h2>Statistics Report for pid %d%s%s%s%s</h2>\n"
 
4089
                      "<hr width=\"100%%\" class=\"hr\">\n"
 
4090
                      "<h3>&gt; General process information</h3>\n"
 
4091
                      "<table border=0><tr><td align=\"left\" nowrap width=\"1%%\">\n"
 
4092
                      "<p><b>pid = </b> %d (process #%d, nbproc = %d)<br>\n"
 
4093
                      "<b>uptime = </b> %dd %dh%02dm%02ds<br>\n"
 
4094
                      "<b>system limits:</b> memmax = %s%s; ulimit-n = %d<br>\n"
 
4095
                      "<b>maxsock = </b> %d; <b>maxconn = </b> %d; <b>maxpipes = </b> %d<br>\n"
 
4096
                      "current conns = %d; current pipes = %d/%d; conn rate = %d/sec<br>\n"
 
4097
                      "Running tasks: %d/%d; idle = %d %%<br>\n"
 
4098
                      "</td><td align=\"center\" nowrap>\n"
 
4099
                      "<table class=\"lgd\"><tr>\n"
 
4100
                      "<td class=\"active4\">&nbsp;</td><td class=\"noborder\">active UP </td>"
 
4101
                      "<td class=\"backup4\">&nbsp;</td><td class=\"noborder\">backup UP </td>"
 
4102
                      "</tr><tr>\n"
 
4103
                      "<td class=\"active3\"></td><td class=\"noborder\">active UP, going down </td>"
 
4104
                      "<td class=\"backup3\"></td><td class=\"noborder\">backup UP, going down </td>"
 
4105
                      "</tr><tr>\n"
 
4106
                      "<td class=\"active2\"></td><td class=\"noborder\">active DOWN, going up </td>"
 
4107
                      "<td class=\"backup2\"></td><td class=\"noborder\">backup DOWN, going up </td>"
 
4108
                      "</tr><tr>\n"
 
4109
                      "<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN &nbsp;</td>"
 
4110
                      "<td class=\"active9\"></td><td class=\"noborder\">not checked </td>"
 
4111
                      "</tr><tr>\n"
 
4112
                      "<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) &nbsp;</td>"
 
4113
                      "</tr><tr>\n"
 
4114
                      "<td class=\"active8\"></td><td class=\"noborder\" colspan=\"3\">active or backup SOFT STOPPED for maintenance &nbsp;</td>"
 
4115
                      "</tr></table>\n"
 
4116
                      "Note: \"NOLB\"/\"DRAIN\" = UP with load-balancing disabled."
 
4117
                      "</td>"
 
4118
                      "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
 
4119
                      "<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
 
4120
                      "",
 
4121
                      (uri->flags & ST_HIDEVER) ? "" : (STATS_VERSION_STRING),
 
4122
                      pid, (uri->flags & ST_SHNODE) ? " on " : "",
 
4123
                      (uri->flags & ST_SHNODE) ? (uri->node ? uri->node : global.node) : "",
 
4124
                      (uri->flags & ST_SHDESC) ? ": " : "",
 
4125
                      (uri->flags & ST_SHDESC) ? (uri->desc ? uri->desc : global.desc) : "",
 
4126
                      pid, relative_pid, global.nbproc,
 
4127
                      up / 86400, (up % 86400) / 3600,
 
4128
                      (up % 3600) / 60, (up % 60),
 
4129
                      global.rlimit_memmax ? ultoa(global.rlimit_memmax) : "unlimited",
 
4130
                      global.rlimit_memmax ? " MB" : "",
 
4131
                      global.rlimit_nofile,
 
4132
                      global.maxsock, global.maxconn, global.maxpipes,
 
4133
                      actconn, pipes_used, pipes_used+pipes_free, read_freq_ctr(&global.conn_per_sec),
 
4134
                      run_queue_cur, nb_tasks_cur, idle_pct
 
4135
                      );
 
4136
 
 
4137
        /* scope_txt = search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
 
4138
        memcpy(scope_txt, bo_ptr(si->ob->buf) + appctx->ctx.stats.scope_str, appctx->ctx.stats.scope_len);
 
4139
        scope_txt[appctx->ctx.stats.scope_len] = '\0';
 
4140
 
 
4141
        chunk_appendf(&trash,
 
4142
                      "<li><form method=\"GET\" action=\"%s%s%s\">Scope : <input value=\"%s\" name=\"" STAT_SCOPE_INPUT_NAME "\" size=\"8\" maxlength=\"%d\" tabindex=\"1\"/></form>\n",
 
4143
                      uri->uri_prefix,
 
4144
                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4145
                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4146
                      (appctx->ctx.stats.scope_len > 0) ? scope_txt : "",
 
4147
                      STAT_SCOPE_TXT_MAXLEN);
 
4148
 
 
4149
        /* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
 
4150
        scope_txt[0] = 0;
 
4151
        if (appctx->ctx.stats.scope_len) {
 
4152
                strcpy(scope_txt, STAT_SCOPE_PATTERN);
 
4153
                memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), bo_ptr(si->ob->buf) + appctx->ctx.stats.scope_str, appctx->ctx.stats.scope_len);
 
4154
                scope_txt[strlen(STAT_SCOPE_PATTERN) + appctx->ctx.stats.scope_len] = 0;
 
4155
        }
 
4156
 
 
4157
        if (appctx->ctx.stats.flags & STAT_HIDE_DOWN)
 
4158
                chunk_appendf(&trash,
 
4159
                              "<li><a href=\"%s%s%s%s\">Show all servers</a><br>\n",
 
4160
                              uri->uri_prefix,
 
4161
                              "",
 
4162
                              (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4163
                              scope_txt);
 
4164
        else
 
4165
                chunk_appendf(&trash,
 
4166
                              "<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
 
4167
                              uri->uri_prefix,
 
4168
                              ";up",
 
4169
                              (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4170
                              scope_txt);
 
4171
 
 
4172
        if (uri->refresh > 0) {
 
4173
                if (appctx->ctx.stats.flags & STAT_NO_REFRESH)
 
4174
                        chunk_appendf(&trash,
 
4175
                                      "<li><a href=\"%s%s%s%s\">Enable refresh</a><br>\n",
 
4176
                                      uri->uri_prefix,
 
4177
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4178
                                      "",
 
4179
                                      scope_txt);
 
4180
                else
 
4181
                        chunk_appendf(&trash,
 
4182
                                      "<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
 
4183
                                      uri->uri_prefix,
 
4184
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4185
                                      ";norefresh",
 
4186
                                      scope_txt);
 
4187
        }
 
4188
 
 
4189
        chunk_appendf(&trash,
 
4190
                      "<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
 
4191
                      uri->uri_prefix,
 
4192
                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4193
                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4194
                      scope_txt);
 
4195
 
 
4196
        chunk_appendf(&trash,
 
4197
                      "<li><a href=\"%s;csv%s%s\">CSV export</a><br>\n",
 
4198
                      uri->uri_prefix,
 
4199
                      (uri->refresh > 0) ? ";norefresh" : "",
 
4200
                      scope_txt);
 
4201
 
 
4202
        chunk_appendf(&trash,
 
4203
                      "</ul></td>"
 
4204
                      "<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
 
4205
                      "<b>External resources:</b><ul style=\"margin-top: 0.25em;\">\n"
 
4206
                      "<li><a href=\"" PRODUCT_URL "\">Primary site</a><br>\n"
 
4207
                      "<li><a href=\"" PRODUCT_URL_UPD "\">Updates (v" PRODUCT_BRANCH ")</a><br>\n"
 
4208
                      "<li><a href=\"" PRODUCT_URL_DOC "\">Online manual</a><br>\n"
 
4209
                      "</ul>"
 
4210
                      "</td>"
 
4211
                      "</tr></table>\n"
 
4212
                      ""
 
4213
                      );
 
4214
 
 
4215
        if (appctx->ctx.stats.st_code) {
 
4216
                switch (appctx->ctx.stats.st_code) {
 
4217
                case STAT_STATUS_DONE:
 
4218
                        chunk_appendf(&trash,
 
4219
                                      "<p><div class=active4>"
 
4220
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4221
                                      "Action processed successfully."
 
4222
                                      "</div>\n", uri->uri_prefix,
 
4223
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4224
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4225
                                      scope_txt);
 
4226
                        break;
 
4227
                case STAT_STATUS_NONE:
 
4228
                        chunk_appendf(&trash,
 
4229
                                      "<p><div class=active3>"
 
4230
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4231
                                      "Nothing has changed."
 
4232
                                      "</div>\n", uri->uri_prefix,
 
4233
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4234
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4235
                                      scope_txt);
 
4236
                        break;
 
4237
                case STAT_STATUS_PART:
 
4238
                        chunk_appendf(&trash,
 
4239
                                      "<p><div class=active3>"
 
4240
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4241
                                      "Action partially processed.<br>"
 
4242
                                      "Some server names are probably unknown or ambiguous (duplicated names in the backend)."
 
4243
                                      "</div>\n", uri->uri_prefix,
 
4244
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4245
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4246
                                      scope_txt);
 
4247
                        break;
 
4248
                case STAT_STATUS_ERRP:
 
4249
                        chunk_appendf(&trash,
 
4250
                                      "<p><div class=active0>"
 
4251
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4252
                                      "Action not processed because of invalid parameters."
 
4253
                                      "<ul>"
 
4254
                                      "<li>The action is maybe unknown.</li>"
 
4255
                                      "<li>The backend name is probably unknown or ambiguous (duplicated names).</li>"
 
4256
                                      "<li>Some server names are probably unknown or ambiguous (duplicated names in the backend).</li>"
 
4257
                                      "</ul>"
 
4258
                                      "</div>\n", uri->uri_prefix,
 
4259
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4260
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4261
                                      scope_txt);
 
4262
                        break;
 
4263
                case STAT_STATUS_EXCD:
 
4264
                        chunk_appendf(&trash,
 
4265
                                      "<p><div class=active0>"
 
4266
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4267
                                      "<b>Action not processed : the buffer couldn't store all the data.<br>"
 
4268
                                      "You should retry with less servers at a time.</b>"
 
4269
                                      "</div>\n", uri->uri_prefix,
 
4270
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4271
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4272
                                      scope_txt);
 
4273
                        break;
 
4274
                case STAT_STATUS_DENY:
 
4275
                        chunk_appendf(&trash,
 
4276
                                      "<p><div class=active0>"
 
4277
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4278
                                      "<b>Action denied.</b>"
 
4279
                                      "</div>\n", uri->uri_prefix,
 
4280
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4281
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4282
                                      scope_txt);
 
4283
                        break;
 
4284
                default:
 
4285
                        chunk_appendf(&trash,
 
4286
                                      "<p><div class=active9>"
 
4287
                                      "<a class=lfsb href=\"%s%s%s%s\" title=\"Remove this message\">[X]</a> "
 
4288
                                      "Unexpected result."
 
4289
                                      "</div>\n", uri->uri_prefix,
 
4290
                                      (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4291
                                      (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4292
                                      scope_txt);
 
4293
                }
 
4294
                chunk_appendf(&trash, "<p>\n");
 
4295
        }
 
4296
}
 
4297
 
 
4298
/* Dumps the HTML stats trailer block to the trash. The caller is responsible
 
4299
 * for clearing the trash if needed.
 
4300
 */
 
4301
static void stats_dump_html_end()
 
4302
{
 
4303
        chunk_appendf(&trash, "</body></html>\n");
 
4304
}
 
4305
 
 
4306
/* This function dumps statistics onto the stream interface's read buffer in
 
4307
 * either CSV or HTML format. <uri> contains some HTML-specific parameters that
 
4308
 * are ignored for CSV format (hence <uri> may be NULL there). It returns 0 if
 
4309
 * it had to stop writing data and an I/O is needed, 1 if the dump is finished
 
4310
 * and the session must be closed, or -1 in case of any error. This function is
 
4311
 * used by both the CLI and the HTTP handlers.
 
4312
 */
 
4313
static int stats_dump_stat_to_buffer(struct stream_interface *si, struct uri_auth *uri)
 
4314
{
 
4315
        struct appctx *appctx = __objt_appctx(si->end);
 
4316
        struct channel *rep = si->ib;
 
4317
        struct proxy *px;
 
4318
 
 
4319
        chunk_reset(&trash);
 
4320
 
 
4321
        switch (appctx->st2) {
 
4322
        case STAT_ST_INIT:
 
4323
                appctx->st2 = STAT_ST_HEAD; /* let's start producing data */
 
4324
                /* fall through */
 
4325
 
 
4326
        case STAT_ST_HEAD:
 
4327
                if (appctx->ctx.stats.flags & STAT_FMT_HTML)
 
4328
                        stats_dump_html_head(uri);
 
4329
                else
 
4330
                        stats_dump_csv_header();
 
4331
 
 
4332
                if (bi_putchk(rep, &trash) == -1)
 
4333
                        return 0;
 
4334
 
 
4335
                appctx->st2 = STAT_ST_INFO;
 
4336
                /* fall through */
 
4337
 
 
4338
        case STAT_ST_INFO:
 
4339
                if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
4340
                        stats_dump_html_info(si, uri);
 
4341
                        if (bi_putchk(rep, &trash) == -1)
 
4342
                                return 0;
 
4343
                }
 
4344
 
 
4345
                appctx->ctx.stats.px = proxy;
 
4346
                appctx->ctx.stats.px_st = STAT_PX_ST_INIT;
 
4347
                appctx->st2 = STAT_ST_LIST;
 
4348
                /* fall through */
 
4349
 
 
4350
        case STAT_ST_LIST:
 
4351
                /* dump proxies */
 
4352
                while (appctx->ctx.stats.px) {
 
4353
                        if (buffer_almost_full(rep->buf)) {
 
4354
                                rep->flags |= CF_WAKE_WRITE;
 
4355
                                return 0;
 
4356
                        }
 
4357
 
 
4358
                        px = appctx->ctx.stats.px;
 
4359
                        /* skip the disabled proxies, global frontend and non-networked ones */
 
4360
                        if (px->state != PR_STSTOPPED && px->uuid > 0 && (px->cap & (PR_CAP_FE | PR_CAP_BE)))
 
4361
                                if (stats_dump_proxy_to_buffer(si, px, uri) == 0)
 
4362
                                        return 0;
 
4363
 
 
4364
                        appctx->ctx.stats.px = px->next;
 
4365
                        appctx->ctx.stats.px_st = STAT_PX_ST_INIT;
 
4366
                }
 
4367
                /* here, we just have reached the last proxy */
 
4368
 
 
4369
                appctx->st2 = STAT_ST_END;
 
4370
                /* fall through */
 
4371
 
 
4372
        case STAT_ST_END:
 
4373
                if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
 
4374
                        stats_dump_html_end();
 
4375
                        if (bi_putchk(rep, &trash) == -1)
 
4376
                                return 0;
 
4377
                }
 
4378
 
 
4379
                appctx->st2 = STAT_ST_FIN;
 
4380
                /* fall through */
 
4381
 
 
4382
        case STAT_ST_FIN:
 
4383
                return 1;
 
4384
 
 
4385
        default:
 
4386
                /* unknown state ! */
 
4387
                appctx->st2 = STAT_ST_FIN;
 
4388
                return -1;
 
4389
        }
 
4390
}
 
4391
 
 
4392
/* We reached the stats page through a POST request. The appctx is
 
4393
 * expected to have already been allocated by the caller.
 
4394
 * Parse the posted data and enable/disable servers if necessary.
 
4395
 * Returns 1 if request was parsed or zero if it needs more data.
 
4396
 */
 
4397
static int stats_process_http_post(struct stream_interface *si)
 
4398
{
 
4399
        struct session *s = session_from_task(si->owner);
 
4400
        struct appctx *appctx = objt_appctx(si->end);
 
4401
 
 
4402
        struct proxy *px = NULL;
 
4403
        struct server *sv = NULL;
 
4404
 
 
4405
        char key[LINESIZE];
 
4406
        int action = ST_ADM_ACTION_NONE;
 
4407
        int reprocess = 0;
 
4408
 
 
4409
        int total_servers = 0;
 
4410
        int altered_servers = 0;
 
4411
 
 
4412
        char *first_param, *cur_param, *next_param, *end_params;
 
4413
        char *st_cur_param = NULL;
 
4414
        char *st_next_param = NULL;
 
4415
 
 
4416
        struct chunk *temp;
 
4417
        int reql;
 
4418
 
 
4419
        temp = get_trash_chunk();
 
4420
        if (temp->size < s->txn.req.body_len) {
 
4421
                /* too large request */
 
4422
                appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
 
4423
                goto out;
 
4424
        }
 
4425
 
 
4426
        reql = bo_getblk(si->ob, temp->str, s->txn.req.body_len, s->txn.req.eoh + 2);
 
4427
        if (reql <= 0) {
 
4428
                /* we need more data */
 
4429
                appctx->ctx.stats.st_code = STAT_STATUS_NONE;
 
4430
                return 0;
 
4431
        }
 
4432
 
 
4433
        first_param = temp->str;
 
4434
        end_params  = temp->str + reql;
 
4435
        cur_param = next_param = end_params;
 
4436
        *end_params = '\0';
 
4437
 
 
4438
        appctx->ctx.stats.st_code = STAT_STATUS_NONE;
 
4439
 
 
4440
        /*
 
4441
         * Parse the parameters in reverse order to only store the last value.
 
4442
         * From the html form, the backend and the action are at the end.
 
4443
         */
 
4444
        while (cur_param > first_param) {
 
4445
                char *value;
 
4446
                int poffset, plen;
 
4447
 
 
4448
                cur_param--;
 
4449
 
 
4450
                if ((*cur_param == '&') || (cur_param == first_param)) {
 
4451
                reprocess_servers:
 
4452
                        /* Parse the key */
 
4453
                        poffset = (cur_param != first_param ? 1 : 0);
 
4454
                        plen = next_param - cur_param + (cur_param == first_param ? 1 : 0);
 
4455
                        if ((plen > 0) && (plen <= sizeof(key))) {
 
4456
                                strncpy(key, cur_param + poffset, plen);
 
4457
                                key[plen - 1] = '\0';
 
4458
                        } else {
 
4459
                                appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
 
4460
                                goto out;
 
4461
                        }
 
4462
 
 
4463
                        /* Parse the value */
 
4464
                        value = key;
 
4465
                        while (*value != '\0' && *value != '=') {
 
4466
                                value++;
 
4467
                        }
 
4468
                        if (*value == '=') {
 
4469
                                /* Ok, a value is found, we can mark the end of the key */
 
4470
                                *value++ = '\0';
 
4471
                        }
 
4472
                        if (url_decode(key) < 0 || url_decode(value) < 0)
 
4473
                                break;
 
4474
 
 
4475
                        /* Now we can check the key to see what to do */
 
4476
                        if (!px && (strcmp(key, "b") == 0)) {
 
4477
                                if ((px = findproxy(value, PR_CAP_BE)) == NULL) {
 
4478
                                        /* the backend name is unknown or ambiguous (duplicate names) */
 
4479
                                        appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
 
4480
                                        goto out;
 
4481
                                }
 
4482
                        }
 
4483
                        else if (!action && (strcmp(key, "action") == 0)) {
 
4484
                                if (strcmp(value, "ready") == 0) {
 
4485
                                        action = ST_ADM_ACTION_READY;
 
4486
                                }
 
4487
                                else if (strcmp(value, "drain") == 0) {
 
4488
                                        action = ST_ADM_ACTION_DRAIN;
 
4489
                                }
 
4490
                                else if (strcmp(value, "maint") == 0) {
 
4491
                                        action = ST_ADM_ACTION_MAINT;
 
4492
                                }
 
4493
                                else if (strcmp(value, "shutdown") == 0) {
 
4494
                                        action = ST_ADM_ACTION_SHUTDOWN;
 
4495
                                }
 
4496
                                else if (strcmp(value, "dhlth") == 0) {
 
4497
                                        action = ST_ADM_ACTION_DHLTH;
 
4498
                                }
 
4499
                                else if (strcmp(value, "ehlth") == 0) {
 
4500
                                        action = ST_ADM_ACTION_EHLTH;
 
4501
                                }
 
4502
                                else if (strcmp(value, "hrunn") == 0) {
 
4503
                                        action = ST_ADM_ACTION_HRUNN;
 
4504
                                }
 
4505
                                else if (strcmp(value, "hnolb") == 0) {
 
4506
                                        action = ST_ADM_ACTION_HNOLB;
 
4507
                                }
 
4508
                                else if (strcmp(value, "hdown") == 0) {
 
4509
                                        action = ST_ADM_ACTION_HDOWN;
 
4510
                                }
 
4511
                                else if (strcmp(value, "dagent") == 0) {
 
4512
                                        action = ST_ADM_ACTION_DAGENT;
 
4513
                                }
 
4514
                                else if (strcmp(value, "eagent") == 0) {
 
4515
                                        action = ST_ADM_ACTION_EAGENT;
 
4516
                                }
 
4517
                                else if (strcmp(value, "arunn") == 0) {
 
4518
                                        action = ST_ADM_ACTION_ARUNN;
 
4519
                                }
 
4520
                                else if (strcmp(value, "adown") == 0) {
 
4521
                                        action = ST_ADM_ACTION_ADOWN;
 
4522
                                }
 
4523
                                /* else these are the old supported methods */
 
4524
                                else if (strcmp(value, "disable") == 0) {
 
4525
                                        action = ST_ADM_ACTION_DISABLE;
 
4526
                                }
 
4527
                                else if (strcmp(value, "enable") == 0) {
 
4528
                                        action = ST_ADM_ACTION_ENABLE;
 
4529
                                }
 
4530
                                else if (strcmp(value, "stop") == 0) {
 
4531
                                        action = ST_ADM_ACTION_STOP;
 
4532
                                }
 
4533
                                else if (strcmp(value, "start") == 0) {
 
4534
                                        action = ST_ADM_ACTION_START;
 
4535
                                }
 
4536
                                else {
 
4537
                                        appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
 
4538
                                        goto out;
 
4539
                                }
 
4540
                        }
 
4541
                        else if (strcmp(key, "s") == 0) {
 
4542
                                if (!(px && action)) {
 
4543
                                        /*
 
4544
                                         * Indicates that we'll need to reprocess the parameters
 
4545
                                         * as soon as backend and action are known
 
4546
                                         */
 
4547
                                        if (!reprocess) {
 
4548
                                                st_cur_param  = cur_param;
 
4549
                                                st_next_param = next_param;
 
4550
                                        }
 
4551
                                        reprocess = 1;
 
4552
                                }
 
4553
                                else if ((sv = findserver(px, value)) != NULL) {
 
4554
                                        switch (action) {
 
4555
                                        case ST_ADM_ACTION_DISABLE:
 
4556
                                                if (!(sv->admin & SRV_ADMF_FMAINT)) {
 
4557
                                                        altered_servers++;
 
4558
                                                        total_servers++;
 
4559
                                                        srv_set_admin_flag(sv, SRV_ADMF_FMAINT);
 
4560
                                                }
 
4561
                                                break;
 
4562
                                        case ST_ADM_ACTION_ENABLE:
 
4563
                                                if (sv->admin & SRV_ADMF_FMAINT) {
 
4564
                                                        altered_servers++;
 
4565
                                                        total_servers++;
 
4566
                                                        srv_clr_admin_flag(sv, SRV_ADMF_FMAINT);
 
4567
                                                }
 
4568
                                                break;
 
4569
                                        case ST_ADM_ACTION_STOP:
 
4570
                                                if (!(sv->admin & SRV_ADMF_FDRAIN)) {
 
4571
                                                        srv_set_admin_flag(sv, SRV_ADMF_FDRAIN);
 
4572
                                                        altered_servers++;
 
4573
                                                        total_servers++;
 
4574
                                                }
 
4575
                                                break;
 
4576
                                        case ST_ADM_ACTION_START:
 
4577
                                                if (sv->admin & SRV_ADMF_FDRAIN) {
 
4578
                                                        srv_clr_admin_flag(sv, SRV_ADMF_FDRAIN);
 
4579
                                                        altered_servers++;
 
4580
                                                        total_servers++;
 
4581
                                                }
 
4582
                                                break;
 
4583
                                        case ST_ADM_ACTION_DHLTH:
 
4584
                                                if (sv->check.state & CHK_ST_CONFIGURED) {
 
4585
                                                        sv->check.state &= ~CHK_ST_ENABLED;
 
4586
                                                        altered_servers++;
 
4587
                                                        total_servers++;
 
4588
                                                }
 
4589
                                                break;
 
4590
                                        case ST_ADM_ACTION_EHLTH:
 
4591
                                                if (sv->check.state & CHK_ST_CONFIGURED) {
 
4592
                                                        sv->check.state |= CHK_ST_ENABLED;
 
4593
                                                        altered_servers++;
 
4594
                                                        total_servers++;
 
4595
                                                }
 
4596
                                                break;
 
4597
                                        case ST_ADM_ACTION_HRUNN:
 
4598
                                                if (!(sv->track)) {
 
4599
                                                        sv->check.health = sv->check.rise + sv->check.fall - 1;
 
4600
                                                        srv_set_running(sv, "changed from Web interface");
 
4601
                                                        altered_servers++;
 
4602
                                                        total_servers++;
 
4603
                                                }
 
4604
                                                break;
 
4605
                                        case ST_ADM_ACTION_HNOLB:
 
4606
                                                if (!(sv->track)) {
 
4607
                                                        sv->check.health = sv->check.rise + sv->check.fall - 1;
 
4608
                                                        srv_set_stopping(sv, "changed from Web interface");
 
4609
                                                        altered_servers++;
 
4610
                                                        total_servers++;
 
4611
                                                }
 
4612
                                                break;
 
4613
                                        case ST_ADM_ACTION_HDOWN:
 
4614
                                                if (!(sv->track)) {
 
4615
                                                        sv->check.health = 0;
 
4616
                                                        srv_set_stopped(sv, "changed from Web interface");
 
4617
                                                        altered_servers++;
 
4618
                                                        total_servers++;
 
4619
                                                }
 
4620
                                                break;
 
4621
                                        case ST_ADM_ACTION_DAGENT:
 
4622
                                                if (sv->agent.state & CHK_ST_CONFIGURED) {
 
4623
                                                        sv->agent.state &= ~CHK_ST_ENABLED;
 
4624
                                                        altered_servers++;
 
4625
                                                        total_servers++;
 
4626
                                                }
 
4627
                                                break;
 
4628
                                        case ST_ADM_ACTION_EAGENT:
 
4629
                                                if (sv->agent.state & CHK_ST_CONFIGURED) {
 
4630
                                                        sv->agent.state |= CHK_ST_ENABLED;
 
4631
                                                        altered_servers++;
 
4632
                                                        total_servers++;
 
4633
                                                }
 
4634
                                                break;
 
4635
                                        case ST_ADM_ACTION_ARUNN:
 
4636
                                                if (sv->agent.state & CHK_ST_ENABLED) {
 
4637
                                                        sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
 
4638
                                                        srv_set_running(sv, "changed from Web interface");
 
4639
                                                        altered_servers++;
 
4640
                                                        total_servers++;
 
4641
                                                }
 
4642
                                                break;
 
4643
                                        case ST_ADM_ACTION_ADOWN:
 
4644
                                                if (sv->agent.state & CHK_ST_ENABLED) {
 
4645
                                                        sv->agent.health = 0;
 
4646
                                                        srv_set_stopped(sv, "changed from Web interface");
 
4647
                                                        altered_servers++;
 
4648
                                                        total_servers++;
 
4649
                                                }
 
4650
                                                break;
 
4651
                                        case ST_ADM_ACTION_READY:
 
4652
                                                srv_adm_set_ready(sv);
 
4653
                                                altered_servers++;
 
4654
                                                total_servers++;
 
4655
                                                break;
 
4656
                                        case ST_ADM_ACTION_DRAIN:
 
4657
                                                srv_adm_set_drain(sv);
 
4658
                                                altered_servers++;
 
4659
                                                total_servers++;
 
4660
                                                break;
 
4661
                                        case ST_ADM_ACTION_MAINT:
 
4662
                                                srv_adm_set_maint(sv);
 
4663
                                                altered_servers++;
 
4664
                                                total_servers++;
 
4665
                                                break;
 
4666
                                        case ST_ADM_ACTION_SHUTDOWN:
 
4667
                                                if (px->state != PR_STSTOPPED) {
 
4668
                                                        struct session *sess, *sess_bck;
 
4669
 
 
4670
                                                        list_for_each_entry_safe(sess, sess_bck, &sv->actconns, by_srv)
 
4671
                                                                if (sess->srv_conn == sv)
 
4672
                                                                        session_shutdown(sess, SN_ERR_KILLED);
 
4673
 
 
4674
                                                        altered_servers++;
 
4675
                                                        total_servers++;
 
4676
                                                }
 
4677
                                                break;
 
4678
                                        }
 
4679
                                } else {
 
4680
                                        /* the server name is unknown or ambiguous (duplicate names) */
 
4681
                                        total_servers++;
 
4682
                                }
 
4683
                        }
 
4684
                        if (reprocess && px && action) {
 
4685
                                /* Now, we know the backend and the action chosen by the user.
 
4686
                                 * We can safely restart from the first server parameter
 
4687
                                 * to reprocess them
 
4688
                                 */
 
4689
                                cur_param  = st_cur_param;
 
4690
                                next_param = st_next_param;
 
4691
                                reprocess = 0;
 
4692
                                goto reprocess_servers;
 
4693
                        }
 
4694
 
 
4695
                        next_param = cur_param;
 
4696
                }
 
4697
        }
 
4698
 
 
4699
        if (total_servers == 0) {
 
4700
                appctx->ctx.stats.st_code = STAT_STATUS_NONE;
 
4701
        }
 
4702
        else if (altered_servers == 0) {
 
4703
                appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
 
4704
        }
 
4705
        else if (altered_servers == total_servers) {
 
4706
                appctx->ctx.stats.st_code = STAT_STATUS_DONE;
 
4707
        }
 
4708
        else {
 
4709
                appctx->ctx.stats.st_code = STAT_STATUS_PART;
 
4710
        }
 
4711
 out:
 
4712
        return 1;
 
4713
}
 
4714
 
 
4715
 
 
4716
static int stats_send_http_headers(struct stream_interface *si)
 
4717
{
 
4718
        struct session *s = session_from_task(si->owner);
 
4719
        struct uri_auth *uri = s->be->uri_auth;
 
4720
        struct appctx *appctx = objt_appctx(si->end);
 
4721
 
 
4722
        chunk_printf(&trash,
 
4723
                     "HTTP/1.1 200 OK\r\n"
 
4724
                     "Cache-Control: no-cache\r\n"
 
4725
                     "Connection: close\r\n"
 
4726
                     "Content-Type: %s\r\n",
 
4727
                     (appctx->ctx.stats.flags & STAT_FMT_HTML) ? "text/html" : "text/plain");
 
4728
 
 
4729
        if (uri->refresh > 0 && !(appctx->ctx.stats.flags & STAT_NO_REFRESH))
 
4730
                chunk_appendf(&trash, "Refresh: %d\r\n",
 
4731
                              uri->refresh);
 
4732
 
 
4733
        /* we don't send the CRLF in chunked mode, it will be sent with the first chunk's size */
 
4734
 
 
4735
        if (appctx->ctx.stats.flags & STAT_CHUNKED)
 
4736
                chunk_appendf(&trash, "Transfer-Encoding: chunked\r\n");
 
4737
        else
 
4738
                chunk_appendf(&trash, "\r\n");
 
4739
 
 
4740
        s->txn.status = 200;
 
4741
        s->logs.tv_request = now;
 
4742
 
 
4743
        if (bi_putchk(si->ib, &trash) == -1)
 
4744
                return 0;
 
4745
 
 
4746
        return 1;
 
4747
}
 
4748
 
 
4749
static int stats_send_http_redirect(struct stream_interface *si)
 
4750
{
 
4751
        char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
 
4752
        struct session *s = session_from_task(si->owner);
 
4753
        struct uri_auth *uri = s->be->uri_auth;
 
4754
        struct appctx *appctx = objt_appctx(si->end);
 
4755
 
 
4756
        /* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
 
4757
        scope_txt[0] = 0;
 
4758
        if (appctx->ctx.stats.scope_len) {
 
4759
                strcpy(scope_txt, STAT_SCOPE_PATTERN);
 
4760
                memcpy(scope_txt + strlen(STAT_SCOPE_PATTERN), bo_ptr(si->ob->buf) + appctx->ctx.stats.scope_str, appctx->ctx.stats.scope_len);
 
4761
                scope_txt[strlen(STAT_SCOPE_PATTERN) + appctx->ctx.stats.scope_len] = 0;
 
4762
        }
 
4763
 
 
4764
        /* We don't want to land on the posted stats page because a refresh will
 
4765
         * repost the data. We don't want this to happen on accident so we redirect
 
4766
         * the browse to the stats page with a GET.
 
4767
         */
 
4768
        chunk_printf(&trash,
 
4769
                     "HTTP/1.1 303 See Other\r\n"
 
4770
                     "Cache-Control: no-cache\r\n"
 
4771
                     "Content-Type: text/plain\r\n"
 
4772
                     "Connection: close\r\n"
 
4773
                     "Location: %s;st=%s%s%s%s\r\n"
 
4774
                     "\r\n",
 
4775
                     uri->uri_prefix,
 
4776
                     ((appctx->ctx.stats.st_code > STAT_STATUS_INIT) &&
 
4777
                      (appctx->ctx.stats.st_code < STAT_STATUS_SIZE) &&
 
4778
                      stat_status_codes[appctx->ctx.stats.st_code]) ?
 
4779
                     stat_status_codes[appctx->ctx.stats.st_code] :
 
4780
                     stat_status_codes[STAT_STATUS_UNKN],
 
4781
                     (appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
 
4782
                     (appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
 
4783
                     scope_txt);
 
4784
 
 
4785
        s->txn.status = 303;
 
4786
        s->logs.tv_request = now;
 
4787
 
 
4788
        if (bi_putchk(si->ib, &trash) == -1)
 
4789
                return 0;
 
4790
 
 
4791
        return 1;
 
4792
}
 
4793
 
 
4794
/* This I/O handler runs as an applet embedded in a stream interface. It is
 
4795
 * used to send HTTP stats over a TCP socket. The mechanism is very simple.
 
4796
 * appctx->st0 contains the operation in progress (dump, done). The handler
 
4797
 * automatically unregisters itself once transfer is complete.
 
4798
 */
 
4799
static void http_stats_io_handler(struct stream_interface *si)
 
4800
{
 
4801
        struct appctx *appctx = __objt_appctx(si->end);
 
4802
        struct session *s = session_from_task(si->owner);
 
4803
        struct channel *req = si->ob;
 
4804
        struct channel *res = si->ib;
 
4805
 
 
4806
        if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
 
4807
                goto out;
 
4808
 
 
4809
        /* check that the output is not closed */
 
4810
        if (res->flags & (CF_SHUTW|CF_SHUTW_NOW))
 
4811
                appctx->st0 = STAT_HTTP_DONE;
 
4812
 
 
4813
        /* all states are processed in sequence */
 
4814
        if (appctx->st0 == STAT_HTTP_HEAD) {
 
4815
                if (stats_send_http_headers(si)) {
 
4816
                        if (s->txn.meth == HTTP_METH_HEAD)
 
4817
                                appctx->st0 = STAT_HTTP_DONE;
 
4818
                        else
 
4819
                                appctx->st0 = STAT_HTTP_DUMP;
 
4820
                }
 
4821
        }
 
4822
 
 
4823
        if (appctx->st0 == STAT_HTTP_DUMP) {
 
4824
                unsigned int prev_len = si->ib->buf->i;
 
4825
                unsigned int data_len;
 
4826
                unsigned int last_len;
 
4827
                unsigned int last_fwd = 0;
 
4828
 
 
4829
                if (appctx->ctx.stats.flags & STAT_CHUNKED) {
 
4830
                        /* One difficulty we're facing is that we must prevent
 
4831
                         * the input data from being automatically forwarded to
 
4832
                         * the output area. For this, we temporarily disable
 
4833
                         * forwarding on the channel.
 
4834
                         */
 
4835
                        last_fwd = si->ib->to_forward;
 
4836
                        si->ib->to_forward = 0;
 
4837
                        chunk_printf(&trash, "\r\n000000\r\n");
 
4838
                        if (bi_putchk(si->ib, &trash) == -1) {
 
4839
                                si->ib->to_forward = last_fwd;
 
4840
                                goto fail;
 
4841
                        }
 
4842
                }
 
4843
 
 
4844
                data_len = si->ib->buf->i;
 
4845
                if (stats_dump_stat_to_buffer(si, s->be->uri_auth))
 
4846
                        appctx->st0 = STAT_HTTP_DONE;
 
4847
 
 
4848
                last_len = si->ib->buf->i;
 
4849
 
 
4850
                /* Now we must either adjust or remove the chunk size. This is
 
4851
                 * not easy because the chunk size might wrap at the end of the
 
4852
                 * buffer, so we pretend we have nothing in the buffer, we write
 
4853
                 * the size, then restore the buffer's contents. Note that we can
 
4854
                 * only do that because no forwarding is scheduled on the stats
 
4855
                 * applet.
 
4856
                 */
 
4857
                if (appctx->ctx.stats.flags & STAT_CHUNKED) {
 
4858
                        si->ib->total  -= (last_len - prev_len);
 
4859
                        si->ib->buf->i -= (last_len - prev_len);
 
4860
 
 
4861
                        if (last_len != data_len) {
 
4862
                                chunk_printf(&trash, "\r\n%06x\r\n", (last_len - data_len));
 
4863
                                bi_putchk(si->ib, &trash);
 
4864
 
 
4865
                                si->ib->total  += (last_len - data_len);
 
4866
                                si->ib->buf->i += (last_len - data_len);
 
4867
                        }
 
4868
                        /* now re-enable forwarding */
 
4869
                        channel_forward(si->ib, last_fwd);
 
4870
                }
 
4871
        }
 
4872
 
 
4873
        if (appctx->st0 == STAT_HTTP_POST) {
 
4874
                if (stats_process_http_post(si))
 
4875
                        appctx->st0 = STAT_HTTP_LAST;
 
4876
                else if (si->ob->flags & CF_SHUTR)
 
4877
                        appctx->st0 = STAT_HTTP_DONE;
 
4878
        }
 
4879
 
 
4880
        if (appctx->st0 == STAT_HTTP_LAST) {
 
4881
                if (stats_send_http_redirect(si))
 
4882
                        appctx->st0 = STAT_HTTP_DONE;
 
4883
        }
 
4884
 
 
4885
        if (appctx->st0 == STAT_HTTP_DONE) {
 
4886
                if (appctx->ctx.stats.flags & STAT_CHUNKED) {
 
4887
                        chunk_printf(&trash, "\r\n0\r\n\r\n");
 
4888
                        if (bi_putchk(si->ib, &trash) == -1)
 
4889
                                goto fail;
 
4890
                }
 
4891
                /* eat the whole request */
 
4892
                bo_skip(si->ob, si->ob->buf->o);
 
4893
                res->flags |= CF_READ_NULL;
 
4894
                si_shutr(si);
 
4895
        }
 
4896
 
 
4897
        if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
 
4898
                si_shutw(si);
 
4899
 
 
4900
        if (appctx->st0 == STAT_HTTP_DONE) {
 
4901
                if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST)) {
 
4902
                        si_shutr(si);
 
4903
                        res->flags |= CF_READ_NULL;
 
4904
                }
 
4905
        }
 
4906
 
 
4907
 fail:
 
4908
        /* update all other flags and resync with the other side */
 
4909
        si_update(si);
 
4910
 
 
4911
        /* we don't want to expire timeouts while we're processing requests */
 
4912
        si->ib->rex = TICK_ETERNITY;
 
4913
        si->ob->wex = TICK_ETERNITY;
 
4914
 
 
4915
 out:
 
4916
        if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO)) {
 
4917
                /* check that we have released everything then unregister */
 
4918
                stream_int_unregister_handler(si);
 
4919
        }
 
4920
}
 
4921
 
 
4922
 
 
4923
static inline const char *get_conn_ctrl_name(const struct connection *conn)
 
4924
{
 
4925
        if (!conn_ctrl_ready(conn))
 
4926
                return "NONE";
 
4927
        return conn->ctrl->name;
 
4928
}
 
4929
 
 
4930
static inline const char *get_conn_xprt_name(const struct connection *conn)
 
4931
{
 
4932
        static char ptr[17];
 
4933
 
 
4934
        if (!conn_xprt_ready(conn))
 
4935
                return "NONE";
 
4936
 
 
4937
        if (conn->xprt == &raw_sock)
 
4938
                return "RAW";
 
4939
 
 
4940
#ifdef USE_OPENSSL
 
4941
        if (conn->xprt == &ssl_sock)
 
4942
                return "SSL";
 
4943
#endif
 
4944
        snprintf(ptr, sizeof(ptr), "%p", conn->xprt);
 
4945
        return ptr;
 
4946
}
 
4947
 
 
4948
static inline const char *get_conn_data_name(const struct connection *conn)
 
4949
{
 
4950
        static char ptr[17];
 
4951
 
 
4952
        if (!conn->data)
 
4953
                return "NONE";
 
4954
 
 
4955
        if (conn->data == &sess_conn_cb)
 
4956
                return "SESS";
 
4957
 
 
4958
        if (conn->data == &si_conn_cb)
 
4959
                return "STRM";
 
4960
 
 
4961
        if (conn->data == &check_conn_cb)
 
4962
                return "CHCK";
 
4963
 
 
4964
        snprintf(ptr, sizeof(ptr), "%p", conn->data);
 
4965
        return ptr;
 
4966
}
 
4967
 
 
4968
/* This function dumps a complete session state onto the stream interface's
 
4969
 * read buffer. The session has to be set in sess->target. It returns
 
4970
 * 0 if the output buffer is full and it needs to be called again, otherwise
 
4971
 * non-zero. It is designed to be called from stats_dump_sess_to_buffer() below.
 
4972
 */
 
4973
static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct session *sess)
 
4974
{
 
4975
        struct appctx *appctx = __objt_appctx(si->end);
2490
4976
        struct tm tm;
2491
 
        struct chunk msg;
2492
 
        struct session *sess;
2493
4977
        extern const char *monthname[12];
2494
4978
        char pn[INET6_ADDRSTRLEN];
2495
 
 
2496
 
        chunk_init(&msg, trash, trashlen);
2497
 
        sess = s->data_ctx.sess.target;
2498
 
 
2499
 
        if (s->data_ctx.sess.section > 0 && s->data_ctx.sess.uid != sess->uniq_id) {
 
4979
        struct connection *conn;
 
4980
        struct appctx *tmpctx;
 
4981
 
 
4982
        chunk_reset(&trash);
 
4983
 
 
4984
        if (appctx->ctx.sess.section > 0 && appctx->ctx.sess.uid != sess->uniq_id) {
2500
4985
                /* session changed, no need to go any further */
2501
 
                chunk_printf(&msg, "  *** session terminated while we were watching it ***\n");
2502
 
                if (buffer_feed_chunk(rep, &msg) >= 0)
 
4986
                chunk_appendf(&trash, "  *** session terminated while we were watching it ***\n");
 
4987
                if (bi_putchk(si->ib, &trash) == -1)
2503
4988
                        return 0;
2504
 
                s->data_ctx.sess.target = NULL;
2505
 
                s->data_ctx.sess.uid = 0;
 
4989
                appctx->ctx.sess.uid = 0;
 
4990
                appctx->ctx.sess.section = 0;
2506
4991
                return 1;
2507
4992
        }
2508
4993
 
2509
 
        switch (s->data_ctx.sess.section) {
 
4994
        switch (appctx->ctx.sess.section) {
2510
4995
        case 0: /* main status of the session */
2511
 
                s->data_ctx.sess.uid = sess->uniq_id;
2512
 
                s->data_ctx.sess.section = 1;
 
4996
                appctx->ctx.sess.uid = sess->uniq_id;
 
4997
                appctx->ctx.sess.section = 1;
2513
4998
                /* fall through */
2514
4999
 
2515
5000
        case 1:
2516
 
                chunk_printf(&msg,
2517
 
                             "%p: id=%u, proto=%s",
 
5001
                get_localtime(sess->logs.accept_date.tv_sec, &tm);
 
5002
                chunk_appendf(&trash,
 
5003
                             "%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s",
2518
5004
                             sess,
 
5005
                             tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
 
5006
                             tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec),
2519
5007
                             sess->uniq_id,
2520
 
                             sess->listener->proto->name);
 
5008
                             sess->listener && sess->listener->proto->name ? sess->listener->proto->name : "?");
2521
5009
 
2522
 
                switch (sess->listener->proto->sock_family) {
 
5010
                conn = objt_conn(sess->si[0].end);
 
5011
                switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
2523
5012
                case AF_INET:
2524
 
                        inet_ntop(AF_INET,
2525
 
                                  (const void *)&((struct sockaddr_in *)&sess->cli_addr)->sin_addr,
2526
 
                                  pn, sizeof(pn));
2527
 
 
2528
 
                        chunk_printf(&msg,
2529
 
                                     " source=%s:%d\n",
2530
 
                                     pn,
2531
 
                                     ntohs(((struct sockaddr_in *)&sess->cli_addr)->sin_port));
2532
 
                        break;
2533
5013
                case AF_INET6:
2534
 
                        inet_ntop(AF_INET6,
2535
 
                                  (const void *)&((struct sockaddr_in6 *)(&sess->cli_addr))->sin6_addr,
2536
 
                                  pn, sizeof(pn));
2537
 
 
2538
 
                        chunk_printf(&msg,
2539
 
                                     " source=%s:%d\n",
2540
 
                                     pn,
2541
 
                                     ntohs(((struct sockaddr_in6 *)&sess->cli_addr)->sin6_port));
 
5014
                        chunk_appendf(&trash, " source=%s:%d\n",
 
5015
                                      pn, get_host_port(&conn->addr.from));
2542
5016
                        break;
2543
5017
                case AF_UNIX:
 
5018
                        chunk_appendf(&trash, " source=unix:%d\n", sess->listener->luid);
 
5019
                        break;
2544
5020
                default:
2545
5021
                        /* no more information to print right now */
2546
 
                        chunk_printf(&msg, "\n");
 
5022
                        chunk_appendf(&trash, "\n");
2547
5023
                        break;
2548
5024
                }
2549
5025
 
2550
 
                chunk_printf(&msg,
 
5026
                chunk_appendf(&trash,
2551
5027
                             "  flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n",
2552
 
                             sess->flags, sess->conn_retries, sess->srv_conn, sess->pend_pos);
 
5028
                             sess->flags, sess->si[1].conn_retries, sess->srv_conn, sess->pend_pos);
2553
5029
 
2554
 
                chunk_printf(&msg,
2555
 
                             "  frontend=%s (id=%u mode=%s), listener=%s (id=%u)\n",
 
5030
                chunk_appendf(&trash,
 
5031
                             "  frontend=%s (id=%u mode=%s), listener=%s (id=%u)",
2556
5032
                             sess->fe->id, sess->fe->uuid, sess->fe->mode ? "http" : "tcp",
2557
5033
                             sess->listener ? sess->listener->name ? sess->listener->name : "?" : "?",
2558
5034
                             sess->listener ? sess->listener->luid : 0);
2559
5035
 
 
5036
                if (conn)
 
5037
                        conn_get_to_addr(conn);
 
5038
 
 
5039
                switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
 
5040
                case AF_INET:
 
5041
                case AF_INET6:
 
5042
                        chunk_appendf(&trash, " addr=%s:%d\n",
 
5043
                                     pn, get_host_port(&conn->addr.to));
 
5044
                        break;
 
5045
                case AF_UNIX:
 
5046
                        chunk_appendf(&trash, " addr=unix:%d\n", sess->listener->luid);
 
5047
                        break;
 
5048
                default:
 
5049
                        /* no more information to print right now */
 
5050
                        chunk_appendf(&trash, "\n");
 
5051
                        break;
 
5052
                }
 
5053
 
2560
5054
                if (sess->be->cap & PR_CAP_BE)
2561
 
                        chunk_printf(&msg,
2562
 
                                     "  backend=%s (id=%u mode=%s) server=%s (id=%u)\n",
 
5055
                        chunk_appendf(&trash,
 
5056
                                     "  backend=%s (id=%u mode=%s)",
2563
5057
                                     sess->be->id,
2564
 
                                     sess->be->uuid, sess->be->mode ? "http" : "tcp",
2565
 
                                     sess->srv ? sess->srv->id : "<none>",
2566
 
                                     sess->srv ? sess->srv->puid : 0);
2567
 
                else
2568
 
                        chunk_printf(&msg, "  backend=<NONE> (id=-1 mode=-) server=<NONE> (id=-1)\n");
2569
 
 
2570
 
                chunk_printf(&msg,
2571
 
                             "  task=%p (state=0x%02x nice=%d calls=%d exp=%s%s)\n",
 
5058
                                     sess->be->uuid, sess->be->mode ? "http" : "tcp");
 
5059
                else
 
5060
                        chunk_appendf(&trash, "  backend=<NONE> (id=-1 mode=-)");
 
5061
 
 
5062
                conn = objt_conn(sess->si[1].end);
 
5063
                if (conn)
 
5064
                        conn_get_from_addr(conn);
 
5065
 
 
5066
                switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
 
5067
                case AF_INET:
 
5068
                case AF_INET6:
 
5069
                        chunk_appendf(&trash, " addr=%s:%d\n",
 
5070
                                     pn, get_host_port(&conn->addr.from));
 
5071
                        break;
 
5072
                case AF_UNIX:
 
5073
                        chunk_appendf(&trash, " addr=unix\n");
 
5074
                        break;
 
5075
                default:
 
5076
                        /* no more information to print right now */
 
5077
                        chunk_appendf(&trash, "\n");
 
5078
                        break;
 
5079
                }
 
5080
 
 
5081
                if (sess->be->cap & PR_CAP_BE)
 
5082
                        chunk_appendf(&trash,
 
5083
                                     "  server=%s (id=%u)",
 
5084
                                     objt_server(sess->target) ? objt_server(sess->target)->id : "<none>",
 
5085
                                     objt_server(sess->target) ? objt_server(sess->target)->puid : 0);
 
5086
                else
 
5087
                        chunk_appendf(&trash, "  server=<NONE> (id=-1)");
 
5088
 
 
5089
                if (conn)
 
5090
                        conn_get_to_addr(conn);
 
5091
 
 
5092
                switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
 
5093
                case AF_INET:
 
5094
                case AF_INET6:
 
5095
                        chunk_appendf(&trash, " addr=%s:%d\n",
 
5096
                                     pn, get_host_port(&conn->addr.to));
 
5097
                        break;
 
5098
                case AF_UNIX:
 
5099
                        chunk_appendf(&trash, " addr=unix\n");
 
5100
                        break;
 
5101
                default:
 
5102
                        /* no more information to print right now */
 
5103
                        chunk_appendf(&trash, "\n");
 
5104
                        break;
 
5105
                }
 
5106
 
 
5107
                chunk_appendf(&trash,
 
5108
                             "  task=%p (state=0x%02x nice=%d calls=%d exp=%s%s",
2572
5109
                             sess->task,
2573
5110
                             sess->task->state,
2574
5111
                             sess->task->nice, sess->task->calls,
2578
5115
                                             TICKS_TO_MS(1000)) : "<NEVER>",
2579
5116
                             task_in_rq(sess->task) ? ", running" : "");
2580
5117
 
2581
 
                get_localtime(sess->logs.accept_date.tv_sec, &tm);
2582
 
                chunk_printf(&msg,
2583
 
                             "  task created [%02d/%s/%04d:%02d:%02d:%02d.%06d] (age=%s)\n",
2584
 
                             tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
2585
 
                             tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec),
 
5118
                chunk_appendf(&trash,
 
5119
                             " age=%s)\n",
2586
5120
                             human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1));
2587
5121
 
2588
 
                chunk_printf(&msg,
2589
 
                             "  si[0]=%p (state=%d flags=0x%02x fd=%d exp=%s, et=0x%03x)\n",
 
5122
                chunk_appendf(&trash,
 
5123
                             "  txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s\n",
 
5124
                             &sess->txn, sess->txn.flags, sess->txn.meth, sess->txn.status,
 
5125
                             http_msg_state_str(sess->txn.req.msg_state), http_msg_state_str(sess->txn.rsp.msg_state));
 
5126
 
 
5127
                chunk_appendf(&trash,
 
5128
                             "  si[0]=%p (state=%s flags=0x%02x endp0=%s:%p exp=%s, et=0x%03x)\n",
2590
5129
                             &sess->si[0],
2591
 
                             sess->si[0].state,
 
5130
                             si_state_str(sess->si[0].state),
2592
5131
                             sess->si[0].flags,
2593
 
                             sess->si[0].fd,
 
5132
                             obj_type_name(sess->si[0].end),
 
5133
                             obj_base_ptr(sess->si[0].end),
2594
5134
                             sess->si[0].exp ?
2595
5135
                                     tick_is_expired(sess->si[0].exp, now_ms) ? "<PAST>" :
2596
5136
                                             human_time(TICKS_TO_MS(sess->si[0].exp - now_ms),
2597
5137
                                             TICKS_TO_MS(1000)) : "<NEVER>",
2598
5138
                             sess->si[0].err_type);
2599
5139
 
2600
 
                chunk_printf(&msg,
2601
 
                             "  si[1]=%p (state=%d flags=0x%02x fd=%d exp=%s, et=0x%03x)\n",
 
5140
                chunk_appendf(&trash,
 
5141
                             "  si[1]=%p (state=%s flags=0x%02x endp1=%s:%p exp=%s, et=0x%03x)\n",
2602
5142
                             &sess->si[1],
2603
 
                             sess->si[1].state,
 
5143
                             si_state_str(sess->si[1].state),
2604
5144
                             sess->si[1].flags,
2605
 
                             sess->si[1].fd,
 
5145
                             obj_type_name(sess->si[1].end),
 
5146
                             obj_base_ptr(sess->si[1].end),
2606
5147
                             sess->si[1].exp ?
2607
5148
                                     tick_is_expired(sess->si[1].exp, now_ms) ? "<PAST>" :
2608
5149
                                             human_time(TICKS_TO_MS(sess->si[1].exp - now_ms),
2609
5150
                                             TICKS_TO_MS(1000)) : "<NEVER>",
2610
5151
                             sess->si[1].err_type);
2611
5152
 
2612
 
                chunk_printf(&msg,
2613
 
                             "  txn=%p (flags=0x%x meth=%d status=%d req.st=%d rsp.st=%d)\n",
2614
 
                             &sess->txn, sess->txn.flags, sess->txn.meth, sess->txn.status,
2615
 
                             sess->txn.req.msg_state, sess->txn.rsp.msg_state);
2616
 
 
2617
 
 
2618
 
                chunk_printf(&msg,
2619
 
                             "  req=%p (f=0x%06x an=0x%x l=%d sndmx=%d pipe=%d fwd=%d)\n"
 
5153
                if ((conn = objt_conn(sess->si[0].end)) != NULL) {
 
5154
                        chunk_appendf(&trash,
 
5155
                                      "  co0=%p ctrl=%s xprt=%s data=%s target=%s:%p\n",
 
5156
                                      conn,
 
5157
                                      get_conn_ctrl_name(conn),
 
5158
                                      get_conn_xprt_name(conn),
 
5159
                                      get_conn_data_name(conn),
 
5160
                                      obj_type_name(conn->target),
 
5161
                                      obj_base_ptr(conn->target));
 
5162
 
 
5163
                        chunk_appendf(&trash,
 
5164
                                      "      flags=0x%08x fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
 
5165
                                      conn->flags,
 
5166
                                      conn->t.sock.fd,
 
5167
                                      conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].state : 0,
 
5168
                                      conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].cache : 0,
 
5169
                                      conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].updated : 0);
 
5170
                }
 
5171
                else if ((tmpctx = objt_appctx(sess->si[0].end)) != NULL) {
 
5172
                        chunk_appendf(&trash,
 
5173
                                      "  app0=%p st0=%d st1=%d st2=%d applet=%s\n",
 
5174
                                      tmpctx,
 
5175
                                      tmpctx->st0,
 
5176
                                      tmpctx->st1,
 
5177
                                      tmpctx->st2,
 
5178
                                      tmpctx->applet->name);
 
5179
                }
 
5180
 
 
5181
                if ((conn = objt_conn(sess->si[1].end)) != NULL) {
 
5182
                        chunk_appendf(&trash,
 
5183
                                      "  co1=%p ctrl=%s xprt=%s data=%s target=%s:%p\n",
 
5184
                                      conn,
 
5185
                                      get_conn_ctrl_name(conn),
 
5186
                                      get_conn_xprt_name(conn),
 
5187
                                      get_conn_data_name(conn),
 
5188
                                      obj_type_name(conn->target),
 
5189
                                      obj_base_ptr(conn->target));
 
5190
 
 
5191
                        chunk_appendf(&trash,
 
5192
                                      "      flags=0x%08x fd=%d fd_spec_e=%02x fd_spec_p=%d updt=%d\n",
 
5193
                                      conn->flags,
 
5194
                                      conn->t.sock.fd,
 
5195
                                      conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].state : 0,
 
5196
                                      conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].cache : 0,
 
5197
                                      conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].updated : 0);
 
5198
                }
 
5199
                else if ((tmpctx = objt_appctx(sess->si[1].end)) != NULL) {
 
5200
                        chunk_appendf(&trash,
 
5201
                                      "  app1=%p st0=%d st1=%d st2=%d applet=%s\n",
 
5202
                                      tmpctx,
 
5203
                                      tmpctx->st0,
 
5204
                                      tmpctx->st1,
 
5205
                                      tmpctx->st2,
 
5206
                                      tmpctx->applet->name);
 
5207
                }
 
5208
 
 
5209
                chunk_appendf(&trash,
 
5210
                             "  req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
2620
5211
                             "      an_exp=%s",
2621
5212
                             sess->req,
2622
5213
                             sess->req->flags, sess->req->analysers,
2623
 
                             sess->req->l, sess->req->send_max,
2624
5214
                             sess->req->pipe ? sess->req->pipe->data : 0,
2625
 
                             sess->req->to_forward,
 
5215
                             sess->req->to_forward, sess->req->total,
2626
5216
                             sess->req->analyse_exp ?
2627
5217
                             human_time(TICKS_TO_MS(sess->req->analyse_exp - now_ms),
2628
5218
                                        TICKS_TO_MS(1000)) : "<NEVER>");
2629
5219
 
2630
 
                chunk_printf(&msg,
 
5220
                chunk_appendf(&trash,
2631
5221
                             " rex=%s",
2632
5222
                             sess->req->rex ?
2633
5223
                             human_time(TICKS_TO_MS(sess->req->rex - now_ms),
2634
5224
                                        TICKS_TO_MS(1000)) : "<NEVER>");
2635
5225
 
2636
 
                chunk_printf(&msg,
 
5226
                chunk_appendf(&trash,
2637
5227
                             " wex=%s\n"
2638
 
                             "      data=%p r=%d w=%d lr=%d total=%lld\n",
 
5228
                             "      buf=%p data=%p o=%d p=%d req.next=%d i=%d size=%d\n",
2639
5229
                             sess->req->wex ?
2640
5230
                             human_time(TICKS_TO_MS(sess->req->wex - now_ms),
2641
5231
                                        TICKS_TO_MS(1000)) : "<NEVER>",
2642
 
                             sess->req->data,
2643
 
                             (int)(sess->req->r - sess->req->data),
2644
 
                             (int)(sess->req->w - sess->req->data),
2645
 
                             (int)(sess->req->lr - sess->req->data),
2646
 
                             sess->req->total);
 
5232
                             sess->req->buf,
 
5233
                             sess->req->buf->data, sess->req->buf->o,
 
5234
                             (int)(sess->req->buf->p - sess->req->buf->data),
 
5235
                             sess->txn.req.next, sess->req->buf->i,
 
5236
                             sess->req->buf->size);
2647
5237
 
2648
 
                chunk_printf(&msg,
2649
 
                             "  res=%p (f=0x%06x an=0x%x l=%d sndmx=%d pipe=%d fwd=%d)\n"
 
5238
                chunk_appendf(&trash,
 
5239
                             "  res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
2650
5240
                             "      an_exp=%s",
2651
5241
                             sess->rep,
2652
5242
                             sess->rep->flags, sess->rep->analysers,
2653
 
                             sess->rep->l, sess->rep->send_max,
2654
5243
                             sess->rep->pipe ? sess->rep->pipe->data : 0,
2655
 
                             sess->rep->to_forward,
 
5244
                             sess->rep->to_forward, sess->rep->total,
2656
5245
                             sess->rep->analyse_exp ?
2657
5246
                             human_time(TICKS_TO_MS(sess->rep->analyse_exp - now_ms),
2658
5247
                                        TICKS_TO_MS(1000)) : "<NEVER>");
2659
5248
 
2660
 
                chunk_printf(&msg,
 
5249
                chunk_appendf(&trash,
2661
5250
                             " rex=%s",
2662
5251
                             sess->rep->rex ?
2663
5252
                             human_time(TICKS_TO_MS(sess->rep->rex - now_ms),
2664
5253
                                        TICKS_TO_MS(1000)) : "<NEVER>");
2665
5254
 
2666
 
                chunk_printf(&msg,
 
5255
                chunk_appendf(&trash,
2667
5256
                             " wex=%s\n"
2668
 
                             "      data=%p r=%d w=%d lr=%d total=%lld\n",
 
5257
                             "      buf=%p data=%p o=%d p=%d rsp.next=%d i=%d size=%d\n",
2669
5258
                             sess->rep->wex ?
2670
5259
                             human_time(TICKS_TO_MS(sess->rep->wex - now_ms),
2671
5260
                                        TICKS_TO_MS(1000)) : "<NEVER>",
2672
 
                             sess->rep->data,
2673
 
                             (int)(sess->rep->r - sess->rep->data),
2674
 
                             (int)(sess->rep->w - sess->rep->data),
2675
 
                             (int)(sess->rep->lr - sess->rep->data),
2676
 
                             sess->rep->total);
 
5261
                             sess->rep->buf,
 
5262
                             sess->rep->buf->data, sess->rep->buf->o,
 
5263
                             (int)(sess->rep->buf->p - sess->rep->buf->data),
 
5264
                             sess->txn.rsp.next, sess->rep->buf->i,
 
5265
                             sess->rep->buf->size);
2677
5266
 
2678
 
                if (buffer_feed_chunk(rep, &msg) >= 0)
 
5267
                if (bi_putchk(si->ib, &trash) == -1)
2679
5268
                        return 0;
2680
5269
 
2681
5270
                /* use other states to dump the contents */
2682
5271
        }
2683
5272
        /* end of dump */
2684
 
        s->data_ctx.sess.uid = 0;
 
5273
        appctx->ctx.sess.uid = 0;
 
5274
        appctx->ctx.sess.section = 0;
2685
5275
        return 1;
2686
5276
}
2687
5277
 
2688
 
/* This function is called to send output to the response buffer.
2689
 
 * It dumps the sessions states onto the output buffer <rep>.
2690
 
 * Expects to be called with client socket shut down on input.
2691
 
 * s->data_ctx must have been zeroed first, and the flags properly set.
2692
 
 * It returns 0 as long as it does not complete, non-zero upon completion.
 
5278
static int stats_pats_list(struct stream_interface *si)
 
5279
{
 
5280
        struct appctx *appctx = __objt_appctx(si->end);
 
5281
 
 
5282
        switch (appctx->st2) {
 
5283
        case STAT_ST_INIT:
 
5284
                /* Display the column headers. If the message cannot be sent,
 
5285
                 * quit the fucntion with returning 0. The function is called
 
5286
                 * later and restart at the state "STAT_ST_INIT".
 
5287
                 */
 
5288
                chunk_reset(&trash);
 
5289
                chunk_appendf(&trash, "# id (file) description\n");
 
5290
                if (bi_putchk(si->ib, &trash) == -1)
 
5291
                        return 0;
 
5292
 
 
5293
                /* Now, we start the browsing of the references lists.
 
5294
                 * Note that the following call to LIST_ELEM return bad pointer. The only
 
5295
                 * avalaible field of this pointer is <list>. It is used with the function
 
5296
                 * pat_list_get_next() for retruning the first avalaible entry
 
5297
                 */
 
5298
                appctx->ctx.map.ref = LIST_ELEM(&pattern_reference, struct pat_ref *, list);
 
5299
                appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
 
5300
                                                        appctx->ctx.map.display_flags);
 
5301
                appctx->st2 = STAT_ST_LIST;
 
5302
                /* fall through */
 
5303
 
 
5304
        case STAT_ST_LIST:
 
5305
                while (appctx->ctx.map.ref) {
 
5306
                        chunk_reset(&trash);
 
5307
 
 
5308
                        /* Build messages. If the reference is used by another category than
 
5309
                         * the listed categorie, display the information in the massage.
 
5310
                         */
 
5311
                        chunk_appendf(&trash, "%d (%s) %s\n", appctx->ctx.map.ref->unique_id,
 
5312
                                      appctx->ctx.map.ref->reference ? appctx->ctx.map.ref->reference : "",
 
5313
                                      appctx->ctx.map.ref->display);
 
5314
 
 
5315
                        if (bi_putchk(si->ib, &trash) == -1) {
 
5316
                                /* let's try again later from this session. We add ourselves into
 
5317
                                 * this session's users so that it can remove us upon termination.
 
5318
                                 */
 
5319
                                return 0;
 
5320
                        }
 
5321
 
 
5322
                        /* get next list entry and check the end of the list */
 
5323
                        appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
 
5324
                                                                appctx->ctx.map.display_flags);
 
5325
                }
 
5326
 
 
5327
                appctx->st2 = STAT_ST_FIN;
 
5328
                /* fall through */
 
5329
 
 
5330
        default:
 
5331
                appctx->st2 = STAT_ST_FIN;
 
5332
                return 1;
 
5333
        }
 
5334
        return 0;
 
5335
}
 
5336
 
 
5337
static int stats_map_lookup(struct stream_interface *si)
 
5338
{
 
5339
        struct appctx *appctx = __objt_appctx(si->end);
 
5340
        struct sample sample;
 
5341
        struct pattern *pat;
 
5342
        int match_method;
 
5343
 
 
5344
        switch (appctx->st2) {
 
5345
        case STAT_ST_INIT:
 
5346
                /* Init to the first entry. The list cannot be change */
 
5347
                appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, list);
 
5348
                appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr, &appctx->ctx.map.ref->pat);
 
5349
                appctx->st2 = STAT_ST_LIST;
 
5350
                /* fall through */
 
5351
 
 
5352
        case STAT_ST_LIST:
 
5353
                /* for each lookup type */
 
5354
                while (appctx->ctx.map.expr) {
 
5355
                        /* initialise chunk to build new message */
 
5356
                        chunk_reset(&trash);
 
5357
 
 
5358
                        /* execute pattern matching */
 
5359
                        sample.type = SMP_T_STR;
 
5360
                        sample.flags |= SMP_F_CONST;
 
5361
                        sample.data.str.len = appctx->ctx.map.chunk.len;
 
5362
                        sample.data.str.str = appctx->ctx.map.chunk.str;
 
5363
                        if (appctx->ctx.map.expr->pat_head->match &&
 
5364
                            sample_convert(&sample, appctx->ctx.map.expr->pat_head->expect_type))
 
5365
                                pat = appctx->ctx.map.expr->pat_head->match(&sample, appctx->ctx.map.expr, 1);
 
5366
                        else
 
5367
                                pat = NULL;
 
5368
 
 
5369
                        /* build return message: set type of match */
 
5370
                        for (match_method=0; match_method<PAT_MATCH_NUM; match_method++)
 
5371
                                if (appctx->ctx.map.expr->pat_head->match == pat_match_fcts[match_method])
 
5372
                                        break;
 
5373
                        if (match_method >= PAT_MATCH_NUM)
 
5374
                                chunk_appendf(&trash, "type=unknown(%p)", appctx->ctx.map.expr->pat_head->match);
 
5375
                        else
 
5376
                                chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
 
5377
 
 
5378
                        /* case sensitive */
 
5379
                        if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
 
5380
                                chunk_appendf(&trash, ", case=insensitive");
 
5381
                        else
 
5382
                                chunk_appendf(&trash, ", case=sensitive");
 
5383
 
 
5384
                        /* Display no match, and set default value */
 
5385
                        if (!pat) {
 
5386
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
5387
                                        chunk_appendf(&trash, ", found=no");
 
5388
                                else
 
5389
                                        chunk_appendf(&trash, ", match=no");
 
5390
                        }
 
5391
 
 
5392
                        /* Display match and match info */
 
5393
                        else {
 
5394
                                /* display match */
 
5395
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP)
 
5396
                                        chunk_appendf(&trash, ", found=yes");
 
5397
                                else
 
5398
                                        chunk_appendf(&trash, ", match=yes");
 
5399
 
 
5400
                                /* display index mode */
 
5401
                                if (pat->sflags & PAT_SF_TREE)
 
5402
                                        chunk_appendf(&trash, ", idx=tree");
 
5403
                                else
 
5404
                                        chunk_appendf(&trash, ", idx=list");
 
5405
 
 
5406
                                /* display pattern */
 
5407
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
 
5408
                                        if (pat->ref && pat->ref->pattern)
 
5409
                                                chunk_appendf(&trash, ", key=\"%s\"", pat->ref->pattern);
 
5410
                                        else
 
5411
                                                chunk_appendf(&trash, ", key=unknown");
 
5412
                                }
 
5413
                                else {
 
5414
                                        if (pat->ref && pat->ref->pattern)
 
5415
                                                chunk_appendf(&trash, ", pattern=\"%s\"", pat->ref->pattern);
 
5416
                                        else
 
5417
                                                chunk_appendf(&trash, ", pattern=unknown");
 
5418
                                }
 
5419
 
 
5420
                                /* display return value */
 
5421
                                if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
 
5422
                                        if (pat->smp && pat->ref && pat->ref->sample)
 
5423
                                                chunk_appendf(&trash, ", value=\"%s\", type=\"%s\"",
 
5424
                                                              pat->ref->sample, smp_to_type[pat->smp->type]);
 
5425
                                        else
 
5426
                                                chunk_appendf(&trash, ", value=none");
 
5427
                                }
 
5428
                        }
 
5429
 
 
5430
                        chunk_appendf(&trash, "\n");
 
5431
 
 
5432
                        /* display response */
 
5433
                        if (bi_putchk(si->ib, &trash) == -1) {
 
5434
                                /* let's try again later from this session. We add ourselves into
 
5435
                                 * this session's users so that it can remove us upon termination.
 
5436
                                 */
 
5437
                                return 0;
 
5438
                        }
 
5439
 
 
5440
                        /* get next entry */
 
5441
                        appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr,
 
5442
                                                                 &appctx->ctx.map.ref->pat);
 
5443
                }
 
5444
 
 
5445
                appctx->st2 = STAT_ST_FIN;
 
5446
                /* fall through */
 
5447
 
 
5448
        default:
 
5449
                appctx->st2 = STAT_ST_FIN;
 
5450
                free(appctx->ctx.map.chunk.str);
 
5451
                return 1;
 
5452
        }
 
5453
}
 
5454
 
 
5455
static int stats_pat_list(struct stream_interface *si)
 
5456
{
 
5457
        struct appctx *appctx = __objt_appctx(si->end);
 
5458
 
 
5459
        switch (appctx->st2) {
 
5460
 
 
5461
        case STAT_ST_INIT:
 
5462
                /* Init to the first entry. The list cannot be change */
 
5463
                appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.ref->head,
 
5464
                                                struct pat_ref_elt *, list);
 
5465
                if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
 
5466
                        appctx->ctx.map.elt = NULL;
 
5467
                appctx->st2 = STAT_ST_LIST;
 
5468
                /* fall through */
 
5469
 
 
5470
        case STAT_ST_LIST:
 
5471
                while (appctx->ctx.map.elt) {
 
5472
                        chunk_reset(&trash);
 
5473
 
 
5474
                        /* build messages */
 
5475
                        if (appctx->ctx.map.elt->sample)
 
5476
                                chunk_appendf(&trash, "%p %s %s\n",
 
5477
                                              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern,
 
5478
                                              appctx->ctx.map.elt->sample);
 
5479
                        else
 
5480
                                chunk_appendf(&trash, "%p %s\n",
 
5481
                                              appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
 
5482
 
 
5483
                        if (bi_putchk(si->ib, &trash) == -1) {
 
5484
                                /* let's try again later from this session. We add ourselves into
 
5485
                                 * this session's users so that it can remove us upon termination.
 
5486
                                 */
 
5487
                                return 0;
 
5488
                        }
 
5489
 
 
5490
                        /* get next list entry and check the end of the list */
 
5491
                        appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.elt->list,
 
5492
                                                        struct pat_ref_elt *, list);
 
5493
                        if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
 
5494
                                break;
 
5495
                }
 
5496
 
 
5497
                appctx->st2 = STAT_ST_FIN;
 
5498
                /* fall through */
 
5499
 
 
5500
        default:
 
5501
                appctx->st2 = STAT_ST_FIN;
 
5502
                return 1;
 
5503
        }
 
5504
}
 
5505
 
 
5506
/* This function dumps all sessions' states onto the stream interface's
 
5507
 * read buffer. It returns 0 if the output buffer is full and it needs
 
5508
 * to be called again, otherwise non-zero. It is designed to be called
 
5509
 * from stats_dump_sess_to_buffer() below.
2693
5510
 */
2694
 
int stats_dump_sess_to_buffer(struct session *s, struct buffer *rep)
 
5511
static int stats_dump_sess_to_buffer(struct stream_interface *si)
2695
5512
{
2696
 
        struct chunk msg;
 
5513
        struct appctx *appctx = __objt_appctx(si->end);
 
5514
        struct connection *conn;
2697
5515
 
2698
 
        if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) {
 
5516
        if (unlikely(si->ib->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
2699
5517
                /* If we're forced to shut down, we might have to remove our
2700
5518
                 * reference to the last session being dumped.
2701
5519
                 */
2702
 
                if (s->data_state == DATA_ST_LIST) {
2703
 
                        if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) {
2704
 
                                LIST_DEL(&s->data_ctx.sess.bref.users);
2705
 
                                LIST_INIT(&s->data_ctx.sess.bref.users);
 
5520
                if (appctx->st2 == STAT_ST_LIST) {
 
5521
                        if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
 
5522
                                LIST_DEL(&appctx->ctx.sess.bref.users);
 
5523
                                LIST_INIT(&appctx->ctx.sess.bref.users);
2706
5524
                        }
2707
5525
                }
2708
5526
                return 1;
2709
5527
        }
2710
5528
 
2711
 
        chunk_init(&msg, trash, trashlen);
 
5529
        chunk_reset(&trash);
2712
5530
 
2713
 
        switch (s->data_state) {
2714
 
        case DATA_ST_INIT:
 
5531
        switch (appctx->st2) {
 
5532
        case STAT_ST_INIT:
2715
5533
                /* the function had not been called yet, let's prepare the
2716
5534
                 * buffer for a response. We initialize the current session
2717
5535
                 * pointer to the first in the global list. When a target
2719
5537
                 * this pointer. We know we have reached the end when this
2720
5538
                 * pointer points back to the head of the sessions list.
2721
5539
                 */
2722
 
                LIST_INIT(&s->data_ctx.sess.bref.users);
2723
 
                s->data_ctx.sess.bref.ref = sessions.n;
2724
 
                s->data_state = DATA_ST_LIST;
 
5540
                LIST_INIT(&appctx->ctx.sess.bref.users);
 
5541
                appctx->ctx.sess.bref.ref = sessions.n;
 
5542
                appctx->st2 = STAT_ST_LIST;
2725
5543
                /* fall through */
2726
5544
 
2727
 
        case DATA_ST_LIST:
 
5545
        case STAT_ST_LIST:
2728
5546
                /* first, let's detach the back-ref from a possible previous session */
2729
 
                if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) {
2730
 
                        LIST_DEL(&s->data_ctx.sess.bref.users);
2731
 
                        LIST_INIT(&s->data_ctx.sess.bref.users);
 
5547
                if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
 
5548
                        LIST_DEL(&appctx->ctx.sess.bref.users);
 
5549
                        LIST_INIT(&appctx->ctx.sess.bref.users);
2732
5550
                }
2733
5551
 
2734
5552
                /* and start from where we stopped */
2735
 
                while (s->data_ctx.sess.bref.ref != &sessions) {
2736
 
                        char pn[INET6_ADDRSTRLEN + strlen(":65535")];
 
5553
                while (appctx->ctx.sess.bref.ref != &sessions) {
 
5554
                        char pn[INET6_ADDRSTRLEN];
2737
5555
                        struct session *curr_sess;
2738
5556
 
2739
 
                        curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list);
 
5557
                        curr_sess = LIST_ELEM(appctx->ctx.sess.bref.ref, struct session *, list);
2740
5558
 
2741
 
                        if (s->data_ctx.sess.target) {
2742
 
                                if (s->data_ctx.sess.target != curr_sess)
 
5559
                        if (appctx->ctx.sess.target) {
 
5560
                                if (appctx->ctx.sess.target != (void *)-1 && appctx->ctx.sess.target != curr_sess)
2743
5561
                                        goto next_sess;
2744
5562
 
2745
 
                                LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
 
5563
                                LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users);
2746
5564
                                /* call the proper dump() function and return if we're missing space */
2747
 
                                if (!stats_dump_full_sess_to_buffer(s, rep))
 
5565
                                if (!stats_dump_full_sess_to_buffer(si, curr_sess))
2748
5566
                                        return 0;
2749
5567
 
2750
5568
                                /* session dump complete */
2751
 
                                LIST_DEL(&s->data_ctx.sess.bref.users);
2752
 
                                LIST_INIT(&s->data_ctx.sess.bref.users);
2753
 
                                s->data_ctx.sess.target = NULL;
2754
 
                                break;
 
5569
                                LIST_DEL(&appctx->ctx.sess.bref.users);
 
5570
                                LIST_INIT(&appctx->ctx.sess.bref.users);
 
5571
                                if (appctx->ctx.sess.target != (void *)-1) {
 
5572
                                        appctx->ctx.sess.target = NULL;
 
5573
                                        break;
 
5574
                                }
 
5575
                                else
 
5576
                                        goto next_sess;
2755
5577
                        }
2756
5578
 
2757
 
                        chunk_printf(&msg,
 
5579
                        chunk_appendf(&trash,
2758
5580
                                     "%p: proto=%s",
2759
5581
                                     curr_sess,
2760
5582
                                     curr_sess->listener->proto->name);
2761
5583
 
2762
 
                        switch (curr_sess->listener->proto->sock_family) {
 
5584
 
 
5585
                        conn = objt_conn(curr_sess->si[0].end);
 
5586
                        switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
2763
5587
                        case AF_INET:
2764
 
                                inet_ntop(AF_INET,
2765
 
                                          (const void *)&((struct sockaddr_in *)&curr_sess->cli_addr)->sin_addr,
2766
 
                                          pn, sizeof(pn));
2767
 
 
2768
 
                                chunk_printf(&msg,
2769
 
                                             " src=%s:%d fe=%s be=%s srv=%s",
2770
 
                                             pn,
2771
 
                                             ntohs(((struct sockaddr_in *)&curr_sess->cli_addr)->sin_port),
2772
 
                                             curr_sess->fe->id,
2773
 
                                             (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
2774
 
                                             curr_sess->srv ? curr_sess->srv->id : "<none>"
2775
 
                                             );
2776
 
                                break;
2777
5588
                        case AF_INET6:
2778
 
                                inet_ntop(AF_INET6,
2779
 
                                          (const void *)&((struct sockaddr_in6 *)(&curr_sess->cli_addr))->sin6_addr,
2780
 
                                          pn, sizeof(pn));
2781
 
 
2782
 
                                chunk_printf(&msg,
 
5589
                                chunk_appendf(&trash,
2783
5590
                                             " src=%s:%d fe=%s be=%s srv=%s",
2784
5591
                                             pn,
2785
 
                                             ntohs(((struct sockaddr_in6 *)&curr_sess->cli_addr)->sin6_port),
 
5592
                                             get_host_port(&conn->addr.from),
2786
5593
                                             curr_sess->fe->id,
2787
5594
                                             (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
2788
 
                                             curr_sess->srv ? curr_sess->srv->id : "<none>"
 
5595
                                             objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "<none>"
2789
5596
                                             );
2790
 
 
2791
5597
                                break;
2792
5598
                        case AF_UNIX:
2793
 
                                /* no more information to print right now */
 
5599
                                chunk_appendf(&trash,
 
5600
                                             " src=unix:%d fe=%s be=%s srv=%s",
 
5601
                                             curr_sess->listener->luid,
 
5602
                                             curr_sess->fe->id,
 
5603
                                             (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
 
5604
                                             objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "<none>"
 
5605
                                             );
2794
5606
                                break;
2795
5607
                        }
2796
5608
 
2797
 
                        chunk_printf(&msg,
 
5609
                        chunk_appendf(&trash,
2798
5610
                                     " ts=%02x age=%s calls=%d",
2799
5611
                                     curr_sess->task->state,
2800
5612
                                     human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1),
2801
5613
                                     curr_sess->task->calls);
2802
5614
 
2803
 
                        chunk_printf(&msg,
2804
 
                                     " rq[f=%06xh,l=%d,an=%02xh,rx=%s",
 
5615
                        chunk_appendf(&trash,
 
5616
                                     " rq[f=%06xh,i=%d,an=%02xh,rx=%s",
2805
5617
                                     curr_sess->req->flags,
2806
 
                                     curr_sess->req->l,
 
5618
                                     curr_sess->req->buf->i,
2807
5619
                                     curr_sess->req->analysers,
2808
5620
                                     curr_sess->req->rex ?
2809
5621
                                     human_time(TICKS_TO_MS(curr_sess->req->rex - now_ms),
2810
5622
                                                TICKS_TO_MS(1000)) : "");
2811
5623
 
2812
 
                        chunk_printf(&msg,
 
5624
                        chunk_appendf(&trash,
2813
5625
                                     ",wx=%s",
2814
5626
                                     curr_sess->req->wex ?
2815
5627
                                     human_time(TICKS_TO_MS(curr_sess->req->wex - now_ms),
2816
5628
                                                TICKS_TO_MS(1000)) : "");
2817
5629
 
2818
 
                        chunk_printf(&msg,
 
5630
                        chunk_appendf(&trash,
2819
5631
                                     ",ax=%s]",
2820
5632
                                     curr_sess->req->analyse_exp ?
2821
5633
                                     human_time(TICKS_TO_MS(curr_sess->req->analyse_exp - now_ms),
2822
5634
                                                TICKS_TO_MS(1000)) : "");
2823
5635
 
2824
 
                        chunk_printf(&msg,
2825
 
                                     " rp[f=%06xh,l=%d,an=%02xh,rx=%s",
 
5636
                        chunk_appendf(&trash,
 
5637
                                     " rp[f=%06xh,i=%d,an=%02xh,rx=%s",
2826
5638
                                     curr_sess->rep->flags,
2827
 
                                     curr_sess->rep->l,
 
5639
                                     curr_sess->rep->buf->i,
2828
5640
                                     curr_sess->rep->analysers,
2829
5641
                                     curr_sess->rep->rex ?
2830
5642
                                     human_time(TICKS_TO_MS(curr_sess->rep->rex - now_ms),
2831
5643
                                                TICKS_TO_MS(1000)) : "");
2832
5644
 
2833
 
                        chunk_printf(&msg,
 
5645
                        chunk_appendf(&trash,
2834
5646
                                     ",wx=%s",
2835
5647
                                     curr_sess->rep->wex ?
2836
5648
                                     human_time(TICKS_TO_MS(curr_sess->rep->wex - now_ms),
2837
5649
                                                TICKS_TO_MS(1000)) : "");
2838
5650
 
2839
 
                        chunk_printf(&msg,
 
5651
                        chunk_appendf(&trash,
2840
5652
                                     ",ax=%s]",
2841
5653
                                     curr_sess->rep->analyse_exp ?
2842
5654
                                     human_time(TICKS_TO_MS(curr_sess->rep->analyse_exp - now_ms),
2843
5655
                                                TICKS_TO_MS(1000)) : "");
2844
5656
 
2845
 
                        chunk_printf(&msg,
 
5657
                        conn = objt_conn(curr_sess->si[0].end);
 
5658
                        chunk_appendf(&trash,
2846
5659
                                     " s0=[%d,%1xh,fd=%d,ex=%s]",
2847
5660
                                     curr_sess->si[0].state,
2848
5661
                                     curr_sess->si[0].flags,
2849
 
                                     curr_sess->si[0].fd,
 
5662
                                     conn ? conn->t.sock.fd : -1,
2850
5663
                                     curr_sess->si[0].exp ?
2851
5664
                                     human_time(TICKS_TO_MS(curr_sess->si[0].exp - now_ms),
2852
5665
                                                TICKS_TO_MS(1000)) : "");
2853
5666
 
2854
 
                        chunk_printf(&msg,
 
5667
                        conn = objt_conn(curr_sess->si[1].end);
 
5668
                        chunk_appendf(&trash,
2855
5669
                                     " s1=[%d,%1xh,fd=%d,ex=%s]",
2856
5670
                                     curr_sess->si[1].state,
2857
5671
                                     curr_sess->si[1].flags,
2858
 
                                     curr_sess->si[1].fd,
 
5672
                                     conn ? conn->t.sock.fd : -1,
2859
5673
                                     curr_sess->si[1].exp ?
2860
5674
                                     human_time(TICKS_TO_MS(curr_sess->si[1].exp - now_ms),
2861
5675
                                                TICKS_TO_MS(1000)) : "");
2862
5676
 
2863
 
                        chunk_printf(&msg,
 
5677
                        chunk_appendf(&trash,
2864
5678
                                     " exp=%s",
2865
5679
                                     curr_sess->task->expire ?
2866
5680
                                     human_time(TICKS_TO_MS(curr_sess->task->expire - now_ms),
2867
5681
                                                TICKS_TO_MS(1000)) : "");
2868
5682
                        if (task_in_rq(curr_sess->task))
2869
 
                                chunk_printf(&msg, " run(nice=%d)", curr_sess->task->nice);
2870
 
 
2871
 
                        chunk_printf(&msg, "\n");
2872
 
 
2873
 
                        if (buffer_feed_chunk(rep, &msg) >= 0) {
 
5683
                                chunk_appendf(&trash, " run(nice=%d)", curr_sess->task->nice);
 
5684
 
 
5685
                        chunk_appendf(&trash, "\n");
 
5686
 
 
5687
                        if (bi_putchk(si->ib, &trash) == -1) {
2874
5688
                                /* let's try again later from this session. We add ourselves into
2875
5689
                                 * this session's users so that it can remove us upon termination.
2876
5690
                                 */
2877
 
                                LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users);
 
5691
                                LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users);
2878
5692
                                return 0;
2879
5693
                        }
2880
5694
 
2881
5695
                next_sess:
2882
 
                        s->data_ctx.sess.bref.ref = curr_sess->list.n;
 
5696
                        appctx->ctx.sess.bref.ref = curr_sess->list.n;
2883
5697
                }
2884
5698
 
2885
 
                if (s->data_ctx.sess.target) {
 
5699
                if (appctx->ctx.sess.target && appctx->ctx.sess.target != (void *)-1) {
2886
5700
                        /* specified session not found */
2887
 
                        if (s->data_ctx.sess.section > 0)
2888
 
                                chunk_printf(&msg, "  *** session terminated while we were watching it ***\n");
 
5701
                        if (appctx->ctx.sess.section > 0)
 
5702
                                chunk_appendf(&trash, "  *** session terminated while we were watching it ***\n");
2889
5703
                        else
2890
 
                                chunk_printf(&msg, "Session not found.\n");
 
5704
                                chunk_appendf(&trash, "Session not found.\n");
2891
5705
 
2892
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
 
5706
                        if (bi_putchk(si->ib, &trash) == -1)
2893
5707
                                return 0;
2894
5708
 
2895
 
                        s->data_ctx.sess.target = NULL;
2896
 
                        s->data_ctx.sess.uid = 0;
 
5709
                        appctx->ctx.sess.target = NULL;
 
5710
                        appctx->ctx.sess.uid = 0;
2897
5711
                        return 1;
2898
5712
                }
2899
5713
 
2900
 
                s->data_state = DATA_ST_FIN;
 
5714
                appctx->st2 = STAT_ST_FIN;
2901
5715
                /* fall through */
2902
5716
 
2903
5717
        default:
2904
 
                s->data_state = DATA_ST_FIN;
2905
 
                return 1;
2906
 
        }
 
5718
                appctx->st2 = STAT_ST_FIN;
 
5719
                return 1;
 
5720
        }
 
5721
}
 
5722
 
 
5723
/* This is called when the stream interface is closed. For instance, upon an
 
5724
 * external abort, we won't call the i/o handler anymore so we may need to
 
5725
 * remove back references to the session currently being dumped.
 
5726
 */
 
5727
static void cli_release_handler(struct stream_interface *si)
 
5728
{
 
5729
        struct appctx *appctx = __objt_appctx(si->end);
 
5730
 
 
5731
        if (appctx->st0 == STAT_CLI_O_SESS && appctx->st2 == STAT_ST_LIST) {
 
5732
                if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users))
 
5733
                        LIST_DEL(&appctx->ctx.sess.bref.users);
 
5734
        }
 
5735
        else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
 
5736
                free(appctx->ctx.cli.err);
 
5737
        }
 
5738
        else if (appctx->st0 == STAT_CLI_O_MLOOK) {
 
5739
                free(appctx->ctx.map.chunk.str);
 
5740
        }
 
5741
}
 
5742
 
 
5743
/* This function is used to either dump tables states (when action is set
 
5744
 * to STAT_CLI_O_TAB) or clear tables (when action is STAT_CLI_O_CLR).
 
5745
 * It returns 0 if the output buffer is full and it needs to be called
 
5746
 * again, otherwise non-zero.
 
5747
 */
 
5748
static int stats_table_request(struct stream_interface *si, int action)
 
5749
{
 
5750
        struct appctx *appctx = __objt_appctx(si->end);
 
5751
        struct session *s = session_from_task(si->owner);
 
5752
        struct ebmb_node *eb;
 
5753
        int dt;
 
5754
        int skip_entry;
 
5755
        int show = action == STAT_CLI_O_TAB;
 
5756
 
 
5757
        /*
 
5758
         * We have 3 possible states in appctx->st2 :
 
5759
         *   - STAT_ST_INIT : the first call
 
5760
         *   - STAT_ST_INFO : the proxy pointer points to the next table to
 
5761
         *     dump, the entry pointer is NULL ;
 
5762
         *   - STAT_ST_LIST : the proxy pointer points to the current table
 
5763
         *     and the entry pointer points to the next entry to be dumped,
 
5764
         *     and the refcount on the next entry is held ;
 
5765
         *   - STAT_ST_END : nothing left to dump, the buffer may contain some
 
5766
         *     data though.
 
5767
         */
 
5768
 
 
5769
        if (unlikely(si->ib->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
 
5770
                /* in case of abort, remove any refcount we might have set on an entry */
 
5771
                if (appctx->st2 == STAT_ST_LIST) {
 
5772
                        appctx->ctx.table.entry->ref_cnt--;
 
5773
                        stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
 
5774
                }
 
5775
                return 1;
 
5776
        }
 
5777
 
 
5778
        chunk_reset(&trash);
 
5779
 
 
5780
        while (appctx->st2 != STAT_ST_FIN) {
 
5781
                switch (appctx->st2) {
 
5782
                case STAT_ST_INIT:
 
5783
                        appctx->ctx.table.proxy = appctx->ctx.table.target;
 
5784
                        if (!appctx->ctx.table.proxy)
 
5785
                                appctx->ctx.table.proxy = proxy;
 
5786
 
 
5787
                        appctx->ctx.table.entry = NULL;
 
5788
                        appctx->st2 = STAT_ST_INFO;
 
5789
                        break;
 
5790
 
 
5791
                case STAT_ST_INFO:
 
5792
                        if (!appctx->ctx.table.proxy ||
 
5793
                            (appctx->ctx.table.target &&
 
5794
                             appctx->ctx.table.proxy != appctx->ctx.table.target)) {
 
5795
                                appctx->st2 = STAT_ST_END;
 
5796
                                break;
 
5797
                        }
 
5798
 
 
5799
                        if (appctx->ctx.table.proxy->table.size) {
 
5800
                                if (show && !stats_dump_table_head_to_buffer(&trash, si, appctx->ctx.table.proxy,
 
5801
                                                                             appctx->ctx.table.target))
 
5802
                                        return 0;
 
5803
 
 
5804
                                if (appctx->ctx.table.target &&
 
5805
                                    s->listener->bind_conf->level >= ACCESS_LVL_OPER) {
 
5806
                                        /* dump entries only if table explicitly requested */
 
5807
                                        eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
 
5808
                                        if (eb) {
 
5809
                                                appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
 
5810
                                                appctx->ctx.table.entry->ref_cnt++;
 
5811
                                                appctx->st2 = STAT_ST_LIST;
 
5812
                                                break;
 
5813
                                        }
 
5814
                                }
 
5815
                        }
 
5816
                        appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
 
5817
                        break;
 
5818
 
 
5819
                case STAT_ST_LIST:
 
5820
                        skip_entry = 0;
 
5821
 
 
5822
                        if (appctx->ctx.table.data_type >= 0) {
 
5823
                                /* we're filtering on some data contents */
 
5824
                                void *ptr;
 
5825
                                long long data;
 
5826
 
 
5827
                                dt = appctx->ctx.table.data_type;
 
5828
                                ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
 
5829
                                                        appctx->ctx.table.entry,
 
5830
                                                        dt);
 
5831
 
 
5832
                                data = 0;
 
5833
                                switch (stktable_data_types[dt].std_type) {
 
5834
                                case STD_T_SINT:
 
5835
                                        data = stktable_data_cast(ptr, std_t_sint);
 
5836
                                        break;
 
5837
                                case STD_T_UINT:
 
5838
                                        data = stktable_data_cast(ptr, std_t_uint);
 
5839
                                        break;
 
5840
                                case STD_T_ULL:
 
5841
                                        data = stktable_data_cast(ptr, std_t_ull);
 
5842
                                        break;
 
5843
                                case STD_T_FRQP:
 
5844
                                        data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
 
5845
                                                                    appctx->ctx.table.proxy->table.data_arg[dt].u);
 
5846
                                        break;
 
5847
                                }
 
5848
 
 
5849
                                /* skip the entry if the data does not match the test and the value */
 
5850
                                if ((data < appctx->ctx.table.value &&
 
5851
                                     (appctx->ctx.table.data_op == STD_OP_EQ ||
 
5852
                                      appctx->ctx.table.data_op == STD_OP_GT ||
 
5853
                                      appctx->ctx.table.data_op == STD_OP_GE)) ||
 
5854
                                    (data == appctx->ctx.table.value &&
 
5855
                                     (appctx->ctx.table.data_op == STD_OP_NE ||
 
5856
                                      appctx->ctx.table.data_op == STD_OP_GT ||
 
5857
                                      appctx->ctx.table.data_op == STD_OP_LT)) ||
 
5858
                                    (data > appctx->ctx.table.value &&
 
5859
                                     (appctx->ctx.table.data_op == STD_OP_EQ ||
 
5860
                                      appctx->ctx.table.data_op == STD_OP_LT ||
 
5861
                                      appctx->ctx.table.data_op == STD_OP_LE)))
 
5862
                                        skip_entry = 1;
 
5863
                        }
 
5864
 
 
5865
                        if (show && !skip_entry &&
 
5866
                            !stats_dump_table_entry_to_buffer(&trash, si, appctx->ctx.table.proxy,
 
5867
                                                              appctx->ctx.table.entry))
 
5868
                            return 0;
 
5869
 
 
5870
                        appctx->ctx.table.entry->ref_cnt--;
 
5871
 
 
5872
                        eb = ebmb_next(&appctx->ctx.table.entry->key);
 
5873
                        if (eb) {
 
5874
                                struct stksess *old = appctx->ctx.table.entry;
 
5875
                                appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
 
5876
                                if (show)
 
5877
                                        stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
 
5878
                                else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
 
5879
                                        stksess_kill(&appctx->ctx.table.proxy->table, old);
 
5880
                                appctx->ctx.table.entry->ref_cnt++;
 
5881
                                break;
 
5882
                        }
 
5883
 
 
5884
 
 
5885
                        if (show)
 
5886
                                stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
 
5887
                        else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
 
5888
                                stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
 
5889
 
 
5890
                        appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
 
5891
                        appctx->st2 = STAT_ST_INFO;
 
5892
                        break;
 
5893
 
 
5894
                case STAT_ST_END:
 
5895
                        appctx->st2 = STAT_ST_FIN;
 
5896
                        break;
 
5897
                }
 
5898
        }
 
5899
        return 1;
2907
5900
}
2908
5901
 
2909
5902
/* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
2924
5917
        if (end > out->size)
2925
5918
                return ptr;
2926
5919
 
2927
 
        chunk_printf(out, "  %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
 
5920
        chunk_appendf(out, "  %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
2928
5921
 
2929
5922
        while (ptr < len && ptr < bsize) {
2930
5923
                c = buf[ptr];
2964
5957
        return ptr;
2965
5958
}
2966
5959
 
2967
 
/* This function is called to send output to the response buffer.
2968
 
 * It dumps the errors logged in proxies onto the output buffer <rep>.
2969
 
 * Expects to be called with client socket shut down on input.
2970
 
 * s->data_ctx must have been zeroed first, and the flags properly set.
2971
 
 * It returns 0 as long as it does not complete, non-zero upon completion.
 
5960
/* This function dumps all captured errors onto the stream interface's
 
5961
 * read buffer. It returns 0 if the output buffer is full and it needs
 
5962
 * to be called again, otherwise non-zero.
2972
5963
 */
2973
 
int stats_dump_errors_to_buffer(struct session *s, struct buffer *rep)
 
5964
static int stats_dump_errors_to_buffer(struct stream_interface *si)
2974
5965
{
 
5966
        struct appctx *appctx = __objt_appctx(si->end);
2975
5967
        extern const char *monthname[12];
2976
 
        struct chunk msg;
2977
5968
 
2978
 
        if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW)))
 
5969
        if (unlikely(si->ib->flags & (CF_WRITE_ERROR|CF_SHUTW)))
2979
5970
                return 1;
2980
5971
 
2981
 
        chunk_init(&msg, trash, trashlen);
 
5972
        chunk_reset(&trash);
2982
5973
 
2983
 
        if (!s->data_ctx.errors.px) {
 
5974
        if (!appctx->ctx.errors.px) {
2984
5975
                /* the function had not been called yet, let's prepare the
2985
5976
                 * buffer for a response.
2986
5977
                 */
2987
5978
                struct tm tm;
2988
5979
 
2989
5980
                get_localtime(date.tv_sec, &tm);
2990
 
                chunk_printf(&msg, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
 
5981
                chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
2991
5982
                             tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
2992
5983
                             tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
2993
5984
                             error_snapshot_id);
2994
5985
 
2995
 
                if (buffer_feed_chunk(rep, &msg) >= 0) {
 
5986
                if (bi_putchk(si->ib, &trash) == -1) {
2996
5987
                        /* Socket buffer full. Let's try again later from the same point */
2997
5988
                        return 0;
2998
5989
                }
2999
5990
 
3000
 
                s->data_ctx.errors.px = proxy;
3001
 
                s->data_ctx.errors.buf = 0;
3002
 
                s->data_ctx.errors.bol = 0;
3003
 
                s->data_ctx.errors.ptr = -1;
 
5991
                appctx->ctx.errors.px = proxy;
 
5992
                appctx->ctx.errors.buf = 0;
 
5993
                appctx->ctx.errors.bol = 0;
 
5994
                appctx->ctx.errors.ptr = -1;
3004
5995
        }
3005
5996
 
3006
5997
        /* we have two inner loops here, one for the proxy, the other one for
3007
5998
         * the buffer.
3008
5999
         */
3009
 
        while (s->data_ctx.errors.px) {
 
6000
        while (appctx->ctx.errors.px) {
3010
6001
                struct error_snapshot *es;
3011
6002
 
3012
 
                if (s->data_ctx.errors.buf == 0)
3013
 
                        es = &s->data_ctx.errors.px->invalid_req;
 
6003
                if (appctx->ctx.errors.buf == 0)
 
6004
                        es = &appctx->ctx.errors.px->invalid_req;
3014
6005
                else
3015
 
                        es = &s->data_ctx.errors.px->invalid_rep;
 
6006
                        es = &appctx->ctx.errors.px->invalid_rep;
3016
6007
 
3017
6008
                if (!es->when.tv_sec)
3018
6009
                        goto next;
3019
6010
 
3020
 
                if (s->data_ctx.errors.iid >= 0 &&
3021
 
                    s->data_ctx.errors.px->uuid != s->data_ctx.errors.iid &&
3022
 
                    es->oe->uuid != s->data_ctx.errors.iid)
 
6011
                if (appctx->ctx.errors.iid >= 0 &&
 
6012
                    appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
 
6013
                    es->oe->uuid != appctx->ctx.errors.iid)
3023
6014
                        goto next;
3024
6015
 
3025
 
                if (s->data_ctx.errors.ptr < 0) {
 
6016
                if (appctx->ctx.errors.ptr < 0) {
3026
6017
                        /* just print headers now */
3027
6018
 
3028
6019
                        char pn[INET6_ADDRSTRLEN];
3029
6020
                        struct tm tm;
 
6021
                        int port;
3030
6022
 
3031
6023
                        get_localtime(es->when.tv_sec, &tm);
3032
 
                        chunk_printf(&msg, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
 
6024
                        chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
3033
6025
                                     tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
3034
6026
                                     tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
3035
6027
 
3036
 
 
3037
 
                        if (es->src.ss_family == AF_INET)
3038
 
                                inet_ntop(AF_INET,
3039
 
                                          (const void *)&((struct sockaddr_in *)&es->src)->sin_addr,
3040
 
                                          pn, sizeof(pn));
3041
 
                        else
3042
 
                                inet_ntop(AF_INET6,
3043
 
                                          (const void *)&((struct sockaddr_in6 *)(&es->src))->sin6_addr,
3044
 
                                          pn, sizeof(pn));
3045
 
 
3046
 
                        switch (s->data_ctx.errors.buf) {
 
6028
                        switch (addr_to_str(&es->src, pn, sizeof(pn))) {
 
6029
                        case AF_INET:
 
6030
                        case AF_INET6:
 
6031
                                port = get_host_port(&es->src);
 
6032
                                break;
 
6033
                        default:
 
6034
                                port = 0;
 
6035
                        }
 
6036
 
 
6037
                        switch (appctx->ctx.errors.buf) {
3047
6038
                        case 0:
3048
 
                                chunk_printf(&msg,
 
6039
                                chunk_appendf(&trash,
3049
6040
                                             " frontend %s (#%d): invalid request\n"
3050
 
                                             "  src %s, session #%d, backend %s (#%d), server %s (#%d)\n"
3051
 
                                             "  HTTP internal state %d, buffer flags 0x%08x, event #%u\n"
3052
 
                                             "  request length %d bytes, error at position %d:\n \n",
3053
 
                                             s->data_ctx.errors.px->id, s->data_ctx.errors.px->uuid,
3054
 
                                             pn, es->sid, (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
3055
 
                                             (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1,
3056
 
                                             es->srv ? es->srv->id : "<NONE>",
3057
 
                                             es->srv ? es->srv->puid : -1,
3058
 
                                             es->state, es->flags, es->ev_id,
3059
 
                                             es->len, es->pos);
 
6041
                                             "  backend %s (#%d)",
 
6042
                                             appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
 
6043
                                             (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
 
6044
                                             (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
3060
6045
                                break;
3061
6046
                        case 1:
3062
 
                                chunk_printf(&msg,
 
6047
                                chunk_appendf(&trash,
3063
6048
                                             " backend %s (#%d) : invalid response\n"
3064
 
                                             "  src %s, session #%d, frontend %s (#%d), server %s (#%d)\n"
3065
 
                                             "  HTTP internal state %d, buffer flags 0x%08x, event #%u\n"
3066
 
                                             "  response length %d bytes, error at position %d:\n \n",
3067
 
                                             s->data_ctx.errors.px->id, s->data_ctx.errors.px->uuid,
3068
 
                                             pn, es->sid, es->oe->id, es->oe->uuid,
3069
 
                                             es->srv ? es->srv->id : "<NONE>",
3070
 
                                             es->srv ? es->srv->puid : -1,
3071
 
                                             es->state, es->flags, es->ev_id,
3072
 
                                             es->len, es->pos);
 
6049
                                             "  frontend %s (#%d)",
 
6050
                                             appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
 
6051
                                             es->oe->id, es->oe->uuid);
3073
6052
                                break;
3074
6053
                        }
3075
6054
 
3076
 
                        if (buffer_feed_chunk(rep, &msg) >= 0) {
 
6055
                        chunk_appendf(&trash,
 
6056
                                     ", server %s (#%d), event #%u\n"
 
6057
                                     "  src %s:%d, session #%d, session flags 0x%08x\n"
 
6058
                                     "  HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
 
6059
                                     "  HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
 
6060
                                     "  buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
 
6061
                                     "  pending %d bytes, wrapping at %d, error at position %d:\n \n",
 
6062
                                     es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
 
6063
                                     es->ev_id,
 
6064
                                     pn, port, es->sid, es->s_flags,
 
6065
                                     es->state, es->m_flags, es->t_flags,
 
6066
                                     es->m_clen, es->m_blen,
 
6067
                                     es->b_flags, es->b_out, es->b_tot,
 
6068
                                     es->len, es->b_wrap, es->pos);
 
6069
 
 
6070
                        if (bi_putchk(si->ib, &trash) == -1) {
3077
6071
                                /* Socket buffer full. Let's try again later from the same point */
3078
6072
                                return 0;
3079
6073
                        }
3080
 
                        s->data_ctx.errors.ptr = 0;
3081
 
                        s->data_ctx.errors.sid = es->sid;
 
6074
                        appctx->ctx.errors.ptr = 0;
 
6075
                        appctx->ctx.errors.sid = es->sid;
3082
6076
                }
3083
6077
 
3084
 
                if (s->data_ctx.errors.sid != es->sid) {
 
6078
                if (appctx->ctx.errors.sid != es->sid) {
3085
6079
                        /* the snapshot changed while we were dumping it */
3086
 
                        chunk_printf(&msg,
 
6080
                        chunk_appendf(&trash,
3087
6081
                                     "  WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
3088
 
                        if (buffer_feed_chunk(rep, &msg) >= 0)
 
6082
                        if (bi_putchk(si->ib, &trash) == -1)
3089
6083
                                return 0;
3090
6084
                        goto next;
3091
6085
                }
3092
6086
 
3093
6087
                /* OK, ptr >= 0, so we have to dump the current line */
3094
 
                while (s->data_ctx.errors.ptr < es->len && s->data_ctx.errors.ptr < sizeof(es->buf)) {
 
6088
                while (appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < sizeof(es->buf)) {
3095
6089
                        int newptr;
3096
6090
                        int newline;
3097
6091
 
3098
 
                        newline = s->data_ctx.errors.bol;
3099
 
                        newptr = dump_text_line(&msg, es->buf, sizeof(es->buf), es->len, &newline, s->data_ctx.errors.ptr);
3100
 
                        if (newptr == s->data_ctx.errors.ptr)
 
6092
                        newline = appctx->ctx.errors.bol;
 
6093
                        newptr = dump_text_line(&trash, es->buf, sizeof(es->buf), es->len, &newline, appctx->ctx.errors.ptr);
 
6094
                        if (newptr == appctx->ctx.errors.ptr)
3101
6095
                                return 0;
3102
6096
 
3103
 
                        if (buffer_feed_chunk(rep, &msg) >= 0) {
 
6097
                        if (bi_putchk(si->ib, &trash) == -1) {
3104
6098
                                /* Socket buffer full. Let's try again later from the same point */
3105
6099
                                return 0;
3106
6100
                        }
3107
 
                        s->data_ctx.errors.ptr = newptr;
3108
 
                        s->data_ctx.errors.bol = newline;
 
6101
                        appctx->ctx.errors.ptr = newptr;
 
6102
                        appctx->ctx.errors.bol = newline;
3109
6103
                };
3110
6104
        next:
3111
 
                s->data_ctx.errors.bol = 0;
3112
 
                s->data_ctx.errors.ptr = -1;
3113
 
                s->data_ctx.errors.buf++;
3114
 
                if (s->data_ctx.errors.buf > 1) {
3115
 
                        s->data_ctx.errors.buf = 0;
3116
 
                        s->data_ctx.errors.px = s->data_ctx.errors.px->next;
 
6105
                appctx->ctx.errors.bol = 0;
 
6106
                appctx->ctx.errors.ptr = -1;
 
6107
                appctx->ctx.errors.buf++;
 
6108
                if (appctx->ctx.errors.buf > 1) {
 
6109
                        appctx->ctx.errors.buf = 0;
 
6110
                        appctx->ctx.errors.px = appctx->ctx.errors.px->next;
3117
6111
                }
3118
6112
        }
3119
6113
 
3121
6115
        return 1;
3122
6116
}
3123
6117
 
3124
 
 
3125
 
static struct cfg_kw_list cfg_kws = {{ },{
 
6118
/* parse the "level" argument on the bind lines */
 
6119
static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
 
6120
{
 
6121
        if (!*args[cur_arg + 1]) {
 
6122
                memprintf(err, "'%s' : missing level", args[cur_arg]);
 
6123
                return ERR_ALERT | ERR_FATAL;
 
6124
        }
 
6125
 
 
6126
        if (!strcmp(args[cur_arg+1], "user"))
 
6127
                conf->level = ACCESS_LVL_USER;
 
6128
        else if (!strcmp(args[cur_arg+1], "operator"))
 
6129
                conf->level = ACCESS_LVL_OPER;
 
6130
        else if (!strcmp(args[cur_arg+1], "admin"))
 
6131
                conf->level = ACCESS_LVL_ADMIN;
 
6132
        else {
 
6133
                memprintf(err, "'%s' only supports 'user', 'operator', and 'admin' (got '%s')",
 
6134
                          args[cur_arg], args[cur_arg+1]);
 
6135
                return ERR_ALERT | ERR_FATAL;
 
6136
        }
 
6137
 
 
6138
        return 0;
 
6139
}
 
6140
 
 
6141
struct si_applet http_stats_applet = {
 
6142
        .obj_type = OBJ_TYPE_APPLET,
 
6143
        .name = "<STATS>", /* used for logging */
 
6144
        .fct = http_stats_io_handler,
 
6145
        .release = NULL,
 
6146
};
 
6147
 
 
6148
static struct si_applet cli_applet = {
 
6149
        .obj_type = OBJ_TYPE_APPLET,
 
6150
        .name = "<CLI>", /* used for logging */
 
6151
        .fct = cli_io_handler,
 
6152
        .release = cli_release_handler,
 
6153
};
 
6154
 
 
6155
static struct cfg_kw_list cfg_kws = {ILH, {
3126
6156
        { CFG_GLOBAL, "stats", stats_parse_global },
3127
6157
        { 0, NULL, NULL },
3128
6158
}};
3129
6159
 
 
6160
static struct bind_kw_list bind_kws = { "STAT", { }, {
 
6161
        { "level",    bind_parse_level,    1 }, /* set the unix socket admin level */
 
6162
        { NULL, NULL, 0 },
 
6163
}};
 
6164
 
3130
6165
__attribute__((constructor))
3131
6166
static void __dumpstats_module_init(void)
3132
6167
{
3133
6168
        cfg_register_keywords(&cfg_kws);
 
6169
        bind_register_keywords(&bind_kws);
3134
6170
}
3135
6171
 
3136
6172
/*