blob: 3662c50a3f1fa136c7127efcc10dc3927183dc06 [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
70#ifdef USE_OPENSSL
71#include <proto/ssl_sock.h>
72#include <types/ssl_sock.h>
73#endif
74
75/* These are the field names for each INF_* field position. Please pay attention
76 * to always use the exact same name except that the strings for new names must
77 * be lower case or CamelCase while the enum entries must be upper case.
78 */
79const char *info_field_names[INF_TOTAL_FIELDS] = {
80 [INF_NAME] = "Name",
81 [INF_VERSION] = "Version",
82 [INF_RELEASE_DATE] = "Release_date",
83 [INF_NBPROC] = "Nbproc",
84 [INF_PROCESS_NUM] = "Process_num",
85 [INF_PID] = "Pid",
86 [INF_UPTIME] = "Uptime",
87 [INF_UPTIME_SEC] = "Uptime_sec",
88 [INF_MEMMAX_MB] = "Memmax_MB",
89 [INF_POOL_ALLOC_MB] = "PoolAlloc_MB",
90 [INF_POOL_USED_MB] = "PoolUsed_MB",
91 [INF_POOL_FAILED] = "PoolFailed",
92 [INF_ULIMIT_N] = "Ulimit-n",
93 [INF_MAXSOCK] = "Maxsock",
94 [INF_MAXCONN] = "Maxconn",
95 [INF_HARD_MAXCONN] = "Hard_maxconn",
96 [INF_CURR_CONN] = "CurrConns",
97 [INF_CUM_CONN] = "CumConns",
98 [INF_CUM_REQ] = "CumReq",
99 [INF_MAX_SSL_CONNS] = "MaxSslConns",
100 [INF_CURR_SSL_CONNS] = "CurrSslConns",
101 [INF_CUM_SSL_CONNS] = "CumSslConns",
102 [INF_MAXPIPES] = "Maxpipes",
103 [INF_PIPES_USED] = "PipesUsed",
104 [INF_PIPES_FREE] = "PipesFree",
105 [INF_CONN_RATE] = "ConnRate",
106 [INF_CONN_RATE_LIMIT] = "ConnRateLimit",
107 [INF_MAX_CONN_RATE] = "MaxConnRate",
108 [INF_SESS_RATE] = "SessRate",
109 [INF_SESS_RATE_LIMIT] = "SessRateLimit",
110 [INF_MAX_SESS_RATE] = "MaxSessRate",
111 [INF_SSL_RATE] = "SslRate",
112 [INF_SSL_RATE_LIMIT] = "SslRateLimit",
113 [INF_MAX_SSL_RATE] = "MaxSslRate",
114 [INF_SSL_FRONTEND_KEY_RATE] = "SslFrontendKeyRate",
115 [INF_SSL_FRONTEND_MAX_KEY_RATE] = "SslFrontendMaxKeyRate",
116 [INF_SSL_FRONTEND_SESSION_REUSE_PCT] = "SslFrontendSessionReuse_pct",
117 [INF_SSL_BACKEND_KEY_RATE] = "SslBackendKeyRate",
118 [INF_SSL_BACKEND_MAX_KEY_RATE] = "SslBackendMaxKeyRate",
119 [INF_SSL_CACHE_LOOKUPS] = "SslCacheLookups",
120 [INF_SSL_CACHE_MISSES] = "SslCacheMisses",
121 [INF_COMPRESS_BPS_IN] = "CompressBpsIn",
122 [INF_COMPRESS_BPS_OUT] = "CompressBpsOut",
123 [INF_COMPRESS_BPS_RATE_LIM] = "CompressBpsRateLim",
124 [INF_ZLIB_MEM_USAGE] = "ZlibMemUsage",
125 [INF_MAX_ZLIB_MEM_USAGE] = "MaxZlibMemUsage",
126 [INF_TASKS] = "Tasks",
127 [INF_RUN_QUEUE] = "Run_queue",
128 [INF_IDLE_PCT] = "Idle_pct",
129 [INF_NODE] = "node",
130 [INF_DESCRIPTION] = "description",
131};
132
133/* one line of stats */
134static struct field info[INF_TOTAL_FIELDS];
135
William Lallemand74c24fb2016-11-21 17:18:36 +0100136static int stats_dump_env_to_buffer(struct stream_interface *si);
137static int stats_dump_info_to_buffer(struct stream_interface *si);
William Lallemand74c24fb2016-11-21 17:18:36 +0100138static int stats_dump_errors_to_buffer(struct stream_interface *si);
139static int stats_table_request(struct stream_interface *si, int show);
William Lallemand74c24fb2016-11-21 17:18:36 +0100140
William Lallemand74c24fb2016-11-21 17:18:36 +0100141
142static struct applet cli_applet;
143
144static const char stats_sock_usage_msg[] =
145 "Unknown command. Please enter one of the following commands only :\n"
146 " clear counters : clear max statistics counters (add 'all' for all counters)\n"
147 " clear table : remove an entry from a table\n"
148 " help : this message\n"
149 " prompt : toggle interactive mode with prompt\n"
150 " quit : disconnect\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100151 " show env [var] : dump environment variables known to the process\n"
152 " show info : report information about the running process\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100153 " show stat : report counters for each proxy and server\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100154 " show errors : report last request and response errors for each proxy\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100155 " show table [id]: report table usage stats or dump this table's contents\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100156 " set table [id] : update or create a table entry's data\n"
157 " set timeout : change a timeout setting\n"
158 " set maxconn : change a maxconn setting\n"
159 " set rate-limit : change a rate limiting value\n"
160 " disable : put a server or frontend in maintenance mode\n"
161 " enable : re-enable a server or frontend which is in maintenance mode\n"
162 " shutdown : kill a session or a frontend (eg:to release listening ports)\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100163 "";
164
165static const char stats_permission_denied_msg[] =
166 "Permission denied\n"
167 "";
168
169
170static char *dynamic_usage_msg = NULL;
171
172/* List head of cli keywords */
173static struct cli_kw_list cli_keywords = {
174 .list = LIST_HEAD_INIT(cli_keywords.list)
175};
176
177extern const char *stat_status_codes[];
178
179char *cli_gen_usage_msg()
180{
181 struct cli_kw_list *kw_list;
182 struct cli_kw *kw;
183 struct chunk *tmp = get_trash_chunk();
184 struct chunk out;
185
186 free(dynamic_usage_msg);
187 dynamic_usage_msg = NULL;
188
189 if (LIST_ISEMPTY(&cli_keywords.list))
190 return NULL;
191
192 chunk_reset(tmp);
193 chunk_strcat(tmp, stats_sock_usage_msg);
194 list_for_each_entry(kw_list, &cli_keywords.list, list) {
195 kw = &kw_list->kw[0];
196 while (kw->usage) {
197 chunk_appendf(tmp, " %s\n", kw->usage);
198 kw++;
199 }
200 }
201 chunk_init(&out, NULL, 0);
202 chunk_dup(&out, tmp);
203 dynamic_usage_msg = out.str;
204 return dynamic_usage_msg;
205}
206
207struct cli_kw* cli_find_kw(char **args)
208{
209 struct cli_kw_list *kw_list;
210 struct cli_kw *kw;/* current cli_kw */
211 char **tmp_args;
212 const char **tmp_str_kw;
213 int found = 0;
214
215 if (LIST_ISEMPTY(&cli_keywords.list))
216 return NULL;
217
218 list_for_each_entry(kw_list, &cli_keywords.list, list) {
219 kw = &kw_list->kw[0];
220 while (*kw->str_kw) {
221 tmp_args = args;
222 tmp_str_kw = kw->str_kw;
223 while (*tmp_str_kw) {
224 if (strcmp(*tmp_str_kw, *tmp_args) == 0) {
225 found = 1;
226 } else {
227 found = 0;
228 break;
229 }
230 tmp_args++;
231 tmp_str_kw++;
232 }
233 if (found)
234 return (kw);
235 kw++;
236 }
237 }
238 return NULL;
239}
240
241void cli_register_kw(struct cli_kw_list *kw_list)
242{
243 LIST_ADDQ(&cli_keywords.list, &kw_list->list);
244}
245
246
247/* allocate a new stats frontend named <name>, and return it
248 * (or NULL in case of lack of memory).
249 */
250static struct proxy *alloc_stats_fe(const char *name, const char *file, int line)
251{
252 struct proxy *fe;
253
254 fe = calloc(1, sizeof(*fe));
255 if (!fe)
256 return NULL;
257
258 init_new_proxy(fe);
259 fe->next = proxy;
260 proxy = fe;
261 fe->last_change = now.tv_sec;
262 fe->id = strdup("GLOBAL");
263 fe->cap = PR_CAP_FE;
264 fe->maxconn = 10; /* default to 10 concurrent connections */
265 fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
266 fe->conf.file = strdup(file);
267 fe->conf.line = line;
268 fe->accept = frontend_accept;
269 fe->default_target = &cli_applet.obj_type;
270
271 /* the stats frontend is the only one able to assign ID #0 */
272 fe->conf.id.key = fe->uuid = 0;
273 eb32_insert(&used_proxy_id, &fe->conf.id);
274 return fe;
275}
276
277/* This function parses a "stats" statement in the "global" section. It returns
278 * -1 if there is any error, otherwise zero. If it returns -1, it will write an
279 * error message into the <err> buffer which will be preallocated. The trailing
280 * '\n' must not be written. The function must be called with <args> pointing to
281 * the first word after "stats".
282 */
283static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
284 struct proxy *defpx, const char *file, int line,
285 char **err)
286{
287 struct bind_conf *bind_conf;
288 struct listener *l;
289
290 if (!strcmp(args[1], "socket")) {
291 int cur_arg;
292
293 if (*args[2] == 0) {
294 memprintf(err, "'%s %s' in global section expects an address or a path to a UNIX socket", args[0], args[1]);
295 return -1;
296 }
297
298 if (!global.stats_fe) {
299 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
300 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
301 return -1;
302 }
303 }
304
305 bind_conf = bind_conf_alloc(&global.stats_fe->conf.bind, file, line, args[2]);
306 bind_conf->level = ACCESS_LVL_OPER; /* default access level */
307
308 if (!str2listener(args[2], global.stats_fe, bind_conf, file, line, err)) {
309 memprintf(err, "parsing [%s:%d] : '%s %s' : %s\n",
310 file, line, args[0], args[1], err && *err ? *err : "error");
311 return -1;
312 }
313
314 cur_arg = 3;
315 while (*args[cur_arg]) {
316 static int bind_dumped;
317 struct bind_kw *kw;
318
319 kw = bind_find_kw(args[cur_arg]);
320 if (kw) {
321 if (!kw->parse) {
322 memprintf(err, "'%s %s' : '%s' option is not implemented in this version (check build options).",
323 args[0], args[1], args[cur_arg]);
324 return -1;
325 }
326
327 if (kw->parse(args, cur_arg, global.stats_fe, bind_conf, err) != 0) {
328 if (err && *err)
329 memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
330 else
331 memprintf(err, "'%s %s' : error encountered while processing '%s'",
332 args[0], args[1], args[cur_arg]);
333 return -1;
334 }
335
336 cur_arg += 1 + kw->skip;
337 continue;
338 }
339
340 if (!bind_dumped) {
341 bind_dump_kws(err);
342 indent_msg(err, 4);
343 bind_dumped = 1;
344 }
345
346 memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s",
347 args[0], args[1], args[cur_arg],
348 err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
349 return -1;
350 }
351
352 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
353 l->maxconn = global.stats_fe->maxconn;
354 l->backlog = global.stats_fe->backlog;
355 l->accept = session_accept_fd;
356 l->handler = process_stream;
357 l->default_target = global.stats_fe->default_target;
358 l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
359 l->nice = -64; /* we want to boost priority for local stats */
360 global.maxsock += l->maxconn;
361 }
362 }
363 else if (!strcmp(args[1], "timeout")) {
364 unsigned timeout;
365 const char *res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
366
367 if (res) {
368 memprintf(err, "'%s %s' : unexpected character '%c'", args[0], args[1], *res);
369 return -1;
370 }
371
372 if (!timeout) {
373 memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
374 return -1;
375 }
376 if (!global.stats_fe) {
377 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
378 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
379 return -1;
380 }
381 }
382 global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
383 }
384 else if (!strcmp(args[1], "maxconn")) {
385 int maxconn = atol(args[2]);
386
387 if (maxconn <= 0) {
388 memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
389 return -1;
390 }
391
392 if (!global.stats_fe) {
393 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
394 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
395 return -1;
396 }
397 }
398 global.stats_fe->maxconn = maxconn;
399 }
400 else if (!strcmp(args[1], "bind-process")) { /* enable the socket only on some processes */
401 int cur_arg = 2;
402 unsigned long set = 0;
403
404 if (!global.stats_fe) {
405 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
406 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
407 return -1;
408 }
409 }
410
411 while (*args[cur_arg]) {
412 unsigned int low, high;
413
414 if (strcmp(args[cur_arg], "all") == 0) {
415 set = 0;
416 break;
417 }
418 else if (strcmp(args[cur_arg], "odd") == 0) {
419 set |= ~0UL/3UL; /* 0x555....555 */
420 }
421 else if (strcmp(args[cur_arg], "even") == 0) {
422 set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
423 }
424 else if (isdigit((int)*args[cur_arg])) {
425 char *dash = strchr(args[cur_arg], '-');
426
427 low = high = str2uic(args[cur_arg]);
428 if (dash)
429 high = str2uic(dash + 1);
430
431 if (high < low) {
432 unsigned int swap = low;
433 low = high;
434 high = swap;
435 }
436
437 if (low < 1 || high > LONGBITS) {
438 memprintf(err, "'%s %s' supports process numbers from 1 to %d.\n",
439 args[0], args[1], LONGBITS);
440 return -1;
441 }
442 while (low <= high)
443 set |= 1UL << (low++ - 1);
444 }
445 else {
446 memprintf(err,
447 "'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
448 args[0], args[1], LONGBITS);
449 return -1;
450 }
451 cur_arg++;
452 }
453 global.stats_fe->bind_proc = set;
454 }
455 else {
456 memprintf(err, "'%s' only supports 'socket', 'maxconn', 'bind-process' and 'timeout' (got '%s')", args[0], args[1]);
457 return -1;
458 }
459 return 0;
460}
461
Willy Tarreaude57a572016-11-23 17:01:39 +0100462/* Verifies that the CLI at least has a level at least as high as <level>
463 * (typically ACCESS_LVL_ADMIN). Returns 1 if OK, otherwise 0. In case of
464 * failure, an error message is prepared and the appctx's state is adjusted
465 * to print it so that a return 1 is enough to abort any processing.
466 */
467int cli_has_level(struct appctx *appctx, int level)
468{
469 struct stream_interface *si = appctx->owner;
470 struct stream *s = si_strm(si);
471
472 if (strm_li(s)->bind_conf->level < level) {
473 appctx->ctx.cli.msg = stats_permission_denied_msg;
474 appctx->st0 = STAT_CLI_PRINT;
475 return 0;
476 }
477 return 1;
478}
479
William Lallemand74c24fb2016-11-21 17:18:36 +0100480
481/* print a string of text buffer to <out>. The format is :
482 * Non-printable chars \t, \n, \r and \e are * encoded in C format.
483 * Other non-printable chars are encoded "\xHH". Space, '\', and '=' are also escaped.
484 * Print stopped if null char or <bsize> is reached, or if no more place in the chunk.
485 */
486static int dump_text(struct chunk *out, const char *buf, int bsize)
487{
488 unsigned char c;
489 int ptr = 0;
490
491 while (buf[ptr] && ptr < bsize) {
492 c = buf[ptr];
493 if (isprint(c) && isascii(c) && c != '\\' && c != ' ' && c != '=') {
494 if (out->len > out->size - 1)
495 break;
496 out->str[out->len++] = c;
497 }
498 else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\' || c == ' ' || c == '=') {
499 if (out->len > out->size - 2)
500 break;
501 out->str[out->len++] = '\\';
502 switch (c) {
503 case ' ': c = ' '; break;
504 case '\t': c = 't'; break;
505 case '\n': c = 'n'; break;
506 case '\r': c = 'r'; break;
507 case '\e': c = 'e'; break;
508 case '\\': c = '\\'; break;
509 case '=': c = '='; break;
510 }
511 out->str[out->len++] = c;
512 }
513 else {
514 if (out->len > out->size - 4)
515 break;
516 out->str[out->len++] = '\\';
517 out->str[out->len++] = 'x';
518 out->str[out->len++] = hextab[(c >> 4) & 0xF];
519 out->str[out->len++] = hextab[c & 0xF];
520 }
521 ptr++;
522 }
523
524 return ptr;
525}
526
527/* print a buffer in hexa.
528 * Print stopped if <bsize> is reached, or if no more place in the chunk.
529 */
530static int dump_binary(struct chunk *out, const char *buf, int bsize)
531{
532 unsigned char c;
533 int ptr = 0;
534
535 while (ptr < bsize) {
536 c = buf[ptr];
537
538 if (out->len > out->size - 2)
539 break;
540 out->str[out->len++] = hextab[(c >> 4) & 0xF];
541 out->str[out->len++] = hextab[c & 0xF];
542
543 ptr++;
544 }
545 return ptr;
546}
547
548/* Dump the status of a table to a stream interface's
549 * read buffer. It returns 0 if the output buffer is full
550 * and needs to be called again, otherwise non-zero.
551 */
552static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
553 struct proxy *proxy, struct proxy *target)
554{
555 struct stream *s = si_strm(si);
556
557 chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
558 proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
559
560 /* any other information should be dumped here */
561
562 if (target && strm_li(s)->bind_conf->level < ACCESS_LVL_OPER)
563 chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
564
565 if (bi_putchk(si_ic(si), msg) == -1) {
566 si_applet_cant_put(si);
567 return 0;
568 }
569
570 return 1;
571}
572
573/* Dump the a table entry to a stream interface's
574 * read buffer. It returns 0 if the output buffer is full
575 * and needs to be called again, otherwise non-zero.
576 */
577static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
578 struct proxy *proxy, struct stksess *entry)
579{
580 int dt;
581
582 chunk_appendf(msg, "%p:", entry);
583
584 if (proxy->table.type == SMP_T_IPV4) {
585 char addr[INET_ADDRSTRLEN];
586 inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
587 chunk_appendf(msg, " key=%s", addr);
588 }
589 else if (proxy->table.type == SMP_T_IPV6) {
590 char addr[INET6_ADDRSTRLEN];
591 inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
592 chunk_appendf(msg, " key=%s", addr);
593 }
594 else if (proxy->table.type == SMP_T_SINT) {
595 chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
596 }
597 else if (proxy->table.type == SMP_T_STR) {
598 chunk_appendf(msg, " key=");
599 dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
600 }
601 else {
602 chunk_appendf(msg, " key=");
603 dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
604 }
605
606 chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
607
608 for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
609 void *ptr;
610
611 if (proxy->table.data_ofs[dt] == 0)
612 continue;
613 if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
614 chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
615 else
616 chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
617
618 ptr = stktable_data_ptr(&proxy->table, entry, dt);
619 switch (stktable_data_types[dt].std_type) {
620 case STD_T_SINT:
621 chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
622 break;
623 case STD_T_UINT:
624 chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
625 break;
626 case STD_T_ULL:
627 chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
628 break;
629 case STD_T_FRQP:
630 chunk_appendf(msg, "%d",
631 read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
632 proxy->table.data_arg[dt].u));
633 break;
634 }
635 }
636 chunk_appendf(msg, "\n");
637
638 if (bi_putchk(si_ic(si), msg) == -1) {
639 si_applet_cant_put(si);
640 return 0;
641 }
642
643 return 1;
644}
645
646static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
647{
648 struct stream *s = si_strm(si);
649 struct appctx *appctx = __objt_appctx(si->end);
650 struct proxy *px = appctx->ctx.table.target;
651 struct stksess *ts;
652 uint32_t uint32_key;
653 unsigned char ip6_key[sizeof(struct in6_addr)];
654 long long value;
655 int data_type;
656 int cur_arg;
657 void *ptr;
658 struct freq_ctr_period *frqp;
659
660 appctx->st0 = STAT_CLI_OUTPUT;
661
662 if (!*args[4]) {
663 appctx->ctx.cli.msg = "Key value expected\n";
664 appctx->st0 = STAT_CLI_PRINT;
665 return;
666 }
667
668 switch (px->table.type) {
669 case SMP_T_IPV4:
670 uint32_key = htonl(inetaddr_host(args[4]));
671 static_table_key->key = &uint32_key;
672 break;
673 case SMP_T_IPV6:
674 inet_pton(AF_INET6, args[4], ip6_key);
675 static_table_key->key = &ip6_key;
676 break;
677 case SMP_T_SINT:
678 {
679 char *endptr;
680 unsigned long val;
681 errno = 0;
682 val = strtoul(args[4], &endptr, 10);
683 if ((errno == ERANGE && val == ULONG_MAX) ||
684 (errno != 0 && val == 0) || endptr == args[4] ||
685 val > 0xffffffff) {
686 appctx->ctx.cli.msg = "Invalid key\n";
687 appctx->st0 = STAT_CLI_PRINT;
688 return;
689 }
690 uint32_key = (uint32_t) val;
691 static_table_key->key = &uint32_key;
692 break;
693 }
694 break;
695 case SMP_T_STR:
696 static_table_key->key = args[4];
697 static_table_key->key_len = strlen(args[4]);
698 break;
699 default:
700 switch (action) {
701 case STAT_CLI_O_TAB:
702 appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
703 break;
704 case STAT_CLI_O_CLR:
705 appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
706 break;
707 default:
708 appctx->ctx.cli.msg = "Unknown action\n";
709 break;
710 }
711 appctx->st0 = STAT_CLI_PRINT;
712 return;
713 }
714
715 /* check permissions */
716 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
717 appctx->ctx.cli.msg = stats_permission_denied_msg;
718 appctx->st0 = STAT_CLI_PRINT;
719 return;
720 }
721
722 ts = stktable_lookup_key(&px->table, static_table_key);
723
724 switch (action) {
725 case STAT_CLI_O_TAB:
726 if (!ts)
727 return;
728 chunk_reset(&trash);
729 if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
730 return;
731 stats_dump_table_entry_to_buffer(&trash, si, px, ts);
732 return;
733
734 case STAT_CLI_O_CLR:
735 if (!ts)
736 return;
737 if (ts->ref_cnt) {
738 /* don't delete an entry which is currently referenced */
739 appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
740 appctx->st0 = STAT_CLI_PRINT;
741 return;
742 }
743 stksess_kill(&px->table, ts);
744 break;
745
746 case STAT_CLI_O_SET:
747 if (ts)
748 stktable_touch(&px->table, ts, 1);
749 else {
750 ts = stksess_new(&px->table, static_table_key);
751 if (!ts) {
752 /* don't delete an entry which is currently referenced */
753 appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
754 appctx->st0 = STAT_CLI_PRINT;
755 return;
756 }
757 stktable_store(&px->table, ts, 1);
758 }
759
760 for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
761 if (strncmp(args[cur_arg], "data.", 5) != 0) {
762 appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
763 appctx->st0 = STAT_CLI_PRINT;
764 return;
765 }
766
767 data_type = stktable_get_data_type(args[cur_arg] + 5);
768 if (data_type < 0) {
769 appctx->ctx.cli.msg = "Unknown data type\n";
770 appctx->st0 = STAT_CLI_PRINT;
771 return;
772 }
773
774 if (!px->table.data_ofs[data_type]) {
775 appctx->ctx.cli.msg = "Data type not stored in this table\n";
776 appctx->st0 = STAT_CLI_PRINT;
777 return;
778 }
779
780 if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
781 appctx->ctx.cli.msg = "Require a valid integer value to store\n";
782 appctx->st0 = STAT_CLI_PRINT;
783 return;
784 }
785
786 ptr = stktable_data_ptr(&px->table, ts, data_type);
787
788 switch (stktable_data_types[data_type].std_type) {
789 case STD_T_SINT:
790 stktable_data_cast(ptr, std_t_sint) = value;
791 break;
792 case STD_T_UINT:
793 stktable_data_cast(ptr, std_t_uint) = value;
794 break;
795 case STD_T_ULL:
796 stktable_data_cast(ptr, std_t_ull) = value;
797 break;
798 case STD_T_FRQP:
799 /* We set both the current and previous values. That way
800 * the reported frequency is stable during all the period
801 * then slowly fades out. This allows external tools to
802 * push measures without having to update them too often.
803 */
804 frqp = &stktable_data_cast(ptr, std_t_frqp);
805 frqp->curr_tick = now_ms;
806 frqp->prev_ctr = 0;
807 frqp->curr_ctr = value;
808 break;
809 }
810 }
811 break;
812
813 default:
814 appctx->ctx.cli.msg = "Unknown action\n";
815 appctx->st0 = STAT_CLI_PRINT;
816 break;
817 }
818}
819
820static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
821{
822 struct appctx *appctx = __objt_appctx(si->end);
823
824 if (action != STAT_CLI_O_TAB && action != STAT_CLI_O_CLR) {
825 appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
826 appctx->st0 = STAT_CLI_PRINT;
827 return;
828 }
829
830 /* condition on stored data value */
831 appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
832 if (appctx->ctx.table.data_type < 0) {
833 appctx->ctx.cli.msg = "Unknown data type\n";
834 appctx->st0 = STAT_CLI_PRINT;
835 return;
836 }
837
838 if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
839 appctx->ctx.cli.msg = "Data type not stored in this table\n";
840 appctx->st0 = STAT_CLI_PRINT;
841 return;
842 }
843
844 appctx->ctx.table.data_op = get_std_op(args[4]);
845 if (appctx->ctx.table.data_op < 0) {
846 appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
847 appctx->st0 = STAT_CLI_PRINT;
848 return;
849 }
850
851 if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
852 appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
853 appctx->st0 = STAT_CLI_PRINT;
854 return;
855 }
856}
857
858static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
859{
860 struct appctx *appctx = __objt_appctx(si->end);
861
862 appctx->ctx.table.data_type = -1;
863 appctx->st2 = STAT_ST_INIT;
864 appctx->ctx.table.target = NULL;
865 appctx->ctx.table.proxy = NULL;
866 appctx->ctx.table.entry = NULL;
867 appctx->st0 = action;
868
869 if (*args[2]) {
870 appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
871 if (!appctx->ctx.table.target) {
872 appctx->ctx.cli.msg = "No such table\n";
873 appctx->st0 = STAT_CLI_PRINT;
874 return;
875 }
876 }
877 else {
878 if (action != STAT_CLI_O_TAB)
879 goto err_args;
880 return;
881 }
882
883 if (strcmp(args[3], "key") == 0)
884 stats_sock_table_key_request(si, args, action);
885 else if (strncmp(args[3], "data.", 5) == 0)
886 stats_sock_table_data_request(si, args, action);
887 else if (*args[3])
888 goto err_args;
889
890 return;
891
892err_args:
893 switch (action) {
894 case STAT_CLI_O_TAB:
895 appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
896 break;
897 case STAT_CLI_O_CLR:
898 appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
899 break;
900 default:
901 appctx->ctx.cli.msg = "Unknown action\n";
902 break;
903 }
904 appctx->st0 = STAT_CLI_PRINT;
905}
906
907/* Expects to find a frontend named <arg> and returns it, otherwise displays various
908 * adequate error messages and returns NULL. This function also expects the stream
909 * level to be admin.
910 */
911static struct proxy *expect_frontend_admin(struct stream *s, struct stream_interface *si, const char *arg)
912{
913 struct appctx *appctx = __objt_appctx(si->end);
914 struct proxy *px;
915
916 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
917 appctx->ctx.cli.msg = stats_permission_denied_msg;
918 appctx->st0 = STAT_CLI_PRINT;
919 return NULL;
920 }
921
922 if (!*arg) {
923 appctx->ctx.cli.msg = "A frontend name is expected.\n";
924 appctx->st0 = STAT_CLI_PRINT;
925 return NULL;
926 }
927
928 px = proxy_fe_by_name(arg);
929 if (!px) {
930 appctx->ctx.cli.msg = "No such frontend.\n";
931 appctx->st0 = STAT_CLI_PRINT;
932 return NULL;
933 }
934 return px;
935}
936
937/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
938 * and returns the pointer to the server. Otherwise, display adequate error messages
939 * and returns NULL. This function also expects the stream level to be admin. Note:
940 * the <arg> is modified to remove the '/'.
941 */
William Lallemand222baf22016-11-19 02:00:33 +0100942struct server *expect_server_admin(struct stream *s, struct stream_interface *si, char *arg)
William Lallemand74c24fb2016-11-21 17:18:36 +0100943{
944 struct appctx *appctx = __objt_appctx(si->end);
945 struct proxy *px;
946 struct server *sv;
947 char *line;
948
949 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
950 appctx->ctx.cli.msg = stats_permission_denied_msg;
951 appctx->st0 = STAT_CLI_PRINT;
952 return NULL;
953 }
954
955 /* split "backend/server" and make <line> point to server */
956 for (line = arg; *line; line++)
957 if (*line == '/') {
958 *line++ = '\0';
959 break;
960 }
961
962 if (!*line || !*arg) {
963 appctx->ctx.cli.msg = "Require 'backend/server'.\n";
964 appctx->st0 = STAT_CLI_PRINT;
965 return NULL;
966 }
967
968 if (!get_backend_server(arg, line, &px, &sv)) {
969 appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
970 appctx->st0 = STAT_CLI_PRINT;
971 return NULL;
972 }
973
974 if (px->state == PR_STSTOPPED) {
975 appctx->ctx.cli.msg = "Proxy is disabled.\n";
976 appctx->st0 = STAT_CLI_PRINT;
977 return NULL;
978 }
979
980 return sv;
981}
982
William Lallemand74c24fb2016-11-21 17:18:36 +0100983/* Processes the stats interpreter on the statistics socket. This function is
984 * called from an applet running in a stream interface. The function returns 1
985 * if the request was understood, otherwise zero. It sets appctx->st0 to a value
986 * designating the function which will have to process the request, which can
987 * also be the print function to display the return message set into cli.msg.
988 */
989static int stats_sock_parse_request(struct stream_interface *si, char *line)
990{
991 struct stream *s = si_strm(si);
992 struct appctx *appctx = __objt_appctx(si->end);
993 char *args[MAX_STATS_ARGS + 1];
994 struct cli_kw *kw;
995 int arg;
996 int i, j;
997
998 while (isspace((unsigned char)*line))
999 line++;
1000
1001 arg = 0;
1002 args[arg] = line;
1003
1004 while (*line && arg < MAX_STATS_ARGS) {
1005 if (*line == '\\') {
1006 line++;
1007 if (*line == '\0')
1008 break;
1009 }
1010 else if (isspace((unsigned char)*line)) {
1011 *line++ = '\0';
1012
1013 while (isspace((unsigned char)*line))
1014 line++;
1015
1016 args[++arg] = line;
1017 continue;
1018 }
1019
1020 line++;
1021 }
1022
1023 while (++arg <= MAX_STATS_ARGS)
1024 args[arg] = line;
1025
1026 /* remove \ */
1027 arg = 0;
1028 while (*args[arg] != '\0') {
1029 j = 0;
1030 for (i=0; args[arg][i] != '\0'; i++) {
1031 if (args[arg][i] == '\\')
1032 continue;
1033 args[arg][j] = args[arg][i];
1034 j++;
1035 }
1036 args[arg][j] = '\0';
1037 arg++;
1038 }
1039
1040 appctx->ctx.stats.scope_str = 0;
1041 appctx->ctx.stats.scope_len = 0;
1042 appctx->ctx.stats.flags = 0;
1043 if ((kw = cli_find_kw(args))) {
1044 if (kw->parse) {
1045 if (kw->parse(args, appctx, kw->private) == 0 && kw->io_handler) {
1046 appctx->st0 = STAT_CLI_O_CUSTOM;
1047 appctx->io_handler = kw->io_handler;
1048 appctx->io_release = kw->io_release;
1049 }
1050 }
1051 } else if (strcmp(args[0], "show") == 0) {
William Lallemand933efcd2016-11-22 12:34:16 +01001052 if (strcmp(args[1], "env") == 0) {
William Lallemand74c24fb2016-11-21 17:18:36 +01001053 extern char **environ;
1054
1055 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
1056 appctx->ctx.cli.msg = stats_permission_denied_msg;
1057 appctx->st0 = STAT_CLI_PRINT;
1058 return 1;
1059 }
1060 appctx->ctx.env.var = environ;
1061 appctx->st2 = STAT_ST_INIT;
1062 appctx->st0 = STAT_CLI_O_ENV; // stats_dump_env_to_buffer
1063
1064 if (*args[2]) {
1065 int len = strlen(args[2]);
1066
1067 for (; *appctx->ctx.env.var; appctx->ctx.env.var++) {
1068 if (strncmp(*appctx->ctx.env.var, args[2], len) == 0 &&
1069 (*appctx->ctx.env.var)[len] == '=')
1070 break;
1071 }
1072 if (!*appctx->ctx.env.var) {
1073 appctx->ctx.cli.msg = "Variable not found\n";
1074 appctx->st0 = STAT_CLI_PRINT;
1075 return 1;
1076 }
1077 appctx->st2 = STAT_ST_END;
1078 }
1079 }
1080 else if (strcmp(args[1], "stat") == 0) {
William Lallemand69e96442016-11-19 00:58:54 +01001081 if (*args[2] && *args[3] && *args[4]) {
William Lallemand74c24fb2016-11-21 17:18:36 +01001082 appctx->ctx.stats.flags |= STAT_BOUND;
1083 appctx->ctx.stats.iid = atoi(args[2]);
1084 appctx->ctx.stats.type = atoi(args[3]);
1085 appctx->ctx.stats.sid = atoi(args[4]);
1086 if (strcmp(args[5], "typed") == 0)
1087 appctx->ctx.stats.flags |= STAT_FMT_TYPED;
1088 }
1089 else if (strcmp(args[2], "typed") == 0)
1090 appctx->ctx.stats.flags |= STAT_FMT_TYPED;
1091
1092 appctx->st2 = STAT_ST_INIT;
1093 appctx->st0 = STAT_CLI_O_STAT; // stats_dump_stat_to_buffer
1094 }
1095 else if (strcmp(args[1], "info") == 0) {
1096 if (strcmp(args[2], "typed") == 0)
1097 appctx->ctx.stats.flags |= STAT_FMT_TYPED;
1098 appctx->st2 = STAT_ST_INIT;
1099 appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer
1100 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001101 else if (strcmp(args[1], "errors") == 0) {
1102 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
1103 appctx->ctx.cli.msg = stats_permission_denied_msg;
1104 appctx->st0 = STAT_CLI_PRINT;
1105 return 1;
1106 }
1107 if (*args[2])
1108 appctx->ctx.errors.iid = atoi(args[2]);
1109 else
1110 appctx->ctx.errors.iid = -1;
1111 appctx->ctx.errors.px = NULL;
1112 appctx->st2 = STAT_ST_INIT;
1113 appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
1114 }
1115 else if (strcmp(args[1], "table") == 0) {
1116 stats_sock_table_request(si, args, STAT_CLI_O_TAB);
1117 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001118 else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
1119 return 0;
1120 }
1121 }
1122 else if (strcmp(args[0], "clear") == 0) {
1123 if (strcmp(args[1], "counters") == 0) {
1124 struct proxy *px;
1125 struct server *sv;
1126 struct listener *li;
1127 int clrall = 0;
1128
1129 if (strcmp(args[2], "all") == 0)
1130 clrall = 1;
1131
1132 /* check permissions */
1133 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER ||
1134 (clrall && strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN)) {
1135 appctx->ctx.cli.msg = stats_permission_denied_msg;
1136 appctx->st0 = STAT_CLI_PRINT;
1137 return 1;
1138 }
1139
1140 for (px = proxy; px; px = px->next) {
1141 if (clrall) {
1142 memset(&px->be_counters, 0, sizeof(px->be_counters));
1143 memset(&px->fe_counters, 0, sizeof(px->fe_counters));
1144 }
1145 else {
1146 px->be_counters.conn_max = 0;
1147 px->be_counters.p.http.rps_max = 0;
1148 px->be_counters.sps_max = 0;
1149 px->be_counters.cps_max = 0;
1150 px->be_counters.nbpend_max = 0;
1151
1152 px->fe_counters.conn_max = 0;
1153 px->fe_counters.p.http.rps_max = 0;
1154 px->fe_counters.sps_max = 0;
1155 px->fe_counters.cps_max = 0;
1156 px->fe_counters.nbpend_max = 0;
1157 }
1158
1159 for (sv = px->srv; sv; sv = sv->next)
1160 if (clrall)
1161 memset(&sv->counters, 0, sizeof(sv->counters));
1162 else {
1163 sv->counters.cur_sess_max = 0;
1164 sv->counters.nbpend_max = 0;
1165 sv->counters.sps_max = 0;
1166 }
1167
1168 list_for_each_entry(li, &px->conf.listeners, by_fe)
1169 if (li->counters) {
1170 if (clrall)
1171 memset(li->counters, 0, sizeof(*li->counters));
1172 else
1173 li->counters->conn_max = 0;
1174 }
1175 }
1176
1177 global.cps_max = 0;
1178 global.sps_max = 0;
1179 return 1;
1180 }
1181 else if (strcmp(args[1], "table") == 0) {
1182 stats_sock_table_request(si, args, STAT_CLI_O_CLR);
1183 /* end of processing */
1184 return 1;
1185 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001186 else {
1187 /* unknown "clear" argument */
1188 return 0;
1189 }
1190 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001191 else if (strcmp(args[0], "set") == 0) {
William Lallemand6b160942016-11-22 12:34:35 +01001192 if (strcmp(args[1], "timeout") == 0) {
William Lallemand74c24fb2016-11-21 17:18:36 +01001193 if (strcmp(args[2], "cli") == 0) {
1194 unsigned timeout;
1195 const char *res;
1196
1197 if (!*args[3]) {
1198 appctx->ctx.cli.msg = "Expects an integer value.\n";
1199 appctx->st0 = STAT_CLI_PRINT;
1200 return 1;
1201 }
1202
1203 res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
1204 if (res || timeout < 1) {
1205 appctx->ctx.cli.msg = "Invalid timeout value.\n";
1206 appctx->st0 = STAT_CLI_PRINT;
1207 return 1;
1208 }
1209
1210 s->req.rto = s->res.wto = 1 + MS_TO_TICKS(timeout*1000);
1211 task_wakeup(s->task, TASK_WOKEN_MSG); // recompute timeouts
1212 return 1;
1213 }
1214 else {
1215 appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
1216 appctx->st0 = STAT_CLI_PRINT;
1217 return 1;
1218 }
1219 }
1220 else if (strcmp(args[1], "maxconn") == 0) {
1221 if (strcmp(args[2], "frontend") == 0) {
1222 struct proxy *px;
1223 struct listener *l;
1224 int v;
1225
1226 px = expect_frontend_admin(s, si, args[3]);
1227 if (!px)
1228 return 1;
1229
1230 if (!*args[4]) {
1231 appctx->ctx.cli.msg = "Integer value expected.\n";
1232 appctx->st0 = STAT_CLI_PRINT;
1233 return 1;
1234 }
1235
1236 v = atoi(args[4]);
1237 if (v < 0) {
1238 appctx->ctx.cli.msg = "Value out of range.\n";
1239 appctx->st0 = STAT_CLI_PRINT;
1240 return 1;
1241 }
1242
1243 /* OK, the value is fine, so we assign it to the proxy and to all of
1244 * its listeners. The blocked ones will be dequeued.
1245 */
1246 px->maxconn = v;
1247 list_for_each_entry(l, &px->conf.listeners, by_fe) {
1248 l->maxconn = v;
1249 if (l->state == LI_FULL)
1250 resume_listener(l);
1251 }
1252
1253 if (px->maxconn > px->feconn && !LIST_ISEMPTY(&px->listener_queue))
1254 dequeue_all_listeners(&px->listener_queue);
1255
1256 return 1;
1257 }
1258 else if (strcmp(args[2], "server") == 0) {
1259 struct server *sv;
1260 const char *warning;
1261
1262 sv = expect_server_admin(s, si, args[3]);
1263 if (!sv)
1264 return 1;
1265
1266 warning = server_parse_maxconn_change_request(sv, args[4]);
1267 if (warning) {
1268 appctx->ctx.cli.msg = warning;
1269 appctx->st0 = STAT_CLI_PRINT;
1270 }
1271
1272 return 1;
1273 }
1274 else if (strcmp(args[2], "global") == 0) {
1275 int v;
1276
1277 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1278 appctx->ctx.cli.msg = stats_permission_denied_msg;
1279 appctx->st0 = STAT_CLI_PRINT;
1280 return 1;
1281 }
1282
1283 if (!*args[3]) {
1284 appctx->ctx.cli.msg = "Expects an integer value.\n";
1285 appctx->st0 = STAT_CLI_PRINT;
1286 return 1;
1287 }
1288
1289 v = atoi(args[3]);
1290 if (v > global.hardmaxconn) {
1291 appctx->ctx.cli.msg = "Value out of range.\n";
1292 appctx->st0 = STAT_CLI_PRINT;
1293 return 1;
1294 }
1295
1296 /* check for unlimited values */
1297 if (v <= 0)
1298 v = global.hardmaxconn;
1299
1300 global.maxconn = v;
1301
1302 /* Dequeues all of the listeners waiting for a resource */
1303 if (!LIST_ISEMPTY(&global_listener_queue))
1304 dequeue_all_listeners(&global_listener_queue);
1305
1306 return 1;
1307 }
1308 else {
1309 appctx->ctx.cli.msg = "'set maxconn' only supports 'frontend', 'server', and 'global'.\n";
1310 appctx->st0 = STAT_CLI_PRINT;
1311 return 1;
1312 }
1313 }
1314 else if (strcmp(args[1], "rate-limit") == 0) {
1315 if (strcmp(args[2], "connections") == 0) {
1316 if (strcmp(args[3], "global") == 0) {
1317 int v;
1318
1319 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1320 appctx->ctx.cli.msg = stats_permission_denied_msg;
1321 appctx->st0 = STAT_CLI_PRINT;
1322 return 1;
1323 }
1324
1325 if (!*args[4]) {
1326 appctx->ctx.cli.msg = "Expects an integer value.\n";
1327 appctx->st0 = STAT_CLI_PRINT;
1328 return 1;
1329 }
1330
1331 v = atoi(args[4]);
1332 if (v < 0) {
1333 appctx->ctx.cli.msg = "Value out of range.\n";
1334 appctx->st0 = STAT_CLI_PRINT;
1335 return 1;
1336 }
1337
1338 global.cps_lim = v;
1339
1340 /* Dequeues all of the listeners waiting for a resource */
1341 if (!LIST_ISEMPTY(&global_listener_queue))
1342 dequeue_all_listeners(&global_listener_queue);
1343
1344 return 1;
1345 }
1346 else {
1347 appctx->ctx.cli.msg = "'set rate-limit connections' only supports 'global'.\n";
1348 appctx->st0 = STAT_CLI_PRINT;
1349 return 1;
1350 }
1351 }
1352 else if (strcmp(args[2], "sessions") == 0) {
1353 if (strcmp(args[3], "global") == 0) {
1354 int v;
1355
1356 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1357 appctx->ctx.cli.msg = stats_permission_denied_msg;
1358 appctx->st0 = STAT_CLI_PRINT;
1359 return 1;
1360 }
1361
1362 if (!*args[4]) {
1363 appctx->ctx.cli.msg = "Expects an integer value.\n";
1364 appctx->st0 = STAT_CLI_PRINT;
1365 return 1;
1366 }
1367
1368 v = atoi(args[4]);
1369 if (v < 0) {
1370 appctx->ctx.cli.msg = "Value out of range.\n";
1371 appctx->st0 = STAT_CLI_PRINT;
1372 return 1;
1373 }
1374
1375 global.sps_lim = v;
1376
1377 /* Dequeues all of the listeners waiting for a resource */
1378 if (!LIST_ISEMPTY(&global_listener_queue))
1379 dequeue_all_listeners(&global_listener_queue);
1380
1381 return 1;
1382 }
1383 else {
1384 appctx->ctx.cli.msg = "'set rate-limit sessions' only supports 'global'.\n";
1385 appctx->st0 = STAT_CLI_PRINT;
1386 return 1;
1387 }
1388 }
1389#ifdef USE_OPENSSL
1390 else if (strcmp(args[2], "ssl-sessions") == 0) {
1391 if (strcmp(args[3], "global") == 0) {
1392 int v;
1393
1394 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1395 appctx->ctx.cli.msg = stats_permission_denied_msg;
1396 appctx->st0 = STAT_CLI_PRINT;
1397 return 1;
1398 }
1399
1400 if (!*args[4]) {
1401 appctx->ctx.cli.msg = "Expects an integer value.\n";
1402 appctx->st0 = STAT_CLI_PRINT;
1403 return 1;
1404 }
1405
1406 v = atoi(args[4]);
1407 if (v < 0) {
1408 appctx->ctx.cli.msg = "Value out of range.\n";
1409 appctx->st0 = STAT_CLI_PRINT;
1410 return 1;
1411 }
1412
1413 global.ssl_lim = v;
1414
1415 /* Dequeues all of the listeners waiting for a resource */
1416 if (!LIST_ISEMPTY(&global_listener_queue))
1417 dequeue_all_listeners(&global_listener_queue);
1418
1419 return 1;
1420 }
1421 else {
1422 appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
1423 appctx->st0 = STAT_CLI_PRINT;
1424 return 1;
1425 }
1426 }
1427#endif
1428 else if (strcmp(args[2], "http-compression") == 0) {
1429 if (strcmp(args[3], "global") == 0) {
1430 int v;
1431
1432 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1433 appctx->ctx.cli.msg = stats_permission_denied_msg;
1434 appctx->st0 = STAT_CLI_PRINT;
1435 return 1;
1436 }
1437
1438 if (!*args[4]) {
1439 appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
1440 appctx->st0 = STAT_CLI_PRINT;
1441 return 1;
1442 }
1443
1444 v = atoi(args[4]);
1445 global.comp_rate_lim = v * 1024; /* Kilo to bytes. */
1446 }
1447 else {
1448 appctx->ctx.cli.msg = "'set rate-limit http-compression' only supports 'global'.\n";
1449 appctx->st0 = STAT_CLI_PRINT;
1450 return 1;
1451 }
1452 }
1453 else {
1454 appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
1455 appctx->st0 = STAT_CLI_PRINT;
1456 return 1;
1457 }
1458 }
1459 else if (strcmp(args[1], "table") == 0) {
1460 stats_sock_table_request(si, args, STAT_CLI_O_SET);
William Lallemand32af2032016-10-29 18:09:35 +02001461 } else { /* unknown "set" parameter */
William Lallemand74c24fb2016-11-21 17:18:36 +01001462 return 0;
1463 }
1464 }
1465 else if (strcmp(args[0], "enable") == 0) {
1466 if (strcmp(args[1], "agent") == 0) {
1467 struct server *sv;
1468
1469 sv = expect_server_admin(s, si, args[2]);
1470 if (!sv)
1471 return 1;
1472
1473 if (!(sv->agent.state & CHK_ST_CONFIGURED)) {
1474 appctx->ctx.cli.msg = "Agent was not configured on this server, cannot enable.\n";
1475 appctx->st0 = STAT_CLI_PRINT;
1476 return 1;
1477 }
1478
1479 sv->agent.state |= CHK_ST_ENABLED;
1480 return 1;
1481 }
1482 else if (strcmp(args[1], "health") == 0) {
1483 struct server *sv;
1484
1485 sv = expect_server_admin(s, si, args[2]);
1486 if (!sv)
1487 return 1;
1488
1489 if (!(sv->check.state & CHK_ST_CONFIGURED)) {
1490 appctx->ctx.cli.msg = "Health checks are not configured on this server, cannot enable.\n";
1491 appctx->st0 = STAT_CLI_PRINT;
1492 return 1;
1493 }
1494
1495 sv->check.state |= CHK_ST_ENABLED;
1496 return 1;
1497 }
1498 else if (strcmp(args[1], "server") == 0) {
1499 struct server *sv;
1500
1501 sv = expect_server_admin(s, si, args[2]);
1502 if (!sv)
1503 return 1;
1504
1505 srv_adm_set_ready(sv);
1506 return 1;
1507 }
1508 else if (strcmp(args[1], "frontend") == 0) {
1509 struct proxy *px;
1510
1511 px = expect_frontend_admin(s, si, args[2]);
1512 if (!px)
1513 return 1;
1514
1515 if (px->state == PR_STSTOPPED) {
1516 appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
1517 appctx->st0 = STAT_CLI_PRINT;
1518 return 1;
1519 }
1520
1521 if (px->state != PR_STPAUSED) {
1522 appctx->ctx.cli.msg = "Frontend is already enabled.\n";
1523 appctx->st0 = STAT_CLI_PRINT;
1524 return 1;
1525 }
1526
1527 if (!resume_proxy(px)) {
1528 appctx->ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
1529 appctx->st0 = STAT_CLI_PRINT;
1530 return 1;
1531 }
1532 return 1;
1533 }
1534 else { /* unknown "enable" parameter */
1535 appctx->ctx.cli.msg = "'enable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
1536 appctx->st0 = STAT_CLI_PRINT;
1537 return 1;
1538 }
1539 }
1540 else if (strcmp(args[0], "disable") == 0) {
1541 if (strcmp(args[1], "agent") == 0) {
1542 struct server *sv;
1543
1544 sv = expect_server_admin(s, si, args[2]);
1545 if (!sv)
1546 return 1;
1547
1548 sv->agent.state &= ~CHK_ST_ENABLED;
1549 return 1;
1550 }
1551 else if (strcmp(args[1], "health") == 0) {
1552 struct server *sv;
1553
1554 sv = expect_server_admin(s, si, args[2]);
1555 if (!sv)
1556 return 1;
1557
1558 sv->check.state &= ~CHK_ST_ENABLED;
1559 return 1;
1560 }
1561 else if (strcmp(args[1], "server") == 0) {
1562 struct server *sv;
1563
1564 sv = expect_server_admin(s, si, args[2]);
1565 if (!sv)
1566 return 1;
1567
1568 srv_adm_set_maint(sv);
1569 return 1;
1570 }
1571 else if (strcmp(args[1], "frontend") == 0) {
1572 struct proxy *px;
1573
1574 px = expect_frontend_admin(s, si, args[2]);
1575 if (!px)
1576 return 1;
1577
1578 if (px->state == PR_STSTOPPED) {
1579 appctx->ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
1580 appctx->st0 = STAT_CLI_PRINT;
1581 return 1;
1582 }
1583
1584 if (px->state == PR_STPAUSED) {
1585 appctx->ctx.cli.msg = "Frontend is already disabled.\n";
1586 appctx->st0 = STAT_CLI_PRINT;
1587 return 1;
1588 }
1589
1590 if (!pause_proxy(px)) {
1591 appctx->ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
1592 appctx->st0 = STAT_CLI_PRINT;
1593 return 1;
1594 }
1595 return 1;
1596 }
1597 else { /* unknown "disable" parameter */
1598 appctx->ctx.cli.msg = "'disable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
1599 appctx->st0 = STAT_CLI_PRINT;
1600 return 1;
1601 }
1602 }
1603 else if (strcmp(args[0], "shutdown") == 0) {
1604 if (strcmp(args[1], "frontend") == 0) {
1605 struct proxy *px;
1606
1607 px = expect_frontend_admin(s, si, args[2]);
1608 if (!px)
1609 return 1;
1610
1611 if (px->state == PR_STSTOPPED) {
1612 appctx->ctx.cli.msg = "Frontend was already shut down.\n";
1613 appctx->st0 = STAT_CLI_PRINT;
1614 return 1;
1615 }
1616
1617 Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
1618 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
1619 send_log(px, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
1620 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
1621 stop_proxy(px);
1622 return 1;
1623 }
1624 else if (strcmp(args[1], "session") == 0) {
1625 struct stream *sess, *ptr;
1626
1627 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1628 appctx->ctx.cli.msg = stats_permission_denied_msg;
1629 appctx->st0 = STAT_CLI_PRINT;
1630 return 1;
1631 }
1632
1633 if (!*args[2]) {
1634 appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
1635 appctx->st0 = STAT_CLI_PRINT;
1636 return 1;
1637 }
1638
1639 ptr = (void *)strtoul(args[2], NULL, 0);
1640
1641 /* first, look for the requested stream in the stream table */
1642 list_for_each_entry(sess, &streams, list) {
1643 if (sess == ptr)
1644 break;
1645 }
1646
1647 /* do we have the stream ? */
1648 if (sess != ptr) {
1649 appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
1650 appctx->st0 = STAT_CLI_PRINT;
1651 return 1;
1652 }
1653
1654 stream_shutdown(sess, SF_ERR_KILLED);
1655 return 1;
1656 }
1657 else if (strcmp(args[1], "sessions") == 0) {
1658 if (strcmp(args[2], "server") == 0) {
1659 struct server *sv;
1660 struct stream *sess, *sess_bck;
1661
1662 sv = expect_server_admin(s, si, args[3]);
1663 if (!sv)
1664 return 1;
1665
1666 /* kill all the stream that are on this server */
1667 list_for_each_entry_safe(sess, sess_bck, &sv->actconns, by_srv)
1668 if (sess->srv_conn == sv)
1669 stream_shutdown(sess, SF_ERR_KILLED);
1670
1671 return 1;
1672 }
1673 else {
1674 appctx->ctx.cli.msg = "'shutdown sessions' only supports 'server'.\n";
1675 appctx->st0 = STAT_CLI_PRINT;
1676 return 1;
1677 }
1678 }
1679 else { /* unknown "disable" parameter */
1680 appctx->ctx.cli.msg = "'shutdown' only supports 'frontend', 'session' and 'sessions'.\n";
1681 appctx->st0 = STAT_CLI_PRINT;
1682 return 1;
1683 }
1684 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001685 else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
1686 return 0;
1687 }
1688 return 1;
1689}
1690
1691/* This I/O handler runs as an applet embedded in a stream interface. It is
1692 * used to processes I/O from/to the stats unix socket. The system relies on a
1693 * state machine handling requests and various responses. We read a request,
1694 * then we process it and send the response, and we possibly display a prompt.
1695 * Then we can read again. The state is stored in appctx->st0 and is one of the
1696 * STAT_CLI_* constants. appctx->st1 is used to indicate whether prompt is enabled
1697 * or not.
1698 */
1699static void cli_io_handler(struct appctx *appctx)
1700{
1701 struct stream_interface *si = appctx->owner;
1702 struct channel *req = si_oc(si);
1703 struct channel *res = si_ic(si);
1704 int reql;
1705 int len;
1706
1707 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
1708 goto out;
1709
1710 while (1) {
1711 if (appctx->st0 == STAT_CLI_INIT) {
1712 /* Stats output not initialized yet */
1713 memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
1714 appctx->st0 = STAT_CLI_GETREQ;
1715 }
1716 else if (appctx->st0 == STAT_CLI_END) {
1717 /* Let's close for real now. We just close the request
1718 * side, the conditions below will complete if needed.
1719 */
1720 si_shutw(si);
1721 break;
1722 }
1723 else if (appctx->st0 == STAT_CLI_GETREQ) {
1724 /* ensure we have some output room left in the event we
1725 * would want to return some info right after parsing.
1726 */
1727 if (buffer_almost_full(si_ib(si))) {
1728 si_applet_cant_put(si);
1729 break;
1730 }
1731
1732 reql = bo_getline(si_oc(si), trash.str, trash.size);
1733 if (reql <= 0) { /* closed or EOL not found */
1734 if (reql == 0)
1735 break;
1736 appctx->st0 = STAT_CLI_END;
1737 continue;
1738 }
1739
1740 /* seek for a possible unescaped semi-colon. If we find
1741 * one, we replace it with an LF and skip only this part.
1742 */
1743 for (len = 0; len < reql; len++) {
1744 if (trash.str[len] == '\\') {
1745 len++;
1746 continue;
1747 }
1748 if (trash.str[len] == ';') {
1749 trash.str[len] = '\n';
1750 reql = len + 1;
1751 break;
1752 }
1753 }
1754
1755 /* now it is time to check that we have a full line,
1756 * remove the trailing \n and possibly \r, then cut the
1757 * line.
1758 */
1759 len = reql - 1;
1760 if (trash.str[len] != '\n') {
1761 appctx->st0 = STAT_CLI_END;
1762 continue;
1763 }
1764
1765 if (len && trash.str[len-1] == '\r')
1766 len--;
1767
1768 trash.str[len] = '\0';
1769
1770 appctx->st0 = STAT_CLI_PROMPT;
1771 if (len) {
1772 if (strcmp(trash.str, "quit") == 0) {
1773 appctx->st0 = STAT_CLI_END;
1774 continue;
1775 }
1776 else if (strcmp(trash.str, "prompt") == 0)
1777 appctx->st1 = !appctx->st1;
1778 else if (strcmp(trash.str, "help") == 0 ||
1779 !stats_sock_parse_request(si, trash.str)) {
1780 cli_gen_usage_msg();
1781 if (dynamic_usage_msg)
1782 appctx->ctx.cli.msg = dynamic_usage_msg;
1783 else
1784 appctx->ctx.cli.msg = stats_sock_usage_msg;
1785 appctx->st0 = STAT_CLI_PRINT;
1786 }
1787 /* NB: stats_sock_parse_request() may have put
1788 * another STAT_CLI_O_* into appctx->st0.
1789 */
1790 }
1791 else if (!appctx->st1) {
1792 /* if prompt is disabled, print help on empty lines,
1793 * so that the user at least knows how to enable
1794 * prompt and find help.
1795 */
1796 cli_gen_usage_msg();
1797 if (dynamic_usage_msg)
1798 appctx->ctx.cli.msg = dynamic_usage_msg;
1799 else
1800 appctx->ctx.cli.msg = stats_sock_usage_msg;
1801 appctx->st0 = STAT_CLI_PRINT;
1802 }
1803
1804 /* re-adjust req buffer */
1805 bo_skip(si_oc(si), reql);
1806 req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
1807 }
1808 else { /* output functions */
1809 switch (appctx->st0) {
1810 case STAT_CLI_PROMPT:
1811 break;
1812 case STAT_CLI_PRINT:
1813 if (bi_putstr(si_ic(si), appctx->ctx.cli.msg) != -1)
1814 appctx->st0 = STAT_CLI_PROMPT;
1815 else
1816 si_applet_cant_put(si);
1817 break;
1818 case STAT_CLI_PRINT_FREE:
1819 if (bi_putstr(si_ic(si), appctx->ctx.cli.err) != -1) {
1820 free(appctx->ctx.cli.err);
1821 appctx->st0 = STAT_CLI_PROMPT;
1822 }
1823 else
1824 si_applet_cant_put(si);
1825 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01001826 case STAT_CLI_O_INFO:
1827 if (stats_dump_info_to_buffer(si))
1828 appctx->st0 = STAT_CLI_PROMPT;
1829 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01001830 case STAT_CLI_O_STAT:
1831 if (stats_dump_stat_to_buffer(si, NULL))
1832 appctx->st0 = STAT_CLI_PROMPT;
1833 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01001834 case STAT_CLI_O_ERR: /* errors dump */
1835 if (stats_dump_errors_to_buffer(si))
1836 appctx->st0 = STAT_CLI_PROMPT;
1837 break;
1838 case STAT_CLI_O_TAB:
1839 case STAT_CLI_O_CLR:
1840 if (stats_table_request(si, appctx->st0))
1841 appctx->st0 = STAT_CLI_PROMPT;
1842 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01001843 case STAT_CLI_O_ENV: /* environment dump */
1844 if (stats_dump_env_to_buffer(si))
1845 appctx->st0 = STAT_CLI_PROMPT;
1846 break;
1847 case STAT_CLI_O_CUSTOM: /* use custom pointer */
1848 if (appctx->io_handler)
1849 if (appctx->io_handler(appctx)) {
1850 appctx->st0 = STAT_CLI_PROMPT;
1851 if (appctx->io_release) {
1852 appctx->io_release(appctx);
1853 appctx->io_release = NULL;
1854 }
1855 }
1856 break;
1857 default: /* abnormal state */
1858 si->flags |= SI_FL_ERR;
1859 break;
1860 }
1861
1862 /* The post-command prompt is either LF alone or LF + '> ' in interactive mode */
1863 if (appctx->st0 == STAT_CLI_PROMPT) {
1864 if (bi_putstr(si_ic(si), appctx->st1 ? "\n> " : "\n") != -1)
1865 appctx->st0 = STAT_CLI_GETREQ;
1866 else
1867 si_applet_cant_put(si);
1868 }
1869
1870 /* If the output functions are still there, it means they require more room. */
1871 if (appctx->st0 >= STAT_CLI_OUTPUT)
1872 break;
1873
1874 /* Now we close the output if one of the writers did so,
1875 * or if we're not in interactive mode and the request
1876 * buffer is empty. This still allows pipelined requests
1877 * to be sent in non-interactive mode.
1878 */
1879 if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!appctx->st1 && !req->buf->o)) {
1880 appctx->st0 = STAT_CLI_END;
1881 continue;
1882 }
1883
1884 /* switch state back to GETREQ to read next requests */
1885 appctx->st0 = STAT_CLI_GETREQ;
1886 }
1887 }
1888
1889 if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST)) {
1890 DPRINTF(stderr, "%s@%d: si to buf closed. req=%08x, res=%08x, st=%d\n",
1891 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
1892 /* Other side has closed, let's abort if we have no more processing to do
1893 * and nothing more to consume. This is comparable to a broken pipe, so
1894 * we forward the close to the request side so that it flows upstream to
1895 * the client.
1896 */
1897 si_shutw(si);
1898 }
1899
1900 if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && (appctx->st0 < STAT_CLI_OUTPUT)) {
1901 DPRINTF(stderr, "%s@%d: buf to si closed. req=%08x, res=%08x, st=%d\n",
1902 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
1903 /* We have no more processing to do, and nothing more to send, and
1904 * the client side has closed. So we'll forward this state downstream
1905 * on the response buffer.
1906 */
1907 si_shutr(si);
1908 res->flags |= CF_READ_NULL;
1909 }
1910
1911 out:
1912 DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rqh=%d, rqs=%d, rh=%d, rs=%d\n",
1913 __FUNCTION__, __LINE__,
1914 si->state, req->flags, res->flags, req->buf->i, req->buf->o, res->buf->i, res->buf->o);
1915}
1916
1917/* Dump all fields from <info> into <out> using the "show info" format (name: value) */
1918static int stats_dump_info_fields(struct chunk *out, const struct field *info)
1919{
1920 int field;
1921
1922 for (field = 0; field < INF_TOTAL_FIELDS; field++) {
1923 if (!field_format(info, field))
1924 continue;
1925
1926 if (!chunk_appendf(out, "%s: ", info_field_names[field]))
1927 return 0;
1928 if (!stats_emit_raw_data_field(out, &info[field]))
1929 return 0;
1930 if (!chunk_strcat(out, "\n"))
1931 return 0;
1932 }
1933 return 1;
1934}
1935
1936/* Dump all fields from <info> into <out> using the "show info typed" format */
1937static int stats_dump_typed_info_fields(struct chunk *out, const struct field *info)
1938{
1939 int field;
1940
1941 for (field = 0; field < INF_TOTAL_FIELDS; field++) {
1942 if (!field_format(info, field))
1943 continue;
1944
1945 if (!chunk_appendf(out, "%d.%s.%u:", field, info_field_names[field], info[INF_PROCESS_NUM].u.u32))
1946 return 0;
1947 if (!stats_emit_field_tags(out, &info[field], ':'))
1948 return 0;
1949 if (!stats_emit_typed_data_field(out, &info[field]))
1950 return 0;
1951 if (!chunk_strcat(out, "\n"))
1952 return 0;
1953 }
1954 return 1;
1955}
1956
1957/* Fill <info> with HAProxy global info. <info> is preallocated
1958 * array of length <len>. The length of the aray must be
1959 * INF_TOTAL_FIELDS. If this length is less then this value, the
1960 * function returns 0, otherwise, it returns 1.
1961 */
1962int stats_fill_info(struct field *info, int len)
1963{
1964 unsigned int up = (now.tv_sec - start_date.tv_sec);
1965 struct chunk *out = get_trash_chunk();
1966
1967#ifdef USE_OPENSSL
1968 int ssl_sess_rate = read_freq_ctr(&global.ssl_per_sec);
1969 int ssl_key_rate = read_freq_ctr(&global.ssl_fe_keys_per_sec);
1970 int ssl_reuse = 0;
1971
1972 if (ssl_key_rate < ssl_sess_rate) {
1973 /* count the ssl reuse ratio and avoid overflows in both directions */
1974 ssl_reuse = 100 - (100 * ssl_key_rate + (ssl_sess_rate - 1) / 2) / ssl_sess_rate;
1975 }
1976#endif
1977
1978 if (len < INF_TOTAL_FIELDS)
1979 return 0;
1980
1981 chunk_reset(out);
1982 memset(info, 0, sizeof(*info) * len);
1983
1984 info[INF_NAME] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, PRODUCT_NAME);
1985 info[INF_VERSION] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, HAPROXY_VERSION);
1986 info[INF_RELEASE_DATE] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, HAPROXY_DATE);
1987
1988 info[INF_NBPROC] = mkf_u32(FO_CONFIG|FS_SERVICE, global.nbproc);
1989 info[INF_PROCESS_NUM] = mkf_u32(FO_KEY, relative_pid);
1990 info[INF_PID] = mkf_u32(FO_STATUS, pid);
1991
1992 info[INF_UPTIME] = mkf_str(FN_DURATION, chunk_newstr(out));
1993 chunk_appendf(out, "%ud %uh%02um%02us", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
1994
1995 info[INF_UPTIME_SEC] = mkf_u32(FN_DURATION, up);
1996 info[INF_MEMMAX_MB] = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_memmax);
1997 info[INF_POOL_ALLOC_MB] = mkf_u32(0, (unsigned)(pool_total_allocated() / 1048576L));
1998 info[INF_POOL_USED_MB] = mkf_u32(0, (unsigned)(pool_total_used() / 1048576L));
1999 info[INF_POOL_FAILED] = mkf_u32(FN_COUNTER, pool_total_failures());
2000 info[INF_ULIMIT_N] = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_nofile);
2001 info[INF_MAXSOCK] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxsock);
2002 info[INF_MAXCONN] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxconn);
2003 info[INF_HARD_MAXCONN] = mkf_u32(FO_CONFIG|FN_LIMIT, global.hardmaxconn);
2004 info[INF_CURR_CONN] = mkf_u32(0, actconn);
2005 info[INF_CUM_CONN] = mkf_u32(FN_COUNTER, totalconn);
2006 info[INF_CUM_REQ] = mkf_u32(FN_COUNTER, global.req_count);
2007#ifdef USE_OPENSSL
2008 info[INF_MAX_SSL_CONNS] = mkf_u32(FN_MAX, global.maxsslconn);
2009 info[INF_CURR_SSL_CONNS] = mkf_u32(0, sslconns);
2010 info[INF_CUM_SSL_CONNS] = mkf_u32(FN_COUNTER, totalsslconns);
2011#endif
2012 info[INF_MAXPIPES] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxpipes);
2013 info[INF_PIPES_USED] = mkf_u32(0, pipes_used);
2014 info[INF_PIPES_FREE] = mkf_u32(0, pipes_free);
2015 info[INF_CONN_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.conn_per_sec));
2016 info[INF_CONN_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.cps_lim);
2017 info[INF_MAX_CONN_RATE] = mkf_u32(FN_MAX, global.cps_max);
2018 info[INF_SESS_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.sess_per_sec));
2019 info[INF_SESS_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.sps_lim);
2020 info[INF_MAX_SESS_RATE] = mkf_u32(FN_RATE, global.sps_max);
2021
2022#ifdef USE_OPENSSL
2023 info[INF_SSL_RATE] = mkf_u32(FN_RATE, ssl_sess_rate);
2024 info[INF_SSL_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.ssl_lim);
2025 info[INF_MAX_SSL_RATE] = mkf_u32(FN_MAX, global.ssl_max);
2026 info[INF_SSL_FRONTEND_KEY_RATE] = mkf_u32(0, ssl_key_rate);
2027 info[INF_SSL_FRONTEND_MAX_KEY_RATE] = mkf_u32(FN_MAX, global.ssl_fe_keys_max);
2028 info[INF_SSL_FRONTEND_SESSION_REUSE_PCT] = mkf_u32(0, ssl_reuse);
2029 info[INF_SSL_BACKEND_KEY_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.ssl_be_keys_per_sec));
2030 info[INF_SSL_BACKEND_MAX_KEY_RATE] = mkf_u32(FN_MAX, global.ssl_be_keys_max);
2031 info[INF_SSL_CACHE_LOOKUPS] = mkf_u32(FN_COUNTER, global.shctx_lookups);
2032 info[INF_SSL_CACHE_MISSES] = mkf_u32(FN_COUNTER, global.shctx_misses);
2033#endif
2034 info[INF_COMPRESS_BPS_IN] = mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_in));
2035 info[INF_COMPRESS_BPS_OUT] = mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_out));
2036 info[INF_COMPRESS_BPS_RATE_LIM] = mkf_u32(FO_CONFIG|FN_LIMIT, global.comp_rate_lim);
2037#ifdef USE_ZLIB
2038 info[INF_ZLIB_MEM_USAGE] = mkf_u32(0, zlib_used_memory);
2039 info[INF_MAX_ZLIB_MEM_USAGE] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxzlibmem);
2040#endif
2041 info[INF_TASKS] = mkf_u32(0, nb_tasks_cur);
2042 info[INF_RUN_QUEUE] = mkf_u32(0, run_queue_cur);
2043 info[INF_IDLE_PCT] = mkf_u32(FN_AVG, idle_pct);
2044 info[INF_NODE] = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.node);
2045 if (global.desc)
2046 info[INF_DESCRIPTION] = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.desc);
2047
2048 return 1;
2049}
2050
2051/* This function dumps information onto the stream interface's read buffer.
2052 * It returns 0 as long as it does not complete, non-zero upon completion.
2053 * No state is used.
2054 */
2055static int stats_dump_info_to_buffer(struct stream_interface *si)
2056{
2057 struct appctx *appctx = __objt_appctx(si->end);
2058
2059 if (!stats_fill_info(info, INF_TOTAL_FIELDS))
2060 return 0;
2061
2062 chunk_reset(&trash);
2063
2064 if (appctx->ctx.stats.flags & STAT_FMT_TYPED)
2065 stats_dump_typed_info_fields(&trash, info);
2066 else
2067 stats_dump_info_fields(&trash, info);
2068
2069 if (bi_putchk(si_ic(si), &trash) == -1) {
2070 si_applet_cant_put(si);
2071 return 0;
2072 }
2073
2074 return 1;
2075}
2076
William Lallemand74c24fb2016-11-21 17:18:36 +01002077
William Lallemand74c24fb2016-11-21 17:18:36 +01002078/* This is called when the stream interface is closed. For instance, upon an
2079 * external abort, we won't call the i/o handler anymore so we may need to
2080 * remove back references to the stream currently being dumped.
2081 */
2082static void cli_release_handler(struct appctx *appctx)
2083{
2084 if (appctx->io_release) {
2085 appctx->io_release(appctx);
2086 appctx->io_release = NULL;
2087 }
William Lallemand74c24fb2016-11-21 17:18:36 +01002088 else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) &&
2089 appctx->st2 == STAT_ST_LIST) {
2090 appctx->ctx.table.entry->ref_cnt--;
2091 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
2092 }
2093 else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
2094 free(appctx->ctx.cli.err);
2095 appctx->ctx.cli.err = NULL;
2096 }
William Lallemand74c24fb2016-11-21 17:18:36 +01002097}
2098
2099/* This function is used to either dump tables states (when action is set
2100 * to STAT_CLI_O_TAB) or clear tables (when action is STAT_CLI_O_CLR).
2101 * It returns 0 if the output buffer is full and it needs to be called
2102 * again, otherwise non-zero.
2103 */
2104static int stats_table_request(struct stream_interface *si, int action)
2105{
2106 struct appctx *appctx = __objt_appctx(si->end);
2107 struct stream *s = si_strm(si);
2108 struct ebmb_node *eb;
2109 int dt;
2110 int skip_entry;
2111 int show = action == STAT_CLI_O_TAB;
2112
2113 /*
2114 * We have 3 possible states in appctx->st2 :
2115 * - STAT_ST_INIT : the first call
2116 * - STAT_ST_INFO : the proxy pointer points to the next table to
2117 * dump, the entry pointer is NULL ;
2118 * - STAT_ST_LIST : the proxy pointer points to the current table
2119 * and the entry pointer points to the next entry to be dumped,
2120 * and the refcount on the next entry is held ;
2121 * - STAT_ST_END : nothing left to dump, the buffer may contain some
2122 * data though.
2123 */
2124
2125 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
2126 /* in case of abort, remove any refcount we might have set on an entry */
2127 if (appctx->st2 == STAT_ST_LIST) {
2128 appctx->ctx.table.entry->ref_cnt--;
2129 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
2130 }
2131 return 1;
2132 }
2133
2134 chunk_reset(&trash);
2135
2136 while (appctx->st2 != STAT_ST_FIN) {
2137 switch (appctx->st2) {
2138 case STAT_ST_INIT:
2139 appctx->ctx.table.proxy = appctx->ctx.table.target;
2140 if (!appctx->ctx.table.proxy)
2141 appctx->ctx.table.proxy = proxy;
2142
2143 appctx->ctx.table.entry = NULL;
2144 appctx->st2 = STAT_ST_INFO;
2145 break;
2146
2147 case STAT_ST_INFO:
2148 if (!appctx->ctx.table.proxy ||
2149 (appctx->ctx.table.target &&
2150 appctx->ctx.table.proxy != appctx->ctx.table.target)) {
2151 appctx->st2 = STAT_ST_END;
2152 break;
2153 }
2154
2155 if (appctx->ctx.table.proxy->table.size) {
2156 if (show && !stats_dump_table_head_to_buffer(&trash, si, appctx->ctx.table.proxy,
2157 appctx->ctx.table.target))
2158 return 0;
2159
2160 if (appctx->ctx.table.target &&
2161 strm_li(s)->bind_conf->level >= ACCESS_LVL_OPER) {
2162 /* dump entries only if table explicitly requested */
2163 eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
2164 if (eb) {
2165 appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
2166 appctx->ctx.table.entry->ref_cnt++;
2167 appctx->st2 = STAT_ST_LIST;
2168 break;
2169 }
2170 }
2171 }
2172 appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
2173 break;
2174
2175 case STAT_ST_LIST:
2176 skip_entry = 0;
2177
2178 if (appctx->ctx.table.data_type >= 0) {
2179 /* we're filtering on some data contents */
2180 void *ptr;
2181 long long data;
2182
2183 dt = appctx->ctx.table.data_type;
2184 ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
2185 appctx->ctx.table.entry,
2186 dt);
2187
2188 data = 0;
2189 switch (stktable_data_types[dt].std_type) {
2190 case STD_T_SINT:
2191 data = stktable_data_cast(ptr, std_t_sint);
2192 break;
2193 case STD_T_UINT:
2194 data = stktable_data_cast(ptr, std_t_uint);
2195 break;
2196 case STD_T_ULL:
2197 data = stktable_data_cast(ptr, std_t_ull);
2198 break;
2199 case STD_T_FRQP:
2200 data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
2201 appctx->ctx.table.proxy->table.data_arg[dt].u);
2202 break;
2203 }
2204
2205 /* skip the entry if the data does not match the test and the value */
2206 if ((data < appctx->ctx.table.value &&
2207 (appctx->ctx.table.data_op == STD_OP_EQ ||
2208 appctx->ctx.table.data_op == STD_OP_GT ||
2209 appctx->ctx.table.data_op == STD_OP_GE)) ||
2210 (data == appctx->ctx.table.value &&
2211 (appctx->ctx.table.data_op == STD_OP_NE ||
2212 appctx->ctx.table.data_op == STD_OP_GT ||
2213 appctx->ctx.table.data_op == STD_OP_LT)) ||
2214 (data > appctx->ctx.table.value &&
2215 (appctx->ctx.table.data_op == STD_OP_EQ ||
2216 appctx->ctx.table.data_op == STD_OP_LT ||
2217 appctx->ctx.table.data_op == STD_OP_LE)))
2218 skip_entry = 1;
2219 }
2220
2221 if (show && !skip_entry &&
2222 !stats_dump_table_entry_to_buffer(&trash, si, appctx->ctx.table.proxy,
2223 appctx->ctx.table.entry))
2224 return 0;
2225
2226 appctx->ctx.table.entry->ref_cnt--;
2227
2228 eb = ebmb_next(&appctx->ctx.table.entry->key);
2229 if (eb) {
2230 struct stksess *old = appctx->ctx.table.entry;
2231 appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
2232 if (show)
2233 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
2234 else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
2235 stksess_kill(&appctx->ctx.table.proxy->table, old);
2236 appctx->ctx.table.entry->ref_cnt++;
2237 break;
2238 }
2239
2240
2241 if (show)
2242 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
2243 else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
2244 stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
2245
2246 appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
2247 appctx->st2 = STAT_ST_INFO;
2248 break;
2249
2250 case STAT_ST_END:
2251 appctx->st2 = STAT_ST_FIN;
2252 break;
2253 }
2254 }
2255 return 1;
2256}
2257
2258/* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
2259 * <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
2260 * which is 60 chars per line. Non-printable chars \t, \n, \r and \e are
2261 * encoded in C format. Other non-printable chars are encoded "\xHH". Original
2262 * lines are respected within the limit of 70 output chars. Lines that are
2263 * continuation of a previous truncated line begin with "+" instead of " "
2264 * after the offset. The new pointer is returned.
2265 */
2266static int dump_text_line(struct chunk *out, const char *buf, int bsize, int len,
2267 int *line, int ptr)
2268{
2269 int end;
2270 unsigned char c;
2271
2272 end = out->len + 80;
2273 if (end > out->size)
2274 return ptr;
2275
2276 chunk_appendf(out, " %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
2277
2278 while (ptr < len && ptr < bsize) {
2279 c = buf[ptr];
2280 if (isprint(c) && isascii(c) && c != '\\') {
2281 if (out->len > end - 2)
2282 break;
2283 out->str[out->len++] = c;
2284 } else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\') {
2285 if (out->len > end - 3)
2286 break;
2287 out->str[out->len++] = '\\';
2288 switch (c) {
2289 case '\t': c = 't'; break;
2290 case '\n': c = 'n'; break;
2291 case '\r': c = 'r'; break;
2292 case '\e': c = 'e'; break;
2293 case '\\': c = '\\'; break;
2294 }
2295 out->str[out->len++] = c;
2296 } else {
2297 if (out->len > end - 5)
2298 break;
2299 out->str[out->len++] = '\\';
2300 out->str[out->len++] = 'x';
2301 out->str[out->len++] = hextab[(c >> 4) & 0xF];
2302 out->str[out->len++] = hextab[c & 0xF];
2303 }
2304 if (buf[ptr++] == '\n') {
2305 /* we had a line break, let's return now */
2306 out->str[out->len++] = '\n';
2307 *line = ptr;
2308 return ptr;
2309 }
2310 }
2311 /* we have an incomplete line, we return it as-is */
2312 out->str[out->len++] = '\n';
2313 return ptr;
2314}
2315
William Lallemand74c24fb2016-11-21 17:18:36 +01002316/* This function dumps all captured errors onto the stream interface's
2317 * read buffer. It returns 0 if the output buffer is full and it needs
2318 * to be called again, otherwise non-zero.
2319 */
2320static int stats_dump_errors_to_buffer(struct stream_interface *si)
2321{
2322 struct appctx *appctx = __objt_appctx(si->end);
2323 extern const char *monthname[12];
2324
2325 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
2326 return 1;
2327
2328 chunk_reset(&trash);
2329
2330 if (!appctx->ctx.errors.px) {
2331 /* the function had not been called yet, let's prepare the
2332 * buffer for a response.
2333 */
2334 struct tm tm;
2335
2336 get_localtime(date.tv_sec, &tm);
2337 chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
2338 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
2339 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
2340 error_snapshot_id);
2341
2342 if (bi_putchk(si_ic(si), &trash) == -1) {
2343 /* Socket buffer full. Let's try again later from the same point */
2344 si_applet_cant_put(si);
2345 return 0;
2346 }
2347
2348 appctx->ctx.errors.px = proxy;
2349 appctx->ctx.errors.buf = 0;
2350 appctx->ctx.errors.bol = 0;
2351 appctx->ctx.errors.ptr = -1;
2352 }
2353
2354 /* we have two inner loops here, one for the proxy, the other one for
2355 * the buffer.
2356 */
2357 while (appctx->ctx.errors.px) {
2358 struct error_snapshot *es;
2359
2360 if (appctx->ctx.errors.buf == 0)
2361 es = &appctx->ctx.errors.px->invalid_req;
2362 else
2363 es = &appctx->ctx.errors.px->invalid_rep;
2364
2365 if (!es->when.tv_sec)
2366 goto next;
2367
2368 if (appctx->ctx.errors.iid >= 0 &&
2369 appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
2370 es->oe->uuid != appctx->ctx.errors.iid)
2371 goto next;
2372
2373 if (appctx->ctx.errors.ptr < 0) {
2374 /* just print headers now */
2375
2376 char pn[INET6_ADDRSTRLEN];
2377 struct tm tm;
2378 int port;
2379
2380 get_localtime(es->when.tv_sec, &tm);
2381 chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
2382 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
2383 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
2384
2385 switch (addr_to_str(&es->src, pn, sizeof(pn))) {
2386 case AF_INET:
2387 case AF_INET6:
2388 port = get_host_port(&es->src);
2389 break;
2390 default:
2391 port = 0;
2392 }
2393
2394 switch (appctx->ctx.errors.buf) {
2395 case 0:
2396 chunk_appendf(&trash,
2397 " frontend %s (#%d): invalid request\n"
2398 " backend %s (#%d)",
2399 appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
2400 (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
2401 (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
2402 break;
2403 case 1:
2404 chunk_appendf(&trash,
2405 " backend %s (#%d): invalid response\n"
2406 " frontend %s (#%d)",
2407 appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
2408 es->oe->id, es->oe->uuid);
2409 break;
2410 }
2411
2412 chunk_appendf(&trash,
2413 ", server %s (#%d), event #%u\n"
2414 " src %s:%d, session #%d, session flags 0x%08x\n"
2415 " HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
2416 " HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
2417 " buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
2418 " pending %d bytes, wrapping at %d, error at position %d:\n \n",
2419 es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
2420 es->ev_id,
2421 pn, port, es->sid, es->s_flags,
2422 es->state, es->m_flags, es->t_flags,
2423 es->m_clen, es->m_blen,
2424 es->b_flags, es->b_out, es->b_tot,
2425 es->len, es->b_wrap, es->pos);
2426
2427 if (bi_putchk(si_ic(si), &trash) == -1) {
2428 /* Socket buffer full. Let's try again later from the same point */
2429 si_applet_cant_put(si);
2430 return 0;
2431 }
2432 appctx->ctx.errors.ptr = 0;
2433 appctx->ctx.errors.sid = es->sid;
2434 }
2435
2436 if (appctx->ctx.errors.sid != es->sid) {
2437 /* the snapshot changed while we were dumping it */
2438 chunk_appendf(&trash,
2439 " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
2440 if (bi_putchk(si_ic(si), &trash) == -1) {
2441 si_applet_cant_put(si);
2442 return 0;
2443 }
2444 goto next;
2445 }
2446
2447 /* OK, ptr >= 0, so we have to dump the current line */
2448 while (es->buf && appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < global.tune.bufsize) {
2449 int newptr;
2450 int newline;
2451
2452 newline = appctx->ctx.errors.bol;
2453 newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->len, &newline, appctx->ctx.errors.ptr);
2454 if (newptr == appctx->ctx.errors.ptr)
2455 return 0;
2456
2457 if (bi_putchk(si_ic(si), &trash) == -1) {
2458 /* Socket buffer full. Let's try again later from the same point */
2459 si_applet_cant_put(si);
2460 return 0;
2461 }
2462 appctx->ctx.errors.ptr = newptr;
2463 appctx->ctx.errors.bol = newline;
2464 };
2465 next:
2466 appctx->ctx.errors.bol = 0;
2467 appctx->ctx.errors.ptr = -1;
2468 appctx->ctx.errors.buf++;
2469 if (appctx->ctx.errors.buf > 1) {
2470 appctx->ctx.errors.buf = 0;
2471 appctx->ctx.errors.px = appctx->ctx.errors.px->next;
2472 }
2473 }
2474
2475 /* dump complete */
2476 return 1;
2477}
2478
2479/* This function dumps all environmnent variables to the buffer. It returns 0
2480 * if the output buffer is full and it needs to be called again, otherwise
2481 * non-zero. Dumps only one entry if st2 == STAT_ST_END.
2482 */
2483static int stats_dump_env_to_buffer(struct stream_interface *si)
2484{
2485 struct appctx *appctx = __objt_appctx(si->end);
2486
2487 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
2488 return 1;
2489
2490 chunk_reset(&trash);
2491
2492 /* we have two inner loops here, one for the proxy, the other one for
2493 * the buffer.
2494 */
2495 while (*appctx->ctx.env.var) {
2496 chunk_printf(&trash, "%s\n", *appctx->ctx.env.var);
2497
2498 if (bi_putchk(si_ic(si), &trash) == -1) {
2499 si_applet_cant_put(si);
2500 return 0;
2501 }
2502 if (appctx->st2 == STAT_ST_END)
2503 break;
2504 appctx->ctx.env.var++;
2505 }
2506
2507 /* dump complete */
2508 return 1;
2509}
2510
2511/* parse the "level" argument on the bind lines */
2512static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
2513{
2514 if (!*args[cur_arg + 1]) {
2515 memprintf(err, "'%s' : missing level", args[cur_arg]);
2516 return ERR_ALERT | ERR_FATAL;
2517 }
2518
2519 if (!strcmp(args[cur_arg+1], "user"))
2520 conf->level = ACCESS_LVL_USER;
2521 else if (!strcmp(args[cur_arg+1], "operator"))
2522 conf->level = ACCESS_LVL_OPER;
2523 else if (!strcmp(args[cur_arg+1], "admin"))
2524 conf->level = ACCESS_LVL_ADMIN;
2525 else {
2526 memprintf(err, "'%s' only supports 'user', 'operator', and 'admin' (got '%s')",
2527 args[cur_arg], args[cur_arg+1]);
2528 return ERR_ALERT | ERR_FATAL;
2529 }
2530
2531 return 0;
2532}
2533
2534static struct applet cli_applet = {
2535 .obj_type = OBJ_TYPE_APPLET,
2536 .name = "<CLI>", /* used for logging */
2537 .fct = cli_io_handler,
2538 .release = cli_release_handler,
2539};
2540
2541static struct cfg_kw_list cfg_kws = {ILH, {
2542 { CFG_GLOBAL, "stats", stats_parse_global },
2543 { 0, NULL, NULL },
2544}};
2545
2546static struct bind_kw_list bind_kws = { "STAT", { }, {
2547 { "level", bind_parse_level, 1 }, /* set the unix socket admin level */
2548 { NULL, NULL, 0 },
2549}};
2550
2551__attribute__((constructor))
2552static void __dumpstats_module_init(void)
2553{
2554 cfg_register_keywords(&cfg_kws);
2555 bind_register_keywords(&bind_kws);
2556}
2557
2558/*
2559 * Local variables:
2560 * c-indent-level: 8
2561 * c-basic-offset: 8
2562 * End:
2563 */