87
LIST_INIT(&fe->pendconns);
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);
95
/* Timeouts are defined as -1, so we cannot use the zeroed area
98
proxy_reset_timeouts(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;
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);
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".
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,
116
if (!strcmp(args[0], "socket")) {
117
struct sockaddr_un *su;
296
struct bind_conf *bind_conf;
299
if (!strcmp(args[1], "socket")) {
121
snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
125
if (global.stats_sock.state != LI_NEW) {
126
snprintf(err, errlen, "'stats socket' already specified in global section");
130
su = str2sun(args[1]);
132
snprintf(err, errlen, "'stats socket' path would require truncation");
135
memcpy(&global.stats_sock.addr, su, sizeof(struct sockaddr_un)); // guaranteed to fit
303
memprintf(err, "'%s %s' in global section expects an address or a path to a UNIX socket", args[0], args[1]);
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]);
142
global.stats_fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
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;
155
global.stats_sock.next = global.stats_fe->listen;
156
global.stats_fe->listen = &global.stats_sock;
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 */
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");
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]);
164
else if (!strcmp(args[cur_arg], "gid")) {
165
global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
168
else if (!strcmp(args[cur_arg], "mode")) {
169
global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
172
else if (!strcmp(args[cur_arg], "user")) {
174
user = getpwnam(args[cur_arg + 1]);
176
snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
180
global.stats_sock.perm.ux.uid = user->pw_uid;
183
else if (!strcmp(args[cur_arg], "group")) {
185
group = getgrnam(args[cur_arg + 1]);
187
snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
191
global.stats_sock.perm.ux.gid = group->gr_gid;
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;
202
snprintf(err, errlen, "'stats socket level' only supports 'user', 'operator', and 'admin'");
208
snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', 'level', and 'mode'");
213
uxst_add_listener(&global.stats_sock);
325
static int bind_dumped;
328
kw = bind_find_kw(args[cur_arg]);
331
memprintf(err, "'%s %s' : '%s' option is not implemented in this version (check build options).",
332
args[0], args[1], args[cur_arg]);
336
if (kw->parse(args, cur_arg, curpx, bind_conf, err) != 0) {
338
memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
340
memprintf(err, "'%s %s' : error encountered while processing '%s'",
341
args[0], args[1], args[cur_arg]);
345
cur_arg += 1 + kw->skip;
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 : "");
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;
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);
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);
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]);
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]);
235
391
global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
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]);
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]);
244
global.maxsock -= global.stats_sock.maxconn;
245
global.stats_sock.maxconn = maxconn;
246
global.maxsock += global.stats_sock.maxconn;
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]);
407
global.stats_fe->maxconn = maxconn;
409
else if (!strcmp(args[1], "bind-process")) { /* enable the socket only on some processes */
411
unsigned long set = 0;
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]);
420
while (*args[cur_arg]) {
421
unsigned int low, high;
423
if (strcmp(args[cur_arg], "all") == 0) {
427
else if (strcmp(args[cur_arg], "odd") == 0) {
428
set |= ~0UL/3UL; /* 0x555....555 */
430
else if (strcmp(args[cur_arg], "even") == 0) {
431
set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
433
else if (isdigit((int)*args[cur_arg])) {
434
char *dash = strchr(args[cur_arg], '-');
436
low = high = str2uic(args[cur_arg]);
438
high = str2uic(dash + 1);
441
unsigned int swap = low;
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);
452
set |= 1UL << (low++ - 1);
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);
462
global.stats_fe->bind_proc = set;
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]);
255
int print_csv_header(struct chunk *msg)
257
return chunk_printf(msg,
260
"scur,smax,slim,stot,"
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,"
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.
476
static void stats_dump_csv_header()
478
chunk_appendf(&trash,
481
"scur,smax,slim,stot,"
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,"
494
"comp_in,comp_out,comp_byp,comp_rsp,lastsess,last_chk,last_agt,qtime,ctime,rtime,ttime,"
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.
503
static int dump_text(struct chunk *out, const char *buf, int bsize)
508
while (buf[ptr] && ptr < bsize) {
510
if (isprint(c) && isascii(c) && c != '\\' && c != ' ') {
511
if (out->len > out->size - 1)
513
out->str[out->len++] = c;
515
else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\' || c == ' ') {
516
if (out->len > out->size - 2)
518
out->str[out->len++] = '\\';
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;
527
out->str[out->len++] = c;
530
if (out->len > out->size - 4)
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];
543
/* print a buffer in hexa.
544
* Print stopped if <bsize> is reached, or if no more place in the chunk.
546
static int dump_binary(struct chunk *out, const char *buf, int bsize)
551
while (ptr < bsize) {
554
if (out->len > out->size - 2)
556
out->str[out->len++] = hextab[(c >> 4) & 0xF];
557
out->str[out->len++] = hextab[c & 0xF];
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.
568
static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
569
struct proxy *proxy, struct proxy *target)
571
struct session *s = session_from_task(si->owner);
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);
576
/* any other information should be dumped here */
578
if (target && s->listener->bind_conf->level < ACCESS_LVL_OPER)
579
chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
581
if (bi_putchk(si->ib, msg) == -1)
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.
591
static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
592
struct proxy *proxy, struct stksess *entry)
596
chunk_appendf(msg, "%p:", entry);
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);
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);
608
else if (proxy->table.type == STKTABLE_TYPE_INTEGER) {
609
chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
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);
616
chunk_appendf(msg, " key=");
617
dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
620
chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
622
for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
625
if (proxy->table.data_ofs[dt] == 0)
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);
630
chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
632
ptr = stktable_data_ptr(&proxy->table, entry, dt);
633
switch (stktable_data_types[dt].std_type) {
635
chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
638
chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
641
chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
644
chunk_appendf(msg, "%d",
645
read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
646
proxy->table.data_arg[dt].u));
650
chunk_appendf(msg, "\n");
652
if (bi_putchk(si->ib, msg) == -1)
658
static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
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;
665
unsigned char ip6_key[sizeof(struct in6_addr)];
670
struct freq_ctr_period *frqp;
672
appctx->st0 = STAT_CLI_OUTPUT;
675
appctx->ctx.cli.msg = "Key value expected\n";
676
appctx->st0 = STAT_CLI_PRINT;
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;
685
case STKTABLE_TYPE_IPV6:
686
inet_pton(AF_INET6, args[4], ip6_key);
687
static_table_key->key = &ip6_key;
689
case STKTABLE_TYPE_INTEGER:
694
val = strtoul(args[4], &endptr, 10);
695
if ((errno == ERANGE && val == ULONG_MAX) ||
696
(errno != 0 && val == 0) || endptr == args[4] ||
698
appctx->ctx.cli.msg = "Invalid key\n";
699
appctx->st0 = STAT_CLI_PRINT;
702
uint32_key = (uint32_t) val;
703
static_table_key->key = &uint32_key;
707
case STKTABLE_TYPE_STRING:
708
static_table_key->key = args[4];
709
static_table_key->key_len = strlen(args[4]);
714
appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
717
appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
720
appctx->ctx.cli.msg = "Unknown action\n";
723
appctx->st0 = STAT_CLI_PRINT;
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;
734
ts = stktable_lookup_key(&px->table, static_table_key);
741
if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
743
stats_dump_table_entry_to_buffer(&trash, si, px, ts);
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;
755
stksess_kill(&px->table, ts);
760
stktable_touch(&px->table, ts, 1);
762
ts = stksess_new(&px->table, static_table_key);
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;
769
stktable_store(&px->table, ts, 1);
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;
779
data_type = stktable_get_data_type(args[cur_arg] + 5);
781
appctx->ctx.cli.msg = "Unknown data type\n";
782
appctx->st0 = STAT_CLI_PRINT;
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;
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;
798
ptr = stktable_data_ptr(&px->table, ts, data_type);
800
switch (stktable_data_types[data_type].std_type) {
802
stktable_data_cast(ptr, std_t_sint) = value;
805
stktable_data_cast(ptr, std_t_uint) = value;
808
stktable_data_cast(ptr, std_t_ull) = value;
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.
816
frqp = &stktable_data_cast(ptr, std_t_frqp);
817
frqp->curr_tick = now_ms;
819
frqp->curr_ctr = value;
826
appctx->ctx.cli.msg = "Unknown action\n";
827
appctx->st0 = STAT_CLI_PRINT;
832
static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
834
struct appctx *appctx = __objt_appctx(si->end);
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;
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;
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;
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;
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;
870
static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
872
struct appctx *appctx = __objt_appctx(si->end);
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;
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;
890
if (action != STAT_CLI_O_TAB)
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);
907
appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
910
appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
913
appctx->ctx.cli.msg = "Unknown action\n";
916
appctx->st0 = STAT_CLI_PRINT;
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
923
static struct proxy *expect_frontend_admin(struct session *s, struct stream_interface *si, const char *arg)
925
struct appctx *appctx = __objt_appctx(si->end);
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;
935
appctx->ctx.cli.msg = "A frontend name is expected.\n";
936
appctx->st0 = STAT_CLI_PRINT;
940
px = findproxy(arg, PR_CAP_FE);
942
appctx->ctx.cli.msg = "No such frontend.\n";
943
appctx->st0 = STAT_CLI_PRINT;
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 '/'.
954
static struct server *expect_server_admin(struct session *s, struct stream_interface *si, char *arg)
956
struct appctx *appctx = __objt_appctx(si->end);
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;
967
/* split "backend/server" and make <line> point to server */
968
for (line = arg; *line; line++)
974
if (!*line || !*arg) {
975
appctx->ctx.cli.msg = "Require 'backend/server'.\n";
976
appctx->st0 = STAT_CLI_PRINT;
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;
986
if (px->state == PR_STSTOPPED) {
987
appctx->ctx.cli.msg = "Proxy is disabled.\n";
988
appctx->st0 = STAT_CLI_PRINT;
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
1001
struct pat_ref *pat_list_get_next(struct pat_ref *getnext, struct list *end,
1004
struct pat_ref *ref = getnext;
1008
/* Get next list entry. */
1009
ref = LIST_NEXT(&ref->list, struct pat_ref *, list);
1011
/* If the entry is the last of the list, return NULL. */
1012
if (&ref->list == end)
1015
/* If the entry match the flag, return it. */
1016
if (ref->flags & flags)
1022
struct pat_ref *pat_ref_lookup_ref(const char *reference)
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);
1034
/* Perform the unique id lookup. */
1035
return pat_ref_lookupid(id);
1038
/* Perform the string lookup. */
1039
return pat_ref_lookup(reference);
1042
/* This function is used with map and acl management. It permits to browse
1046
struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
1048
struct pattern_expr *expr;
1049
expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
1050
if (&expr->list == end)
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.
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)
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];
288
1069
while (isspace((unsigned char)*line))
541
s->data_ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
542
si->st0 = STAT_CLI_PRINT;
1495
appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
1496
appctx->st0 = STAT_CLI_PRINT;
1500
else if (strcmp(args[1], "maxconn") == 0) {
1501
if (strcmp(args[2], "frontend") == 0) {
1506
px = expect_frontend_admin(s, si, args[3]);
1511
appctx->ctx.cli.msg = "Integer value expected.\n";
1512
appctx->st0 = STAT_CLI_PRINT;
1518
appctx->ctx.cli.msg = "Value out of range.\n";
1519
appctx->st0 = STAT_CLI_PRINT;
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.
1527
list_for_each_entry(l, &px->conf.listeners, by_fe) {
1529
if (l->state == LI_FULL)
1533
if (px->maxconn > px->feconn && !LIST_ISEMPTY(&s->fe->listener_queue))
1534
dequeue_all_listeners(&s->fe->listener_queue);
1538
else if (strcmp(args[2], "global") == 0) {
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;
1548
appctx->ctx.cli.msg = "Expects an integer value.\n";
1549
appctx->st0 = STAT_CLI_PRINT;
1554
if (v > global.hardmaxconn) {
1555
appctx->ctx.cli.msg = "Value out of range.\n";
1556
appctx->st0 = STAT_CLI_PRINT;
1560
/* check for unlimited values */
1562
v = global.hardmaxconn;
1566
/* Dequeues all of the listeners waiting for a resource */
1567
if (!LIST_ISEMPTY(&global_listener_queue))
1568
dequeue_all_listeners(&global_listener_queue);
1573
appctx->ctx.cli.msg = "'set maxconn' only supports 'frontend' and 'global'.\n";
1574
appctx->st0 = STAT_CLI_PRINT;
1578
else if (strcmp(args[1], "rate-limit") == 0) {
1579
if (strcmp(args[2], "connections") == 0) {
1580
if (strcmp(args[3], "global") == 0) {
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;
1590
appctx->ctx.cli.msg = "Expects an integer value.\n";
1591
appctx->st0 = STAT_CLI_PRINT;
1597
appctx->ctx.cli.msg = "Value out of range.\n";
1598
appctx->st0 = STAT_CLI_PRINT;
1604
/* Dequeues all of the listeners waiting for a resource */
1605
if (!LIST_ISEMPTY(&global_listener_queue))
1606
dequeue_all_listeners(&global_listener_queue);
1611
appctx->ctx.cli.msg = "'set rate-limit connections' only supports 'global'.\n";
1612
appctx->st0 = STAT_CLI_PRINT;
1616
else if (strcmp(args[2], "sessions") == 0) {
1617
if (strcmp(args[3], "global") == 0) {
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;
1627
appctx->ctx.cli.msg = "Expects an integer value.\n";
1628
appctx->st0 = STAT_CLI_PRINT;
1634
appctx->ctx.cli.msg = "Value out of range.\n";
1635
appctx->st0 = STAT_CLI_PRINT;
1641
/* Dequeues all of the listeners waiting for a resource */
1642
if (!LIST_ISEMPTY(&global_listener_queue))
1643
dequeue_all_listeners(&global_listener_queue);
1648
appctx->ctx.cli.msg = "'set rate-limit sessions' only supports 'global'.\n";
1649
appctx->st0 = STAT_CLI_PRINT;
1654
else if (strcmp(args[2], "ssl-sessions") == 0) {
1655
if (strcmp(args[3], "global") == 0) {
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;
1665
appctx->ctx.cli.msg = "Expects an integer value.\n";
1666
appctx->st0 = STAT_CLI_PRINT;
1672
appctx->ctx.cli.msg = "Value out of range.\n";
1673
appctx->st0 = STAT_CLI_PRINT;
1679
/* Dequeues all of the listeners waiting for a resource */
1680
if (!LIST_ISEMPTY(&global_listener_queue))
1681
dequeue_all_listeners(&global_listener_queue);
1686
appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
1687
appctx->st0 = STAT_CLI_PRINT;
1692
else if (strcmp(args[2], "http-compression") == 0) {
1693
if (strcmp(args[3], "global") == 0) {
1697
appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
1698
appctx->st0 = STAT_CLI_PRINT;
1703
global.comp_rate_lim = v * 1024; /* Kilo to bytes. */
1706
appctx->ctx.cli.msg = "'set rate-limit http-compression' only supports 'global'.\n";
1707
appctx->st0 = STAT_CLI_PRINT;
1712
appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
1713
appctx->st0 = STAT_CLI_PRINT;
1717
else if (strcmp(args[1], "table") == 0) {
1718
stats_sock_table_request(si, args, STAT_CLI_O_SET);
1720
else if (strcmp(args[1], "map") == 0) {
1724
appctx->ctx.map.display_flags = PAT_REF_MAP;
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;
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;
1741
/* If the entry identifier start with a '#', it is considered as
1744
if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
1745
struct pat_ref_elt *ref;
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;
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;
1765
/* Try to delete the entry. */
1767
if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
1769
memprintf(&err, "%s.\n", err);
1770
appctx->ctx.cli.err = err;
1771
appctx->st0 = STAT_CLI_PRINT_FREE;
1776
/* Else, use the entry identifier as pattern
1777
* string, and update the value.
1780
if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
1782
memprintf(&err, "%s.\n", err);
1783
appctx->ctx.cli.err = err;
1784
appctx->st0 = STAT_CLI_PRINT_FREE;
1789
/* The set is done, send message. */
1790
appctx->ctx.cli.msg = "Done.\n";
1791
appctx->st0 = STAT_CLI_PRINT;
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
1800
/* Expect one parameter: the new response in base64 encoding */
1802
appctx->ctx.cli.msg = "'set ssl ocsp-response' expects response in base64 encoding.\n";
1803
appctx->st0 = STAT_CLI_PRINT;
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;
1814
if (ssl_sock_update_ocsp_response(&trash, &err)) {
1816
memprintf(&err, "%s.\n", err);
1817
appctx->ctx.cli.err = err;
1818
appctx->st0 = STAT_CLI_PRINT_FREE;
1822
appctx->ctx.cli.msg = "OCSP Response updated!";
1823
appctx->st0 = STAT_CLI_PRINT;
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;
1832
appctx->ctx.cli.msg = "'set ssl' only supports 'ocsp-response'.\n";
1833
appctx->st0 = STAT_CLI_PRINT;
546
1838
else { /* unknown "set" parameter */
550
1842
else if (strcmp(args[0], "enable") == 0) {
551
if (strcmp(args[1], "server") == 0) {
1843
if (strcmp(args[1], "agent") == 0) {
1846
sv = expect_server_admin(s, si, args[2]);
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;
1856
sv->agent.state |= CHK_ST_ENABLED;
1859
else if (strcmp(args[1], "health") == 0) {
1862
sv = expect_server_admin(s, si, args[2]);
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;
1872
sv->check.state |= CHK_ST_ENABLED;
1875
else if (strcmp(args[1], "server") == 0) {
1878
sv = expect_server_admin(s, si, args[2]);
1882
srv_adm_set_ready(sv);
1885
else if (strcmp(args[1], "frontend") == 0) {
552
1886
struct proxy *px;
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;
561
/* split "backend/server" and make <line> point to server */
562
for (line = args[2]; *line; line++)
568
if (!*line || !*args[2]) {
569
s->data_ctx.cli.msg = "Require 'backend/server'.\n";
570
si->st0 = STAT_CLI_PRINT;
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;
1888
px = expect_frontend_admin(s, si, args[2]);
580
1892
if (px->state == PR_STSTOPPED) {
581
s->data_ctx.cli.msg = "Proxy is disabled.\n";
582
si->st0 = STAT_CLI_PRINT;
586
if (sv->state & SRV_MAINTAIN) {
587
/* The server is really in maintenance, we can change the server state */
589
/* If this server tracks the status of another one,
590
* we must restore the good status.
592
if (sv->tracked->state & SRV_RUNNING) {
594
sv->health = sv->rise; /* up, but will fall down at first failure */
596
sv->state &= ~SRV_MAINTAIN;
601
sv->health = sv->rise; /* up, but will fall down at first failure */
1893
appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
1894
appctx->st0 = STAT_CLI_PRINT;
1898
if (px->state != PR_STPAUSED) {
1899
appctx->ctx.cli.msg = "Frontend is already enabled.\n";
1900
appctx->st0 = STAT_CLI_PRINT;
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;
607
1911
else { /* unknown "enable" parameter */
1912
appctx->ctx.cli.msg = "'enable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
1913
appctx->st0 = STAT_CLI_PRINT;
611
1917
else if (strcmp(args[0], "disable") == 0) {
612
if (strcmp(args[1], "server") == 0) {
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;
622
/* split "backend/server" and make <line> point to server */
623
for (line = args[2]; *line; line++)
1918
if (strcmp(args[1], "agent") == 0) {
1921
sv = expect_server_admin(s, si, args[2]);
1925
sv->agent.state &= ~CHK_ST_ENABLED;
1928
else if (strcmp(args[1], "health") == 0) {
1931
sv = expect_server_admin(s, si, args[2]);
1935
sv->check.state &= ~CHK_ST_ENABLED;
1938
else if (strcmp(args[1], "server") == 0) {
1941
sv = expect_server_admin(s, si, args[2]);
1945
srv_adm_set_maint(sv);
1948
else if (strcmp(args[1], "frontend") == 0) {
1951
px = expect_frontend_admin(s, si, args[2]);
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;
1961
if (px->state == PR_STPAUSED) {
1962
appctx->ctx.cli.msg = "Frontend is already disabled.\n";
1963
appctx->st0 = STAT_CLI_PRINT;
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;
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;
1980
else if (strcmp(args[0], "shutdown") == 0) {
1981
if (strcmp(args[1], "frontend") == 0) {
1984
px = expect_frontend_admin(s, si, args[2]);
1988
if (px->state == PR_STSTOPPED) {
1989
appctx->ctx.cli.msg = "Frontend was already shut down.\n";
1990
appctx->st0 = STAT_CLI_PRINT;
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);
2001
else if (strcmp(args[1], "session") == 0) {
2002
struct session *sess, *ptr;
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;
2011
appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
2012
appctx->st0 = STAT_CLI_PRINT;
2016
ptr = (void *)strtoul(args[2], NULL, 0);
2018
/* first, look for the requested session in the session table */
2019
list_for_each_entry(sess, &sessions, list) {
629
if (!*line || !*args[2]) {
630
s->data_ctx.cli.msg = "Require 'backend/server'.\n";
631
si->st0 = STAT_CLI_PRINT;
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;
641
if (px->state == PR_STSTOPPED) {
642
s->data_ctx.cli.msg = "Proxy is disabled.\n";
643
si->st0 = STAT_CLI_PRINT;
647
if (! (sv->state & SRV_MAINTAIN)) {
648
/* Not already in maintenance, we can change the server state */
649
sv->state |= SRV_MAINTAIN;
2024
/* do we have the session ? */
2026
appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
2027
appctx->st0 = STAT_CLI_PRINT;
2031
session_shutdown(sess, SN_ERR_KILLED);
2034
else if (strcmp(args[1], "sessions") == 0) {
2035
if (strcmp(args[2], "server") == 0) {
2037
struct session *sess, *sess_bck;
2039
sv = expect_server_admin(s, si, args[3]);
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);
2051
appctx->ctx.cli.msg = "'shutdown sessions' only supports 'server'.\n";
2052
appctx->st0 = STAT_CLI_PRINT;
655
2056
else { /* unknown "disable" parameter */
2057
appctx->ctx.cli.msg = "'shutdown' only supports 'frontend', 'session' and 'sessions'.\n";
2058
appctx->st0 = STAT_CLI_PRINT;
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;
2067
appctx->ctx.map.display_flags = PAT_REF_ACL;
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;
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;
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;
2095
/* If the entry identifier start with a '#', it is considered as
2098
if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
2099
struct pat_ref_elt *ref;
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;
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;
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;
2128
/* Else, use the entry identifier as pattern
2129
* string and try to delete the entry.
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;
2139
/* The deletion is done, send message. */
2140
appctx->ctx.cli.msg = "Done.\n";
2141
appctx->st0 = STAT_CLI_PRINT;
2144
else { /* unknown "del" parameter */
2145
appctx->ctx.cli.msg = "'del' only supports 'map' or 'acl'.\n";
2146
appctx->st0 = STAT_CLI_PRINT;
2150
else if (strcmp(args[0], "add") == 0) {
2151
if (strcmp(args[1], "map") == 0 ||
2152
strcmp(args[1], "acl") == 0) {
2157
if (args[1][0] == 'm')
2158
appctx->ctx.map.display_flags = PAT_REF_MAP;
2160
appctx->ctx.map.display_flags = PAT_REF_ACL;
2162
/* If the keywork is "map", we expect three parameters, if it
2163
* is "acl", we expect only two parameters
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;
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;
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";
2186
appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
2187
appctx->st0 = STAT_CLI_PRINT;
2191
/* The command "add acl" is prohibited if the reference
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;
2204
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
2205
ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
2207
ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
2210
memprintf(&err, "%s.\n", err);
2211
appctx->ctx.cli.err = err;
2212
appctx->st0 = STAT_CLI_PRINT_FREE;
2216
/* The add is done, send message. */
2217
appctx->ctx.cli.msg = "Done.\n";
2218
appctx->st0 = STAT_CLI_PRINT;
2221
else { /* unknown "del" parameter */
2222
appctx->ctx.cli.msg = "'add' only supports 'map'.\n";
2223
appctx->st0 = STAT_CLI_PRINT;
659
2227
else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
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.
868
int stats_dump_raw_to_buffer(struct session *s, struct buffer *rep)
874
chunk_init(&msg, trash, trashlen);
876
switch (s->data_state) {
878
/* the function had not been called yet */
879
s->data_state = 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)
889
s->data_state = DATA_ST_INFO;
893
up = (now.tv_sec - start_date.tv_sec);
894
if (s->data_ctx.stats.flags & STAT_SHOW_INFO) {
896
"Name: " PRODUCT_NAME "\n"
897
"Version: " HAPROXY_VERSION "\n"
898
"Release_date: " HAPROXY_DATE "\n"
902
"Uptime: %dd %dh%02dm%02ds\n"
920
up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
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:""
929
if (buffer_feed_chunk(rep, &msg) >= 0)
933
s->data_ctx.stats.px = proxy;
934
s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
936
s->data_ctx.stats.sv = NULL;
937
s->data_ctx.stats.sv_st = 0;
939
s->data_state = DATA_ST_LIST;
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)
954
s->data_ctx.stats.px = px->next;
955
s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
957
/* here, we just have reached the last proxy */
960
s->data_state = DATA_ST_END;
964
s->data_state = DATA_ST_FIN;
971
/* unknown state ! */
972
s->data_state = DATA_ST_FIN;
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.
982
int stats_http_redir(struct session *s, struct buffer *rep, struct uri_auth *uri)
986
chunk_init(&msg, trash, trashlen);
988
switch (s->data_state) {
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");
999
if (buffer_feed_chunk(rep, &msg) >= 0)
1002
s->txn.status = 303;
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;
1009
s->data_state = DATA_ST_FIN;
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.
1021
void http_stats_io_handler(struct stream_interface *si)
1023
struct session *s = si->private;
1024
struct buffer *req = si->ob;
1025
struct buffer *res = si->ib;
1027
if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
1030
/* check that the output is not closed */
1031
if (res->flags & (BF_SHUTW|BF_SHUTW_NOW))
1035
if (s->txn.meth == HTTP_METH_POST) {
1036
if (stats_http_redir(s, res, s->be->uri_auth)) {
1041
if (stats_dump_http(s, res, s->be->uri_auth)) {
1048
if ((res->flags & BF_SHUTR) && (si->state == SI_ST_EST))
1051
if ((req->flags & BF_SHUTW) && (si->state == SI_ST_EST) && si->st0) {
1053
res->flags |= BF_READ_NULL;
1056
/* update all other flags and resync with the other side */
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;
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);
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
1081
int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri)
1087
chunk_init(&msg, trash, trashlen);
1089
switch (s->data_state) {
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");
1098
if (uri->refresh > 0 && !(s->data_ctx.stats.flags & STAT_NO_REFRESH))
1099
chunk_printf(&msg, "Refresh: %d\r\n",
1102
chunk_printf(&msg, "\r\n");
1104
s->txn.status = 200;
1105
if (buffer_feed_chunk(rep, &msg) >= 0)
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;
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;
1119
s->data_state = DATA_ST_HEAD; /* let's start producing data */
1123
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1124
/* WARNING! This must fit in the first buffer !!! */
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"
1132
" font-family: arial, helvetica, sans-serif;"
1134
" font-weight: normal;"
1136
" background: white;"
1142
" font-size: x-large;"
1143
" margin-bottom: 0.5em;"
1146
" font-family: helvetica, arial;"
1147
" font-size: x-large;"
1148
" font-weight: bold;"
1149
" font-style: italic;"
1152
" margin-bottom: 0em;"
1155
" font-family: helvetica, arial;"
1157
" font-weight: bold;"
1159
" background: #e8e8d0;"
1161
" margin-bottom: 0em;"
1164
" margin-top: 0.25em;"
1165
" margin-right: 2em;"
1167
".hr {margin-top: 0.25em;"
1168
" border-color: black;"
1169
" border-bottom-style: solid;"
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) */
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;}"
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"
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"
1213
"</style></head>\n",
1214
(uri->flags&ST_SHNODE) ? " on " : "",
1215
(uri->flags&ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
1218
print_csv_header(&msg);
1220
if (buffer_feed_chunk(rep, &msg) >= 0)
1223
s->data_state = DATA_ST_INFO;
1227
up = (now.tv_sec - start_date.tv_sec);
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 !
1233
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
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>> 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\"> </td><td class=\"noborder\">active UP </td>"
1250
"<td class=\"backup3\"> </td><td class=\"noborder\">backup UP </td>"
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>"
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>"
1258
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
1259
"<td class=\"active6\"></td><td class=\"noborder\">not checked </td>"
1261
"<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) </td>"
1263
"Note: UP with load-balancing disabled is reported as \"NOLB\"."
1265
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
1266
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
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
1282
if (s->data_ctx.stats.flags & STAT_HIDE_DOWN)
1284
"<li><a href=\"%s%s%s\">Show all servers</a><br>\n",
1287
(s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
1290
"<li><a href=\"%s%s%s\">Hide 'DOWN' servers</a><br>\n",
1293
(s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
1295
if (uri->refresh > 0) {
1296
if (s->data_ctx.stats.flags & STAT_NO_REFRESH)
1298
"<li><a href=\"%s%s%s\">Enable refresh</a><br>\n",
1300
(s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1304
"<li><a href=\"%s%s%s\">Disable refresh</a><br>\n",
1306
(s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1311
"<li><a href=\"%s%s%s\">Refresh now</a><br>\n",
1313
(s->data_ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
1314
(s->data_ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "");
1317
"<li><a href=\"%s;csv%s\">CSV export</a><br>\n",
1319
(uri->refresh > 0) ? ";norefresh" : "");
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"
1334
if (s->data_ctx.stats.st_code) {
1335
if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_DONE) == 0) {
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);
1342
else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_NONE) == 0) {
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);
1349
else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_PART) == 0) {
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);
1357
else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_ERRP) == 0) {
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."
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>"
1367
"</div>\n", uri->uri_prefix);
1369
else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_EXCD) == 0) {
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);
1377
else if (strcmp(s->data_ctx.stats.st_code, STAT_STATUS_DENY) == 0) {
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);
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);
1391
chunk_printf(&msg,"<p>\n");
1394
if (buffer_feed_chunk(rep, &msg) >= 0)
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;
1405
while (s->data_ctx.stats.px) {
1406
if (buffer_almost_full(rep))
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)
1414
s->data_ctx.stats.px = px->next;
1415
s->data_ctx.stats.px_st = DATA_ST_PX_INIT;
1417
/* here, we just have reached the last proxy */
1419
s->data_state = 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)
1429
s->data_state = DATA_ST_FIN;
1436
/* unknown state ! */
1437
s->data_state = DATA_ST_FIN;
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.
1448
int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri)
1450
struct buffer *rep = s->rep;
1451
struct server *sv, *svs; /* server and server-state, server-state=server or server->tracked */
2465
static int stats_dump_info_to_buffer(struct stream_interface *si)
2467
unsigned int up = (now.tv_sec - start_date.tv_sec);
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);
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;
2480
chunk_printf(&trash,
2481
"Name: " PRODUCT_NAME "\n"
2482
"Version: " HAPROXY_VERSION "\n"
2483
"Release_date: " HAPROXY_DATE "\n"
2487
"Uptime: %dd %dh%02dm%02ds\n"
2493
"Hard_maxconn: %d\n"
2499
"CurrSslConns: %d\n"
2506
"ConnRateLimit: %d\n"
2509
"SessRateLimit: %d\n"
2513
"SslRateLimit: %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"
2523
"CompressBpsIn: %u\n"
2524
"CompressBpsOut: %u\n"
2525
"CompressBpsRateLim: %u\n"
2527
"ZlibMemUsage: %ld\n"
2528
"MaxZlibMemUsage: %ld\n"
2539
up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60),
2541
global.rlimit_memmax,
2542
global.rlimit_nofile,
2543
global.maxsock, global.maxconn, global.hardmaxconn,
2544
actconn, totalconn, global.req_count,
2546
global.maxsslconn, sslconns, totalsslconns,
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,
2552
ssl_sess_rate, global.ssl_lim, global.ssl_max,
2553
ssl_key_rate, global.ssl_fe_keys_max,
2555
read_freq_ctr(&global.ssl_be_keys_per_sec), global.ssl_be_keys_max,
2556
global.shctx_lookups, global.shctx_misses,
2558
read_freq_ctr(&global.comp_bps_in), read_freq_ctr(&global.comp_bps_out),
2559
global.comp_rate_lim,
2561
zlib_used_memory, global.maxzlibmem,
2563
nb_tasks_cur, run_queue_cur, idle_pct,
2564
global.node, global.desc ? global.desc : ""
2567
if (bi_putchk(si->ib, &trash) == -1)
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.
2577
static int stats_dump_pools_to_buffer(struct stream_interface *si)
2579
dump_pools_to_trash();
2580
if (bi_putchk(si->ib, &trash) == -1)
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.
2589
static int stats_dump_fe_stats(struct stream_interface *si, struct proxy *px)
2591
struct appctx *appctx = __objt_appctx(si->end);
2594
if (!(px->cap & PR_CAP_FE))
2597
if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_FE)))
2600
if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
2601
chunk_appendf(&trash,
2603
"<tr class=\"frontend\">");
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>");
2610
chunk_appendf(&trash,
2612
"<a name=\"%s/Frontend\"></a>"
2613
"<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
2614
"<td colspan=3></td>"
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>"
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)));
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)));
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>"
2640
U2H(px->fe_counters.sps_max),
2641
U2H(px->fe_counters.cps_max),
2642
U2H(px->fe_counters.sps_max));
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));
2649
chunk_appendf(&trash,
2650
"</table></div></u></td>"
2651
/* sessions rate : limit */
2653
LIM2A(px->fe_sps_lim, "-"));
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>"
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));
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> 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>"
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));
2693
chunk_appendf(&trash,
2694
"</table></div></u></td>"
2695
/* sessions: lbtot, lastsess */
2696
"<td></td><td></td>"
2700
U2H(px->fe_counters.bytes_in));
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>":"");
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>"
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");
2729
else { /* CSV mode */
2730
chunk_appendf(&trash,
2731
/* pxid, name, queue cur, queue max, */
2733
/* sessions : current, max, limit, total */
2735
/* bytes : in, out */
2737
/* denied: req, resp */
2739
/* errors : request, connect, response */
2741
/* warnings: retries, redispatches */
2743
/* server status : reflect frontend status */
2745
/* rest of server: nothing */
2747
/* pid, iid, sid, throttle, lbtot, tracked, type */
2749
/* rate, rate_lim, rate_max */
2751
/* check_status, check_code, check_duration */
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);
2764
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2765
if (px->mode == PR_MODE_HTTP) {
2767
chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[i]);
2768
chunk_appendf(&trash, "%lld,", px->fe_counters.p.http.rsp[0]);
2771
chunk_appendf(&trash, ",,,,,,");
2773
/* failed health analyses */
2774
chunk_appendf(&trash, ",");
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);
2781
/* errors: cli_aborts, srv_aborts */
2782
chunk_appendf(&trash, ",,");
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);
2788
/* compression: comp_rsp */
2789
chunk_appendf(&trash, "%lld,",
2790
px->fe_counters.p.http.comp_rsp);
2792
/* lastsess, last_chk, last_agt, qtime, ctime, rtime, ttime, */
2793
chunk_appendf(&trash, ",,,,,,,");
2795
/* finish with EOL */
2796
chunk_appendf(&trash, "\n");
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
2806
static int stats_dump_li_stats(struct stream_interface *si, struct proxy *px, struct listener *l, int flags)
2808
struct appctx *appctx = __objt_appctx(si->end);
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>");
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>"
2822
(flags & ST_SHLGNDS)?"<u>":"",
2823
px->id, l->name, l->name);
2825
if (flags & ST_SHLGNDS) {
2826
char str[INET6_ADDRSTRLEN];
2829
chunk_appendf(&trash, "<div class=tips>");
2831
port = get_host_port(&l->addr);
2832
switch (addr_to_str(&l->addr, str, sizeof(str))) {
2834
chunk_appendf(&trash, "IPv4: %s:%d, ", str, port);
2837
chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, port);
2840
chunk_appendf(&trash, "unix, ");
2843
chunk_appendf(&trash, "(%s), ", strerror(errno));
2848
chunk_appendf(&trash, "id: %d</div>", l->luid);
2851
chunk_appendf(&trash,
2853
"%s</td><td colspan=3></td>"
2854
/* sessions rate: current, max, limit */
2855
"<td colspan=3> </td>"
2856
/* sessions: current, max, limit, total, lbtot, lastsess */
2857
"<td>%s</td><td>%s</td><td>%s</td>"
2858
"<td>%s</td><td> </td><td> </td>"
2859
/* bytes: in, out */
2860
"<td>%s</td><td>%s</td>"
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));
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>"
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");
2882
else { /* CSV mode */
2883
chunk_appendf(&trash,
2884
/* pxid, name, queue cur, queue max, */
2886
/* sessions: current, max, limit, total */
2888
/* bytes: in, out */
2890
/* denied: req, resp */
2892
/* errors: request, connect, response */
2894
/* warnings: retries, redispatches */
2896
/* server status: reflect listener status */
2898
/* rest of server: nothing */
2900
/* pid, iid, sid, throttle, lbtot, tracked, type */
2902
/* rate, rate_lim, rate_max */
2904
/* check_status, check_code, check_duration */
2906
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2908
/* failed health analyses */
2910
/* requests : req_rate, req_rate_max, req_tot, */
2912
/* errors: cli_aborts, srv_aborts */
2914
/* compression: in, out, bypassed, comp_rsp */
2916
/* lastsess, last_chk, last_agt, qtime, ctime, rtime, ttime, */
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);
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.
2938
static int stats_dump_sv_stats(struct stream_interface *si, struct proxy *px, int flags, struct server *sv, int state)
2940
struct appctx *appctx = __objt_appctx(si->end);
2941
struct server *via, *ref;
2942
char str[INET6_ADDRSTRLEN];
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.
2949
via = sv->track ? sv->track : sv;
2954
if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
2955
static char *srv_hlt_st[10] = {
2961
"NOLB %d/%d ↓",
2963
"DRAIN %d/%d ↓",
2968
if (sv->admin & SRV_ADMF_MAINT)
2969
chunk_appendf(&trash, "<tr class=\"maintain\">");
2971
chunk_appendf(&trash,
2972
"<tr class=\"%s%d\">",
2973
(sv->flags & SRV_F_BACKUP) ? "backup" : "active", state);
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>",
2980
chunk_appendf(&trash,
2981
"<td class=ac><a name=\"%s/%s\"></a>%s"
2982
"<a class=lfsb href=\"#%s/%s\">%s</a>"
2985
(flags & ST_SHLGNDS) ? "<u>" : "",
2986
px->id, sv->id, sv->id);
2988
if (flags & ST_SHLGNDS) {
2989
chunk_appendf(&trash, "<div class=tips>");
2991
switch (addr_to_str(&sv->addr, str, sizeof(str))) {
2993
chunk_appendf(&trash, "IPv4: %s:%d, ", str, get_host_port(&sv->addr));
2996
chunk_appendf(&trash, "IPv6: [%s]:%d, ", str, get_host_port(&sv->addr));
2999
chunk_appendf(&trash, "unix, ");
3002
chunk_appendf(&trash, "(%s), ", strerror(errno));
3004
default: /* address family not supported */
3009
chunk_appendf(&trash, "id: %d", sv->puid);
3013
chunk_appendf(&trash, ", cookie: '");
3015
chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
3016
chunk_htmlencode(&trash, &src);
3018
chunk_appendf(&trash, "'");
3021
chunk_appendf(&trash, "</div>");
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>"
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));
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>"
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));
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];
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>"
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);
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)));
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));
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>"
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);
3103
/* status, lest check */
3104
chunk_appendf(&trash, "<td class=ac>");
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");
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));
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!");
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,
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));
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));
3130
if (sv->agent.status >= HCHK_STATUS_L57DATA)
3131
chunk_appendf(&trash, "/%d", sv->agent.code);
3133
if (sv->agent.status >= HCHK_STATUS_CHECKED && sv->agent.duration >= 0)
3134
chunk_appendf(&trash, " in %lums", sv->agent.duration);
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);
3143
chunk_appendf(&trash, "</div></u>");
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));
3151
if (sv->check.status >= HCHK_STATUS_L57DATA)
3152
chunk_appendf(&trash, "/%d", sv->check.code);
3154
if (sv->check.status >= HCHK_STATUS_CHECKED && sv->check.duration >= 0)
3155
chunk_appendf(&trash, " in %lums", sv->check.duration);
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);
3164
chunk_appendf(&trash, "</div></u>");
3167
chunk_appendf(&trash, "</td><td>");
3169
chunk_appendf(&trash,
3171
"</td><td class=ac>%d</td>"
3173
"<td class=ac>%s</td><td class=ac>%s</td>"
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" : "-");
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);
3184
chunk_appendf(&trash, "/%lld", ref->counters.failed_hana);
3186
chunk_appendf(&trash,
3187
"<div class=tips>Failed Health Checks%s</div></u></td>"
3188
"<td>%lld</td><td>%s</td>"
3190
ref->observe ? "/Health Analyses" : "",
3191
ref->counters.down_trans, human_time(srv_downtime(sv), 1));
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);
3200
chunk_appendf(&trash, "<td colspan=3></td>");
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));
3206
chunk_appendf(&trash, "<td class=ac>-</td></tr>\n");
3208
else { /* CSV mode */
3209
static char *srv_hlt_st[10] = {
3222
chunk_appendf(&trash,
3225
/* queue : current, max */
3227
/* sessions : current, max, limit, total */
3229
/* bytes : in, out */
3231
/* denied: req, resp */
3233
/* errors : request, connect, response */
3235
/* warnings: retries, redispatches */
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);
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,");
3252
chunk_appendf(&trash,
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));
3257
chunk_appendf(&trash,
3258
/* weight, active, backup */
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);
3265
/* check failures: unique, fatal; last change, total downtime */
3266
if (sv->check.state & CHK_ST_ENABLED)
3267
chunk_appendf(&trash,
3269
sv->counters.failed_checks, sv->counters.down_trans,
3270
(int)(now.tv_sec - sv->last_change), srv_downtime(sv));
3272
chunk_appendf(&trash, ",,,,");
3274
/* queue limit, pid, iid, sid, */
3275
chunk_appendf(&trash,
3278
LIM2A(sv->maxqueue, ""),
3279
relative_pid, px->uuid, sv->puid);
3282
if (sv->state == SRV_ST_STARTING && !server_is_draining(sv))
3283
chunk_appendf(&trash, "%d", server_throttle_rate(sv));
3285
/* sessions: lbtot */
3286
chunk_appendf(&trash, ",%lld,", sv->counters.cum_lbconn);
3290
chunk_appendf(&trash, "%s/%s,",
3291
sv->track->proxy->id, sv->track->id);
3293
chunk_appendf(&trash, ",");
3296
chunk_appendf(&trash, "%d,", STATS_TYPE_SV);
3299
chunk_appendf(&trash, "%u,,%u,",
3300
read_freq_ctr(&sv->sess_per_sec),
3301
sv->counters.sps_max);
3303
if (sv->check.state & CHK_ST_ENABLED) {
3305
chunk_appendf(&trash, "%s,", get_check_status_info(sv->check.status));
3308
if (sv->check.status >= HCHK_STATUS_L57DATA)
3309
chunk_appendf(&trash, "%u,", sv->check.code);
3311
chunk_appendf(&trash, ",");
3313
/* check_duration */
3314
if (sv->check.status >= HCHK_STATUS_CHECKED)
3315
chunk_appendf(&trash, "%lu,", sv->check.duration);
3317
chunk_appendf(&trash, ",");
3321
chunk_appendf(&trash, ",,,");
3323
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
3324
if (px->mode == PR_MODE_HTTP) {
3326
chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[i]);
3328
chunk_appendf(&trash, "%lld,", sv->counters.p.http.rsp[0]);
3331
chunk_appendf(&trash, ",,,,,,");
3333
/* failed health analyses */
3334
chunk_appendf(&trash, "%lld,", sv->counters.failed_hana);
3336
/* requests : req_rate, req_rate_max, req_tot, */
3337
chunk_appendf(&trash, ",,,");
3339
/* errors: cli_aborts, srv_aborts */
3340
chunk_appendf(&trash, "%lld,%lld,",
3341
sv->counters.cli_aborts, sv->counters.srv_aborts);
3343
/* compression: in, out, bypassed, comp_rsp */
3344
chunk_appendf(&trash, ",,,,");
3347
chunk_appendf(&trash, "%d,", srv_lastsession(sv));
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) : "");
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));
3360
/* finish with EOL */
3361
chunk_appendf(&trash, "\n");
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.
3370
static int stats_dump_be_stats(struct stream_interface *si, struct proxy *px, int flags)
3372
struct appctx *appctx = __objt_appctx(si->end);
3376
if (!(px->cap & PR_CAP_BE))
3379
if ((appctx->ctx.stats.flags & STAT_BOUND) && !(appctx->ctx.stats.type & (1 << STATS_TYPE_BE)))
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>");
3388
chunk_appendf(&trash,
3391
"%s<a name=\"%s/Backend\"></a>"
3392
"<a class=lfsb href=\"#%s/Backend\">Backend</a>"
3394
(flags & ST_SHLGNDS)?"<u>":"",
3397
if (flags & ST_SHLGNDS) {
3399
chunk_appendf(&trash, "<div class=tips>balancing: %s",
3400
backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
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, "'");
3409
chunk_appendf(&trash, "</div>");
3412
chunk_appendf(&trash,
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>"
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));
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>"
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));
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> 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>"
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));
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)));
3466
chunk_appendf(&trash,
3467
"</table></div></u></td>"
3468
/* sessions: lbtot, last */
3469
"<td>%s</td><td>%s</td>"
3473
U2H(px->be_counters.cum_lbconn),
3474
human_time(be_lastsession(px), 1),
3475
U2H(px->be_counters.bytes_in));
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>":"");
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> </td><td class=ac>%d</td>"
3501
"<td class=ac>%d</td><td class=ac>%d</td>"
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);
3515
chunk_appendf(&trash,
3516
/* rest of backend: nothing, down transitions, total downtime, throttle */
3517
"<td class=ac> </td><td>%d</td>"
3522
px->srv?human_time(be_downtime(px), 1):" ");
3524
else { /* CSV mode */
3525
chunk_appendf(&trash,
3528
/* queue : current, max */
3530
/* sessions : current, max, limit, total */
3532
/* bytes : in, out */
3534
/* denied: req, resp */
3536
/* errors : request, connect, response */
3538
/* warnings: retries, redispatches */
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. */
3546
/* rest of backend: nothing, down transitions, last change, total downtime */
3548
/* pid, iid, sid, throttle, lbtot, tracked, type */
3549
"%d,%d,0,,%lld,,%d,"
3550
/* rate, rate_lim, rate_max, */
3552
/* check_status, check_code, check_duration */
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);
3571
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
3572
if (px->mode == PR_MODE_HTTP) {
3574
chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[i]);
3575
chunk_appendf(&trash, "%lld,", px->be_counters.p.http.rsp[0]);
3578
chunk_appendf(&trash, ",,,,,,");
3580
/* failed health analyses */
3581
chunk_appendf(&trash, ",");
3583
/* requests : req_rate, req_rate_max, req_tot, */
3584
chunk_appendf(&trash, ",,,");
3586
/* errors: cli_aborts, srv_aborts */
3587
chunk_appendf(&trash, "%lld,%lld,",
3588
px->be_counters.cli_aborts, px->be_counters.srv_aborts);
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);
3594
/* compression: comp_rsp */
3595
chunk_appendf(&trash, "%lld,", px->be_counters.p.http.comp_rsp);
3597
/* lastsess, last_chk, last_agt, */
3598
chunk_appendf(&trash, "%d,,,", be_lastsession(px));
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));
3607
/* finish with EOL */
3608
chunk_appendf(&trash, "\n");
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.
3617
static void stats_dump_html_px_hdr(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
3619
struct appctx *appctx = __objt_appctx(si->end);
3620
char scope_txt[STAT_SCOPE_TXT_MAXLEN + sizeof STAT_SCOPE_PATTERN];
3622
if (px->cap & PR_CAP_BE && px->srv && (appctx->ctx.stats.flags & STAT_ADMIN)) {
3623
/* A form to enable/disable this proxy servers */
3625
/* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
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;
3633
chunk_appendf(&trash,
3634
"<form action=\"%s%s%s%s\" method=\"post\">",
3636
(appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
3637
(appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
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%%\">");
3647
chunk_appendf(&trash,
3648
"<a name=\"%s\"></a>%s"
3649
"<a class=px href=\"#%s\">%s</a>",
3651
(uri->flags & ST_SHLGNDS) ? "<u>":"",
3654
if (uri->flags & ST_SHLGNDS) {
3656
chunk_appendf(&trash, "<div class=tips>cap: %s, mode: %s, id: %d",
3657
proxy_cap_str(px->cap), proxy_mode_str(px->mode),
3659
chunk_appendf(&trash, "</div>");
3662
chunk_appendf(&trash,
3664
"<th class=\"%s\" width=\"90%%\">%s</th>"
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 : "");
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>");
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>"
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>"
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.
3700
static void stats_dump_html_px_end(struct stream_interface *si, struct proxy *px)
3702
struct appctx *appctx = __objt_appctx(si->end);
3703
chunk_appendf(&trash, "</table>");
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>"
3725
"<input type=\"hidden\" name=\"b\" value=\"#%d\">"
3726
" <input type=\"submit\" value=\"Apply\">"
3731
chunk_appendf(&trash, "<p>\n");
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.
3741
static int stats_dump_proxy_to_buffer(struct stream_interface *si, struct proxy *px, struct uri_auth *uri)
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;
1455
chunk_init(&msg, trash, trashlen);
1457
switch (s->data_ctx.stats.px_st) {
1458
case DATA_ST_PX_INIT:
3749
chunk_reset(&trash);
3751
switch (appctx->ctx.stats.px_st) {
3752
case STAT_PX_ST_INIT:
1459
3753
/* we are on a new proxy */
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;
1485
if ((s->data_ctx.stats.flags & STAT_BOUND) && (s->data_ctx.stats.iid != -1) &&
1486
(px->uuid != s->data_ctx.stats.iid))
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.
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)
3785
if ((appctx->ctx.stats.flags & STAT_BOUND) &&
3786
(appctx->ctx.stats.iid != -1) &&
3787
(px->uuid != appctx->ctx.stats.iid))
3790
appctx->ctx.stats.px_st = STAT_PX_ST_TH;
1490
3791
/* fall through */
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 */
1497
"<form action=\"%s\" method=\"post\">",
1501
/* print a new table */
1503
"<table class=\"tbl\" width=\"100%%\">\n"
1504
"<tr class=\"titre\">"
1505
"<th class=\"pxname\" width=\"10%%\"");
1507
if (uri->flags&ST_SHLGNDS) {
1509
chunk_printf(&msg, " title=\"cap: %s, mode: %s, id: %d",
1510
proxy_cap_str(px->cap), proxy_mode_str(px->mode),
1513
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>"
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 : "");
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>");
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>"
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>"
1553
if (buffer_feed_chunk(rep, &msg) >= 0)
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)
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 */
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)) {
1567
"<tr class=\"frontend\">");
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>");
1576
"<a name=\"%s/Frontend\"></a>"
1577
"<a class=lfsb href=\"#%s/Frontend\">Frontend</a></td>"
1578
"<td colspan=3></td>"
1582
if (px->mode == PR_MODE_HTTP) {
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>"
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, "-"));
1594
/* sessions rate : current, max, limit */
1595
"<td>%s</td><td>%s</td><td>%s</td>"
1597
U2H0(read_freq_ctr(&px->fe_sess_per_sec)),
1598
U2H1(px->counters.fe_sps_max), LIM2A2(px->fe_sps_lim, "-"));
1602
/* sessions: current, max, limit */
1603
"<td>%s</td><td>%s</td><td>%s</td>"
1606
U2H3(px->feconn), U2H4(px->counters.feconn_max), U2H5(px->maxconn));
1608
/* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1609
if (px->mode == PR_MODE_HTTP) {
1612
chunk_printf(&msg, " title=\"%lld requests:", px->counters.cum_fe_req);
1614
for (i = 1; i < 6; i++)
1615
chunk_printf(&msg, " %dxx=%lld,", i, px->counters.fe.http.rsp[i]);
1617
chunk_printf(&msg, " other=%lld\"", px->counters.fe.http.rsp[0]);
1621
/* sessions: total, lbtot */
1622
">%s%s%s</td><td></td>"
1623
/* bytes : in, out */
1624
"<td>%s</td><td>%s</td>"
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));
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>"
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");
1649
/* pxid, name, queue cur, queue max, */
1651
/* sessions : current, max, limit, total */
1653
/* bytes : in, out */
1655
/* denied: req, resp */
1657
/* errors : request, connect, response */
1659
/* warnings: retries, redispatches */
1661
/* server status : reflect frontend status */
1663
/* rest of server: nothing */
1665
/* pid, iid, sid, throttle, lbtot, tracked, type */
1667
/* rate, rate_lim, rate_max */
1669
/* check_status, check_code, check_duration */
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);
1682
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
1683
if (px->mode == PR_MODE_HTTP) {
1687
chunk_printf(&msg, "%lld,", px->counters.fe.http.rsp[i]);
1689
chunk_printf(&msg, "%lld,", px->counters.fe.http.rsp[0]);
1691
chunk_printf(&msg, ",,,,,,");
1694
/* failed health analyses */
1695
chunk_printf(&msg, ",");
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);
1702
/* errors: cli_aborts, srv_aborts */
1703
chunk_printf(&msg, ",,");
1705
/* finish with EOL */
1706
chunk_printf(&msg, "\n");
1709
if (buffer_feed_chunk(rep, &msg) >= 0)
3805
if (stats_dump_fe_stats(si, px))
3806
if (bi_putchk(rep, &trash) == -1)
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 */
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;
1723
l = s->data_ctx.stats.l;
3821
l = LIST_ELEM(appctx->ctx.stats.l, struct listener *, by_fe);
1724
3822
if (!l->counters)
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)))
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)
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>");
1741
chunk_printf(&msg, "<td class=ac");
1743
if (uri->flags&ST_SHLGNDS) {
1744
char str[INET6_ADDRSTRLEN], *fmt = NULL;
1747
chunk_printf(&msg, " title=\"IP: ");
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);
1753
if (l->addr.ss_family == AF_INET) {
1754
if (inet_ntop(AF_INET,
1755
(const void *)&((struct sockaddr_in *)&l->addr)->sin_addr,
1759
if (inet_ntop(AF_INET6,
1760
(const void *)&((struct sockaddr_in6 *)(&l->addr))->sin6_addr,
1766
chunk_printf(&msg, fmt, str, port);
1768
chunk_printf(&msg, "(%s)", strerror(errno));
1771
chunk_printf(&msg, ", id: %d", l->luid);
1773
chunk_printf(&msg, "\"");
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> </td>"
1782
/* sessions: current, max, limit, total, lbtot */
1783
"<td>%s</td><td>%s</td><td>%s</td>"
1784
"<td>%s</td><td> </td>"
1785
/* bytes: in, out */
1786
"<td>%s</td><td>%s</td>"
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));
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>"
1806
U2H0(l->counters->denied_req), U2H1(l->counters->denied_resp),
1807
U2H2(l->counters->failed_req),
1808
(l->nbconn < l->maxconn) ? "OPEN" : "FULL");
1811
/* pxid, name, queue cur, queue max, */
1813
/* sessions: current, max, limit, total */
1815
/* bytes: in, out */
1817
/* denied: req, resp */
1819
/* errors: request, connect, response */
1821
/* warnings: retries, redispatches */
1823
/* server status: reflect listener status */
1825
/* rest of server: nothing */
1827
/* pid, iid, sid, throttle, lbtot, tracked, type */
1829
/* rate, rate_lim, rate_max */
1831
/* check_status, check_code, check_duration */
1833
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
1835
/* failed health analyses */
1837
/* requests : req_rate, req_rate_max, req_tot, */
1839
/* errors: cli_aborts, srv_aborts */
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);
1852
if (buffer_feed_chunk(rep, &msg) >= 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)
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 */
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) {
1865
if (buffer_almost_full(rep))
3848
if (buffer_almost_full(rep->buf)) {
3849
rep->flags |= CF_WAKE_WRITE;
1868
sv = s->data_ctx.stats.sv;
1870
if (s->data_ctx.stats.flags & STAT_BOUND) {
1871
if (!(s->data_ctx.stats.type & (1 << STATS_TYPE_SV)))
3853
sv = appctx->ctx.stats.sv;
3855
if (appctx->ctx.stats.flags & STAT_BOUND) {
3856
if (!(appctx->ctx.stats.type & (1 << STATS_TYPE_SV)))
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)
1883
/* FIXME: produce some small strings for "UP/DOWN x/y &#xxxx;" */
1884
if (!(svs->state & SRV_CHECKED))
1886
else if (svs->state & SRV_RUNNING) {
1887
if (svs->health == svs->rise + svs->fall - 1)
1888
sv_state = 3; /* UP */
1890
sv_state = 2; /* going down */
1892
if (svs->state & SRV_GOINGDOWN)
1897
sv_state = 1; /* going up */
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
3876
if ((svs->check.state & CHK_ST_ENABLED) &&
3877
(svs->check.health < svs->check.rise + svs->check.fall - 1))
3882
if (server_is_draining(sv))
3885
if (sv_state == 4 && !(svs->check.state & CHK_ST_ENABLED))
3886
sv_state = 9; /* unchecked UP */
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 */
3893
sv_state = 5; /* NOLB going down */
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 */
3903
sv_state = 0; /* DOWN, unchecked */
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;
1907
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
1908
static char *srv_hlt_st[7] = { "DOWN", "DN %d/%d ↑",
1909
"UP %d/%d ↓", "UP",
1910
"NOLB %d/%d ↓", "NOLB",
1911
"<i>no check</i>" };
1912
if ((sv->state & SRV_MAINTAIN) || (svs->state & SRV_MAINTAIN)) {
1915
"<tr class=\"maintain\">"
1921
"<tr class=\"%s%d\">",
1922
(sv->state & SRV_BACKUP) ? "backup" : "active", sv_state);
1925
if (px->cap & PR_CAP_BE && px->srv && (s->data_ctx.stats.flags & STAT_ADMIN)) {
1927
"<td><input type=\"checkbox\" name=\"s\" value=\"%s\"></td>",
1931
chunk_printf(&msg, "<td class=ac");
1933
if (uri->flags&ST_SHLGNDS) {
1934
char str[INET6_ADDRSTRLEN];
1936
chunk_printf(&msg, " title=\"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));
1942
chunk_printf(&msg, "(%s)", strerror(errno));
1945
chunk_printf(&msg, ", id: %d", sv->puid);
1951
chunk_printf(&msg, ", cookie: '");
1953
chunk_initlen(&src, sv->cookie, 0, strlen(sv->cookie));
1954
chunk_htmlencode(&msg, &src);
1956
chunk_printf(&msg, "'");
1959
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>"
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, "-"));
1980
/* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
1981
if (px->mode == PR_MODE_HTTP) {
1984
chunk_printf(&msg, " title=\"rsp codes:");
1986
for (i = 1; i < 6; i++)
1987
chunk_printf(&msg, " %dxx=%lld,", i, sv->counters.p.http.rsp[i]);
1989
chunk_printf(&msg, " other=%lld\"", sv->counters.p.http.rsp[0]);
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));
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>"
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);
2020
/* status, lest check */
2021
chunk_printf(&msg, "<td class=ac>");
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");
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)");
2033
else if (svs->state & SRV_CHECKED) {
2034
chunk_printf(&msg, "%s ",
2035
human_time(now.tv_sec - svs->last_change, 1));
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));
2043
if (sv->state & SRV_CHECKED) {
2044
chunk_printf(&msg, "</td><td class=ac title=\"%s",
2045
get_check_status_description(sv->check_status));
2047
if (*sv->check_desc) {
2050
chunk_printf(&msg, ": ");
2052
chunk_initlen(&src, sv->check_desc, 0, strlen(sv->check_desc));
2053
chunk_htmlencode(&msg, &src);
2056
chunk_printf(&msg, "\"><u> %s%s",
2057
tv_iszero(&sv->check_start)?"":"* ",
2058
get_check_status_info(sv->check_status));
2060
if (sv->check_status >= HCHK_STATUS_L57DATA)
2061
chunk_printf(&msg, "/%d", sv->check_code);
2063
if (sv->check_status >= HCHK_STATUS_CHECKED && sv->check_duration >= 0)
2064
chunk_printf(&msg, " in %lums</u>", sv->check_duration);
2066
chunk_printf(&msg, "</td><td>");
2070
"</td><td class=ac>%d</td>"
2072
"<td class=ac>%s</td><td class=ac>%s</td>"
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" : "-");
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);
2084
chunk_printf(&msg, "/%lld", svs->counters.failed_hana);
2088
"<td>%lld</td><td>%s</td>"
2090
svs->counters.down_trans, human_time(srv_downtime(sv), 1));
2091
} else if (sv != svs)
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);
2097
"<td colspan=3></td>");
2100
if (sv->state & SRV_WARMINGUP)
2101
chunk_printf(&msg, "<td class=ac>%d %%</td></tr>\n", server_throttle_rate(sv));
2103
chunk_printf(&msg, "<td class=ac>-</td></tr>\n");
2105
static char *srv_hlt_st[7] = { "DOWN,", "DOWN %d/%d,",
2107
"NOLB %d/%d,", "NOLB,",
2112
/* queue : current, max */
2114
/* sessions : current, max, limit, total */
2116
/* bytes : in, out */
2118
/* denied: req, resp */
2120
/* errors : request, connect, response */
2122
/* warnings: retries, redispatches */
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);
2134
if (sv->state & SRV_MAINTAIN) {
2135
chunk_printf(&msg, "MAINT,");
2137
else if (svs != sv && svs->state & SRV_MAINTAIN) {
2138
chunk_printf(&msg, "MAINT(via),");
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));
2148
/* weight, active, backup */
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);
2155
/* check failures: unique, fatal; last change, total downtime */
2156
if (sv->state & SRV_CHECKED)
2159
sv->counters.failed_checks, sv->counters.down_trans,
2160
(int)(now.tv_sec - sv->last_change), srv_downtime(sv));
2165
/* queue limit, pid, iid, sid, */
2169
LIM2A0(sv->maxqueue, ""),
2170
relative_pid, px->uuid, sv->puid);
2173
if (sv->state & SRV_WARMINGUP)
2174
chunk_printf(&msg, "%d", server_throttle_rate(sv));
2176
/* sessions: lbtot */
2177
chunk_printf(&msg, ",%lld,", sv->counters.cum_lbconn);
2181
chunk_printf(&msg, "%s/%s,",
2182
sv->tracked->proxy->id, sv->tracked->id);
2184
chunk_printf(&msg, ",");
2187
chunk_printf(&msg, "%d,", STATS_TYPE_SV);
2190
chunk_printf(&msg, "%u,,%u,",
2191
read_freq_ctr(&sv->sess_per_sec),
2192
sv->counters.sps_max);
2194
if (sv->state & SRV_CHECKED) {
2196
chunk_printf(&msg, "%s,", get_check_status_info(sv->check_status));
2199
if (sv->check_status >= HCHK_STATUS_L57DATA)
2200
chunk_printf(&msg, "%u,", sv->check_code);
2202
chunk_printf(&msg, ",");
2204
/* check_duration */
2205
if (sv->check_status >= HCHK_STATUS_CHECKED)
2206
chunk_printf(&msg, "%lu,", sv->check_duration);
2208
chunk_printf(&msg, ",");
2211
chunk_printf(&msg, ",,,");
2214
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2215
if (px->mode == PR_MODE_HTTP) {
2219
chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[i]);
2221
chunk_printf(&msg, "%lld,", sv->counters.p.http.rsp[0]);
2223
chunk_printf(&msg, ",,,,,,");
2226
/* failed health analyses */
2227
chunk_printf(&msg, "%lld,", sv->counters.failed_hana);
2229
/* requests : req_rate, req_rate_max, req_tot, */
2230
chunk_printf(&msg, ",,,");
2232
/* errors: cli_aborts, srv_aborts */
2233
chunk_printf(&msg, "%lld,%lld,",
2234
sv->counters.cli_aborts, sv->counters.srv_aborts);
2236
/* finish with EOL */
2237
chunk_printf(&msg, "\n");
2239
if (buffer_feed_chunk(rep, &msg) >= 0)
3912
if (stats_dump_sv_stats(si, px, uri ? uri->flags : 0, sv, sv_state))
3913
if (bi_putchk(rep, &trash) == -1)
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 */
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>");
2256
chunk_printf(&msg, "<td class=ac");
2258
if (uri->flags&ST_SHLGNDS) {
2260
chunk_printf(&msg, " title=\"balancing: %s",
2261
backend_lb_algo_str(px->lbprm.algo & BE_LB_ALGO));
2264
if (px->cookie_name) {
2267
chunk_printf(&msg, ", cookie: '");
2269
chunk_initlen(&src, px->cookie_name, 0, strlen(px->cookie_name));
2270
chunk_htmlencode(&msg, &src);
2272
chunk_printf(&msg, "'");
2275
chunk_printf(&msg, "\"");
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>"
2288
(uri->flags & ST_SHLGNDS)?"<u>":"",
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));
2295
/* sessions: current, max, limit */
2296
"<td>%s</td><td>%s</td><td>%s</td>"
2299
U2H2(px->beconn), U2H3(px->counters.beconn_max), U2H4(px->fullconn));
2301
/* http response (via td title): 1xx, 2xx, 3xx, 4xx, 5xx, other */
2302
if (px->mode == PR_MODE_HTTP) {
2305
chunk_printf(&msg, " title=\"rsp codes:");
2307
for (i = 1; i < 6; i++)
2308
chunk_printf(&msg, " %dxx=%lld", i, px->counters.be.http.rsp[i]);
2310
chunk_printf(&msg, " other=%lld\"", px->counters.be.http.rsp[0]);
2314
/* sessions: total, lbtot */
2315
">%s%s%s</td><td>%s</td>"
2316
/* bytes: in, out */
2317
"<td>%s</td><td>%s</td>"
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));
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> </td><td class=ac>%d</td>"
2339
"<td class=ac>%d</td><td class=ac>%d</td>"
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);
2354
/* rest of backend: nothing, down transitions, total downtime, throttle */
2355
"<td class=ac> </td><td>%d</td>"
2360
px->srv?human_time(be_downtime(px), 1):" ");
2365
/* queue : current, max */
2367
/* sessions : current, max, limit, total */
2369
/* bytes : in, out */
2371
/* denied: req, resp */
2373
/* errors : request, connect, response */
2375
/* warnings: retries, redispatches */
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. */
2383
/* rest of backend: nothing, down transitions, last change, total downtime */
2385
/* pid, iid, sid, throttle, lbtot, tracked, type */
2386
"%d,%d,0,,%lld,,%d,"
2387
/* rate, rate_lim, rate_max, */
2389
/* check_status, check_code, check_duration */
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);
2408
/* http response: 1xx, 2xx, 3xx, 4xx, 5xx, other */
2409
if (px->mode == PR_MODE_HTTP) {
2413
chunk_printf(&msg, "%lld,", px->counters.be.http.rsp[i]);
2415
chunk_printf(&msg, "%lld,", px->counters.be.http.rsp[0]);
2417
chunk_printf(&msg, ",,,,,,");
2420
/* failed health analyses */
2421
chunk_printf(&msg, ",");
2423
/* requests : req_rate, req_rate_max, req_tot, */
2424
chunk_printf(&msg, ",,,");
2426
/* errors: cli_aborts, srv_aborts */
2427
chunk_printf(&msg, "%lld,%lld,",
2428
px->counters.cli_aborts, px->counters.srv_aborts);
2430
/* finish with EOL */
2431
chunk_printf(&msg, "\n");
2434
if (buffer_feed_chunk(rep, &msg) >= 0)
2438
s->data_ctx.stats.px_st = DATA_ST_PX_END;
2441
case DATA_ST_PX_END:
2442
if (!(s->data_ctx.stats.flags & STAT_FMT_CSV)) {
2443
chunk_printf(&msg, "</table>");
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 */
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>"
2456
"<input type=\"hidden\" name=\"b\" value=\"#%d\">"
2457
" <input type=\"submit\" value=\"Apply\">"
2462
chunk_printf(&msg, "<p>\n");
2464
if (buffer_feed_chunk(rep, &msg) >= 0)
2468
s->data_ctx.stats.px_st = DATA_ST_PX_FIN;
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)
3926
appctx->ctx.stats.px_st = STAT_PX_ST_END;
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)
3936
appctx->ctx.stats.px_st = STAT_PX_ST_FIN;
3939
case STAT_PX_ST_FIN:
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.
2487
/* returns 1 if dump is not complete */
2488
int stats_dump_full_sess_to_buffer(struct session *s, struct buffer *rep)
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.
3951
static void stats_dump_html_head(struct uri_auth *uri)
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"
3961
" font-family: arial, helvetica, sans-serif;"
3963
" font-weight: normal;"
3965
" background: white;"
3971
" font-size: x-large;"
3972
" margin-bottom: 0.5em;"
3975
" font-family: helvetica, arial;"
3976
" font-size: x-large;"
3977
" font-weight: bold;"
3978
" font-style: italic;"
3981
" margin-bottom: 0em;"
3984
" font-family: helvetica, arial;"
3986
" font-weight: bold;"
3988
" background: #e8e8d0;"
3990
" margin-bottom: 0em;"
3993
" margin-top: 0.25em;"
3994
" margin-right: 2em;"
3996
".hr {margin-top: 0.25em;"
3997
" border-color: black;"
3998
" border-bottom-style: solid;"
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) */
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;}"
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"
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"
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"
4063
"u:hover div.tips {visibility:visible;}\n"
4065
"</style></head>\n",
4066
(uri->flags & ST_SHNODE) ? " on " : "",
4067
(uri->flags & ST_SHNODE) ? (uri->node ? uri->node : global.node) : ""
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.
4075
static void stats_dump_html_info(struct stream_interface *si, struct uri_auth *uri)
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];
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 !
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>> 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\"> </td><td class=\"noborder\">active UP </td>"
4101
"<td class=\"backup4\"> </td><td class=\"noborder\">backup UP </td>"
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>"
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>"
4109
"<td class=\"active0\"></td><td class=\"noborder\">active or backup DOWN </td>"
4110
"<td class=\"active9\"></td><td class=\"noborder\">not checked </td>"
4112
"<td class=\"maintain\"></td><td class=\"noborder\" colspan=\"3\">active or backup DOWN for maintenance (MAINT) </td>"
4114
"<td class=\"active8\"></td><td class=\"noborder\" colspan=\"3\">active or backup SOFT STOPPED for maintenance </td>"
4116
"Note: \"NOLB\"/\"DRAIN\" = UP with load-balancing disabled."
4118
"<td align=\"left\" valign=\"top\" nowrap width=\"1%%\">"
4119
"<b>Display option:</b><ul style=\"margin-top: 0.25em;\">"
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
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';
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",
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);
4149
/* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
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;
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",
4162
(appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
4165
chunk_appendf(&trash,
4166
"<li><a href=\"%s%s%s%s\">Hide 'DOWN' servers</a><br>\n",
4169
(appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
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",
4177
(appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
4181
chunk_appendf(&trash,
4182
"<li><a href=\"%s%s%s%s\">Disable refresh</a><br>\n",
4184
(appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
4189
chunk_appendf(&trash,
4190
"<li><a href=\"%s%s%s%s\">Refresh now</a><br>\n",
4192
(appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
4193
(appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
4196
chunk_appendf(&trash,
4197
"<li><a href=\"%s;csv%s%s\">CSV export</a><br>\n",
4199
(uri->refresh > 0) ? ";norefresh" : "",
4202
chunk_appendf(&trash,
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"
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" : "",
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" : "",
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" : "",
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."
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>"
4258
"</div>\n", uri->uri_prefix,
4259
(appctx->ctx.stats.flags & STAT_HIDE_DOWN) ? ";up" : "",
4260
(appctx->ctx.stats.flags & STAT_NO_REFRESH) ? ";norefresh" : "",
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" : "",
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" : "",
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" : "",
4294
chunk_appendf(&trash, "<p>\n");
4298
/* Dumps the HTML stats trailer block to the trash. The caller is responsible
4299
* for clearing the trash if needed.
4301
static void stats_dump_html_end()
4303
chunk_appendf(&trash, "</body></html>\n");
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.
4313
static int stats_dump_stat_to_buffer(struct stream_interface *si, struct uri_auth *uri)
4315
struct appctx *appctx = __objt_appctx(si->end);
4316
struct channel *rep = si->ib;
4319
chunk_reset(&trash);
4321
switch (appctx->st2) {
4323
appctx->st2 = STAT_ST_HEAD; /* let's start producing data */
4327
if (appctx->ctx.stats.flags & STAT_FMT_HTML)
4328
stats_dump_html_head(uri);
4330
stats_dump_csv_header();
4332
if (bi_putchk(rep, &trash) == -1)
4335
appctx->st2 = 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)
4345
appctx->ctx.stats.px = proxy;
4346
appctx->ctx.stats.px_st = STAT_PX_ST_INIT;
4347
appctx->st2 = STAT_ST_LIST;
4352
while (appctx->ctx.stats.px) {
4353
if (buffer_almost_full(rep->buf)) {
4354
rep->flags |= CF_WAKE_WRITE;
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)
4364
appctx->ctx.stats.px = px->next;
4365
appctx->ctx.stats.px_st = STAT_PX_ST_INIT;
4367
/* here, we just have reached the last proxy */
4369
appctx->st2 = STAT_ST_END;
4373
if (appctx->ctx.stats.flags & STAT_FMT_HTML) {
4374
stats_dump_html_end();
4375
if (bi_putchk(rep, &trash) == -1)
4379
appctx->st2 = STAT_ST_FIN;
4386
/* unknown state ! */
4387
appctx->st2 = STAT_ST_FIN;
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.
4397
static int stats_process_http_post(struct stream_interface *si)
4399
struct session *s = session_from_task(si->owner);
4400
struct appctx *appctx = objt_appctx(si->end);
4402
struct proxy *px = NULL;
4403
struct server *sv = NULL;
4406
int action = ST_ADM_ACTION_NONE;
4409
int total_servers = 0;
4410
int altered_servers = 0;
4412
char *first_param, *cur_param, *next_param, *end_params;
4413
char *st_cur_param = NULL;
4414
char *st_next_param = NULL;
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;
4426
reql = bo_getblk(si->ob, temp->str, s->txn.req.body_len, s->txn.req.eoh + 2);
4428
/* we need more data */
4429
appctx->ctx.stats.st_code = STAT_STATUS_NONE;
4433
first_param = temp->str;
4434
end_params = temp->str + reql;
4435
cur_param = next_param = end_params;
4438
appctx->ctx.stats.st_code = STAT_STATUS_NONE;
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.
4444
while (cur_param > first_param) {
4450
if ((*cur_param == '&') || (cur_param == first_param)) {
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';
4459
appctx->ctx.stats.st_code = STAT_STATUS_EXCD;
4463
/* Parse the value */
4465
while (*value != '\0' && *value != '=') {
4468
if (*value == '=') {
4469
/* Ok, a value is found, we can mark the end of the key */
4472
if (url_decode(key) < 0 || url_decode(value) < 0)
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;
4483
else if (!action && (strcmp(key, "action") == 0)) {
4484
if (strcmp(value, "ready") == 0) {
4485
action = ST_ADM_ACTION_READY;
4487
else if (strcmp(value, "drain") == 0) {
4488
action = ST_ADM_ACTION_DRAIN;
4490
else if (strcmp(value, "maint") == 0) {
4491
action = ST_ADM_ACTION_MAINT;
4493
else if (strcmp(value, "shutdown") == 0) {
4494
action = ST_ADM_ACTION_SHUTDOWN;
4496
else if (strcmp(value, "dhlth") == 0) {
4497
action = ST_ADM_ACTION_DHLTH;
4499
else if (strcmp(value, "ehlth") == 0) {
4500
action = ST_ADM_ACTION_EHLTH;
4502
else if (strcmp(value, "hrunn") == 0) {
4503
action = ST_ADM_ACTION_HRUNN;
4505
else if (strcmp(value, "hnolb") == 0) {
4506
action = ST_ADM_ACTION_HNOLB;
4508
else if (strcmp(value, "hdown") == 0) {
4509
action = ST_ADM_ACTION_HDOWN;
4511
else if (strcmp(value, "dagent") == 0) {
4512
action = ST_ADM_ACTION_DAGENT;
4514
else if (strcmp(value, "eagent") == 0) {
4515
action = ST_ADM_ACTION_EAGENT;
4517
else if (strcmp(value, "arunn") == 0) {
4518
action = ST_ADM_ACTION_ARUNN;
4520
else if (strcmp(value, "adown") == 0) {
4521
action = ST_ADM_ACTION_ADOWN;
4523
/* else these are the old supported methods */
4524
else if (strcmp(value, "disable") == 0) {
4525
action = ST_ADM_ACTION_DISABLE;
4527
else if (strcmp(value, "enable") == 0) {
4528
action = ST_ADM_ACTION_ENABLE;
4530
else if (strcmp(value, "stop") == 0) {
4531
action = ST_ADM_ACTION_STOP;
4533
else if (strcmp(value, "start") == 0) {
4534
action = ST_ADM_ACTION_START;
4537
appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
4541
else if (strcmp(key, "s") == 0) {
4542
if (!(px && action)) {
4544
* Indicates that we'll need to reprocess the parameters
4545
* as soon as backend and action are known
4548
st_cur_param = cur_param;
4549
st_next_param = next_param;
4553
else if ((sv = findserver(px, value)) != NULL) {
4555
case ST_ADM_ACTION_DISABLE:
4556
if (!(sv->admin & SRV_ADMF_FMAINT)) {
4559
srv_set_admin_flag(sv, SRV_ADMF_FMAINT);
4562
case ST_ADM_ACTION_ENABLE:
4563
if (sv->admin & SRV_ADMF_FMAINT) {
4566
srv_clr_admin_flag(sv, SRV_ADMF_FMAINT);
4569
case ST_ADM_ACTION_STOP:
4570
if (!(sv->admin & SRV_ADMF_FDRAIN)) {
4571
srv_set_admin_flag(sv, SRV_ADMF_FDRAIN);
4576
case ST_ADM_ACTION_START:
4577
if (sv->admin & SRV_ADMF_FDRAIN) {
4578
srv_clr_admin_flag(sv, SRV_ADMF_FDRAIN);
4583
case ST_ADM_ACTION_DHLTH:
4584
if (sv->check.state & CHK_ST_CONFIGURED) {
4585
sv->check.state &= ~CHK_ST_ENABLED;
4590
case ST_ADM_ACTION_EHLTH:
4591
if (sv->check.state & CHK_ST_CONFIGURED) {
4592
sv->check.state |= CHK_ST_ENABLED;
4597
case ST_ADM_ACTION_HRUNN:
4599
sv->check.health = sv->check.rise + sv->check.fall - 1;
4600
srv_set_running(sv, "changed from Web interface");
4605
case ST_ADM_ACTION_HNOLB:
4607
sv->check.health = sv->check.rise + sv->check.fall - 1;
4608
srv_set_stopping(sv, "changed from Web interface");
4613
case ST_ADM_ACTION_HDOWN:
4615
sv->check.health = 0;
4616
srv_set_stopped(sv, "changed from Web interface");
4621
case ST_ADM_ACTION_DAGENT:
4622
if (sv->agent.state & CHK_ST_CONFIGURED) {
4623
sv->agent.state &= ~CHK_ST_ENABLED;
4628
case ST_ADM_ACTION_EAGENT:
4629
if (sv->agent.state & CHK_ST_CONFIGURED) {
4630
sv->agent.state |= CHK_ST_ENABLED;
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");
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");
4651
case ST_ADM_ACTION_READY:
4652
srv_adm_set_ready(sv);
4656
case ST_ADM_ACTION_DRAIN:
4657
srv_adm_set_drain(sv);
4661
case ST_ADM_ACTION_MAINT:
4662
srv_adm_set_maint(sv);
4666
case ST_ADM_ACTION_SHUTDOWN:
4667
if (px->state != PR_STSTOPPED) {
4668
struct session *sess, *sess_bck;
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);
4680
/* the server name is unknown or ambiguous (duplicate names) */
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
4689
cur_param = st_cur_param;
4690
next_param = st_next_param;
4692
goto reprocess_servers;
4695
next_param = cur_param;
4699
if (total_servers == 0) {
4700
appctx->ctx.stats.st_code = STAT_STATUS_NONE;
4702
else if (altered_servers == 0) {
4703
appctx->ctx.stats.st_code = STAT_STATUS_ERRP;
4705
else if (altered_servers == total_servers) {
4706
appctx->ctx.stats.st_code = STAT_STATUS_DONE;
4709
appctx->ctx.stats.st_code = STAT_STATUS_PART;
4716
static int stats_send_http_headers(struct stream_interface *si)
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);
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");
4729
if (uri->refresh > 0 && !(appctx->ctx.stats.flags & STAT_NO_REFRESH))
4730
chunk_appendf(&trash, "Refresh: %d\r\n",
4733
/* we don't send the CRLF in chunked mode, it will be sent with the first chunk's size */
4735
if (appctx->ctx.stats.flags & STAT_CHUNKED)
4736
chunk_appendf(&trash, "Transfer-Encoding: chunked\r\n");
4738
chunk_appendf(&trash, "\r\n");
4740
s->txn.status = 200;
4741
s->logs.tv_request = now;
4743
if (bi_putchk(si->ib, &trash) == -1)
4749
static int stats_send_http_redirect(struct stream_interface *si)
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);
4756
/* scope_txt = search pattern + search query, appctx->ctx.stats.scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
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;
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.
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"
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" : "",
4785
s->txn.status = 303;
4786
s->logs.tv_request = now;
4788
if (bi_putchk(si->ib, &trash) == -1)
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.
4799
static void http_stats_io_handler(struct stream_interface *si)
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;
4806
if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
4809
/* check that the output is not closed */
4810
if (res->flags & (CF_SHUTW|CF_SHUTW_NOW))
4811
appctx->st0 = STAT_HTTP_DONE;
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;
4819
appctx->st0 = STAT_HTTP_DUMP;
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;
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.
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;
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;
4848
last_len = si->ib->buf->i;
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
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);
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);
4865
si->ib->total += (last_len - data_len);
4866
si->ib->buf->i += (last_len - data_len);
4868
/* now re-enable forwarding */
4869
channel_forward(si->ib, last_fwd);
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;
4880
if (appctx->st0 == STAT_HTTP_LAST) {
4881
if (stats_send_http_redirect(si))
4882
appctx->st0 = STAT_HTTP_DONE;
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)
4891
/* eat the whole request */
4892
bo_skip(si->ob, si->ob->buf->o);
4893
res->flags |= CF_READ_NULL;
4897
if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST))
4900
if (appctx->st0 == STAT_HTTP_DONE) {
4901
if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST)) {
4903
res->flags |= CF_READ_NULL;
4908
/* update all other flags and resync with the other side */
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;
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);
4923
static inline const char *get_conn_ctrl_name(const struct connection *conn)
4925
if (!conn_ctrl_ready(conn))
4927
return conn->ctrl->name;
4930
static inline const char *get_conn_xprt_name(const struct connection *conn)
4932
static char ptr[17];
4934
if (!conn_xprt_ready(conn))
4937
if (conn->xprt == &raw_sock)
4941
if (conn->xprt == &ssl_sock)
4944
snprintf(ptr, sizeof(ptr), "%p", conn->xprt);
4948
static inline const char *get_conn_data_name(const struct connection *conn)
4950
static char ptr[17];
4955
if (conn->data == &sess_conn_cb)
4958
if (conn->data == &si_conn_cb)
4961
if (conn->data == &check_conn_cb)
4964
snprintf(ptr, sizeof(ptr), "%p", conn->data);
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.
4973
static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct session *sess)
4975
struct appctx *appctx = __objt_appctx(si->end);
2492
struct session *sess;
2493
4977
extern const char *monthname[12];
2494
4978
char pn[INET6_ADDRSTRLEN];
2496
chunk_init(&msg, trash, trashlen);
2497
sess = s->data_ctx.sess.target;
2499
if (s->data_ctx.sess.section > 0 && s->data_ctx.sess.uid != sess->uniq_id) {
4979
struct connection *conn;
4980
struct appctx *tmpctx;
4982
chunk_reset(&trash);
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)
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;
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 */
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",
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),
2520
sess->listener->proto->name);
5008
sess->listener && sess->listener->proto->name ? sess->listener->proto->name : "?");
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) {
2525
(const void *)&((struct sockaddr_in *)&sess->cli_addr)->sin_addr,
2531
ntohs(((struct sockaddr_in *)&sess->cli_addr)->sin_port));
2535
(const void *)&((struct sockaddr_in6 *)(&sess->cli_addr))->sin6_addr,
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));
5018
chunk_appendf(&trash, " source=unix:%d\n", sess->listener->luid);
2545
5021
/* no more information to print right now */
2546
chunk_printf(&msg, "\n");
5022
chunk_appendf(&trash, "\n");
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);
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);
5037
conn_get_to_addr(conn);
5039
switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
5042
chunk_appendf(&trash, " addr=%s:%d\n",
5043
pn, get_host_port(&conn->addr.to));
5046
chunk_appendf(&trash, " addr=unix:%d\n", sess->listener->luid);
5049
/* no more information to print right now */
5050
chunk_appendf(&trash, "\n");
2560
5054
if (sess->be->cap & PR_CAP_BE)
2562
" backend=%s (id=%u mode=%s) server=%s (id=%u)\n",
5055
chunk_appendf(&trash,
5056
" backend=%s (id=%u mode=%s)",
2564
sess->be->uuid, sess->be->mode ? "http" : "tcp",
2565
sess->srv ? sess->srv->id : "<none>",
2566
sess->srv ? sess->srv->puid : 0);
2568
chunk_printf(&msg, " backend=<NONE> (id=-1 mode=-) server=<NONE> (id=-1)\n");
2571
" task=%p (state=0x%02x nice=%d calls=%d exp=%s%s)\n",
5058
sess->be->uuid, sess->be->mode ? "http" : "tcp");
5060
chunk_appendf(&trash, " backend=<NONE> (id=-1 mode=-)");
5062
conn = objt_conn(sess->si[1].end);
5064
conn_get_from_addr(conn);
5066
switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
5069
chunk_appendf(&trash, " addr=%s:%d\n",
5070
pn, get_host_port(&conn->addr.from));
5073
chunk_appendf(&trash, " addr=unix\n");
5076
/* no more information to print right now */
5077
chunk_appendf(&trash, "\n");
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);
5087
chunk_appendf(&trash, " server=<NONE> (id=-1)");
5090
conn_get_to_addr(conn);
5092
switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
5095
chunk_appendf(&trash, " addr=%s:%d\n",
5096
pn, get_host_port(&conn->addr.to));
5099
chunk_appendf(&trash, " addr=unix\n");
5102
/* no more information to print right now */
5103
chunk_appendf(&trash, "\n");
5107
chunk_appendf(&trash,
5108
" task=%p (state=0x%02x nice=%d calls=%d exp=%s%s",
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" : "");
2581
get_localtime(sess->logs.accept_date.tv_sec, &tm);
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,
2586
5120
human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1));
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));
5127
chunk_appendf(&trash,
5128
" si[0]=%p (state=%s flags=0x%02x endp0=%s:%p exp=%s, et=0x%03x)\n",
5130
si_state_str(sess->si[0].state),
2592
5131
sess->si[0].flags,
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);
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",
5143
si_state_str(sess->si[1].state),
2604
5144
sess->si[1].flags,
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);
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);
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",
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));
5163
chunk_appendf(&trash,
5164
" flags=0x%08x fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
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);
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",
5178
tmpctx->applet->name);
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",
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));
5191
chunk_appendf(&trash,
5192
" flags=0x%08x fd=%d fd_spec_e=%02x fd_spec_p=%d updt=%d\n",
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);
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",
5206
tmpctx->applet->name);
5209
chunk_appendf(&trash,
5210
" req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
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>");
5220
chunk_appendf(&trash,
2632
5222
sess->req->rex ?
2633
5223
human_time(TICKS_TO_MS(sess->req->rex - now_ms),
2634
5224
TICKS_TO_MS(1000)) : "<NEVER>");
5226
chunk_appendf(&trash,
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>",
2643
(int)(sess->req->r - sess->req->data),
2644
(int)(sess->req->w - sess->req->data),
2645
(int)(sess->req->lr - sess->req->data),
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);
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"
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>");
5249
chunk_appendf(&trash,
2662
5251
sess->rep->rex ?
2663
5252
human_time(TICKS_TO_MS(sess->rep->rex - now_ms),
2664
5253
TICKS_TO_MS(1000)) : "<NEVER>");
5255
chunk_appendf(&trash,
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>",
2673
(int)(sess->rep->r - sess->rep->data),
2674
(int)(sess->rep->w - sess->rep->data),
2675
(int)(sess->rep->lr - sess->rep->data),
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);
2678
if (buffer_feed_chunk(rep, &msg) >= 0)
5267
if (bi_putchk(si->ib, &trash) == -1)
2681
5270
/* use other states to dump the contents */
2683
5272
/* end of dump */
2684
s->data_ctx.sess.uid = 0;
5273
appctx->ctx.sess.uid = 0;
5274
appctx->ctx.sess.section = 0;
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)
5280
struct appctx *appctx = __objt_appctx(si->end);
5282
switch (appctx->st2) {
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".
5288
chunk_reset(&trash);
5289
chunk_appendf(&trash, "# id (file) description\n");
5290
if (bi_putchk(si->ib, &trash) == -1)
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
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;
5305
while (appctx->ctx.map.ref) {
5306
chunk_reset(&trash);
5308
/* Build messages. If the reference is used by another category than
5309
* the listed categorie, display the information in the massage.
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);
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.
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);
5327
appctx->st2 = STAT_ST_FIN;
5331
appctx->st2 = STAT_ST_FIN;
5337
static int stats_map_lookup(struct stream_interface *si)
5339
struct appctx *appctx = __objt_appctx(si->end);
5340
struct sample sample;
5341
struct pattern *pat;
5344
switch (appctx->st2) {
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;
5353
/* for each lookup type */
5354
while (appctx->ctx.map.expr) {
5355
/* initialise chunk to build new message */
5356
chunk_reset(&trash);
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);
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])
5373
if (match_method >= PAT_MATCH_NUM)
5374
chunk_appendf(&trash, "type=unknown(%p)", appctx->ctx.map.expr->pat_head->match);
5376
chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
5378
/* case sensitive */
5379
if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
5380
chunk_appendf(&trash, ", case=insensitive");
5382
chunk_appendf(&trash, ", case=sensitive");
5384
/* Display no match, and set default value */
5386
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
5387
chunk_appendf(&trash, ", found=no");
5389
chunk_appendf(&trash, ", match=no");
5392
/* Display match and match info */
5395
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
5396
chunk_appendf(&trash, ", found=yes");
5398
chunk_appendf(&trash, ", match=yes");
5400
/* display index mode */
5401
if (pat->sflags & PAT_SF_TREE)
5402
chunk_appendf(&trash, ", idx=tree");
5404
chunk_appendf(&trash, ", idx=list");
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);
5411
chunk_appendf(&trash, ", key=unknown");
5414
if (pat->ref && pat->ref->pattern)
5415
chunk_appendf(&trash, ", pattern=\"%s\"", pat->ref->pattern);
5417
chunk_appendf(&trash, ", pattern=unknown");
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]);
5426
chunk_appendf(&trash, ", value=none");
5430
chunk_appendf(&trash, "\n");
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.
5440
/* get next entry */
5441
appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr,
5442
&appctx->ctx.map.ref->pat);
5445
appctx->st2 = STAT_ST_FIN;
5449
appctx->st2 = STAT_ST_FIN;
5450
free(appctx->ctx.map.chunk.str);
5455
static int stats_pat_list(struct stream_interface *si)
5457
struct appctx *appctx = __objt_appctx(si->end);
5459
switch (appctx->st2) {
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;
5471
while (appctx->ctx.map.elt) {
5472
chunk_reset(&trash);
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);
5480
chunk_appendf(&trash, "%p %s\n",
5481
appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
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.
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)
5497
appctx->st2 = STAT_ST_FIN;
5501
appctx->st2 = STAT_ST_FIN;
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.
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)
5513
struct appctx *appctx = __objt_appctx(si->end);
5514
struct connection *conn;
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.
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);
2711
chunk_init(&msg, trash, trashlen);
5529
chunk_reset(&trash);
2713
switch (s->data_state) {
5531
switch (appctx->st2) {
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