blob: 4db420c61e6153525bd705a143d985eb23276de3 [file] [log] [blame]
William Lallemand74c24fb2016-11-21 17:18:36 +01001/*
2 * Functions dedicated to statistics output and the stats socket
3 *
4 * Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
5 * Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version
10 * 2 of the License, or (at your option) any later version.
11 *
12 */
13
14#include <ctype.h>
15#include <errno.h>
16#include <fcntl.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <pwd.h>
21#include <grp.h>
22
23#include <sys/socket.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26
27#include <common/cfgparse.h>
28#include <common/compat.h>
29#include <common/config.h>
30#include <common/debug.h>
31#include <common/memory.h>
32#include <common/mini-clist.h>
33#include <common/standard.h>
34#include <common/ticks.h>
35#include <common/time.h>
36#include <common/uri_auth.h>
37#include <common/version.h>
38#include <common/base64.h>
39
40#include <types/applet.h>
William Lallemand9ed62032016-11-21 17:49:11 +010041#include <types/cli.h>
William Lallemand74c24fb2016-11-21 17:18:36 +010042#include <types/global.h>
43#include <types/dns.h>
William Lallemand9ed62032016-11-21 17:49:11 +010044#include <types/stats.h>
William Lallemand74c24fb2016-11-21 17:18:36 +010045
46#include <proto/backend.h>
47#include <proto/channel.h>
48#include <proto/checks.h>
49#include <proto/compression.h>
William Lallemand9ed62032016-11-21 17:49:11 +010050#include <proto/stats.h>
William Lallemand74c24fb2016-11-21 17:18:36 +010051#include <proto/fd.h>
52#include <proto/freq_ctr.h>
53#include <proto/frontend.h>
54#include <proto/log.h>
55#include <proto/pattern.h>
56#include <proto/pipe.h>
57#include <proto/listener.h>
58#include <proto/map.h>
59#include <proto/proto_http.h>
60#include <proto/proto_uxst.h>
61#include <proto/proxy.h>
62#include <proto/sample.h>
63#include <proto/session.h>
64#include <proto/stream.h>
65#include <proto/server.h>
66#include <proto/raw_sock.h>
67#include <proto/stream_interface.h>
68#include <proto/task.h>
69
William Lallemand74c24fb2016-11-21 17:18:36 +010070static int stats_dump_env_to_buffer(struct stream_interface *si);
William Lallemand74c24fb2016-11-21 17:18:36 +010071static int stats_dump_errors_to_buffer(struct stream_interface *si);
72static int stats_table_request(struct stream_interface *si, int show);
William Lallemand74c24fb2016-11-21 17:18:36 +010073
William Lallemand74c24fb2016-11-21 17:18:36 +010074
75static struct applet cli_applet;
76
77static const char stats_sock_usage_msg[] =
78 "Unknown command. Please enter one of the following commands only :\n"
79 " clear counters : clear max statistics counters (add 'all' for all counters)\n"
80 " clear table : remove an entry from a table\n"
81 " help : this message\n"
82 " prompt : toggle interactive mode with prompt\n"
83 " quit : disconnect\n"
William Lallemand74c24fb2016-11-21 17:18:36 +010084 " show env [var] : dump environment variables known to the process\n"
William Lallemand74c24fb2016-11-21 17:18:36 +010085 " show errors : report last request and response errors for each proxy\n"
William Lallemand74c24fb2016-11-21 17:18:36 +010086 " show table [id]: report table usage stats or dump this table's contents\n"
William Lallemand74c24fb2016-11-21 17:18:36 +010087 " set table [id] : update or create a table entry's data\n"
88 " set timeout : change a timeout setting\n"
89 " set maxconn : change a maxconn setting\n"
90 " set rate-limit : change a rate limiting value\n"
91 " disable : put a server or frontend in maintenance mode\n"
92 " enable : re-enable a server or frontend which is in maintenance mode\n"
93 " shutdown : kill a session or a frontend (eg:to release listening ports)\n"
William Lallemand74c24fb2016-11-21 17:18:36 +010094 "";
95
96static const char stats_permission_denied_msg[] =
97 "Permission denied\n"
98 "";
99
100
101static char *dynamic_usage_msg = NULL;
102
103/* List head of cli keywords */
104static struct cli_kw_list cli_keywords = {
105 .list = LIST_HEAD_INIT(cli_keywords.list)
106};
107
108extern const char *stat_status_codes[];
109
110char *cli_gen_usage_msg()
111{
112 struct cli_kw_list *kw_list;
113 struct cli_kw *kw;
114 struct chunk *tmp = get_trash_chunk();
115 struct chunk out;
116
117 free(dynamic_usage_msg);
118 dynamic_usage_msg = NULL;
119
120 if (LIST_ISEMPTY(&cli_keywords.list))
121 return NULL;
122
123 chunk_reset(tmp);
124 chunk_strcat(tmp, stats_sock_usage_msg);
125 list_for_each_entry(kw_list, &cli_keywords.list, list) {
126 kw = &kw_list->kw[0];
127 while (kw->usage) {
128 chunk_appendf(tmp, " %s\n", kw->usage);
129 kw++;
130 }
131 }
132 chunk_init(&out, NULL, 0);
133 chunk_dup(&out, tmp);
134 dynamic_usage_msg = out.str;
135 return dynamic_usage_msg;
136}
137
138struct cli_kw* cli_find_kw(char **args)
139{
140 struct cli_kw_list *kw_list;
141 struct cli_kw *kw;/* current cli_kw */
142 char **tmp_args;
143 const char **tmp_str_kw;
144 int found = 0;
145
146 if (LIST_ISEMPTY(&cli_keywords.list))
147 return NULL;
148
149 list_for_each_entry(kw_list, &cli_keywords.list, list) {
150 kw = &kw_list->kw[0];
151 while (*kw->str_kw) {
152 tmp_args = args;
153 tmp_str_kw = kw->str_kw;
154 while (*tmp_str_kw) {
155 if (strcmp(*tmp_str_kw, *tmp_args) == 0) {
156 found = 1;
157 } else {
158 found = 0;
159 break;
160 }
161 tmp_args++;
162 tmp_str_kw++;
163 }
164 if (found)
165 return (kw);
166 kw++;
167 }
168 }
169 return NULL;
170}
171
172void cli_register_kw(struct cli_kw_list *kw_list)
173{
174 LIST_ADDQ(&cli_keywords.list, &kw_list->list);
175}
176
177
178/* allocate a new stats frontend named <name>, and return it
179 * (or NULL in case of lack of memory).
180 */
181static struct proxy *alloc_stats_fe(const char *name, const char *file, int line)
182{
183 struct proxy *fe;
184
185 fe = calloc(1, sizeof(*fe));
186 if (!fe)
187 return NULL;
188
189 init_new_proxy(fe);
190 fe->next = proxy;
191 proxy = fe;
192 fe->last_change = now.tv_sec;
193 fe->id = strdup("GLOBAL");
194 fe->cap = PR_CAP_FE;
195 fe->maxconn = 10; /* default to 10 concurrent connections */
196 fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
197 fe->conf.file = strdup(file);
198 fe->conf.line = line;
199 fe->accept = frontend_accept;
200 fe->default_target = &cli_applet.obj_type;
201
202 /* the stats frontend is the only one able to assign ID #0 */
203 fe->conf.id.key = fe->uuid = 0;
204 eb32_insert(&used_proxy_id, &fe->conf.id);
205 return fe;
206}
207
208/* This function parses a "stats" statement in the "global" section. It returns
209 * -1 if there is any error, otherwise zero. If it returns -1, it will write an
210 * error message into the <err> buffer which will be preallocated. The trailing
211 * '\n' must not be written. The function must be called with <args> pointing to
212 * the first word after "stats".
213 */
214static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
215 struct proxy *defpx, const char *file, int line,
216 char **err)
217{
218 struct bind_conf *bind_conf;
219 struct listener *l;
220
221 if (!strcmp(args[1], "socket")) {
222 int cur_arg;
223
224 if (*args[2] == 0) {
225 memprintf(err, "'%s %s' in global section expects an address or a path to a UNIX socket", args[0], args[1]);
226 return -1;
227 }
228
229 if (!global.stats_fe) {
230 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
231 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
232 return -1;
233 }
234 }
235
236 bind_conf = bind_conf_alloc(&global.stats_fe->conf.bind, file, line, args[2]);
237 bind_conf->level = ACCESS_LVL_OPER; /* default access level */
238
239 if (!str2listener(args[2], global.stats_fe, bind_conf, file, line, err)) {
240 memprintf(err, "parsing [%s:%d] : '%s %s' : %s\n",
241 file, line, args[0], args[1], err && *err ? *err : "error");
242 return -1;
243 }
244
245 cur_arg = 3;
246 while (*args[cur_arg]) {
247 static int bind_dumped;
248 struct bind_kw *kw;
249
250 kw = bind_find_kw(args[cur_arg]);
251 if (kw) {
252 if (!kw->parse) {
253 memprintf(err, "'%s %s' : '%s' option is not implemented in this version (check build options).",
254 args[0], args[1], args[cur_arg]);
255 return -1;
256 }
257
258 if (kw->parse(args, cur_arg, global.stats_fe, bind_conf, err) != 0) {
259 if (err && *err)
260 memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
261 else
262 memprintf(err, "'%s %s' : error encountered while processing '%s'",
263 args[0], args[1], args[cur_arg]);
264 return -1;
265 }
266
267 cur_arg += 1 + kw->skip;
268 continue;
269 }
270
271 if (!bind_dumped) {
272 bind_dump_kws(err);
273 indent_msg(err, 4);
274 bind_dumped = 1;
275 }
276
277 memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s",
278 args[0], args[1], args[cur_arg],
279 err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
280 return -1;
281 }
282
283 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
284 l->maxconn = global.stats_fe->maxconn;
285 l->backlog = global.stats_fe->backlog;
286 l->accept = session_accept_fd;
287 l->handler = process_stream;
288 l->default_target = global.stats_fe->default_target;
289 l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
290 l->nice = -64; /* we want to boost priority for local stats */
291 global.maxsock += l->maxconn;
292 }
293 }
294 else if (!strcmp(args[1], "timeout")) {
295 unsigned timeout;
296 const char *res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
297
298 if (res) {
299 memprintf(err, "'%s %s' : unexpected character '%c'", args[0], args[1], *res);
300 return -1;
301 }
302
303 if (!timeout) {
304 memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
305 return -1;
306 }
307 if (!global.stats_fe) {
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]);
310 return -1;
311 }
312 }
313 global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
314 }
315 else if (!strcmp(args[1], "maxconn")) {
316 int maxconn = atol(args[2]);
317
318 if (maxconn <= 0) {
319 memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
320 return -1;
321 }
322
323 if (!global.stats_fe) {
324 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
325 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
326 return -1;
327 }
328 }
329 global.stats_fe->maxconn = maxconn;
330 }
331 else if (!strcmp(args[1], "bind-process")) { /* enable the socket only on some processes */
332 int cur_arg = 2;
333 unsigned long set = 0;
334
335 if (!global.stats_fe) {
336 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
337 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
338 return -1;
339 }
340 }
341
342 while (*args[cur_arg]) {
343 unsigned int low, high;
344
345 if (strcmp(args[cur_arg], "all") == 0) {
346 set = 0;
347 break;
348 }
349 else if (strcmp(args[cur_arg], "odd") == 0) {
350 set |= ~0UL/3UL; /* 0x555....555 */
351 }
352 else if (strcmp(args[cur_arg], "even") == 0) {
353 set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
354 }
355 else if (isdigit((int)*args[cur_arg])) {
356 char *dash = strchr(args[cur_arg], '-');
357
358 low = high = str2uic(args[cur_arg]);
359 if (dash)
360 high = str2uic(dash + 1);
361
362 if (high < low) {
363 unsigned int swap = low;
364 low = high;
365 high = swap;
366 }
367
368 if (low < 1 || high > LONGBITS) {
369 memprintf(err, "'%s %s' supports process numbers from 1 to %d.\n",
370 args[0], args[1], LONGBITS);
371 return -1;
372 }
373 while (low <= high)
374 set |= 1UL << (low++ - 1);
375 }
376 else {
377 memprintf(err,
378 "'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
379 args[0], args[1], LONGBITS);
380 return -1;
381 }
382 cur_arg++;
383 }
384 global.stats_fe->bind_proc = set;
385 }
386 else {
387 memprintf(err, "'%s' only supports 'socket', 'maxconn', 'bind-process' and 'timeout' (got '%s')", args[0], args[1]);
388 return -1;
389 }
390 return 0;
391}
392
Willy Tarreaude57a572016-11-23 17:01:39 +0100393/* Verifies that the CLI at least has a level at least as high as <level>
394 * (typically ACCESS_LVL_ADMIN). Returns 1 if OK, otherwise 0. In case of
395 * failure, an error message is prepared and the appctx's state is adjusted
396 * to print it so that a return 1 is enough to abort any processing.
397 */
398int cli_has_level(struct appctx *appctx, int level)
399{
400 struct stream_interface *si = appctx->owner;
401 struct stream *s = si_strm(si);
402
403 if (strm_li(s)->bind_conf->level < level) {
404 appctx->ctx.cli.msg = stats_permission_denied_msg;
405 appctx->st0 = STAT_CLI_PRINT;
406 return 0;
407 }
408 return 1;
409}
410
William Lallemand74c24fb2016-11-21 17:18:36 +0100411
William Lallemand74c24fb2016-11-21 17:18:36 +0100412/* Dump the status of a table to a stream interface's
413 * read buffer. It returns 0 if the output buffer is full
414 * and needs to be called again, otherwise non-zero.
415 */
416static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
417 struct proxy *proxy, struct proxy *target)
418{
419 struct stream *s = si_strm(si);
420
421 chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
422 proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
423
424 /* any other information should be dumped here */
425
426 if (target && strm_li(s)->bind_conf->level < ACCESS_LVL_OPER)
427 chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
428
429 if (bi_putchk(si_ic(si), msg) == -1) {
430 si_applet_cant_put(si);
431 return 0;
432 }
433
434 return 1;
435}
436
437/* Dump the a table entry to a stream interface's
438 * read buffer. It returns 0 if the output buffer is full
439 * and needs to be called again, otherwise non-zero.
440 */
441static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
442 struct proxy *proxy, struct stksess *entry)
443{
444 int dt;
445
446 chunk_appendf(msg, "%p:", entry);
447
448 if (proxy->table.type == SMP_T_IPV4) {
449 char addr[INET_ADDRSTRLEN];
450 inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
451 chunk_appendf(msg, " key=%s", addr);
452 }
453 else if (proxy->table.type == SMP_T_IPV6) {
454 char addr[INET6_ADDRSTRLEN];
455 inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
456 chunk_appendf(msg, " key=%s", addr);
457 }
458 else if (proxy->table.type == SMP_T_SINT) {
459 chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
460 }
461 else if (proxy->table.type == SMP_T_STR) {
462 chunk_appendf(msg, " key=");
463 dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
464 }
465 else {
466 chunk_appendf(msg, " key=");
467 dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
468 }
469
470 chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
471
472 for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
473 void *ptr;
474
475 if (proxy->table.data_ofs[dt] == 0)
476 continue;
477 if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
478 chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
479 else
480 chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
481
482 ptr = stktable_data_ptr(&proxy->table, entry, dt);
483 switch (stktable_data_types[dt].std_type) {
484 case STD_T_SINT:
485 chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
486 break;
487 case STD_T_UINT:
488 chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
489 break;
490 case STD_T_ULL:
491 chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
492 break;
493 case STD_T_FRQP:
494 chunk_appendf(msg, "%d",
495 read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
496 proxy->table.data_arg[dt].u));
497 break;
498 }
499 }
500 chunk_appendf(msg, "\n");
501
502 if (bi_putchk(si_ic(si), msg) == -1) {
503 si_applet_cant_put(si);
504 return 0;
505 }
506
507 return 1;
508}
509
510static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
511{
512 struct stream *s = si_strm(si);
513 struct appctx *appctx = __objt_appctx(si->end);
514 struct proxy *px = appctx->ctx.table.target;
515 struct stksess *ts;
516 uint32_t uint32_key;
517 unsigned char ip6_key[sizeof(struct in6_addr)];
518 long long value;
519 int data_type;
520 int cur_arg;
521 void *ptr;
522 struct freq_ctr_period *frqp;
523
524 appctx->st0 = STAT_CLI_OUTPUT;
525
526 if (!*args[4]) {
527 appctx->ctx.cli.msg = "Key value expected\n";
528 appctx->st0 = STAT_CLI_PRINT;
529 return;
530 }
531
532 switch (px->table.type) {
533 case SMP_T_IPV4:
534 uint32_key = htonl(inetaddr_host(args[4]));
535 static_table_key->key = &uint32_key;
536 break;
537 case SMP_T_IPV6:
538 inet_pton(AF_INET6, args[4], ip6_key);
539 static_table_key->key = &ip6_key;
540 break;
541 case SMP_T_SINT:
542 {
543 char *endptr;
544 unsigned long val;
545 errno = 0;
546 val = strtoul(args[4], &endptr, 10);
547 if ((errno == ERANGE && val == ULONG_MAX) ||
548 (errno != 0 && val == 0) || endptr == args[4] ||
549 val > 0xffffffff) {
550 appctx->ctx.cli.msg = "Invalid key\n";
551 appctx->st0 = STAT_CLI_PRINT;
552 return;
553 }
554 uint32_key = (uint32_t) val;
555 static_table_key->key = &uint32_key;
556 break;
557 }
558 break;
559 case SMP_T_STR:
560 static_table_key->key = args[4];
561 static_table_key->key_len = strlen(args[4]);
562 break;
563 default:
564 switch (action) {
565 case STAT_CLI_O_TAB:
566 appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
567 break;
568 case STAT_CLI_O_CLR:
569 appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
570 break;
571 default:
572 appctx->ctx.cli.msg = "Unknown action\n";
573 break;
574 }
575 appctx->st0 = STAT_CLI_PRINT;
576 return;
577 }
578
579 /* check permissions */
580 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
581 appctx->ctx.cli.msg = stats_permission_denied_msg;
582 appctx->st0 = STAT_CLI_PRINT;
583 return;
584 }
585
586 ts = stktable_lookup_key(&px->table, static_table_key);
587
588 switch (action) {
589 case STAT_CLI_O_TAB:
590 if (!ts)
591 return;
592 chunk_reset(&trash);
593 if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
594 return;
595 stats_dump_table_entry_to_buffer(&trash, si, px, ts);
596 return;
597
598 case STAT_CLI_O_CLR:
599 if (!ts)
600 return;
601 if (ts->ref_cnt) {
602 /* don't delete an entry which is currently referenced */
603 appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
604 appctx->st0 = STAT_CLI_PRINT;
605 return;
606 }
607 stksess_kill(&px->table, ts);
608 break;
609
610 case STAT_CLI_O_SET:
611 if (ts)
612 stktable_touch(&px->table, ts, 1);
613 else {
614 ts = stksess_new(&px->table, static_table_key);
615 if (!ts) {
616 /* don't delete an entry which is currently referenced */
617 appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
618 appctx->st0 = STAT_CLI_PRINT;
619 return;
620 }
621 stktable_store(&px->table, ts, 1);
622 }
623
624 for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
625 if (strncmp(args[cur_arg], "data.", 5) != 0) {
626 appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
627 appctx->st0 = STAT_CLI_PRINT;
628 return;
629 }
630
631 data_type = stktable_get_data_type(args[cur_arg] + 5);
632 if (data_type < 0) {
633 appctx->ctx.cli.msg = "Unknown data type\n";
634 appctx->st0 = STAT_CLI_PRINT;
635 return;
636 }
637
638 if (!px->table.data_ofs[data_type]) {
639 appctx->ctx.cli.msg = "Data type not stored in this table\n";
640 appctx->st0 = STAT_CLI_PRINT;
641 return;
642 }
643
644 if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
645 appctx->ctx.cli.msg = "Require a valid integer value to store\n";
646 appctx->st0 = STAT_CLI_PRINT;
647 return;
648 }
649
650 ptr = stktable_data_ptr(&px->table, ts, data_type);
651
652 switch (stktable_data_types[data_type].std_type) {
653 case STD_T_SINT:
654 stktable_data_cast(ptr, std_t_sint) = value;
655 break;
656 case STD_T_UINT:
657 stktable_data_cast(ptr, std_t_uint) = value;
658 break;
659 case STD_T_ULL:
660 stktable_data_cast(ptr, std_t_ull) = value;
661 break;
662 case STD_T_FRQP:
663 /* We set both the current and previous values. That way
664 * the reported frequency is stable during all the period
665 * then slowly fades out. This allows external tools to
666 * push measures without having to update them too often.
667 */
668 frqp = &stktable_data_cast(ptr, std_t_frqp);
669 frqp->curr_tick = now_ms;
670 frqp->prev_ctr = 0;
671 frqp->curr_ctr = value;
672 break;
673 }
674 }
675 break;
676
677 default:
678 appctx->ctx.cli.msg = "Unknown action\n";
679 appctx->st0 = STAT_CLI_PRINT;
680 break;
681 }
682}
683
684static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
685{
686 struct appctx *appctx = __objt_appctx(si->end);
687
688 if (action != STAT_CLI_O_TAB && action != STAT_CLI_O_CLR) {
689 appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
690 appctx->st0 = STAT_CLI_PRINT;
691 return;
692 }
693
694 /* condition on stored data value */
695 appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
696 if (appctx->ctx.table.data_type < 0) {
697 appctx->ctx.cli.msg = "Unknown data type\n";
698 appctx->st0 = STAT_CLI_PRINT;
699 return;
700 }
701
702 if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
703 appctx->ctx.cli.msg = "Data type not stored in this table\n";
704 appctx->st0 = STAT_CLI_PRINT;
705 return;
706 }
707
708 appctx->ctx.table.data_op = get_std_op(args[4]);
709 if (appctx->ctx.table.data_op < 0) {
710 appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
711 appctx->st0 = STAT_CLI_PRINT;
712 return;
713 }
714
715 if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
716 appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
717 appctx->st0 = STAT_CLI_PRINT;
718 return;
719 }
720}
721
722static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
723{
724 struct appctx *appctx = __objt_appctx(si->end);
725
726 appctx->ctx.table.data_type = -1;
727 appctx->st2 = STAT_ST_INIT;
728 appctx->ctx.table.target = NULL;
729 appctx->ctx.table.proxy = NULL;
730 appctx->ctx.table.entry = NULL;
731 appctx->st0 = action;
732
733 if (*args[2]) {
734 appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
735 if (!appctx->ctx.table.target) {
736 appctx->ctx.cli.msg = "No such table\n";
737 appctx->st0 = STAT_CLI_PRINT;
738 return;
739 }
740 }
741 else {
742 if (action != STAT_CLI_O_TAB)
743 goto err_args;
744 return;
745 }
746
747 if (strcmp(args[3], "key") == 0)
748 stats_sock_table_key_request(si, args, action);
749 else if (strncmp(args[3], "data.", 5) == 0)
750 stats_sock_table_data_request(si, args, action);
751 else if (*args[3])
752 goto err_args;
753
754 return;
755
756err_args:
757 switch (action) {
758 case STAT_CLI_O_TAB:
759 appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
760 break;
761 case STAT_CLI_O_CLR:
762 appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
763 break;
764 default:
765 appctx->ctx.cli.msg = "Unknown action\n";
766 break;
767 }
768 appctx->st0 = STAT_CLI_PRINT;
769}
770
771/* Expects to find a frontend named <arg> and returns it, otherwise displays various
772 * adequate error messages and returns NULL. This function also expects the stream
773 * level to be admin.
774 */
775static struct proxy *expect_frontend_admin(struct stream *s, struct stream_interface *si, const char *arg)
776{
777 struct appctx *appctx = __objt_appctx(si->end);
778 struct proxy *px;
779
780 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
781 appctx->ctx.cli.msg = stats_permission_denied_msg;
782 appctx->st0 = STAT_CLI_PRINT;
783 return NULL;
784 }
785
786 if (!*arg) {
787 appctx->ctx.cli.msg = "A frontend name is expected.\n";
788 appctx->st0 = STAT_CLI_PRINT;
789 return NULL;
790 }
791
792 px = proxy_fe_by_name(arg);
793 if (!px) {
794 appctx->ctx.cli.msg = "No such frontend.\n";
795 appctx->st0 = STAT_CLI_PRINT;
796 return NULL;
797 }
798 return px;
799}
800
801/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
802 * and returns the pointer to the server. Otherwise, display adequate error messages
803 * and returns NULL. This function also expects the stream level to be admin. Note:
804 * the <arg> is modified to remove the '/'.
805 */
William Lallemand222baf22016-11-19 02:00:33 +0100806struct server *expect_server_admin(struct stream *s, struct stream_interface *si, char *arg)
William Lallemand74c24fb2016-11-21 17:18:36 +0100807{
808 struct appctx *appctx = __objt_appctx(si->end);
809 struct proxy *px;
810 struct server *sv;
811 char *line;
812
813 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
814 appctx->ctx.cli.msg = stats_permission_denied_msg;
815 appctx->st0 = STAT_CLI_PRINT;
816 return NULL;
817 }
818
819 /* split "backend/server" and make <line> point to server */
820 for (line = arg; *line; line++)
821 if (*line == '/') {
822 *line++ = '\0';
823 break;
824 }
825
826 if (!*line || !*arg) {
827 appctx->ctx.cli.msg = "Require 'backend/server'.\n";
828 appctx->st0 = STAT_CLI_PRINT;
829 return NULL;
830 }
831
832 if (!get_backend_server(arg, line, &px, &sv)) {
833 appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
834 appctx->st0 = STAT_CLI_PRINT;
835 return NULL;
836 }
837
838 if (px->state == PR_STSTOPPED) {
839 appctx->ctx.cli.msg = "Proxy is disabled.\n";
840 appctx->st0 = STAT_CLI_PRINT;
841 return NULL;
842 }
843
844 return sv;
845}
846
William Lallemand74c24fb2016-11-21 17:18:36 +0100847/* Processes the stats interpreter on the statistics socket. This function is
848 * called from an applet running in a stream interface. The function returns 1
849 * if the request was understood, otherwise zero. It sets appctx->st0 to a value
850 * designating the function which will have to process the request, which can
851 * also be the print function to display the return message set into cli.msg.
852 */
853static int stats_sock_parse_request(struct stream_interface *si, char *line)
854{
855 struct stream *s = si_strm(si);
856 struct appctx *appctx = __objt_appctx(si->end);
857 char *args[MAX_STATS_ARGS + 1];
858 struct cli_kw *kw;
859 int arg;
860 int i, j;
861
862 while (isspace((unsigned char)*line))
863 line++;
864
865 arg = 0;
866 args[arg] = line;
867
868 while (*line && arg < MAX_STATS_ARGS) {
869 if (*line == '\\') {
870 line++;
871 if (*line == '\0')
872 break;
873 }
874 else if (isspace((unsigned char)*line)) {
875 *line++ = '\0';
876
877 while (isspace((unsigned char)*line))
878 line++;
879
880 args[++arg] = line;
881 continue;
882 }
883
884 line++;
885 }
886
887 while (++arg <= MAX_STATS_ARGS)
888 args[arg] = line;
889
890 /* remove \ */
891 arg = 0;
892 while (*args[arg] != '\0') {
893 j = 0;
894 for (i=0; args[arg][i] != '\0'; i++) {
895 if (args[arg][i] == '\\')
896 continue;
897 args[arg][j] = args[arg][i];
898 j++;
899 }
900 args[arg][j] = '\0';
901 arg++;
902 }
903
904 appctx->ctx.stats.scope_str = 0;
905 appctx->ctx.stats.scope_len = 0;
906 appctx->ctx.stats.flags = 0;
907 if ((kw = cli_find_kw(args))) {
908 if (kw->parse) {
909 if (kw->parse(args, appctx, kw->private) == 0 && kw->io_handler) {
910 appctx->st0 = STAT_CLI_O_CUSTOM;
911 appctx->io_handler = kw->io_handler;
912 appctx->io_release = kw->io_release;
913 }
914 }
915 } else if (strcmp(args[0], "show") == 0) {
William Lallemand933efcd2016-11-22 12:34:16 +0100916 if (strcmp(args[1], "env") == 0) {
William Lallemand74c24fb2016-11-21 17:18:36 +0100917 extern char **environ;
918
919 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
920 appctx->ctx.cli.msg = stats_permission_denied_msg;
921 appctx->st0 = STAT_CLI_PRINT;
922 return 1;
923 }
924 appctx->ctx.env.var = environ;
925 appctx->st2 = STAT_ST_INIT;
926 appctx->st0 = STAT_CLI_O_ENV; // stats_dump_env_to_buffer
927
928 if (*args[2]) {
929 int len = strlen(args[2]);
930
931 for (; *appctx->ctx.env.var; appctx->ctx.env.var++) {
932 if (strncmp(*appctx->ctx.env.var, args[2], len) == 0 &&
933 (*appctx->ctx.env.var)[len] == '=')
934 break;
935 }
936 if (!*appctx->ctx.env.var) {
937 appctx->ctx.cli.msg = "Variable not found\n";
938 appctx->st0 = STAT_CLI_PRINT;
939 return 1;
940 }
941 appctx->st2 = STAT_ST_END;
942 }
943 }
William Lallemand74c24fb2016-11-21 17:18:36 +0100944 else if (strcmp(args[1], "errors") == 0) {
945 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
946 appctx->ctx.cli.msg = stats_permission_denied_msg;
947 appctx->st0 = STAT_CLI_PRINT;
948 return 1;
949 }
950 if (*args[2])
951 appctx->ctx.errors.iid = atoi(args[2]);
952 else
953 appctx->ctx.errors.iid = -1;
954 appctx->ctx.errors.px = NULL;
955 appctx->st2 = STAT_ST_INIT;
956 appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
957 }
958 else if (strcmp(args[1], "table") == 0) {
959 stats_sock_table_request(si, args, STAT_CLI_O_TAB);
960 }
William Lallemand74c24fb2016-11-21 17:18:36 +0100961 else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
962 return 0;
963 }
964 }
965 else if (strcmp(args[0], "clear") == 0) {
966 if (strcmp(args[1], "counters") == 0) {
967 struct proxy *px;
968 struct server *sv;
969 struct listener *li;
970 int clrall = 0;
971
972 if (strcmp(args[2], "all") == 0)
973 clrall = 1;
974
975 /* check permissions */
976 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER ||
977 (clrall && strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN)) {
978 appctx->ctx.cli.msg = stats_permission_denied_msg;
979 appctx->st0 = STAT_CLI_PRINT;
980 return 1;
981 }
982
983 for (px = proxy; px; px = px->next) {
984 if (clrall) {
985 memset(&px->be_counters, 0, sizeof(px->be_counters));
986 memset(&px->fe_counters, 0, sizeof(px->fe_counters));
987 }
988 else {
989 px->be_counters.conn_max = 0;
990 px->be_counters.p.http.rps_max = 0;
991 px->be_counters.sps_max = 0;
992 px->be_counters.cps_max = 0;
993 px->be_counters.nbpend_max = 0;
994
995 px->fe_counters.conn_max = 0;
996 px->fe_counters.p.http.rps_max = 0;
997 px->fe_counters.sps_max = 0;
998 px->fe_counters.cps_max = 0;
999 px->fe_counters.nbpend_max = 0;
1000 }
1001
1002 for (sv = px->srv; sv; sv = sv->next)
1003 if (clrall)
1004 memset(&sv->counters, 0, sizeof(sv->counters));
1005 else {
1006 sv->counters.cur_sess_max = 0;
1007 sv->counters.nbpend_max = 0;
1008 sv->counters.sps_max = 0;
1009 }
1010
1011 list_for_each_entry(li, &px->conf.listeners, by_fe)
1012 if (li->counters) {
1013 if (clrall)
1014 memset(li->counters, 0, sizeof(*li->counters));
1015 else
1016 li->counters->conn_max = 0;
1017 }
1018 }
1019
1020 global.cps_max = 0;
1021 global.sps_max = 0;
1022 return 1;
1023 }
1024 else if (strcmp(args[1], "table") == 0) {
1025 stats_sock_table_request(si, args, STAT_CLI_O_CLR);
1026 /* end of processing */
1027 return 1;
1028 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001029 else {
1030 /* unknown "clear" argument */
1031 return 0;
1032 }
1033 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001034 else if (strcmp(args[0], "set") == 0) {
William Lallemand6b160942016-11-22 12:34:35 +01001035 if (strcmp(args[1], "timeout") == 0) {
William Lallemand74c24fb2016-11-21 17:18:36 +01001036 if (strcmp(args[2], "cli") == 0) {
1037 unsigned timeout;
1038 const char *res;
1039
1040 if (!*args[3]) {
1041 appctx->ctx.cli.msg = "Expects an integer value.\n";
1042 appctx->st0 = STAT_CLI_PRINT;
1043 return 1;
1044 }
1045
1046 res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
1047 if (res || timeout < 1) {
1048 appctx->ctx.cli.msg = "Invalid timeout value.\n";
1049 appctx->st0 = STAT_CLI_PRINT;
1050 return 1;
1051 }
1052
1053 s->req.rto = s->res.wto = 1 + MS_TO_TICKS(timeout*1000);
1054 task_wakeup(s->task, TASK_WOKEN_MSG); // recompute timeouts
1055 return 1;
1056 }
1057 else {
1058 appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
1059 appctx->st0 = STAT_CLI_PRINT;
1060 return 1;
1061 }
1062 }
1063 else if (strcmp(args[1], "maxconn") == 0) {
1064 if (strcmp(args[2], "frontend") == 0) {
1065 struct proxy *px;
1066 struct listener *l;
1067 int v;
1068
1069 px = expect_frontend_admin(s, si, args[3]);
1070 if (!px)
1071 return 1;
1072
1073 if (!*args[4]) {
1074 appctx->ctx.cli.msg = "Integer value expected.\n";
1075 appctx->st0 = STAT_CLI_PRINT;
1076 return 1;
1077 }
1078
1079 v = atoi(args[4]);
1080 if (v < 0) {
1081 appctx->ctx.cli.msg = "Value out of range.\n";
1082 appctx->st0 = STAT_CLI_PRINT;
1083 return 1;
1084 }
1085
1086 /* OK, the value is fine, so we assign it to the proxy and to all of
1087 * its listeners. The blocked ones will be dequeued.
1088 */
1089 px->maxconn = v;
1090 list_for_each_entry(l, &px->conf.listeners, by_fe) {
1091 l->maxconn = v;
1092 if (l->state == LI_FULL)
1093 resume_listener(l);
1094 }
1095
1096 if (px->maxconn > px->feconn && !LIST_ISEMPTY(&px->listener_queue))
1097 dequeue_all_listeners(&px->listener_queue);
1098
1099 return 1;
1100 }
1101 else if (strcmp(args[2], "server") == 0) {
1102 struct server *sv;
1103 const char *warning;
1104
1105 sv = expect_server_admin(s, si, args[3]);
1106 if (!sv)
1107 return 1;
1108
1109 warning = server_parse_maxconn_change_request(sv, args[4]);
1110 if (warning) {
1111 appctx->ctx.cli.msg = warning;
1112 appctx->st0 = STAT_CLI_PRINT;
1113 }
1114
1115 return 1;
1116 }
1117 else if (strcmp(args[2], "global") == 0) {
1118 int v;
1119
1120 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1121 appctx->ctx.cli.msg = stats_permission_denied_msg;
1122 appctx->st0 = STAT_CLI_PRINT;
1123 return 1;
1124 }
1125
1126 if (!*args[3]) {
1127 appctx->ctx.cli.msg = "Expects an integer value.\n";
1128 appctx->st0 = STAT_CLI_PRINT;
1129 return 1;
1130 }
1131
1132 v = atoi(args[3]);
1133 if (v > global.hardmaxconn) {
1134 appctx->ctx.cli.msg = "Value out of range.\n";
1135 appctx->st0 = STAT_CLI_PRINT;
1136 return 1;
1137 }
1138
1139 /* check for unlimited values */
1140 if (v <= 0)
1141 v = global.hardmaxconn;
1142
1143 global.maxconn = v;
1144
1145 /* Dequeues all of the listeners waiting for a resource */
1146 if (!LIST_ISEMPTY(&global_listener_queue))
1147 dequeue_all_listeners(&global_listener_queue);
1148
1149 return 1;
1150 }
1151 else {
1152 appctx->ctx.cli.msg = "'set maxconn' only supports 'frontend', 'server', and 'global'.\n";
1153 appctx->st0 = STAT_CLI_PRINT;
1154 return 1;
1155 }
1156 }
1157 else if (strcmp(args[1], "rate-limit") == 0) {
1158 if (strcmp(args[2], "connections") == 0) {
1159 if (strcmp(args[3], "global") == 0) {
1160 int v;
1161
1162 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1163 appctx->ctx.cli.msg = stats_permission_denied_msg;
1164 appctx->st0 = STAT_CLI_PRINT;
1165 return 1;
1166 }
1167
1168 if (!*args[4]) {
1169 appctx->ctx.cli.msg = "Expects an integer value.\n";
1170 appctx->st0 = STAT_CLI_PRINT;
1171 return 1;
1172 }
1173
1174 v = atoi(args[4]);
1175 if (v < 0) {
1176 appctx->ctx.cli.msg = "Value out of range.\n";
1177 appctx->st0 = STAT_CLI_PRINT;
1178 return 1;
1179 }
1180
1181 global.cps_lim = v;
1182
1183 /* Dequeues all of the listeners waiting for a resource */
1184 if (!LIST_ISEMPTY(&global_listener_queue))
1185 dequeue_all_listeners(&global_listener_queue);
1186
1187 return 1;
1188 }
1189 else {
1190 appctx->ctx.cli.msg = "'set rate-limit connections' only supports 'global'.\n";
1191 appctx->st0 = STAT_CLI_PRINT;
1192 return 1;
1193 }
1194 }
1195 else if (strcmp(args[2], "sessions") == 0) {
1196 if (strcmp(args[3], "global") == 0) {
1197 int v;
1198
1199 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1200 appctx->ctx.cli.msg = stats_permission_denied_msg;
1201 appctx->st0 = STAT_CLI_PRINT;
1202 return 1;
1203 }
1204
1205 if (!*args[4]) {
1206 appctx->ctx.cli.msg = "Expects an integer value.\n";
1207 appctx->st0 = STAT_CLI_PRINT;
1208 return 1;
1209 }
1210
1211 v = atoi(args[4]);
1212 if (v < 0) {
1213 appctx->ctx.cli.msg = "Value out of range.\n";
1214 appctx->st0 = STAT_CLI_PRINT;
1215 return 1;
1216 }
1217
1218 global.sps_lim = v;
1219
1220 /* Dequeues all of the listeners waiting for a resource */
1221 if (!LIST_ISEMPTY(&global_listener_queue))
1222 dequeue_all_listeners(&global_listener_queue);
1223
1224 return 1;
1225 }
1226 else {
1227 appctx->ctx.cli.msg = "'set rate-limit sessions' only supports 'global'.\n";
1228 appctx->st0 = STAT_CLI_PRINT;
1229 return 1;
1230 }
1231 }
1232#ifdef USE_OPENSSL
1233 else if (strcmp(args[2], "ssl-sessions") == 0) {
1234 if (strcmp(args[3], "global") == 0) {
1235 int v;
1236
1237 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1238 appctx->ctx.cli.msg = stats_permission_denied_msg;
1239 appctx->st0 = STAT_CLI_PRINT;
1240 return 1;
1241 }
1242
1243 if (!*args[4]) {
1244 appctx->ctx.cli.msg = "Expects an integer value.\n";
1245 appctx->st0 = STAT_CLI_PRINT;
1246 return 1;
1247 }
1248
1249 v = atoi(args[4]);
1250 if (v < 0) {
1251 appctx->ctx.cli.msg = "Value out of range.\n";
1252 appctx->st0 = STAT_CLI_PRINT;
1253 return 1;
1254 }
1255
1256 global.ssl_lim = v;
1257
1258 /* Dequeues all of the listeners waiting for a resource */
1259 if (!LIST_ISEMPTY(&global_listener_queue))
1260 dequeue_all_listeners(&global_listener_queue);
1261
1262 return 1;
1263 }
1264 else {
1265 appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
1266 appctx->st0 = STAT_CLI_PRINT;
1267 return 1;
1268 }
1269 }
1270#endif
1271 else if (strcmp(args[2], "http-compression") == 0) {
1272 if (strcmp(args[3], "global") == 0) {
1273 int v;
1274
1275 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1276 appctx->ctx.cli.msg = stats_permission_denied_msg;
1277 appctx->st0 = STAT_CLI_PRINT;
1278 return 1;
1279 }
1280
1281 if (!*args[4]) {
1282 appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
1283 appctx->st0 = STAT_CLI_PRINT;
1284 return 1;
1285 }
1286
1287 v = atoi(args[4]);
1288 global.comp_rate_lim = v * 1024; /* Kilo to bytes. */
1289 }
1290 else {
1291 appctx->ctx.cli.msg = "'set rate-limit http-compression' only supports 'global'.\n";
1292 appctx->st0 = STAT_CLI_PRINT;
1293 return 1;
1294 }
1295 }
1296 else {
1297 appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
1298 appctx->st0 = STAT_CLI_PRINT;
1299 return 1;
1300 }
1301 }
1302 else if (strcmp(args[1], "table") == 0) {
1303 stats_sock_table_request(si, args, STAT_CLI_O_SET);
William Lallemand32af2032016-10-29 18:09:35 +02001304 } else { /* unknown "set" parameter */
William Lallemand74c24fb2016-11-21 17:18:36 +01001305 return 0;
1306 }
1307 }
1308 else if (strcmp(args[0], "enable") == 0) {
1309 if (strcmp(args[1], "agent") == 0) {
1310 struct server *sv;
1311
1312 sv = expect_server_admin(s, si, args[2]);
1313 if (!sv)
1314 return 1;
1315
1316 if (!(sv->agent.state & CHK_ST_CONFIGURED)) {
1317 appctx->ctx.cli.msg = "Agent was not configured on this server, cannot enable.\n";
1318 appctx->st0 = STAT_CLI_PRINT;
1319 return 1;
1320 }
1321
1322 sv->agent.state |= CHK_ST_ENABLED;
1323 return 1;
1324 }
1325 else if (strcmp(args[1], "health") == 0) {
1326 struct server *sv;
1327
1328 sv = expect_server_admin(s, si, args[2]);
1329 if (!sv)
1330 return 1;
1331
1332 if (!(sv->check.state & CHK_ST_CONFIGURED)) {
1333 appctx->ctx.cli.msg = "Health checks are not configured on this server, cannot enable.\n";
1334 appctx->st0 = STAT_CLI_PRINT;
1335 return 1;
1336 }
1337
1338 sv->check.state |= CHK_ST_ENABLED;
1339 return 1;
1340 }
1341 else if (strcmp(args[1], "server") == 0) {
1342 struct server *sv;
1343
1344 sv = expect_server_admin(s, si, args[2]);
1345 if (!sv)
1346 return 1;
1347
1348 srv_adm_set_ready(sv);
1349 return 1;
1350 }
1351 else if (strcmp(args[1], "frontend") == 0) {
1352 struct proxy *px;
1353
1354 px = expect_frontend_admin(s, si, args[2]);
1355 if (!px)
1356 return 1;
1357
1358 if (px->state == PR_STSTOPPED) {
1359 appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
1360 appctx->st0 = STAT_CLI_PRINT;
1361 return 1;
1362 }
1363
1364 if (px->state != PR_STPAUSED) {
1365 appctx->ctx.cli.msg = "Frontend is already enabled.\n";
1366 appctx->st0 = STAT_CLI_PRINT;
1367 return 1;
1368 }
1369
1370 if (!resume_proxy(px)) {
1371 appctx->ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
1372 appctx->st0 = STAT_CLI_PRINT;
1373 return 1;
1374 }
1375 return 1;
1376 }
1377 else { /* unknown "enable" parameter */
1378 appctx->ctx.cli.msg = "'enable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
1379 appctx->st0 = STAT_CLI_PRINT;
1380 return 1;
1381 }
1382 }
1383 else if (strcmp(args[0], "disable") == 0) {
1384 if (strcmp(args[1], "agent") == 0) {
1385 struct server *sv;
1386
1387 sv = expect_server_admin(s, si, args[2]);
1388 if (!sv)
1389 return 1;
1390
1391 sv->agent.state &= ~CHK_ST_ENABLED;
1392 return 1;
1393 }
1394 else if (strcmp(args[1], "health") == 0) {
1395 struct server *sv;
1396
1397 sv = expect_server_admin(s, si, args[2]);
1398 if (!sv)
1399 return 1;
1400
1401 sv->check.state &= ~CHK_ST_ENABLED;
1402 return 1;
1403 }
1404 else if (strcmp(args[1], "server") == 0) {
1405 struct server *sv;
1406
1407 sv = expect_server_admin(s, si, args[2]);
1408 if (!sv)
1409 return 1;
1410
1411 srv_adm_set_maint(sv);
1412 return 1;
1413 }
1414 else if (strcmp(args[1], "frontend") == 0) {
1415 struct proxy *px;
1416
1417 px = expect_frontend_admin(s, si, args[2]);
1418 if (!px)
1419 return 1;
1420
1421 if (px->state == PR_STSTOPPED) {
1422 appctx->ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
1423 appctx->st0 = STAT_CLI_PRINT;
1424 return 1;
1425 }
1426
1427 if (px->state == PR_STPAUSED) {
1428 appctx->ctx.cli.msg = "Frontend is already disabled.\n";
1429 appctx->st0 = STAT_CLI_PRINT;
1430 return 1;
1431 }
1432
1433 if (!pause_proxy(px)) {
1434 appctx->ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
1435 appctx->st0 = STAT_CLI_PRINT;
1436 return 1;
1437 }
1438 return 1;
1439 }
1440 else { /* unknown "disable" parameter */
1441 appctx->ctx.cli.msg = "'disable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
1442 appctx->st0 = STAT_CLI_PRINT;
1443 return 1;
1444 }
1445 }
1446 else if (strcmp(args[0], "shutdown") == 0) {
1447 if (strcmp(args[1], "frontend") == 0) {
1448 struct proxy *px;
1449
1450 px = expect_frontend_admin(s, si, args[2]);
1451 if (!px)
1452 return 1;
1453
1454 if (px->state == PR_STSTOPPED) {
1455 appctx->ctx.cli.msg = "Frontend was already shut down.\n";
1456 appctx->st0 = STAT_CLI_PRINT;
1457 return 1;
1458 }
1459
1460 Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
1461 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
1462 send_log(px, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
1463 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
1464 stop_proxy(px);
1465 return 1;
1466 }
1467 else if (strcmp(args[1], "session") == 0) {
1468 struct stream *sess, *ptr;
1469
1470 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1471 appctx->ctx.cli.msg = stats_permission_denied_msg;
1472 appctx->st0 = STAT_CLI_PRINT;
1473 return 1;
1474 }
1475
1476 if (!*args[2]) {
1477 appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
1478 appctx->st0 = STAT_CLI_PRINT;
1479 return 1;
1480 }
1481
1482 ptr = (void *)strtoul(args[2], NULL, 0);
1483
1484 /* first, look for the requested stream in the stream table */
1485 list_for_each_entry(sess, &streams, list) {
1486 if (sess == ptr)
1487 break;
1488 }
1489
1490 /* do we have the stream ? */
1491 if (sess != ptr) {
1492 appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
1493 appctx->st0 = STAT_CLI_PRINT;
1494 return 1;
1495 }
1496
1497 stream_shutdown(sess, SF_ERR_KILLED);
1498 return 1;
1499 }
1500 else if (strcmp(args[1], "sessions") == 0) {
1501 if (strcmp(args[2], "server") == 0) {
1502 struct server *sv;
1503 struct stream *sess, *sess_bck;
1504
1505 sv = expect_server_admin(s, si, args[3]);
1506 if (!sv)
1507 return 1;
1508
1509 /* kill all the stream that are on this server */
1510 list_for_each_entry_safe(sess, sess_bck, &sv->actconns, by_srv)
1511 if (sess->srv_conn == sv)
1512 stream_shutdown(sess, SF_ERR_KILLED);
1513
1514 return 1;
1515 }
1516 else {
1517 appctx->ctx.cli.msg = "'shutdown sessions' only supports 'server'.\n";
1518 appctx->st0 = STAT_CLI_PRINT;
1519 return 1;
1520 }
1521 }
1522 else { /* unknown "disable" parameter */
1523 appctx->ctx.cli.msg = "'shutdown' only supports 'frontend', 'session' and 'sessions'.\n";
1524 appctx->st0 = STAT_CLI_PRINT;
1525 return 1;
1526 }
1527 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001528 else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
1529 return 0;
1530 }
1531 return 1;
1532}
1533
1534/* This I/O handler runs as an applet embedded in a stream interface. It is
1535 * used to processes I/O from/to the stats unix socket. The system relies on a
1536 * state machine handling requests and various responses. We read a request,
1537 * then we process it and send the response, and we possibly display a prompt.
1538 * Then we can read again. The state is stored in appctx->st0 and is one of the
1539 * STAT_CLI_* constants. appctx->st1 is used to indicate whether prompt is enabled
1540 * or not.
1541 */
1542static void cli_io_handler(struct appctx *appctx)
1543{
1544 struct stream_interface *si = appctx->owner;
1545 struct channel *req = si_oc(si);
1546 struct channel *res = si_ic(si);
1547 int reql;
1548 int len;
1549
1550 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
1551 goto out;
1552
1553 while (1) {
1554 if (appctx->st0 == STAT_CLI_INIT) {
1555 /* Stats output not initialized yet */
1556 memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
1557 appctx->st0 = STAT_CLI_GETREQ;
1558 }
1559 else if (appctx->st0 == STAT_CLI_END) {
1560 /* Let's close for real now. We just close the request
1561 * side, the conditions below will complete if needed.
1562 */
1563 si_shutw(si);
1564 break;
1565 }
1566 else if (appctx->st0 == STAT_CLI_GETREQ) {
1567 /* ensure we have some output room left in the event we
1568 * would want to return some info right after parsing.
1569 */
1570 if (buffer_almost_full(si_ib(si))) {
1571 si_applet_cant_put(si);
1572 break;
1573 }
1574
1575 reql = bo_getline(si_oc(si), trash.str, trash.size);
1576 if (reql <= 0) { /* closed or EOL not found */
1577 if (reql == 0)
1578 break;
1579 appctx->st0 = STAT_CLI_END;
1580 continue;
1581 }
1582
1583 /* seek for a possible unescaped semi-colon. If we find
1584 * one, we replace it with an LF and skip only this part.
1585 */
1586 for (len = 0; len < reql; len++) {
1587 if (trash.str[len] == '\\') {
1588 len++;
1589 continue;
1590 }
1591 if (trash.str[len] == ';') {
1592 trash.str[len] = '\n';
1593 reql = len + 1;
1594 break;
1595 }
1596 }
1597
1598 /* now it is time to check that we have a full line,
1599 * remove the trailing \n and possibly \r, then cut the
1600 * line.
1601 */
1602 len = reql - 1;
1603 if (trash.str[len] != '\n') {
1604 appctx->st0 = STAT_CLI_END;
1605 continue;
1606 }
1607
1608 if (len && trash.str[len-1] == '\r')
1609 len--;
1610
1611 trash.str[len] = '\0';
1612
1613 appctx->st0 = STAT_CLI_PROMPT;
1614 if (len) {
1615 if (strcmp(trash.str, "quit") == 0) {
1616 appctx->st0 = STAT_CLI_END;
1617 continue;
1618 }
1619 else if (strcmp(trash.str, "prompt") == 0)
1620 appctx->st1 = !appctx->st1;
1621 else if (strcmp(trash.str, "help") == 0 ||
1622 !stats_sock_parse_request(si, trash.str)) {
1623 cli_gen_usage_msg();
1624 if (dynamic_usage_msg)
1625 appctx->ctx.cli.msg = dynamic_usage_msg;
1626 else
1627 appctx->ctx.cli.msg = stats_sock_usage_msg;
1628 appctx->st0 = STAT_CLI_PRINT;
1629 }
1630 /* NB: stats_sock_parse_request() may have put
1631 * another STAT_CLI_O_* into appctx->st0.
1632 */
1633 }
1634 else if (!appctx->st1) {
1635 /* if prompt is disabled, print help on empty lines,
1636 * so that the user at least knows how to enable
1637 * prompt and find help.
1638 */
1639 cli_gen_usage_msg();
1640 if (dynamic_usage_msg)
1641 appctx->ctx.cli.msg = dynamic_usage_msg;
1642 else
1643 appctx->ctx.cli.msg = stats_sock_usage_msg;
1644 appctx->st0 = STAT_CLI_PRINT;
1645 }
1646
1647 /* re-adjust req buffer */
1648 bo_skip(si_oc(si), reql);
1649 req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
1650 }
1651 else { /* output functions */
1652 switch (appctx->st0) {
1653 case STAT_CLI_PROMPT:
1654 break;
1655 case STAT_CLI_PRINT:
1656 if (bi_putstr(si_ic(si), appctx->ctx.cli.msg) != -1)
1657 appctx->st0 = STAT_CLI_PROMPT;
1658 else
1659 si_applet_cant_put(si);
1660 break;
1661 case STAT_CLI_PRINT_FREE:
1662 if (bi_putstr(si_ic(si), appctx->ctx.cli.err) != -1) {
1663 free(appctx->ctx.cli.err);
1664 appctx->st0 = STAT_CLI_PROMPT;
1665 }
1666 else
1667 si_applet_cant_put(si);
1668 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01001669 case STAT_CLI_O_ERR: /* errors dump */
1670 if (stats_dump_errors_to_buffer(si))
1671 appctx->st0 = STAT_CLI_PROMPT;
1672 break;
1673 case STAT_CLI_O_TAB:
1674 case STAT_CLI_O_CLR:
1675 if (stats_table_request(si, appctx->st0))
1676 appctx->st0 = STAT_CLI_PROMPT;
1677 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01001678 case STAT_CLI_O_ENV: /* environment dump */
1679 if (stats_dump_env_to_buffer(si))
1680 appctx->st0 = STAT_CLI_PROMPT;
1681 break;
1682 case STAT_CLI_O_CUSTOM: /* use custom pointer */
1683 if (appctx->io_handler)
1684 if (appctx->io_handler(appctx)) {
1685 appctx->st0 = STAT_CLI_PROMPT;
1686 if (appctx->io_release) {
1687 appctx->io_release(appctx);
1688 appctx->io_release = NULL;
1689 }
1690 }
1691 break;
1692 default: /* abnormal state */
1693 si->flags |= SI_FL_ERR;
1694 break;
1695 }
1696
1697 /* The post-command prompt is either LF alone or LF + '> ' in interactive mode */
1698 if (appctx->st0 == STAT_CLI_PROMPT) {
1699 if (bi_putstr(si_ic(si), appctx->st1 ? "\n> " : "\n") != -1)
1700 appctx->st0 = STAT_CLI_GETREQ;
1701 else
1702 si_applet_cant_put(si);
1703 }
1704
1705 /* If the output functions are still there, it means they require more room. */
1706 if (appctx->st0 >= STAT_CLI_OUTPUT)
1707 break;
1708
1709 /* Now we close the output if one of the writers did so,
1710 * or if we're not in interactive mode and the request
1711 * buffer is empty. This still allows pipelined requests
1712 * to be sent in non-interactive mode.
1713 */
1714 if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!appctx->st1 && !req->buf->o)) {
1715 appctx->st0 = STAT_CLI_END;
1716 continue;
1717 }
1718
1719 /* switch state back to GETREQ to read next requests */
1720 appctx->st0 = STAT_CLI_GETREQ;
1721 }
1722 }
1723
1724 if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST)) {
1725 DPRINTF(stderr, "%s@%d: si to buf closed. req=%08x, res=%08x, st=%d\n",
1726 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
1727 /* Other side has closed, let's abort if we have no more processing to do
1728 * and nothing more to consume. This is comparable to a broken pipe, so
1729 * we forward the close to the request side so that it flows upstream to
1730 * the client.
1731 */
1732 si_shutw(si);
1733 }
1734
1735 if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && (appctx->st0 < STAT_CLI_OUTPUT)) {
1736 DPRINTF(stderr, "%s@%d: buf to si closed. req=%08x, res=%08x, st=%d\n",
1737 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
1738 /* We have no more processing to do, and nothing more to send, and
1739 * the client side has closed. So we'll forward this state downstream
1740 * on the response buffer.
1741 */
1742 si_shutr(si);
1743 res->flags |= CF_READ_NULL;
1744 }
1745
1746 out:
1747 DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rqh=%d, rqs=%d, rh=%d, rs=%d\n",
1748 __FUNCTION__, __LINE__,
1749 si->state, req->flags, res->flags, req->buf->i, req->buf->o, res->buf->i, res->buf->o);
1750}
1751
William Lallemand74c24fb2016-11-21 17:18:36 +01001752/* This is called when the stream interface is closed. For instance, upon an
1753 * external abort, we won't call the i/o handler anymore so we may need to
1754 * remove back references to the stream currently being dumped.
1755 */
1756static void cli_release_handler(struct appctx *appctx)
1757{
1758 if (appctx->io_release) {
1759 appctx->io_release(appctx);
1760 appctx->io_release = NULL;
1761 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001762 else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) &&
1763 appctx->st2 == STAT_ST_LIST) {
1764 appctx->ctx.table.entry->ref_cnt--;
1765 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
1766 }
1767 else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
1768 free(appctx->ctx.cli.err);
1769 appctx->ctx.cli.err = NULL;
1770 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001771}
1772
1773/* This function is used to either dump tables states (when action is set
1774 * to STAT_CLI_O_TAB) or clear tables (when action is STAT_CLI_O_CLR).
1775 * It returns 0 if the output buffer is full and it needs to be called
1776 * again, otherwise non-zero.
1777 */
1778static int stats_table_request(struct stream_interface *si, int action)
1779{
1780 struct appctx *appctx = __objt_appctx(si->end);
1781 struct stream *s = si_strm(si);
1782 struct ebmb_node *eb;
1783 int dt;
1784 int skip_entry;
1785 int show = action == STAT_CLI_O_TAB;
1786
1787 /*
1788 * We have 3 possible states in appctx->st2 :
1789 * - STAT_ST_INIT : the first call
1790 * - STAT_ST_INFO : the proxy pointer points to the next table to
1791 * dump, the entry pointer is NULL ;
1792 * - STAT_ST_LIST : the proxy pointer points to the current table
1793 * and the entry pointer points to the next entry to be dumped,
1794 * and the refcount on the next entry is held ;
1795 * - STAT_ST_END : nothing left to dump, the buffer may contain some
1796 * data though.
1797 */
1798
1799 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
1800 /* in case of abort, remove any refcount we might have set on an entry */
1801 if (appctx->st2 == STAT_ST_LIST) {
1802 appctx->ctx.table.entry->ref_cnt--;
1803 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
1804 }
1805 return 1;
1806 }
1807
1808 chunk_reset(&trash);
1809
1810 while (appctx->st2 != STAT_ST_FIN) {
1811 switch (appctx->st2) {
1812 case STAT_ST_INIT:
1813 appctx->ctx.table.proxy = appctx->ctx.table.target;
1814 if (!appctx->ctx.table.proxy)
1815 appctx->ctx.table.proxy = proxy;
1816
1817 appctx->ctx.table.entry = NULL;
1818 appctx->st2 = STAT_ST_INFO;
1819 break;
1820
1821 case STAT_ST_INFO:
1822 if (!appctx->ctx.table.proxy ||
1823 (appctx->ctx.table.target &&
1824 appctx->ctx.table.proxy != appctx->ctx.table.target)) {
1825 appctx->st2 = STAT_ST_END;
1826 break;
1827 }
1828
1829 if (appctx->ctx.table.proxy->table.size) {
1830 if (show && !stats_dump_table_head_to_buffer(&trash, si, appctx->ctx.table.proxy,
1831 appctx->ctx.table.target))
1832 return 0;
1833
1834 if (appctx->ctx.table.target &&
1835 strm_li(s)->bind_conf->level >= ACCESS_LVL_OPER) {
1836 /* dump entries only if table explicitly requested */
1837 eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
1838 if (eb) {
1839 appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
1840 appctx->ctx.table.entry->ref_cnt++;
1841 appctx->st2 = STAT_ST_LIST;
1842 break;
1843 }
1844 }
1845 }
1846 appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
1847 break;
1848
1849 case STAT_ST_LIST:
1850 skip_entry = 0;
1851
1852 if (appctx->ctx.table.data_type >= 0) {
1853 /* we're filtering on some data contents */
1854 void *ptr;
1855 long long data;
1856
1857 dt = appctx->ctx.table.data_type;
1858 ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
1859 appctx->ctx.table.entry,
1860 dt);
1861
1862 data = 0;
1863 switch (stktable_data_types[dt].std_type) {
1864 case STD_T_SINT:
1865 data = stktable_data_cast(ptr, std_t_sint);
1866 break;
1867 case STD_T_UINT:
1868 data = stktable_data_cast(ptr, std_t_uint);
1869 break;
1870 case STD_T_ULL:
1871 data = stktable_data_cast(ptr, std_t_ull);
1872 break;
1873 case STD_T_FRQP:
1874 data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
1875 appctx->ctx.table.proxy->table.data_arg[dt].u);
1876 break;
1877 }
1878
1879 /* skip the entry if the data does not match the test and the value */
1880 if ((data < appctx->ctx.table.value &&
1881 (appctx->ctx.table.data_op == STD_OP_EQ ||
1882 appctx->ctx.table.data_op == STD_OP_GT ||
1883 appctx->ctx.table.data_op == STD_OP_GE)) ||
1884 (data == appctx->ctx.table.value &&
1885 (appctx->ctx.table.data_op == STD_OP_NE ||
1886 appctx->ctx.table.data_op == STD_OP_GT ||
1887 appctx->ctx.table.data_op == STD_OP_LT)) ||
1888 (data > appctx->ctx.table.value &&
1889 (appctx->ctx.table.data_op == STD_OP_EQ ||
1890 appctx->ctx.table.data_op == STD_OP_LT ||
1891 appctx->ctx.table.data_op == STD_OP_LE)))
1892 skip_entry = 1;
1893 }
1894
1895 if (show && !skip_entry &&
1896 !stats_dump_table_entry_to_buffer(&trash, si, appctx->ctx.table.proxy,
1897 appctx->ctx.table.entry))
1898 return 0;
1899
1900 appctx->ctx.table.entry->ref_cnt--;
1901
1902 eb = ebmb_next(&appctx->ctx.table.entry->key);
1903 if (eb) {
1904 struct stksess *old = appctx->ctx.table.entry;
1905 appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
1906 if (show)
1907 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
1908 else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
1909 stksess_kill(&appctx->ctx.table.proxy->table, old);
1910 appctx->ctx.table.entry->ref_cnt++;
1911 break;
1912 }
1913
1914
1915 if (show)
1916 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
1917 else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
1918 stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
1919
1920 appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
1921 appctx->st2 = STAT_ST_INFO;
1922 break;
1923
1924 case STAT_ST_END:
1925 appctx->st2 = STAT_ST_FIN;
1926 break;
1927 }
1928 }
1929 return 1;
1930}
1931
William Lallemand74c24fb2016-11-21 17:18:36 +01001932/* This function dumps all captured errors onto the stream interface's
1933 * read buffer. It returns 0 if the output buffer is full and it needs
1934 * to be called again, otherwise non-zero.
1935 */
1936static int stats_dump_errors_to_buffer(struct stream_interface *si)
1937{
1938 struct appctx *appctx = __objt_appctx(si->end);
1939 extern const char *monthname[12];
1940
1941 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1942 return 1;
1943
1944 chunk_reset(&trash);
1945
1946 if (!appctx->ctx.errors.px) {
1947 /* the function had not been called yet, let's prepare the
1948 * buffer for a response.
1949 */
1950 struct tm tm;
1951
1952 get_localtime(date.tv_sec, &tm);
1953 chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
1954 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
1955 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
1956 error_snapshot_id);
1957
1958 if (bi_putchk(si_ic(si), &trash) == -1) {
1959 /* Socket buffer full. Let's try again later from the same point */
1960 si_applet_cant_put(si);
1961 return 0;
1962 }
1963
1964 appctx->ctx.errors.px = proxy;
1965 appctx->ctx.errors.buf = 0;
1966 appctx->ctx.errors.bol = 0;
1967 appctx->ctx.errors.ptr = -1;
1968 }
1969
1970 /* we have two inner loops here, one for the proxy, the other one for
1971 * the buffer.
1972 */
1973 while (appctx->ctx.errors.px) {
1974 struct error_snapshot *es;
1975
1976 if (appctx->ctx.errors.buf == 0)
1977 es = &appctx->ctx.errors.px->invalid_req;
1978 else
1979 es = &appctx->ctx.errors.px->invalid_rep;
1980
1981 if (!es->when.tv_sec)
1982 goto next;
1983
1984 if (appctx->ctx.errors.iid >= 0 &&
1985 appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
1986 es->oe->uuid != appctx->ctx.errors.iid)
1987 goto next;
1988
1989 if (appctx->ctx.errors.ptr < 0) {
1990 /* just print headers now */
1991
1992 char pn[INET6_ADDRSTRLEN];
1993 struct tm tm;
1994 int port;
1995
1996 get_localtime(es->when.tv_sec, &tm);
1997 chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
1998 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
1999 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
2000
2001 switch (addr_to_str(&es->src, pn, sizeof(pn))) {
2002 case AF_INET:
2003 case AF_INET6:
2004 port = get_host_port(&es->src);
2005 break;
2006 default:
2007 port = 0;
2008 }
2009
2010 switch (appctx->ctx.errors.buf) {
2011 case 0:
2012 chunk_appendf(&trash,
2013 " frontend %s (#%d): invalid request\n"
2014 " backend %s (#%d)",
2015 appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
2016 (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
2017 (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
2018 break;
2019 case 1:
2020 chunk_appendf(&trash,
2021 " backend %s (#%d): invalid response\n"
2022 " frontend %s (#%d)",
2023 appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
2024 es->oe->id, es->oe->uuid);
2025 break;
2026 }
2027
2028 chunk_appendf(&trash,
2029 ", server %s (#%d), event #%u\n"
2030 " src %s:%d, session #%d, session flags 0x%08x\n"
2031 " HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
2032 " HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
2033 " buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
2034 " pending %d bytes, wrapping at %d, error at position %d:\n \n",
2035 es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
2036 es->ev_id,
2037 pn, port, es->sid, es->s_flags,
2038 es->state, es->m_flags, es->t_flags,
2039 es->m_clen, es->m_blen,
2040 es->b_flags, es->b_out, es->b_tot,
2041 es->len, es->b_wrap, es->pos);
2042
2043 if (bi_putchk(si_ic(si), &trash) == -1) {
2044 /* Socket buffer full. Let's try again later from the same point */
2045 si_applet_cant_put(si);
2046 return 0;
2047 }
2048 appctx->ctx.errors.ptr = 0;
2049 appctx->ctx.errors.sid = es->sid;
2050 }
2051
2052 if (appctx->ctx.errors.sid != es->sid) {
2053 /* the snapshot changed while we were dumping it */
2054 chunk_appendf(&trash,
2055 " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
2056 if (bi_putchk(si_ic(si), &trash) == -1) {
2057 si_applet_cant_put(si);
2058 return 0;
2059 }
2060 goto next;
2061 }
2062
2063 /* OK, ptr >= 0, so we have to dump the current line */
2064 while (es->buf && appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < global.tune.bufsize) {
2065 int newptr;
2066 int newline;
2067
2068 newline = appctx->ctx.errors.bol;
2069 newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->len, &newline, appctx->ctx.errors.ptr);
2070 if (newptr == appctx->ctx.errors.ptr)
2071 return 0;
2072
2073 if (bi_putchk(si_ic(si), &trash) == -1) {
2074 /* Socket buffer full. Let's try again later from the same point */
2075 si_applet_cant_put(si);
2076 return 0;
2077 }
2078 appctx->ctx.errors.ptr = newptr;
2079 appctx->ctx.errors.bol = newline;
2080 };
2081 next:
2082 appctx->ctx.errors.bol = 0;
2083 appctx->ctx.errors.ptr = -1;
2084 appctx->ctx.errors.buf++;
2085 if (appctx->ctx.errors.buf > 1) {
2086 appctx->ctx.errors.buf = 0;
2087 appctx->ctx.errors.px = appctx->ctx.errors.px->next;
2088 }
2089 }
2090
2091 /* dump complete */
2092 return 1;
2093}
2094
2095/* This function dumps all environmnent variables to the buffer. It returns 0
2096 * if the output buffer is full and it needs to be called again, otherwise
2097 * non-zero. Dumps only one entry if st2 == STAT_ST_END.
2098 */
2099static int stats_dump_env_to_buffer(struct stream_interface *si)
2100{
2101 struct appctx *appctx = __objt_appctx(si->end);
2102
2103 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
2104 return 1;
2105
2106 chunk_reset(&trash);
2107
2108 /* we have two inner loops here, one for the proxy, the other one for
2109 * the buffer.
2110 */
2111 while (*appctx->ctx.env.var) {
2112 chunk_printf(&trash, "%s\n", *appctx->ctx.env.var);
2113
2114 if (bi_putchk(si_ic(si), &trash) == -1) {
2115 si_applet_cant_put(si);
2116 return 0;
2117 }
2118 if (appctx->st2 == STAT_ST_END)
2119 break;
2120 appctx->ctx.env.var++;
2121 }
2122
2123 /* dump complete */
2124 return 1;
2125}
2126
2127/* parse the "level" argument on the bind lines */
2128static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
2129{
2130 if (!*args[cur_arg + 1]) {
2131 memprintf(err, "'%s' : missing level", args[cur_arg]);
2132 return ERR_ALERT | ERR_FATAL;
2133 }
2134
2135 if (!strcmp(args[cur_arg+1], "user"))
2136 conf->level = ACCESS_LVL_USER;
2137 else if (!strcmp(args[cur_arg+1], "operator"))
2138 conf->level = ACCESS_LVL_OPER;
2139 else if (!strcmp(args[cur_arg+1], "admin"))
2140 conf->level = ACCESS_LVL_ADMIN;
2141 else {
2142 memprintf(err, "'%s' only supports 'user', 'operator', and 'admin' (got '%s')",
2143 args[cur_arg], args[cur_arg+1]);
2144 return ERR_ALERT | ERR_FATAL;
2145 }
2146
2147 return 0;
2148}
2149
2150static struct applet cli_applet = {
2151 .obj_type = OBJ_TYPE_APPLET,
2152 .name = "<CLI>", /* used for logging */
2153 .fct = cli_io_handler,
2154 .release = cli_release_handler,
2155};
2156
2157static struct cfg_kw_list cfg_kws = {ILH, {
2158 { CFG_GLOBAL, "stats", stats_parse_global },
2159 { 0, NULL, NULL },
2160}};
2161
2162static struct bind_kw_list bind_kws = { "STAT", { }, {
2163 { "level", bind_parse_level, 1 }, /* set the unix socket admin level */
2164 { NULL, NULL, 0 },
2165}};
2166
2167__attribute__((constructor))
2168static void __dumpstats_module_init(void)
2169{
2170 cfg_register_keywords(&cfg_kws);
2171 bind_register_keywords(&bind_kws);
2172}
2173
2174/*
2175 * Local variables:
2176 * c-indent-level: 8
2177 * c-basic-offset: 8
2178 * End:
2179 */