blob: 7565c80391269b08e3acc7e23fa823c385b6edf5 [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
136static int stats_dump_backend_to_buffer(struct stream_interface *si);
137static int stats_dump_env_to_buffer(struct stream_interface *si);
138static int stats_dump_info_to_buffer(struct stream_interface *si);
139static int stats_dump_servers_state_to_buffer(struct stream_interface *si);
140static int stats_dump_pools_to_buffer(struct stream_interface *si);
141static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess);
142static int stats_dump_sess_to_buffer(struct stream_interface *si);
143static int stats_dump_errors_to_buffer(struct stream_interface *si);
144static int stats_table_request(struct stream_interface *si, int show);
145static int stats_dump_resolvers_to_buffer(struct stream_interface *si);
146static int stats_pats_list(struct stream_interface *si);
147static int stats_pat_list(struct stream_interface *si);
148static int stats_map_lookup(struct stream_interface *si);
William Lallemand74c24fb2016-11-21 17:18:36 +0100149
150static int dump_servers_state(struct stream_interface *si, struct chunk *buf);
151
152static struct applet cli_applet;
153
154static const char stats_sock_usage_msg[] =
155 "Unknown command. Please enter one of the following commands only :\n"
156 " clear counters : clear max statistics counters (add 'all' for all counters)\n"
157 " clear table : remove an entry from a table\n"
158 " help : this message\n"
159 " prompt : toggle interactive mode with prompt\n"
160 " quit : disconnect\n"
161 " show backend : list backends in the current running config\n"
162 " show env [var] : dump environment variables known to the process\n"
163 " show info : report information about the running process\n"
164 " show pools : report information about the memory pools usage\n"
165 " show stat : report counters for each proxy and server\n"
166 " show stat resolvers [id]: dumps counters from all resolvers section and\n"
167 " associated name servers\n"
168 " show errors : report last request and response errors for each proxy\n"
169 " show sess [id] : report the list of current sessions or dump this session\n"
170 " show table [id]: report table usage stats or dump this table's contents\n"
171 " show servers state [id]: dump volatile server information (for backend <id>)\n"
172 " get weight : report a server's current weight\n"
173 " set weight : change a server's weight\n"
174 " set server : change a server's state, weight or address\n"
175 " set table [id] : update or create a table entry's data\n"
176 " set timeout : change a timeout setting\n"
177 " set maxconn : change a maxconn setting\n"
178 " set rate-limit : change a rate limiting value\n"
179 " disable : put a server or frontend in maintenance mode\n"
180 " enable : re-enable a server or frontend which is in maintenance mode\n"
181 " shutdown : kill a session or a frontend (eg:to release listening ports)\n"
182 " show acl [id] : report available acls or dump an acl's contents\n"
183 " get acl : reports the patterns matching a sample for an ACL\n"
184 " add acl : add acl entry\n"
185 " del acl : delete acl entry\n"
186 " clear acl <id> : clear the content of this acl\n"
187 " show map [id] : report available maps or dump a map's contents\n"
188 " get map : reports the keys and values matching a sample for a map\n"
189 " set map : modify map entry\n"
190 " add map : add map entry\n"
191 " del map : delete map entry\n"
192 " clear map <id> : clear the content of this map\n"
William Lallemand74c24fb2016-11-21 17:18:36 +0100193 "";
194
195static const char stats_permission_denied_msg[] =
196 "Permission denied\n"
197 "";
198
199
200static char *dynamic_usage_msg = NULL;
201
202/* List head of cli keywords */
203static struct cli_kw_list cli_keywords = {
204 .list = LIST_HEAD_INIT(cli_keywords.list)
205};
206
207extern const char *stat_status_codes[];
208
209char *cli_gen_usage_msg()
210{
211 struct cli_kw_list *kw_list;
212 struct cli_kw *kw;
213 struct chunk *tmp = get_trash_chunk();
214 struct chunk out;
215
216 free(dynamic_usage_msg);
217 dynamic_usage_msg = NULL;
218
219 if (LIST_ISEMPTY(&cli_keywords.list))
220 return NULL;
221
222 chunk_reset(tmp);
223 chunk_strcat(tmp, stats_sock_usage_msg);
224 list_for_each_entry(kw_list, &cli_keywords.list, list) {
225 kw = &kw_list->kw[0];
226 while (kw->usage) {
227 chunk_appendf(tmp, " %s\n", kw->usage);
228 kw++;
229 }
230 }
231 chunk_init(&out, NULL, 0);
232 chunk_dup(&out, tmp);
233 dynamic_usage_msg = out.str;
234 return dynamic_usage_msg;
235}
236
237struct cli_kw* cli_find_kw(char **args)
238{
239 struct cli_kw_list *kw_list;
240 struct cli_kw *kw;/* current cli_kw */
241 char **tmp_args;
242 const char **tmp_str_kw;
243 int found = 0;
244
245 if (LIST_ISEMPTY(&cli_keywords.list))
246 return NULL;
247
248 list_for_each_entry(kw_list, &cli_keywords.list, list) {
249 kw = &kw_list->kw[0];
250 while (*kw->str_kw) {
251 tmp_args = args;
252 tmp_str_kw = kw->str_kw;
253 while (*tmp_str_kw) {
254 if (strcmp(*tmp_str_kw, *tmp_args) == 0) {
255 found = 1;
256 } else {
257 found = 0;
258 break;
259 }
260 tmp_args++;
261 tmp_str_kw++;
262 }
263 if (found)
264 return (kw);
265 kw++;
266 }
267 }
268 return NULL;
269}
270
271void cli_register_kw(struct cli_kw_list *kw_list)
272{
273 LIST_ADDQ(&cli_keywords.list, &kw_list->list);
274}
275
276
277/* allocate a new stats frontend named <name>, and return it
278 * (or NULL in case of lack of memory).
279 */
280static struct proxy *alloc_stats_fe(const char *name, const char *file, int line)
281{
282 struct proxy *fe;
283
284 fe = calloc(1, sizeof(*fe));
285 if (!fe)
286 return NULL;
287
288 init_new_proxy(fe);
289 fe->next = proxy;
290 proxy = fe;
291 fe->last_change = now.tv_sec;
292 fe->id = strdup("GLOBAL");
293 fe->cap = PR_CAP_FE;
294 fe->maxconn = 10; /* default to 10 concurrent connections */
295 fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
296 fe->conf.file = strdup(file);
297 fe->conf.line = line;
298 fe->accept = frontend_accept;
299 fe->default_target = &cli_applet.obj_type;
300
301 /* the stats frontend is the only one able to assign ID #0 */
302 fe->conf.id.key = fe->uuid = 0;
303 eb32_insert(&used_proxy_id, &fe->conf.id);
304 return fe;
305}
306
307/* This function parses a "stats" statement in the "global" section. It returns
308 * -1 if there is any error, otherwise zero. If it returns -1, it will write an
309 * error message into the <err> buffer which will be preallocated. The trailing
310 * '\n' must not be written. The function must be called with <args> pointing to
311 * the first word after "stats".
312 */
313static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
314 struct proxy *defpx, const char *file, int line,
315 char **err)
316{
317 struct bind_conf *bind_conf;
318 struct listener *l;
319
320 if (!strcmp(args[1], "socket")) {
321 int cur_arg;
322
323 if (*args[2] == 0) {
324 memprintf(err, "'%s %s' in global section expects an address or a path to a UNIX socket", args[0], args[1]);
325 return -1;
326 }
327
328 if (!global.stats_fe) {
329 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
330 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
331 return -1;
332 }
333 }
334
335 bind_conf = bind_conf_alloc(&global.stats_fe->conf.bind, file, line, args[2]);
336 bind_conf->level = ACCESS_LVL_OPER; /* default access level */
337
338 if (!str2listener(args[2], global.stats_fe, bind_conf, file, line, err)) {
339 memprintf(err, "parsing [%s:%d] : '%s %s' : %s\n",
340 file, line, args[0], args[1], err && *err ? *err : "error");
341 return -1;
342 }
343
344 cur_arg = 3;
345 while (*args[cur_arg]) {
346 static int bind_dumped;
347 struct bind_kw *kw;
348
349 kw = bind_find_kw(args[cur_arg]);
350 if (kw) {
351 if (!kw->parse) {
352 memprintf(err, "'%s %s' : '%s' option is not implemented in this version (check build options).",
353 args[0], args[1], args[cur_arg]);
354 return -1;
355 }
356
357 if (kw->parse(args, cur_arg, global.stats_fe, bind_conf, err) != 0) {
358 if (err && *err)
359 memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
360 else
361 memprintf(err, "'%s %s' : error encountered while processing '%s'",
362 args[0], args[1], args[cur_arg]);
363 return -1;
364 }
365
366 cur_arg += 1 + kw->skip;
367 continue;
368 }
369
370 if (!bind_dumped) {
371 bind_dump_kws(err);
372 indent_msg(err, 4);
373 bind_dumped = 1;
374 }
375
376 memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s",
377 args[0], args[1], args[cur_arg],
378 err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
379 return -1;
380 }
381
382 list_for_each_entry(l, &bind_conf->listeners, by_bind) {
383 l->maxconn = global.stats_fe->maxconn;
384 l->backlog = global.stats_fe->backlog;
385 l->accept = session_accept_fd;
386 l->handler = process_stream;
387 l->default_target = global.stats_fe->default_target;
388 l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
389 l->nice = -64; /* we want to boost priority for local stats */
390 global.maxsock += l->maxconn;
391 }
392 }
393 else if (!strcmp(args[1], "timeout")) {
394 unsigned timeout;
395 const char *res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
396
397 if (res) {
398 memprintf(err, "'%s %s' : unexpected character '%c'", args[0], args[1], *res);
399 return -1;
400 }
401
402 if (!timeout) {
403 memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
404 return -1;
405 }
406 if (!global.stats_fe) {
407 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
408 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
409 return -1;
410 }
411 }
412 global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
413 }
414 else if (!strcmp(args[1], "maxconn")) {
415 int maxconn = atol(args[2]);
416
417 if (maxconn <= 0) {
418 memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
419 return -1;
420 }
421
422 if (!global.stats_fe) {
423 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
424 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
425 return -1;
426 }
427 }
428 global.stats_fe->maxconn = maxconn;
429 }
430 else if (!strcmp(args[1], "bind-process")) { /* enable the socket only on some processes */
431 int cur_arg = 2;
432 unsigned long set = 0;
433
434 if (!global.stats_fe) {
435 if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
436 memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
437 return -1;
438 }
439 }
440
441 while (*args[cur_arg]) {
442 unsigned int low, high;
443
444 if (strcmp(args[cur_arg], "all") == 0) {
445 set = 0;
446 break;
447 }
448 else if (strcmp(args[cur_arg], "odd") == 0) {
449 set |= ~0UL/3UL; /* 0x555....555 */
450 }
451 else if (strcmp(args[cur_arg], "even") == 0) {
452 set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
453 }
454 else if (isdigit((int)*args[cur_arg])) {
455 char *dash = strchr(args[cur_arg], '-');
456
457 low = high = str2uic(args[cur_arg]);
458 if (dash)
459 high = str2uic(dash + 1);
460
461 if (high < low) {
462 unsigned int swap = low;
463 low = high;
464 high = swap;
465 }
466
467 if (low < 1 || high > LONGBITS) {
468 memprintf(err, "'%s %s' supports process numbers from 1 to %d.\n",
469 args[0], args[1], LONGBITS);
470 return -1;
471 }
472 while (low <= high)
473 set |= 1UL << (low++ - 1);
474 }
475 else {
476 memprintf(err,
477 "'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
478 args[0], args[1], LONGBITS);
479 return -1;
480 }
481 cur_arg++;
482 }
483 global.stats_fe->bind_proc = set;
484 }
485 else {
486 memprintf(err, "'%s' only supports 'socket', 'maxconn', 'bind-process' and 'timeout' (got '%s')", args[0], args[1]);
487 return -1;
488 }
489 return 0;
490}
491
492
493/* print a string of text buffer to <out>. The format is :
494 * Non-printable chars \t, \n, \r and \e are * encoded in C format.
495 * Other non-printable chars are encoded "\xHH". Space, '\', and '=' are also escaped.
496 * Print stopped if null char or <bsize> is reached, or if no more place in the chunk.
497 */
498static int dump_text(struct chunk *out, const char *buf, int bsize)
499{
500 unsigned char c;
501 int ptr = 0;
502
503 while (buf[ptr] && ptr < bsize) {
504 c = buf[ptr];
505 if (isprint(c) && isascii(c) && c != '\\' && c != ' ' && c != '=') {
506 if (out->len > out->size - 1)
507 break;
508 out->str[out->len++] = c;
509 }
510 else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\' || c == ' ' || c == '=') {
511 if (out->len > out->size - 2)
512 break;
513 out->str[out->len++] = '\\';
514 switch (c) {
515 case ' ': c = ' '; break;
516 case '\t': c = 't'; break;
517 case '\n': c = 'n'; break;
518 case '\r': c = 'r'; break;
519 case '\e': c = 'e'; break;
520 case '\\': c = '\\'; break;
521 case '=': c = '='; break;
522 }
523 out->str[out->len++] = c;
524 }
525 else {
526 if (out->len > out->size - 4)
527 break;
528 out->str[out->len++] = '\\';
529 out->str[out->len++] = 'x';
530 out->str[out->len++] = hextab[(c >> 4) & 0xF];
531 out->str[out->len++] = hextab[c & 0xF];
532 }
533 ptr++;
534 }
535
536 return ptr;
537}
538
539/* print a buffer in hexa.
540 * Print stopped if <bsize> is reached, or if no more place in the chunk.
541 */
542static int dump_binary(struct chunk *out, const char *buf, int bsize)
543{
544 unsigned char c;
545 int ptr = 0;
546
547 while (ptr < bsize) {
548 c = buf[ptr];
549
550 if (out->len > out->size - 2)
551 break;
552 out->str[out->len++] = hextab[(c >> 4) & 0xF];
553 out->str[out->len++] = hextab[c & 0xF];
554
555 ptr++;
556 }
557 return ptr;
558}
559
560/* Dump the status of a table to a stream interface's
561 * read buffer. It returns 0 if the output buffer is full
562 * and needs to be called again, otherwise non-zero.
563 */
564static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
565 struct proxy *proxy, struct proxy *target)
566{
567 struct stream *s = si_strm(si);
568
569 chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
570 proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
571
572 /* any other information should be dumped here */
573
574 if (target && strm_li(s)->bind_conf->level < ACCESS_LVL_OPER)
575 chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
576
577 if (bi_putchk(si_ic(si), msg) == -1) {
578 si_applet_cant_put(si);
579 return 0;
580 }
581
582 return 1;
583}
584
585/* Dump the a table entry to a stream interface's
586 * read buffer. It returns 0 if the output buffer is full
587 * and needs to be called again, otherwise non-zero.
588 */
589static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
590 struct proxy *proxy, struct stksess *entry)
591{
592 int dt;
593
594 chunk_appendf(msg, "%p:", entry);
595
596 if (proxy->table.type == SMP_T_IPV4) {
597 char addr[INET_ADDRSTRLEN];
598 inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
599 chunk_appendf(msg, " key=%s", addr);
600 }
601 else if (proxy->table.type == SMP_T_IPV6) {
602 char addr[INET6_ADDRSTRLEN];
603 inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
604 chunk_appendf(msg, " key=%s", addr);
605 }
606 else if (proxy->table.type == SMP_T_SINT) {
607 chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
608 }
609 else if (proxy->table.type == SMP_T_STR) {
610 chunk_appendf(msg, " key=");
611 dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
612 }
613 else {
614 chunk_appendf(msg, " key=");
615 dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
616 }
617
618 chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
619
620 for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
621 void *ptr;
622
623 if (proxy->table.data_ofs[dt] == 0)
624 continue;
625 if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
626 chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
627 else
628 chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
629
630 ptr = stktable_data_ptr(&proxy->table, entry, dt);
631 switch (stktable_data_types[dt].std_type) {
632 case STD_T_SINT:
633 chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
634 break;
635 case STD_T_UINT:
636 chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
637 break;
638 case STD_T_ULL:
639 chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
640 break;
641 case STD_T_FRQP:
642 chunk_appendf(msg, "%d",
643 read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
644 proxy->table.data_arg[dt].u));
645 break;
646 }
647 }
648 chunk_appendf(msg, "\n");
649
650 if (bi_putchk(si_ic(si), msg) == -1) {
651 si_applet_cant_put(si);
652 return 0;
653 }
654
655 return 1;
656}
657
658static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
659{
660 struct stream *s = si_strm(si);
661 struct appctx *appctx = __objt_appctx(si->end);
662 struct proxy *px = appctx->ctx.table.target;
663 struct stksess *ts;
664 uint32_t uint32_key;
665 unsigned char ip6_key[sizeof(struct in6_addr)];
666 long long value;
667 int data_type;
668 int cur_arg;
669 void *ptr;
670 struct freq_ctr_period *frqp;
671
672 appctx->st0 = STAT_CLI_OUTPUT;
673
674 if (!*args[4]) {
675 appctx->ctx.cli.msg = "Key value expected\n";
676 appctx->st0 = STAT_CLI_PRINT;
677 return;
678 }
679
680 switch (px->table.type) {
681 case SMP_T_IPV4:
682 uint32_key = htonl(inetaddr_host(args[4]));
683 static_table_key->key = &uint32_key;
684 break;
685 case SMP_T_IPV6:
686 inet_pton(AF_INET6, args[4], ip6_key);
687 static_table_key->key = &ip6_key;
688 break;
689 case SMP_T_SINT:
690 {
691 char *endptr;
692 unsigned long val;
693 errno = 0;
694 val = strtoul(args[4], &endptr, 10);
695 if ((errno == ERANGE && val == ULONG_MAX) ||
696 (errno != 0 && val == 0) || endptr == args[4] ||
697 val > 0xffffffff) {
698 appctx->ctx.cli.msg = "Invalid key\n";
699 appctx->st0 = STAT_CLI_PRINT;
700 return;
701 }
702 uint32_key = (uint32_t) val;
703 static_table_key->key = &uint32_key;
704 break;
705 }
706 break;
707 case SMP_T_STR:
708 static_table_key->key = args[4];
709 static_table_key->key_len = strlen(args[4]);
710 break;
711 default:
712 switch (action) {
713 case STAT_CLI_O_TAB:
714 appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
715 break;
716 case STAT_CLI_O_CLR:
717 appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
718 break;
719 default:
720 appctx->ctx.cli.msg = "Unknown action\n";
721 break;
722 }
723 appctx->st0 = STAT_CLI_PRINT;
724 return;
725 }
726
727 /* check permissions */
728 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
729 appctx->ctx.cli.msg = stats_permission_denied_msg;
730 appctx->st0 = STAT_CLI_PRINT;
731 return;
732 }
733
734 ts = stktable_lookup_key(&px->table, static_table_key);
735
736 switch (action) {
737 case STAT_CLI_O_TAB:
738 if (!ts)
739 return;
740 chunk_reset(&trash);
741 if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
742 return;
743 stats_dump_table_entry_to_buffer(&trash, si, px, ts);
744 return;
745
746 case STAT_CLI_O_CLR:
747 if (!ts)
748 return;
749 if (ts->ref_cnt) {
750 /* don't delete an entry which is currently referenced */
751 appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
752 appctx->st0 = STAT_CLI_PRINT;
753 return;
754 }
755 stksess_kill(&px->table, ts);
756 break;
757
758 case STAT_CLI_O_SET:
759 if (ts)
760 stktable_touch(&px->table, ts, 1);
761 else {
762 ts = stksess_new(&px->table, static_table_key);
763 if (!ts) {
764 /* don't delete an entry which is currently referenced */
765 appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
766 appctx->st0 = STAT_CLI_PRINT;
767 return;
768 }
769 stktable_store(&px->table, ts, 1);
770 }
771
772 for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
773 if (strncmp(args[cur_arg], "data.", 5) != 0) {
774 appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
775 appctx->st0 = STAT_CLI_PRINT;
776 return;
777 }
778
779 data_type = stktable_get_data_type(args[cur_arg] + 5);
780 if (data_type < 0) {
781 appctx->ctx.cli.msg = "Unknown data type\n";
782 appctx->st0 = STAT_CLI_PRINT;
783 return;
784 }
785
786 if (!px->table.data_ofs[data_type]) {
787 appctx->ctx.cli.msg = "Data type not stored in this table\n";
788 appctx->st0 = STAT_CLI_PRINT;
789 return;
790 }
791
792 if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
793 appctx->ctx.cli.msg = "Require a valid integer value to store\n";
794 appctx->st0 = STAT_CLI_PRINT;
795 return;
796 }
797
798 ptr = stktable_data_ptr(&px->table, ts, data_type);
799
800 switch (stktable_data_types[data_type].std_type) {
801 case STD_T_SINT:
802 stktable_data_cast(ptr, std_t_sint) = value;
803 break;
804 case STD_T_UINT:
805 stktable_data_cast(ptr, std_t_uint) = value;
806 break;
807 case STD_T_ULL:
808 stktable_data_cast(ptr, std_t_ull) = value;
809 break;
810 case STD_T_FRQP:
811 /* We set both the current and previous values. That way
812 * the reported frequency is stable during all the period
813 * then slowly fades out. This allows external tools to
814 * push measures without having to update them too often.
815 */
816 frqp = &stktable_data_cast(ptr, std_t_frqp);
817 frqp->curr_tick = now_ms;
818 frqp->prev_ctr = 0;
819 frqp->curr_ctr = value;
820 break;
821 }
822 }
823 break;
824
825 default:
826 appctx->ctx.cli.msg = "Unknown action\n";
827 appctx->st0 = STAT_CLI_PRINT;
828 break;
829 }
830}
831
832static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
833{
834 struct appctx *appctx = __objt_appctx(si->end);
835
836 if (action != STAT_CLI_O_TAB && action != STAT_CLI_O_CLR) {
837 appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
838 appctx->st0 = STAT_CLI_PRINT;
839 return;
840 }
841
842 /* condition on stored data value */
843 appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
844 if (appctx->ctx.table.data_type < 0) {
845 appctx->ctx.cli.msg = "Unknown data type\n";
846 appctx->st0 = STAT_CLI_PRINT;
847 return;
848 }
849
850 if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
851 appctx->ctx.cli.msg = "Data type not stored in this table\n";
852 appctx->st0 = STAT_CLI_PRINT;
853 return;
854 }
855
856 appctx->ctx.table.data_op = get_std_op(args[4]);
857 if (appctx->ctx.table.data_op < 0) {
858 appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
859 appctx->st0 = STAT_CLI_PRINT;
860 return;
861 }
862
863 if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
864 appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
865 appctx->st0 = STAT_CLI_PRINT;
866 return;
867 }
868}
869
870static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
871{
872 struct appctx *appctx = __objt_appctx(si->end);
873
874 appctx->ctx.table.data_type = -1;
875 appctx->st2 = STAT_ST_INIT;
876 appctx->ctx.table.target = NULL;
877 appctx->ctx.table.proxy = NULL;
878 appctx->ctx.table.entry = NULL;
879 appctx->st0 = action;
880
881 if (*args[2]) {
882 appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
883 if (!appctx->ctx.table.target) {
884 appctx->ctx.cli.msg = "No such table\n";
885 appctx->st0 = STAT_CLI_PRINT;
886 return;
887 }
888 }
889 else {
890 if (action != STAT_CLI_O_TAB)
891 goto err_args;
892 return;
893 }
894
895 if (strcmp(args[3], "key") == 0)
896 stats_sock_table_key_request(si, args, action);
897 else if (strncmp(args[3], "data.", 5) == 0)
898 stats_sock_table_data_request(si, args, action);
899 else if (*args[3])
900 goto err_args;
901
902 return;
903
904err_args:
905 switch (action) {
906 case STAT_CLI_O_TAB:
907 appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
908 break;
909 case STAT_CLI_O_CLR:
910 appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
911 break;
912 default:
913 appctx->ctx.cli.msg = "Unknown action\n";
914 break;
915 }
916 appctx->st0 = STAT_CLI_PRINT;
917}
918
919/* Expects to find a frontend named <arg> and returns it, otherwise displays various
920 * adequate error messages and returns NULL. This function also expects the stream
921 * level to be admin.
922 */
923static struct proxy *expect_frontend_admin(struct stream *s, struct stream_interface *si, const char *arg)
924{
925 struct appctx *appctx = __objt_appctx(si->end);
926 struct proxy *px;
927
928 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
929 appctx->ctx.cli.msg = stats_permission_denied_msg;
930 appctx->st0 = STAT_CLI_PRINT;
931 return NULL;
932 }
933
934 if (!*arg) {
935 appctx->ctx.cli.msg = "A frontend name is expected.\n";
936 appctx->st0 = STAT_CLI_PRINT;
937 return NULL;
938 }
939
940 px = proxy_fe_by_name(arg);
941 if (!px) {
942 appctx->ctx.cli.msg = "No such frontend.\n";
943 appctx->st0 = STAT_CLI_PRINT;
944 return NULL;
945 }
946 return px;
947}
948
949/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
950 * and returns the pointer to the server. Otherwise, display adequate error messages
951 * and returns NULL. This function also expects the stream level to be admin. Note:
952 * the <arg> is modified to remove the '/'.
953 */
954static struct server *expect_server_admin(struct stream *s, struct stream_interface *si, char *arg)
955{
956 struct appctx *appctx = __objt_appctx(si->end);
957 struct proxy *px;
958 struct server *sv;
959 char *line;
960
961 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
962 appctx->ctx.cli.msg = stats_permission_denied_msg;
963 appctx->st0 = STAT_CLI_PRINT;
964 return NULL;
965 }
966
967 /* split "backend/server" and make <line> point to server */
968 for (line = arg; *line; line++)
969 if (*line == '/') {
970 *line++ = '\0';
971 break;
972 }
973
974 if (!*line || !*arg) {
975 appctx->ctx.cli.msg = "Require 'backend/server'.\n";
976 appctx->st0 = STAT_CLI_PRINT;
977 return NULL;
978 }
979
980 if (!get_backend_server(arg, line, &px, &sv)) {
981 appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
982 appctx->st0 = STAT_CLI_PRINT;
983 return NULL;
984 }
985
986 if (px->state == PR_STSTOPPED) {
987 appctx->ctx.cli.msg = "Proxy is disabled.\n";
988 appctx->st0 = STAT_CLI_PRINT;
989 return NULL;
990 }
991
992 return sv;
993}
994
William Lallemand74c24fb2016-11-21 17:18:36 +0100995
996/* This function is used with map and acl management. It permits to browse
997 * each reference. The variable <getnext> must contain the current node,
998 * <end> point to the root node and the <flags> permit to filter required
999 * nodes.
1000 */
1001static inline
1002struct pat_ref *pat_list_get_next(struct pat_ref *getnext, struct list *end,
1003 unsigned int flags)
1004{
1005 struct pat_ref *ref = getnext;
1006
1007 while (1) {
1008
1009 /* Get next list entry. */
1010 ref = LIST_NEXT(&ref->list, struct pat_ref *, list);
1011
1012 /* If the entry is the last of the list, return NULL. */
1013 if (&ref->list == end)
1014 return NULL;
1015
1016 /* If the entry match the flag, return it. */
1017 if (ref->flags & flags)
1018 return ref;
1019 }
1020}
1021
1022static inline
1023struct pat_ref *pat_ref_lookup_ref(const char *reference)
1024{
1025 int id;
1026 char *error;
1027
1028 /* If the reference starts by a '#', this is numeric id. */
1029 if (reference[0] == '#') {
1030 /* Try to convert the numeric id. If the conversion fails, the lookup fails. */
1031 id = strtol(reference + 1, &error, 10);
1032 if (*error != '\0')
1033 return NULL;
1034
1035 /* Perform the unique id lookup. */
1036 return pat_ref_lookupid(id);
1037 }
1038
1039 /* Perform the string lookup. */
1040 return pat_ref_lookup(reference);
1041}
1042
1043/* This function is used with map and acl management. It permits to browse
1044 * each reference.
1045 */
1046static inline
1047struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
1048{
1049 struct pattern_expr *expr;
1050 expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
1051 if (&expr->list == end)
1052 return NULL;
1053 return expr;
1054}
1055
1056/* Processes the stats interpreter on the statistics socket. This function is
1057 * called from an applet running in a stream interface. The function returns 1
1058 * if the request was understood, otherwise zero. It sets appctx->st0 to a value
1059 * designating the function which will have to process the request, which can
1060 * also be the print function to display the return message set into cli.msg.
1061 */
1062static int stats_sock_parse_request(struct stream_interface *si, char *line)
1063{
1064 struct stream *s = si_strm(si);
1065 struct appctx *appctx = __objt_appctx(si->end);
1066 char *args[MAX_STATS_ARGS + 1];
1067 struct cli_kw *kw;
1068 int arg;
1069 int i, j;
1070
1071 while (isspace((unsigned char)*line))
1072 line++;
1073
1074 arg = 0;
1075 args[arg] = line;
1076
1077 while (*line && arg < MAX_STATS_ARGS) {
1078 if (*line == '\\') {
1079 line++;
1080 if (*line == '\0')
1081 break;
1082 }
1083 else if (isspace((unsigned char)*line)) {
1084 *line++ = '\0';
1085
1086 while (isspace((unsigned char)*line))
1087 line++;
1088
1089 args[++arg] = line;
1090 continue;
1091 }
1092
1093 line++;
1094 }
1095
1096 while (++arg <= MAX_STATS_ARGS)
1097 args[arg] = line;
1098
1099 /* remove \ */
1100 arg = 0;
1101 while (*args[arg] != '\0') {
1102 j = 0;
1103 for (i=0; args[arg][i] != '\0'; i++) {
1104 if (args[arg][i] == '\\')
1105 continue;
1106 args[arg][j] = args[arg][i];
1107 j++;
1108 }
1109 args[arg][j] = '\0';
1110 arg++;
1111 }
1112
1113 appctx->ctx.stats.scope_str = 0;
1114 appctx->ctx.stats.scope_len = 0;
1115 appctx->ctx.stats.flags = 0;
1116 if ((kw = cli_find_kw(args))) {
1117 if (kw->parse) {
1118 if (kw->parse(args, appctx, kw->private) == 0 && kw->io_handler) {
1119 appctx->st0 = STAT_CLI_O_CUSTOM;
1120 appctx->io_handler = kw->io_handler;
1121 appctx->io_release = kw->io_release;
1122 }
1123 }
1124 } else if (strcmp(args[0], "show") == 0) {
1125 if (strcmp(args[1], "backend") == 0) {
1126 appctx->ctx.be.px = NULL;
1127 appctx->st2 = STAT_ST_INIT;
1128 appctx->st0 = STAT_CLI_O_BACKEND;
1129 }
1130 else if (strcmp(args[1], "env") == 0) {
1131 extern char **environ;
1132
1133 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
1134 appctx->ctx.cli.msg = stats_permission_denied_msg;
1135 appctx->st0 = STAT_CLI_PRINT;
1136 return 1;
1137 }
1138 appctx->ctx.env.var = environ;
1139 appctx->st2 = STAT_ST_INIT;
1140 appctx->st0 = STAT_CLI_O_ENV; // stats_dump_env_to_buffer
1141
1142 if (*args[2]) {
1143 int len = strlen(args[2]);
1144
1145 for (; *appctx->ctx.env.var; appctx->ctx.env.var++) {
1146 if (strncmp(*appctx->ctx.env.var, args[2], len) == 0 &&
1147 (*appctx->ctx.env.var)[len] == '=')
1148 break;
1149 }
1150 if (!*appctx->ctx.env.var) {
1151 appctx->ctx.cli.msg = "Variable not found\n";
1152 appctx->st0 = STAT_CLI_PRINT;
1153 return 1;
1154 }
1155 appctx->st2 = STAT_ST_END;
1156 }
1157 }
1158 else if (strcmp(args[1], "stat") == 0) {
1159 if (strcmp(args[2], "resolvers") == 0) {
1160 struct dns_resolvers *presolvers;
1161
1162 if (*args[3]) {
1163 appctx->ctx.resolvers.ptr = NULL;
1164 list_for_each_entry(presolvers, &dns_resolvers, list) {
1165 if (strcmp(presolvers->id, args[3]) == 0) {
1166 appctx->ctx.resolvers.ptr = presolvers;
1167 break;
1168 }
1169 }
1170 if (appctx->ctx.resolvers.ptr == NULL) {
1171 appctx->ctx.cli.msg = "Can't find that resolvers section\n";
1172 appctx->st0 = STAT_CLI_PRINT;
1173 return 1;
1174 }
1175 }
1176
1177 appctx->st2 = STAT_ST_INIT;
1178 appctx->st0 = STAT_CLI_O_RESOLVERS;
1179 return 1;
1180 }
1181 else if (*args[2] && *args[3] && *args[4]) {
1182 appctx->ctx.stats.flags |= STAT_BOUND;
1183 appctx->ctx.stats.iid = atoi(args[2]);
1184 appctx->ctx.stats.type = atoi(args[3]);
1185 appctx->ctx.stats.sid = atoi(args[4]);
1186 if (strcmp(args[5], "typed") == 0)
1187 appctx->ctx.stats.flags |= STAT_FMT_TYPED;
1188 }
1189 else if (strcmp(args[2], "typed") == 0)
1190 appctx->ctx.stats.flags |= STAT_FMT_TYPED;
1191
1192 appctx->st2 = STAT_ST_INIT;
1193 appctx->st0 = STAT_CLI_O_STAT; // stats_dump_stat_to_buffer
1194 }
1195 else if (strcmp(args[1], "info") == 0) {
1196 if (strcmp(args[2], "typed") == 0)
1197 appctx->ctx.stats.flags |= STAT_FMT_TYPED;
1198 appctx->st2 = STAT_ST_INIT;
1199 appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer
1200 }
1201 else if (strcmp(args[1], "servers") == 0 && strcmp(args[2], "state") == 0) {
1202 appctx->ctx.server_state.iid = 0;
1203 appctx->ctx.server_state.px = NULL;
1204 appctx->ctx.server_state.sv = NULL;
1205
1206 /* check if a backend name has been provided */
1207 if (*args[3]) {
1208 /* read server state from local file */
1209 appctx->ctx.server_state.px = proxy_be_by_name(args[3]);
1210
1211 if (!appctx->ctx.server_state.px) {
1212 appctx->ctx.cli.msg = "Can't find backend.\n";
1213 appctx->st0 = STAT_CLI_PRINT;
1214 return 1;
1215 }
1216 appctx->ctx.server_state.iid = appctx->ctx.server_state.px->uuid;
1217 }
1218 appctx->st2 = STAT_ST_INIT;
1219 appctx->st0 = STAT_CLI_O_SERVERS_STATE; // stats_dump_servers_state_to_buffer
1220 return 1;
1221 }
1222 else if (strcmp(args[1], "pools") == 0) {
1223 appctx->st2 = STAT_ST_INIT;
1224 appctx->st0 = STAT_CLI_O_POOLS; // stats_dump_pools_to_buffer
1225 }
1226 else if (strcmp(args[1], "sess") == 0) {
1227 appctx->st2 = STAT_ST_INIT;
1228 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
1229 appctx->ctx.cli.msg = stats_permission_denied_msg;
1230 appctx->st0 = STAT_CLI_PRINT;
1231 return 1;
1232 }
1233 if (*args[2] && strcmp(args[2], "all") == 0)
1234 appctx->ctx.sess.target = (void *)-1;
1235 else if (*args[2])
1236 appctx->ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
1237 else
1238 appctx->ctx.sess.target = NULL;
1239 appctx->ctx.sess.section = 0; /* start with stream status */
1240 appctx->ctx.sess.pos = 0;
1241 appctx->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
1242 }
1243 else if (strcmp(args[1], "errors") == 0) {
1244 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
1245 appctx->ctx.cli.msg = stats_permission_denied_msg;
1246 appctx->st0 = STAT_CLI_PRINT;
1247 return 1;
1248 }
1249 if (*args[2])
1250 appctx->ctx.errors.iid = atoi(args[2]);
1251 else
1252 appctx->ctx.errors.iid = -1;
1253 appctx->ctx.errors.px = NULL;
1254 appctx->st2 = STAT_ST_INIT;
1255 appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
1256 }
1257 else if (strcmp(args[1], "table") == 0) {
1258 stats_sock_table_request(si, args, STAT_CLI_O_TAB);
1259 }
William Lallemand74c24fb2016-11-21 17:18:36 +01001260 else if (strcmp(args[1], "map") == 0 ||
1261 strcmp(args[1], "acl") == 0) {
1262
1263 /* Set ACL or MAP flags. */
1264 if (args[1][0] == 'm')
1265 appctx->ctx.map.display_flags = PAT_REF_MAP;
1266 else
1267 appctx->ctx.map.display_flags = PAT_REF_ACL;
1268
1269 /* no parameter: display all map available */
1270 if (!*args[2]) {
1271 appctx->st2 = STAT_ST_INIT;
1272 appctx->st0 = STAT_CLI_O_PATS;
1273 return 1;
1274 }
1275
1276 /* lookup into the refs and check the map flag */
1277 appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
1278 if (!appctx->ctx.map.ref ||
1279 !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
1280 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
1281 appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
1282 else
1283 appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
1284 appctx->st0 = STAT_CLI_PRINT;
1285 return 1;
1286 }
1287 appctx->st2 = STAT_ST_INIT;
1288 appctx->st0 = STAT_CLI_O_PAT;
1289 }
1290 else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
1291 return 0;
1292 }
1293 }
1294 else if (strcmp(args[0], "clear") == 0) {
1295 if (strcmp(args[1], "counters") == 0) {
1296 struct proxy *px;
1297 struct server *sv;
1298 struct listener *li;
1299 int clrall = 0;
1300
1301 if (strcmp(args[2], "all") == 0)
1302 clrall = 1;
1303
1304 /* check permissions */
1305 if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER ||
1306 (clrall && strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN)) {
1307 appctx->ctx.cli.msg = stats_permission_denied_msg;
1308 appctx->st0 = STAT_CLI_PRINT;
1309 return 1;
1310 }
1311
1312 for (px = proxy; px; px = px->next) {
1313 if (clrall) {
1314 memset(&px->be_counters, 0, sizeof(px->be_counters));
1315 memset(&px->fe_counters, 0, sizeof(px->fe_counters));
1316 }
1317 else {
1318 px->be_counters.conn_max = 0;
1319 px->be_counters.p.http.rps_max = 0;
1320 px->be_counters.sps_max = 0;
1321 px->be_counters.cps_max = 0;
1322 px->be_counters.nbpend_max = 0;
1323
1324 px->fe_counters.conn_max = 0;
1325 px->fe_counters.p.http.rps_max = 0;
1326 px->fe_counters.sps_max = 0;
1327 px->fe_counters.cps_max = 0;
1328 px->fe_counters.nbpend_max = 0;
1329 }
1330
1331 for (sv = px->srv; sv; sv = sv->next)
1332 if (clrall)
1333 memset(&sv->counters, 0, sizeof(sv->counters));
1334 else {
1335 sv->counters.cur_sess_max = 0;
1336 sv->counters.nbpend_max = 0;
1337 sv->counters.sps_max = 0;
1338 }
1339
1340 list_for_each_entry(li, &px->conf.listeners, by_fe)
1341 if (li->counters) {
1342 if (clrall)
1343 memset(li->counters, 0, sizeof(*li->counters));
1344 else
1345 li->counters->conn_max = 0;
1346 }
1347 }
1348
1349 global.cps_max = 0;
1350 global.sps_max = 0;
1351 return 1;
1352 }
1353 else if (strcmp(args[1], "table") == 0) {
1354 stats_sock_table_request(si, args, STAT_CLI_O_CLR);
1355 /* end of processing */
1356 return 1;
1357 }
1358 else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
1359 /* Set ACL or MAP flags. */
1360 if (args[1][0] == 'm')
1361 appctx->ctx.map.display_flags = PAT_REF_MAP;
1362 else
1363 appctx->ctx.map.display_flags = PAT_REF_ACL;
1364
1365 /* no parameter */
1366 if (!*args[2]) {
1367 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
1368 appctx->ctx.cli.msg = "Missing map identifier.\n";
1369 else
1370 appctx->ctx.cli.msg = "Missing ACL identifier.\n";
1371 appctx->st0 = STAT_CLI_PRINT;
1372 return 1;
1373 }
1374
1375 /* lookup into the refs and check the map flag */
1376 appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
1377 if (!appctx->ctx.map.ref ||
1378 !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
1379 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
1380 appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
1381 else
1382 appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
1383 appctx->st0 = STAT_CLI_PRINT;
1384 return 1;
1385 }
1386
1387 /* Clear all. */
1388 pat_ref_prune(appctx->ctx.map.ref);
1389
1390 /* return response */
1391 appctx->st0 = STAT_CLI_PROMPT;
1392 return 1;
1393 }
1394 else {
1395 /* unknown "clear" argument */
1396 return 0;
1397 }
1398 }
1399 else if (strcmp(args[0], "get") == 0) {
1400 if (strcmp(args[1], "weight") == 0) {
1401 struct proxy *px;
1402 struct server *sv;
1403
1404 /* split "backend/server" and make <line> point to server */
1405 for (line = args[2]; *line; line++)
1406 if (*line == '/') {
1407 *line++ = '\0';
1408 break;
1409 }
1410
1411 if (!*line) {
1412 appctx->ctx.cli.msg = "Require 'backend/server'.\n";
1413 appctx->st0 = STAT_CLI_PRINT;
1414 return 1;
1415 }
1416
1417 if (!get_backend_server(args[2], line, &px, &sv)) {
1418 appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
1419 appctx->st0 = STAT_CLI_PRINT;
1420 return 1;
1421 }
1422
1423 /* return server's effective weight at the moment */
1424 snprintf(trash.str, trash.size, "%d (initial %d)\n", sv->uweight, sv->iweight);
1425 if (bi_putstr(si_ic(si), trash.str) == -1)
1426 si_applet_cant_put(si);
1427
1428 return 1;
1429 }
1430 else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
1431 /* Set flags. */
1432 if (args[1][0] == 'm')
1433 appctx->ctx.map.display_flags = PAT_REF_MAP;
1434 else
1435 appctx->ctx.map.display_flags = PAT_REF_ACL;
1436
1437 /* No parameter. */
1438 if (!*args[2] || !*args[3]) {
1439 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
1440 appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
1441 else
1442 appctx->ctx.cli.msg = "Missing ACL identifier and/or key.\n";
1443 appctx->st0 = STAT_CLI_PRINT;
1444 return 1;
1445 }
1446
1447 /* lookup into the maps */
1448 appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
1449 if (!appctx->ctx.map.ref) {
1450 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
1451 appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
1452 else
1453 appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
1454 appctx->st0 = STAT_CLI_PRINT;
1455 return 1;
1456 }
1457
1458 /* copy input string. The string must be allocated because
1459 * it may be used over multiple iterations. It's released
1460 * at the end and upon abort anyway.
1461 */
1462 appctx->ctx.map.chunk.len = strlen(args[3]);
1463 appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
1464 appctx->ctx.map.chunk.str = strdup(args[3]);
1465 if (!appctx->ctx.map.chunk.str) {
1466 appctx->ctx.cli.msg = "Out of memory error.\n";
1467 appctx->st0 = STAT_CLI_PRINT;
1468 return 1;
1469 }
1470
1471 /* prepare response */
1472 appctx->st2 = STAT_ST_INIT;
1473 appctx->st0 = STAT_CLI_O_MLOOK;
1474 }
1475 else { /* not "get weight" */
1476 return 0;
1477 }
1478 }
1479 else if (strcmp(args[0], "set") == 0) {
1480 if (strcmp(args[1], "weight") == 0) {
1481 struct server *sv;
1482 const char *warning;
1483
1484 sv = expect_server_admin(s, si, args[2]);
1485 if (!sv)
1486 return 1;
1487
1488 warning = server_parse_weight_change_request(sv, args[3]);
1489 if (warning) {
1490 appctx->ctx.cli.msg = warning;
1491 appctx->st0 = STAT_CLI_PRINT;
1492 }
1493 return 1;
1494 }
1495 else if (strcmp(args[1], "server") == 0) {
1496 struct server *sv;
1497 const char *warning;
1498
1499 sv = expect_server_admin(s, si, args[2]);
1500 if (!sv)
1501 return 1;
1502
1503 if (strcmp(args[3], "weight") == 0) {
1504 warning = server_parse_weight_change_request(sv, args[4]);
1505 if (warning) {
1506 appctx->ctx.cli.msg = warning;
1507 appctx->st0 = STAT_CLI_PRINT;
1508 }
1509 }
1510 else if (strcmp(args[3], "state") == 0) {
1511 if (strcmp(args[4], "ready") == 0)
1512 srv_adm_set_ready(sv);
1513 else if (strcmp(args[4], "drain") == 0)
1514 srv_adm_set_drain(sv);
1515 else if (strcmp(args[4], "maint") == 0)
1516 srv_adm_set_maint(sv);
1517 else {
1518 appctx->ctx.cli.msg = "'set server <srv> state' expects 'ready', 'drain' and 'maint'.\n";
1519 appctx->st0 = STAT_CLI_PRINT;
1520 }
1521 }
1522 else if (strcmp(args[3], "health") == 0) {
1523 if (sv->track) {
1524 appctx->ctx.cli.msg = "cannot change health on a tracking server.\n";
1525 appctx->st0 = STAT_CLI_PRINT;
1526 }
1527 else if (strcmp(args[4], "up") == 0) {
1528 sv->check.health = sv->check.rise + sv->check.fall - 1;
1529 srv_set_running(sv, "changed from CLI");
1530 }
1531 else if (strcmp(args[4], "stopping") == 0) {
1532 sv->check.health = sv->check.rise + sv->check.fall - 1;
1533 srv_set_stopping(sv, "changed from CLI");
1534 }
1535 else if (strcmp(args[4], "down") == 0) {
1536 sv->check.health = 0;
1537 srv_set_stopped(sv, "changed from CLI");
1538 }
1539 else {
1540 appctx->ctx.cli.msg = "'set server <srv> health' expects 'up', 'stopping', or 'down'.\n";
1541 appctx->st0 = STAT_CLI_PRINT;
1542 }
1543 }
1544 else if (strcmp(args[3], "agent") == 0) {
1545 if (!(sv->agent.state & CHK_ST_ENABLED)) {
1546 appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
1547 appctx->st0 = STAT_CLI_PRINT;
1548 }
1549 else if (strcmp(args[4], "up") == 0) {
1550 sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
1551 srv_set_running(sv, "changed from CLI");
1552 }
1553 else if (strcmp(args[4], "down") == 0) {
1554 sv->agent.health = 0;
1555 srv_set_stopped(sv, "changed from CLI");
1556 }
1557 else {
1558 appctx->ctx.cli.msg = "'set server <srv> agent' expects 'up' or 'down'.\n";
1559 appctx->st0 = STAT_CLI_PRINT;
1560 }
1561 }
1562 else if (strcmp(args[3], "check-port") == 0) {
1563 int i = 0;
1564 if (strl2irc(args[4], strlen(args[4]), &i) != 0) {
1565 appctx->ctx.cli.msg = "'set server <srv> check-port' expects an integer as argument.\n";
1566 appctx->st0 = STAT_CLI_PRINT;
1567 }
1568 if ((i < 0) || (i > 65535)) {
1569 appctx->ctx.cli.msg = "provided port is not valid.\n";
1570 appctx->st0 = STAT_CLI_PRINT;
1571 }
1572 /* prevent the update of port to 0 if MAPPORTS are in use */
1573 if ((sv->flags & SRV_F_MAPPORTS) && (i == 0)) {
1574 appctx->ctx.cli.msg = "can't unset 'port' since MAPPORTS is in use.\n";
1575 appctx->st0 = STAT_CLI_PRINT;
1576 return 1;
1577 }
1578 sv->check.port = i;
1579 appctx->ctx.cli.msg = "health check port updated.\n";
1580 appctx->st0 = STAT_CLI_PRINT;
1581 }
1582 else if (strcmp(args[3], "addr") == 0) {
1583 char *addr = NULL;
1584 char *port = NULL;
1585 if (strlen(args[4]) == 0) {
1586 appctx->ctx.cli.msg = "set server <b>/<s> addr requires an address and optionally a port.\n";
1587 appctx->st0 = STAT_CLI_PRINT;
1588 return 1;
1589 }
1590 else {
1591 addr = args[4];
1592 }
1593 if (strcmp(args[5], "port") == 0) {
1594 port = args[6];
1595 }
1596 warning = update_server_addr_port(sv, addr, port, "stats socket command");
1597 if (warning) {
1598 appctx->ctx.cli.msg = warning;
1599 appctx->st0 = STAT_CLI_PRINT;
1600 }
1601 srv_clr_admin_flag(sv, SRV_ADMF_RMAINT);
1602 }
1603 else {
1604 appctx->ctx.cli.msg = "'set server <srv>' only supports 'agent', 'health', 'state', 'weight', 'addr' and 'check-port'.\n";
1605 appctx->st0 = STAT_CLI_PRINT;
1606 }
1607 return 1;
1608 }
1609 else if (strcmp(args[1], "timeout") == 0) {
1610 if (strcmp(args[2], "cli") == 0) {
1611 unsigned timeout;
1612 const char *res;
1613
1614 if (!*args[3]) {
1615 appctx->ctx.cli.msg = "Expects an integer value.\n";
1616 appctx->st0 = STAT_CLI_PRINT;
1617 return 1;
1618 }
1619
1620 res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
1621 if (res || timeout < 1) {
1622 appctx->ctx.cli.msg = "Invalid timeout value.\n";
1623 appctx->st0 = STAT_CLI_PRINT;
1624 return 1;
1625 }
1626
1627 s->req.rto = s->res.wto = 1 + MS_TO_TICKS(timeout*1000);
1628 task_wakeup(s->task, TASK_WOKEN_MSG); // recompute timeouts
1629 return 1;
1630 }
1631 else {
1632 appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
1633 appctx->st0 = STAT_CLI_PRINT;
1634 return 1;
1635 }
1636 }
1637 else if (strcmp(args[1], "maxconn") == 0) {
1638 if (strcmp(args[2], "frontend") == 0) {
1639 struct proxy *px;
1640 struct listener *l;
1641 int v;
1642
1643 px = expect_frontend_admin(s, si, args[3]);
1644 if (!px)
1645 return 1;
1646
1647 if (!*args[4]) {
1648 appctx->ctx.cli.msg = "Integer value expected.\n";
1649 appctx->st0 = STAT_CLI_PRINT;
1650 return 1;
1651 }
1652
1653 v = atoi(args[4]);
1654 if (v < 0) {
1655 appctx->ctx.cli.msg = "Value out of range.\n";
1656 appctx->st0 = STAT_CLI_PRINT;
1657 return 1;
1658 }
1659
1660 /* OK, the value is fine, so we assign it to the proxy and to all of
1661 * its listeners. The blocked ones will be dequeued.
1662 */
1663 px->maxconn = v;
1664 list_for_each_entry(l, &px->conf.listeners, by_fe) {
1665 l->maxconn = v;
1666 if (l->state == LI_FULL)
1667 resume_listener(l);
1668 }
1669
1670 if (px->maxconn > px->feconn && !LIST_ISEMPTY(&px->listener_queue))
1671 dequeue_all_listeners(&px->listener_queue);
1672
1673 return 1;
1674 }
1675 else if (strcmp(args[2], "server") == 0) {
1676 struct server *sv;
1677 const char *warning;
1678
1679 sv = expect_server_admin(s, si, args[3]);
1680 if (!sv)
1681 return 1;
1682
1683 warning = server_parse_maxconn_change_request(sv, args[4]);
1684 if (warning) {
1685 appctx->ctx.cli.msg = warning;
1686 appctx->st0 = STAT_CLI_PRINT;
1687 }
1688
1689 return 1;
1690 }
1691 else if (strcmp(args[2], "global") == 0) {
1692 int v;
1693
1694 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1695 appctx->ctx.cli.msg = stats_permission_denied_msg;
1696 appctx->st0 = STAT_CLI_PRINT;
1697 return 1;
1698 }
1699
1700 if (!*args[3]) {
1701 appctx->ctx.cli.msg = "Expects an integer value.\n";
1702 appctx->st0 = STAT_CLI_PRINT;
1703 return 1;
1704 }
1705
1706 v = atoi(args[3]);
1707 if (v > global.hardmaxconn) {
1708 appctx->ctx.cli.msg = "Value out of range.\n";
1709 appctx->st0 = STAT_CLI_PRINT;
1710 return 1;
1711 }
1712
1713 /* check for unlimited values */
1714 if (v <= 0)
1715 v = global.hardmaxconn;
1716
1717 global.maxconn = v;
1718
1719 /* Dequeues all of the listeners waiting for a resource */
1720 if (!LIST_ISEMPTY(&global_listener_queue))
1721 dequeue_all_listeners(&global_listener_queue);
1722
1723 return 1;
1724 }
1725 else {
1726 appctx->ctx.cli.msg = "'set maxconn' only supports 'frontend', 'server', and 'global'.\n";
1727 appctx->st0 = STAT_CLI_PRINT;
1728 return 1;
1729 }
1730 }
1731 else if (strcmp(args[1], "rate-limit") == 0) {
1732 if (strcmp(args[2], "connections") == 0) {
1733 if (strcmp(args[3], "global") == 0) {
1734 int v;
1735
1736 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1737 appctx->ctx.cli.msg = stats_permission_denied_msg;
1738 appctx->st0 = STAT_CLI_PRINT;
1739 return 1;
1740 }
1741
1742 if (!*args[4]) {
1743 appctx->ctx.cli.msg = "Expects an integer value.\n";
1744 appctx->st0 = STAT_CLI_PRINT;
1745 return 1;
1746 }
1747
1748 v = atoi(args[4]);
1749 if (v < 0) {
1750 appctx->ctx.cli.msg = "Value out of range.\n";
1751 appctx->st0 = STAT_CLI_PRINT;
1752 return 1;
1753 }
1754
1755 global.cps_lim = v;
1756
1757 /* Dequeues all of the listeners waiting for a resource */
1758 if (!LIST_ISEMPTY(&global_listener_queue))
1759 dequeue_all_listeners(&global_listener_queue);
1760
1761 return 1;
1762 }
1763 else {
1764 appctx->ctx.cli.msg = "'set rate-limit connections' only supports 'global'.\n";
1765 appctx->st0 = STAT_CLI_PRINT;
1766 return 1;
1767 }
1768 }
1769 else if (strcmp(args[2], "sessions") == 0) {
1770 if (strcmp(args[3], "global") == 0) {
1771 int v;
1772
1773 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1774 appctx->ctx.cli.msg = stats_permission_denied_msg;
1775 appctx->st0 = STAT_CLI_PRINT;
1776 return 1;
1777 }
1778
1779 if (!*args[4]) {
1780 appctx->ctx.cli.msg = "Expects an integer value.\n";
1781 appctx->st0 = STAT_CLI_PRINT;
1782 return 1;
1783 }
1784
1785 v = atoi(args[4]);
1786 if (v < 0) {
1787 appctx->ctx.cli.msg = "Value out of range.\n";
1788 appctx->st0 = STAT_CLI_PRINT;
1789 return 1;
1790 }
1791
1792 global.sps_lim = v;
1793
1794 /* Dequeues all of the listeners waiting for a resource */
1795 if (!LIST_ISEMPTY(&global_listener_queue))
1796 dequeue_all_listeners(&global_listener_queue);
1797
1798 return 1;
1799 }
1800 else {
1801 appctx->ctx.cli.msg = "'set rate-limit sessions' only supports 'global'.\n";
1802 appctx->st0 = STAT_CLI_PRINT;
1803 return 1;
1804 }
1805 }
1806#ifdef USE_OPENSSL
1807 else if (strcmp(args[2], "ssl-sessions") == 0) {
1808 if (strcmp(args[3], "global") == 0) {
1809 int v;
1810
1811 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1812 appctx->ctx.cli.msg = stats_permission_denied_msg;
1813 appctx->st0 = STAT_CLI_PRINT;
1814 return 1;
1815 }
1816
1817 if (!*args[4]) {
1818 appctx->ctx.cli.msg = "Expects an integer value.\n";
1819 appctx->st0 = STAT_CLI_PRINT;
1820 return 1;
1821 }
1822
1823 v = atoi(args[4]);
1824 if (v < 0) {
1825 appctx->ctx.cli.msg = "Value out of range.\n";
1826 appctx->st0 = STAT_CLI_PRINT;
1827 return 1;
1828 }
1829
1830 global.ssl_lim = v;
1831
1832 /* Dequeues all of the listeners waiting for a resource */
1833 if (!LIST_ISEMPTY(&global_listener_queue))
1834 dequeue_all_listeners(&global_listener_queue);
1835
1836 return 1;
1837 }
1838 else {
1839 appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
1840 appctx->st0 = STAT_CLI_PRINT;
1841 return 1;
1842 }
1843 }
1844#endif
1845 else if (strcmp(args[2], "http-compression") == 0) {
1846 if (strcmp(args[3], "global") == 0) {
1847 int v;
1848
1849 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
1850 appctx->ctx.cli.msg = stats_permission_denied_msg;
1851 appctx->st0 = STAT_CLI_PRINT;
1852 return 1;
1853 }
1854
1855 if (!*args[4]) {
1856 appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
1857 appctx->st0 = STAT_CLI_PRINT;
1858 return 1;
1859 }
1860
1861 v = atoi(args[4]);
1862 global.comp_rate_lim = v * 1024; /* Kilo to bytes. */
1863 }
1864 else {
1865 appctx->ctx.cli.msg = "'set rate-limit http-compression' only supports 'global'.\n";
1866 appctx->st0 = STAT_CLI_PRINT;
1867 return 1;
1868 }
1869 }
1870 else {
1871 appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
1872 appctx->st0 = STAT_CLI_PRINT;
1873 return 1;
1874 }
1875 }
1876 else if (strcmp(args[1], "table") == 0) {
1877 stats_sock_table_request(si, args, STAT_CLI_O_SET);
1878 }
1879 else if (strcmp(args[1], "map") == 0) {
1880 char *err;
1881
1882 /* Set flags. */
1883 appctx->ctx.map.display_flags = PAT_REF_MAP;
1884
1885 /* Expect three parameters: map name, key and new value. */
1886 if (!*args[2] || !*args[3] || !*args[4]) {
1887 appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
1888 appctx->st0 = STAT_CLI_PRINT;
1889 return 1;
1890 }
1891
1892 /* Lookup the reference in the maps. */
1893 appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
1894 if (!appctx->ctx.map.ref) {
1895 appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
1896 appctx->st0 = STAT_CLI_PRINT;
1897 return 1;
1898 }
1899
1900 /* If the entry identifier start with a '#', it is considered as
1901 * pointer id
1902 */
1903 if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
1904 struct pat_ref_elt *ref;
1905 long long int conv;
1906 char *error;
1907
1908 /* Convert argument to integer value. */
1909 conv = strtoll(&args[3][1], &error, 16);
1910 if (*error != '\0') {
1911 appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
1912 appctx->st0 = STAT_CLI_PRINT;
1913 return 1;
1914 }
1915
1916 /* Convert and check integer to pointer. */
1917 ref = (struct pat_ref_elt *)(long)conv;
1918 if ((long long int)(long)ref != conv) {
1919 appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
1920 appctx->st0 = STAT_CLI_PRINT;
1921 return 1;
1922 }
1923
1924 /* Try to delete the entry. */
1925 err = NULL;
1926 if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
1927 if (err)
1928 memprintf(&err, "%s.\n", err);
1929 appctx->ctx.cli.err = err;
1930 appctx->st0 = STAT_CLI_PRINT_FREE;
1931 return 1;
1932 }
1933 }
1934 else {
1935 /* Else, use the entry identifier as pattern
1936 * string, and update the value.
1937 */
1938 err = NULL;
1939 if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
1940 if (err)
1941 memprintf(&err, "%s.\n", err);
1942 appctx->ctx.cli.err = err;
1943 appctx->st0 = STAT_CLI_PRINT_FREE;
1944 return 1;
1945 }
1946 }
1947
1948 /* The set is done, send message. */
1949 appctx->st0 = STAT_CLI_PROMPT;
1950 return 1;
William Lallemand32af2032016-10-29 18:09:35 +02001951 } else { /* unknown "set" parameter */
William Lallemand74c24fb2016-11-21 17:18:36 +01001952 return 0;
1953 }
1954 }
1955 else if (strcmp(args[0], "enable") == 0) {
1956 if (strcmp(args[1], "agent") == 0) {
1957 struct server *sv;
1958
1959 sv = expect_server_admin(s, si, args[2]);
1960 if (!sv)
1961 return 1;
1962
1963 if (!(sv->agent.state & CHK_ST_CONFIGURED)) {
1964 appctx->ctx.cli.msg = "Agent was not configured on this server, cannot enable.\n";
1965 appctx->st0 = STAT_CLI_PRINT;
1966 return 1;
1967 }
1968
1969 sv->agent.state |= CHK_ST_ENABLED;
1970 return 1;
1971 }
1972 else if (strcmp(args[1], "health") == 0) {
1973 struct server *sv;
1974
1975 sv = expect_server_admin(s, si, args[2]);
1976 if (!sv)
1977 return 1;
1978
1979 if (!(sv->check.state & CHK_ST_CONFIGURED)) {
1980 appctx->ctx.cli.msg = "Health checks are not configured on this server, cannot enable.\n";
1981 appctx->st0 = STAT_CLI_PRINT;
1982 return 1;
1983 }
1984
1985 sv->check.state |= CHK_ST_ENABLED;
1986 return 1;
1987 }
1988 else if (strcmp(args[1], "server") == 0) {
1989 struct server *sv;
1990
1991 sv = expect_server_admin(s, si, args[2]);
1992 if (!sv)
1993 return 1;
1994
1995 srv_adm_set_ready(sv);
1996 return 1;
1997 }
1998 else if (strcmp(args[1], "frontend") == 0) {
1999 struct proxy *px;
2000
2001 px = expect_frontend_admin(s, si, args[2]);
2002 if (!px)
2003 return 1;
2004
2005 if (px->state == PR_STSTOPPED) {
2006 appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
2007 appctx->st0 = STAT_CLI_PRINT;
2008 return 1;
2009 }
2010
2011 if (px->state != PR_STPAUSED) {
2012 appctx->ctx.cli.msg = "Frontend is already enabled.\n";
2013 appctx->st0 = STAT_CLI_PRINT;
2014 return 1;
2015 }
2016
2017 if (!resume_proxy(px)) {
2018 appctx->ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
2019 appctx->st0 = STAT_CLI_PRINT;
2020 return 1;
2021 }
2022 return 1;
2023 }
2024 else { /* unknown "enable" parameter */
2025 appctx->ctx.cli.msg = "'enable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
2026 appctx->st0 = STAT_CLI_PRINT;
2027 return 1;
2028 }
2029 }
2030 else if (strcmp(args[0], "disable") == 0) {
2031 if (strcmp(args[1], "agent") == 0) {
2032 struct server *sv;
2033
2034 sv = expect_server_admin(s, si, args[2]);
2035 if (!sv)
2036 return 1;
2037
2038 sv->agent.state &= ~CHK_ST_ENABLED;
2039 return 1;
2040 }
2041 else if (strcmp(args[1], "health") == 0) {
2042 struct server *sv;
2043
2044 sv = expect_server_admin(s, si, args[2]);
2045 if (!sv)
2046 return 1;
2047
2048 sv->check.state &= ~CHK_ST_ENABLED;
2049 return 1;
2050 }
2051 else if (strcmp(args[1], "server") == 0) {
2052 struct server *sv;
2053
2054 sv = expect_server_admin(s, si, args[2]);
2055 if (!sv)
2056 return 1;
2057
2058 srv_adm_set_maint(sv);
2059 return 1;
2060 }
2061 else if (strcmp(args[1], "frontend") == 0) {
2062 struct proxy *px;
2063
2064 px = expect_frontend_admin(s, si, args[2]);
2065 if (!px)
2066 return 1;
2067
2068 if (px->state == PR_STSTOPPED) {
2069 appctx->ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
2070 appctx->st0 = STAT_CLI_PRINT;
2071 return 1;
2072 }
2073
2074 if (px->state == PR_STPAUSED) {
2075 appctx->ctx.cli.msg = "Frontend is already disabled.\n";
2076 appctx->st0 = STAT_CLI_PRINT;
2077 return 1;
2078 }
2079
2080 if (!pause_proxy(px)) {
2081 appctx->ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
2082 appctx->st0 = STAT_CLI_PRINT;
2083 return 1;
2084 }
2085 return 1;
2086 }
2087 else { /* unknown "disable" parameter */
2088 appctx->ctx.cli.msg = "'disable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
2089 appctx->st0 = STAT_CLI_PRINT;
2090 return 1;
2091 }
2092 }
2093 else if (strcmp(args[0], "shutdown") == 0) {
2094 if (strcmp(args[1], "frontend") == 0) {
2095 struct proxy *px;
2096
2097 px = expect_frontend_admin(s, si, args[2]);
2098 if (!px)
2099 return 1;
2100
2101 if (px->state == PR_STSTOPPED) {
2102 appctx->ctx.cli.msg = "Frontend was already shut down.\n";
2103 appctx->st0 = STAT_CLI_PRINT;
2104 return 1;
2105 }
2106
2107 Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
2108 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
2109 send_log(px, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
2110 px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
2111 stop_proxy(px);
2112 return 1;
2113 }
2114 else if (strcmp(args[1], "session") == 0) {
2115 struct stream *sess, *ptr;
2116
2117 if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
2118 appctx->ctx.cli.msg = stats_permission_denied_msg;
2119 appctx->st0 = STAT_CLI_PRINT;
2120 return 1;
2121 }
2122
2123 if (!*args[2]) {
2124 appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
2125 appctx->st0 = STAT_CLI_PRINT;
2126 return 1;
2127 }
2128
2129 ptr = (void *)strtoul(args[2], NULL, 0);
2130
2131 /* first, look for the requested stream in the stream table */
2132 list_for_each_entry(sess, &streams, list) {
2133 if (sess == ptr)
2134 break;
2135 }
2136
2137 /* do we have the stream ? */
2138 if (sess != ptr) {
2139 appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
2140 appctx->st0 = STAT_CLI_PRINT;
2141 return 1;
2142 }
2143
2144 stream_shutdown(sess, SF_ERR_KILLED);
2145 return 1;
2146 }
2147 else if (strcmp(args[1], "sessions") == 0) {
2148 if (strcmp(args[2], "server") == 0) {
2149 struct server *sv;
2150 struct stream *sess, *sess_bck;
2151
2152 sv = expect_server_admin(s, si, args[3]);
2153 if (!sv)
2154 return 1;
2155
2156 /* kill all the stream that are on this server */
2157 list_for_each_entry_safe(sess, sess_bck, &sv->actconns, by_srv)
2158 if (sess->srv_conn == sv)
2159 stream_shutdown(sess, SF_ERR_KILLED);
2160
2161 return 1;
2162 }
2163 else {
2164 appctx->ctx.cli.msg = "'shutdown sessions' only supports 'server'.\n";
2165 appctx->st0 = STAT_CLI_PRINT;
2166 return 1;
2167 }
2168 }
2169 else { /* unknown "disable" parameter */
2170 appctx->ctx.cli.msg = "'shutdown' only supports 'frontend', 'session' and 'sessions'.\n";
2171 appctx->st0 = STAT_CLI_PRINT;
2172 return 1;
2173 }
2174 }
2175 else if (strcmp(args[0], "del") == 0) {
2176 if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
2177 if (args[1][0] == 'm')
2178 appctx->ctx.map.display_flags = PAT_REF_MAP;
2179 else
2180 appctx->ctx.map.display_flags = PAT_REF_ACL;
2181
2182 /* Expect two parameters: map name and key. */
2183 if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
2184 if (!*args[2] || !*args[3]) {
2185 appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
2186 appctx->st0 = STAT_CLI_PRINT;
2187 return 1;
2188 }
2189 }
2190
2191 else {
2192 if (!*args[2] || !*args[3]) {
2193 appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
2194 appctx->st0 = STAT_CLI_PRINT;
2195 return 1;
2196 }
2197 }
2198
2199 /* Lookup the reference in the maps. */
2200 appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
2201 if (!appctx->ctx.map.ref ||
2202 !(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
2203 appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
2204 appctx->st0 = STAT_CLI_PRINT;
2205 return 1;
2206 }
2207
2208 /* If the entry identifier start with a '#', it is considered as
2209 * pointer id
2210 */
2211 if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
2212 struct pat_ref_elt *ref;
2213 long long int conv;
2214 char *error;
2215
2216 /* Convert argument to integer value. */
2217 conv = strtoll(&args[3][1], &error, 16);
2218 if (*error != '\0') {
2219 appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
2220 appctx->st0 = STAT_CLI_PRINT;
2221 return 1;
2222 }
2223
2224 /* Convert and check integer to pointer. */
2225 ref = (struct pat_ref_elt *)(long)conv;
2226 if ((long long int)(long)ref != conv) {
2227 appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
2228 appctx->st0 = STAT_CLI_PRINT;
2229 return 1;
2230 }
2231
2232 /* Try to delete the entry. */
2233 if (!pat_ref_delete_by_id(appctx->ctx.map.ref, ref)) {
2234 /* The entry is not found, send message. */
2235 appctx->ctx.cli.msg = "Key not found.\n";
2236 appctx->st0 = STAT_CLI_PRINT;
2237 return 1;
2238 }
2239 }
2240 else {
2241 /* Else, use the entry identifier as pattern
2242 * string and try to delete the entry.
2243 */
2244 if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
2245 /* The entry is not found, send message. */
2246 appctx->ctx.cli.msg = "Key not found.\n";
2247 appctx->st0 = STAT_CLI_PRINT;
2248 return 1;
2249 }
2250 }
2251
2252 /* The deletion is done, send message. */
2253 appctx->st0 = STAT_CLI_PROMPT;
2254 return 1;
2255 }
2256 else { /* unknown "del" parameter */
2257 appctx->ctx.cli.msg = "'del' only supports 'map' or 'acl'.\n";
2258 appctx->st0 = STAT_CLI_PRINT;
2259 return 1;
2260 }
2261 }
2262 else if (strcmp(args[0], "add") == 0) {
2263 if (strcmp(args[1], "map") == 0 ||
2264 strcmp(args[1], "acl") == 0) {
2265 int ret;
2266 char *err;
2267
2268 /* Set flags. */
2269 if (args[1][0] == 'm')
2270 appctx->ctx.map.display_flags = PAT_REF_MAP;
2271 else
2272 appctx->ctx.map.display_flags = PAT_REF_ACL;
2273
2274 /* If the keywork is "map", we expect three parameters, if it
2275 * is "acl", we expect only two parameters
2276 */
2277 if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
2278 if (!*args[2] || !*args[3] || !*args[4]) {
2279 appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
2280 appctx->st0 = STAT_CLI_PRINT;
2281 return 1;
2282 }
2283 }
2284 else {
2285 if (!*args[2] || !*args[3]) {
2286 appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
2287 appctx->st0 = STAT_CLI_PRINT;
2288 return 1;
2289 }
2290 }
2291
2292 /* Lookup for the reference. */
2293 appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
2294 if (!appctx->ctx.map.ref) {
2295 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
2296 appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
2297 else
2298 appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
2299 appctx->st0 = STAT_CLI_PRINT;
2300 return 1;
2301 }
2302
2303 /* The command "add acl" is prohibited if the reference
2304 * use samples.
2305 */
2306 if ((appctx->ctx.map.display_flags & PAT_REF_ACL) &&
2307 (appctx->ctx.map.ref->flags & PAT_REF_SMP)) {
2308 appctx->ctx.cli.msg = "This ACL is shared with a map containing samples. "
2309 "You must use the command 'add map' to add values.\n";
2310 appctx->st0 = STAT_CLI_PRINT;
2311 return 1;
2312 }
2313
2314 /* Add value. */
2315 err = NULL;
2316 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
2317 ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
2318 else
2319 ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
2320 if (!ret) {
2321 if (err)
2322 memprintf(&err, "%s.\n", err);
2323 appctx->ctx.cli.err = err;
2324 appctx->st0 = STAT_CLI_PRINT_FREE;
2325 return 1;
2326 }
2327
2328 /* The add is done, send message. */
2329 appctx->st0 = STAT_CLI_PROMPT;
2330 return 1;
2331 }
2332 else { /* unknown "del" parameter */
2333 appctx->ctx.cli.msg = "'add' only supports 'map'.\n";
2334 appctx->st0 = STAT_CLI_PRINT;
2335 return 1;
2336 }
2337 }
2338 else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
2339 return 0;
2340 }
2341 return 1;
2342}
2343
2344/* This I/O handler runs as an applet embedded in a stream interface. It is
2345 * used to processes I/O from/to the stats unix socket. The system relies on a
2346 * state machine handling requests and various responses. We read a request,
2347 * then we process it and send the response, and we possibly display a prompt.
2348 * Then we can read again. The state is stored in appctx->st0 and is one of the
2349 * STAT_CLI_* constants. appctx->st1 is used to indicate whether prompt is enabled
2350 * or not.
2351 */
2352static void cli_io_handler(struct appctx *appctx)
2353{
2354 struct stream_interface *si = appctx->owner;
2355 struct channel *req = si_oc(si);
2356 struct channel *res = si_ic(si);
2357 int reql;
2358 int len;
2359
2360 if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
2361 goto out;
2362
2363 while (1) {
2364 if (appctx->st0 == STAT_CLI_INIT) {
2365 /* Stats output not initialized yet */
2366 memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
2367 appctx->st0 = STAT_CLI_GETREQ;
2368 }
2369 else if (appctx->st0 == STAT_CLI_END) {
2370 /* Let's close for real now. We just close the request
2371 * side, the conditions below will complete if needed.
2372 */
2373 si_shutw(si);
2374 break;
2375 }
2376 else if (appctx->st0 == STAT_CLI_GETREQ) {
2377 /* ensure we have some output room left in the event we
2378 * would want to return some info right after parsing.
2379 */
2380 if (buffer_almost_full(si_ib(si))) {
2381 si_applet_cant_put(si);
2382 break;
2383 }
2384
2385 reql = bo_getline(si_oc(si), trash.str, trash.size);
2386 if (reql <= 0) { /* closed or EOL not found */
2387 if (reql == 0)
2388 break;
2389 appctx->st0 = STAT_CLI_END;
2390 continue;
2391 }
2392
2393 /* seek for a possible unescaped semi-colon. If we find
2394 * one, we replace it with an LF and skip only this part.
2395 */
2396 for (len = 0; len < reql; len++) {
2397 if (trash.str[len] == '\\') {
2398 len++;
2399 continue;
2400 }
2401 if (trash.str[len] == ';') {
2402 trash.str[len] = '\n';
2403 reql = len + 1;
2404 break;
2405 }
2406 }
2407
2408 /* now it is time to check that we have a full line,
2409 * remove the trailing \n and possibly \r, then cut the
2410 * line.
2411 */
2412 len = reql - 1;
2413 if (trash.str[len] != '\n') {
2414 appctx->st0 = STAT_CLI_END;
2415 continue;
2416 }
2417
2418 if (len && trash.str[len-1] == '\r')
2419 len--;
2420
2421 trash.str[len] = '\0';
2422
2423 appctx->st0 = STAT_CLI_PROMPT;
2424 if (len) {
2425 if (strcmp(trash.str, "quit") == 0) {
2426 appctx->st0 = STAT_CLI_END;
2427 continue;
2428 }
2429 else if (strcmp(trash.str, "prompt") == 0)
2430 appctx->st1 = !appctx->st1;
2431 else if (strcmp(trash.str, "help") == 0 ||
2432 !stats_sock_parse_request(si, trash.str)) {
2433 cli_gen_usage_msg();
2434 if (dynamic_usage_msg)
2435 appctx->ctx.cli.msg = dynamic_usage_msg;
2436 else
2437 appctx->ctx.cli.msg = stats_sock_usage_msg;
2438 appctx->st0 = STAT_CLI_PRINT;
2439 }
2440 /* NB: stats_sock_parse_request() may have put
2441 * another STAT_CLI_O_* into appctx->st0.
2442 */
2443 }
2444 else if (!appctx->st1) {
2445 /* if prompt is disabled, print help on empty lines,
2446 * so that the user at least knows how to enable
2447 * prompt and find help.
2448 */
2449 cli_gen_usage_msg();
2450 if (dynamic_usage_msg)
2451 appctx->ctx.cli.msg = dynamic_usage_msg;
2452 else
2453 appctx->ctx.cli.msg = stats_sock_usage_msg;
2454 appctx->st0 = STAT_CLI_PRINT;
2455 }
2456
2457 /* re-adjust req buffer */
2458 bo_skip(si_oc(si), reql);
2459 req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
2460 }
2461 else { /* output functions */
2462 switch (appctx->st0) {
2463 case STAT_CLI_PROMPT:
2464 break;
2465 case STAT_CLI_PRINT:
2466 if (bi_putstr(si_ic(si), appctx->ctx.cli.msg) != -1)
2467 appctx->st0 = STAT_CLI_PROMPT;
2468 else
2469 si_applet_cant_put(si);
2470 break;
2471 case STAT_CLI_PRINT_FREE:
2472 if (bi_putstr(si_ic(si), appctx->ctx.cli.err) != -1) {
2473 free(appctx->ctx.cli.err);
2474 appctx->st0 = STAT_CLI_PROMPT;
2475 }
2476 else
2477 si_applet_cant_put(si);
2478 break;
2479 case STAT_CLI_O_BACKEND:
2480 if (stats_dump_backend_to_buffer(si))
2481 appctx->st0 = STAT_CLI_PROMPT;
2482 break;
2483 case STAT_CLI_O_INFO:
2484 if (stats_dump_info_to_buffer(si))
2485 appctx->st0 = STAT_CLI_PROMPT;
2486 break;
2487 case STAT_CLI_O_SERVERS_STATE:
2488 if (stats_dump_servers_state_to_buffer(si))
2489 appctx->st0 = STAT_CLI_PROMPT;
2490 break;
2491 case STAT_CLI_O_STAT:
2492 if (stats_dump_stat_to_buffer(si, NULL))
2493 appctx->st0 = STAT_CLI_PROMPT;
2494 break;
2495 case STAT_CLI_O_RESOLVERS:
2496 if (stats_dump_resolvers_to_buffer(si))
2497 appctx->st0 = STAT_CLI_PROMPT;
2498 break;
2499 case STAT_CLI_O_SESS:
2500 if (stats_dump_sess_to_buffer(si))
2501 appctx->st0 = STAT_CLI_PROMPT;
2502 break;
2503 case STAT_CLI_O_ERR: /* errors dump */
2504 if (stats_dump_errors_to_buffer(si))
2505 appctx->st0 = STAT_CLI_PROMPT;
2506 break;
2507 case STAT_CLI_O_TAB:
2508 case STAT_CLI_O_CLR:
2509 if (stats_table_request(si, appctx->st0))
2510 appctx->st0 = STAT_CLI_PROMPT;
2511 break;
2512 case STAT_CLI_O_PATS:
2513 if (stats_pats_list(si))
2514 appctx->st0 = STAT_CLI_PROMPT;
2515 break;
2516 case STAT_CLI_O_PAT:
2517 if (stats_pat_list(si))
2518 appctx->st0 = STAT_CLI_PROMPT;
2519 break;
2520 case STAT_CLI_O_MLOOK:
2521 if (stats_map_lookup(si))
2522 appctx->st0 = STAT_CLI_PROMPT;
2523 break;
2524 case STAT_CLI_O_POOLS:
2525 if (stats_dump_pools_to_buffer(si))
2526 appctx->st0 = STAT_CLI_PROMPT;
2527 break;
William Lallemand74c24fb2016-11-21 17:18:36 +01002528 case STAT_CLI_O_ENV: /* environment dump */
2529 if (stats_dump_env_to_buffer(si))
2530 appctx->st0 = STAT_CLI_PROMPT;
2531 break;
2532 case STAT_CLI_O_CUSTOM: /* use custom pointer */
2533 if (appctx->io_handler)
2534 if (appctx->io_handler(appctx)) {
2535 appctx->st0 = STAT_CLI_PROMPT;
2536 if (appctx->io_release) {
2537 appctx->io_release(appctx);
2538 appctx->io_release = NULL;
2539 }
2540 }
2541 break;
2542 default: /* abnormal state */
2543 si->flags |= SI_FL_ERR;
2544 break;
2545 }
2546
2547 /* The post-command prompt is either LF alone or LF + '> ' in interactive mode */
2548 if (appctx->st0 == STAT_CLI_PROMPT) {
2549 if (bi_putstr(si_ic(si), appctx->st1 ? "\n> " : "\n") != -1)
2550 appctx->st0 = STAT_CLI_GETREQ;
2551 else
2552 si_applet_cant_put(si);
2553 }
2554
2555 /* If the output functions are still there, it means they require more room. */
2556 if (appctx->st0 >= STAT_CLI_OUTPUT)
2557 break;
2558
2559 /* Now we close the output if one of the writers did so,
2560 * or if we're not in interactive mode and the request
2561 * buffer is empty. This still allows pipelined requests
2562 * to be sent in non-interactive mode.
2563 */
2564 if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!appctx->st1 && !req->buf->o)) {
2565 appctx->st0 = STAT_CLI_END;
2566 continue;
2567 }
2568
2569 /* switch state back to GETREQ to read next requests */
2570 appctx->st0 = STAT_CLI_GETREQ;
2571 }
2572 }
2573
2574 if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST)) {
2575 DPRINTF(stderr, "%s@%d: si to buf closed. req=%08x, res=%08x, st=%d\n",
2576 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
2577 /* Other side has closed, let's abort if we have no more processing to do
2578 * and nothing more to consume. This is comparable to a broken pipe, so
2579 * we forward the close to the request side so that it flows upstream to
2580 * the client.
2581 */
2582 si_shutw(si);
2583 }
2584
2585 if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && (appctx->st0 < STAT_CLI_OUTPUT)) {
2586 DPRINTF(stderr, "%s@%d: buf to si closed. req=%08x, res=%08x, st=%d\n",
2587 __FUNCTION__, __LINE__, req->flags, res->flags, si->state);
2588 /* We have no more processing to do, and nothing more to send, and
2589 * the client side has closed. So we'll forward this state downstream
2590 * on the response buffer.
2591 */
2592 si_shutr(si);
2593 res->flags |= CF_READ_NULL;
2594 }
2595
2596 out:
2597 DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rqh=%d, rqs=%d, rh=%d, rs=%d\n",
2598 __FUNCTION__, __LINE__,
2599 si->state, req->flags, res->flags, req->buf->i, req->buf->o, res->buf->i, res->buf->o);
2600}
2601
2602/* Dump all fields from <info> into <out> using the "show info" format (name: value) */
2603static int stats_dump_info_fields(struct chunk *out, const struct field *info)
2604{
2605 int field;
2606
2607 for (field = 0; field < INF_TOTAL_FIELDS; field++) {
2608 if (!field_format(info, field))
2609 continue;
2610
2611 if (!chunk_appendf(out, "%s: ", info_field_names[field]))
2612 return 0;
2613 if (!stats_emit_raw_data_field(out, &info[field]))
2614 return 0;
2615 if (!chunk_strcat(out, "\n"))
2616 return 0;
2617 }
2618 return 1;
2619}
2620
2621/* Dump all fields from <info> into <out> using the "show info typed" format */
2622static int stats_dump_typed_info_fields(struct chunk *out, const struct field *info)
2623{
2624 int field;
2625
2626 for (field = 0; field < INF_TOTAL_FIELDS; field++) {
2627 if (!field_format(info, field))
2628 continue;
2629
2630 if (!chunk_appendf(out, "%d.%s.%u:", field, info_field_names[field], info[INF_PROCESS_NUM].u.u32))
2631 return 0;
2632 if (!stats_emit_field_tags(out, &info[field], ':'))
2633 return 0;
2634 if (!stats_emit_typed_data_field(out, &info[field]))
2635 return 0;
2636 if (!chunk_strcat(out, "\n"))
2637 return 0;
2638 }
2639 return 1;
2640}
2641
2642/* Fill <info> with HAProxy global info. <info> is preallocated
2643 * array of length <len>. The length of the aray must be
2644 * INF_TOTAL_FIELDS. If this length is less then this value, the
2645 * function returns 0, otherwise, it returns 1.
2646 */
2647int stats_fill_info(struct field *info, int len)
2648{
2649 unsigned int up = (now.tv_sec - start_date.tv_sec);
2650 struct chunk *out = get_trash_chunk();
2651
2652#ifdef USE_OPENSSL
2653 int ssl_sess_rate = read_freq_ctr(&global.ssl_per_sec);
2654 int ssl_key_rate = read_freq_ctr(&global.ssl_fe_keys_per_sec);
2655 int ssl_reuse = 0;
2656
2657 if (ssl_key_rate < ssl_sess_rate) {
2658 /* count the ssl reuse ratio and avoid overflows in both directions */
2659 ssl_reuse = 100 - (100 * ssl_key_rate + (ssl_sess_rate - 1) / 2) / ssl_sess_rate;
2660 }
2661#endif
2662
2663 if (len < INF_TOTAL_FIELDS)
2664 return 0;
2665
2666 chunk_reset(out);
2667 memset(info, 0, sizeof(*info) * len);
2668
2669 info[INF_NAME] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, PRODUCT_NAME);
2670 info[INF_VERSION] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, HAPROXY_VERSION);
2671 info[INF_RELEASE_DATE] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, HAPROXY_DATE);
2672
2673 info[INF_NBPROC] = mkf_u32(FO_CONFIG|FS_SERVICE, global.nbproc);
2674 info[INF_PROCESS_NUM] = mkf_u32(FO_KEY, relative_pid);
2675 info[INF_PID] = mkf_u32(FO_STATUS, pid);
2676
2677 info[INF_UPTIME] = mkf_str(FN_DURATION, chunk_newstr(out));
2678 chunk_appendf(out, "%ud %uh%02um%02us", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
2679
2680 info[INF_UPTIME_SEC] = mkf_u32(FN_DURATION, up);
2681 info[INF_MEMMAX_MB] = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_memmax);
2682 info[INF_POOL_ALLOC_MB] = mkf_u32(0, (unsigned)(pool_total_allocated() / 1048576L));
2683 info[INF_POOL_USED_MB] = mkf_u32(0, (unsigned)(pool_total_used() / 1048576L));
2684 info[INF_POOL_FAILED] = mkf_u32(FN_COUNTER, pool_total_failures());
2685 info[INF_ULIMIT_N] = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_nofile);
2686 info[INF_MAXSOCK] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxsock);
2687 info[INF_MAXCONN] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxconn);
2688 info[INF_HARD_MAXCONN] = mkf_u32(FO_CONFIG|FN_LIMIT, global.hardmaxconn);
2689 info[INF_CURR_CONN] = mkf_u32(0, actconn);
2690 info[INF_CUM_CONN] = mkf_u32(FN_COUNTER, totalconn);
2691 info[INF_CUM_REQ] = mkf_u32(FN_COUNTER, global.req_count);
2692#ifdef USE_OPENSSL
2693 info[INF_MAX_SSL_CONNS] = mkf_u32(FN_MAX, global.maxsslconn);
2694 info[INF_CURR_SSL_CONNS] = mkf_u32(0, sslconns);
2695 info[INF_CUM_SSL_CONNS] = mkf_u32(FN_COUNTER, totalsslconns);
2696#endif
2697 info[INF_MAXPIPES] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxpipes);
2698 info[INF_PIPES_USED] = mkf_u32(0, pipes_used);
2699 info[INF_PIPES_FREE] = mkf_u32(0, pipes_free);
2700 info[INF_CONN_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.conn_per_sec));
2701 info[INF_CONN_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.cps_lim);
2702 info[INF_MAX_CONN_RATE] = mkf_u32(FN_MAX, global.cps_max);
2703 info[INF_SESS_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.sess_per_sec));
2704 info[INF_SESS_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.sps_lim);
2705 info[INF_MAX_SESS_RATE] = mkf_u32(FN_RATE, global.sps_max);
2706
2707#ifdef USE_OPENSSL
2708 info[INF_SSL_RATE] = mkf_u32(FN_RATE, ssl_sess_rate);
2709 info[INF_SSL_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.ssl_lim);
2710 info[INF_MAX_SSL_RATE] = mkf_u32(FN_MAX, global.ssl_max);
2711 info[INF_SSL_FRONTEND_KEY_RATE] = mkf_u32(0, ssl_key_rate);
2712 info[INF_SSL_FRONTEND_MAX_KEY_RATE] = mkf_u32(FN_MAX, global.ssl_fe_keys_max);
2713 info[INF_SSL_FRONTEND_SESSION_REUSE_PCT] = mkf_u32(0, ssl_reuse);
2714 info[INF_SSL_BACKEND_KEY_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.ssl_be_keys_per_sec));
2715 info[INF_SSL_BACKEND_MAX_KEY_RATE] = mkf_u32(FN_MAX, global.ssl_be_keys_max);
2716 info[INF_SSL_CACHE_LOOKUPS] = mkf_u32(FN_COUNTER, global.shctx_lookups);
2717 info[INF_SSL_CACHE_MISSES] = mkf_u32(FN_COUNTER, global.shctx_misses);
2718#endif
2719 info[INF_COMPRESS_BPS_IN] = mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_in));
2720 info[INF_COMPRESS_BPS_OUT] = mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_out));
2721 info[INF_COMPRESS_BPS_RATE_LIM] = mkf_u32(FO_CONFIG|FN_LIMIT, global.comp_rate_lim);
2722#ifdef USE_ZLIB
2723 info[INF_ZLIB_MEM_USAGE] = mkf_u32(0, zlib_used_memory);
2724 info[INF_MAX_ZLIB_MEM_USAGE] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxzlibmem);
2725#endif
2726 info[INF_TASKS] = mkf_u32(0, nb_tasks_cur);
2727 info[INF_RUN_QUEUE] = mkf_u32(0, run_queue_cur);
2728 info[INF_IDLE_PCT] = mkf_u32(FN_AVG, idle_pct);
2729 info[INF_NODE] = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.node);
2730 if (global.desc)
2731 info[INF_DESCRIPTION] = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.desc);
2732
2733 return 1;
2734}
2735
2736/* This function dumps information onto the stream interface's read buffer.
2737 * It returns 0 as long as it does not complete, non-zero upon completion.
2738 * No state is used.
2739 */
2740static int stats_dump_info_to_buffer(struct stream_interface *si)
2741{
2742 struct appctx *appctx = __objt_appctx(si->end);
2743
2744 if (!stats_fill_info(info, INF_TOTAL_FIELDS))
2745 return 0;
2746
2747 chunk_reset(&trash);
2748
2749 if (appctx->ctx.stats.flags & STAT_FMT_TYPED)
2750 stats_dump_typed_info_fields(&trash, info);
2751 else
2752 stats_dump_info_fields(&trash, info);
2753
2754 if (bi_putchk(si_ic(si), &trash) == -1) {
2755 si_applet_cant_put(si);
2756 return 0;
2757 }
2758
2759 return 1;
2760}
2761
2762/* dumps server state information into <buf> for all the servers found in <backend>
2763 * These information are all the parameters which may change during HAProxy runtime.
2764 * By default, we only export to the last known server state file format.
2765 * These information can be used at next startup to recover same level of server state.
2766 */
2767static int dump_servers_state(struct stream_interface *si, struct chunk *buf)
2768{
2769 struct appctx *appctx = __objt_appctx(si->end);
2770 struct server *srv;
2771 char srv_addr[INET6_ADDRSTRLEN + 1];
2772 time_t srv_time_since_last_change;
2773 int bk_f_forced_id, srv_f_forced_id;
2774
2775
2776 /* we don't want to report any state if the backend is not enabled on this process */
2777 if (appctx->ctx.server_state.px->bind_proc && !(appctx->ctx.server_state.px->bind_proc & (1UL << (relative_pid - 1))))
2778 return 1;
2779
2780 if (!appctx->ctx.server_state.sv)
2781 appctx->ctx.server_state.sv = appctx->ctx.server_state.px->srv;
2782
2783 for (; appctx->ctx.server_state.sv != NULL; appctx->ctx.server_state.sv = srv->next) {
2784 srv = appctx->ctx.server_state.sv;
2785 srv_addr[0] = '\0';
2786
2787 switch (srv->addr.ss_family) {
2788 case AF_INET:
2789 inet_ntop(srv->addr.ss_family, &((struct sockaddr_in *)&srv->addr)->sin_addr,
2790 srv_addr, INET_ADDRSTRLEN + 1);
2791 break;
2792 case AF_INET6:
2793 inet_ntop(srv->addr.ss_family, &((struct sockaddr_in6 *)&srv->addr)->sin6_addr,
2794 srv_addr, INET6_ADDRSTRLEN + 1);
2795 break;
2796 }
2797 srv_time_since_last_change = now.tv_sec - srv->last_change;
2798 bk_f_forced_id = appctx->ctx.server_state.px->options & PR_O_FORCED_ID ? 1 : 0;
2799 srv_f_forced_id = srv->flags & SRV_F_FORCED_ID ? 1 : 0;
2800
2801 chunk_appendf(buf,
2802 "%d %s "
2803 "%d %s %s "
2804 "%d %d %d %d %ld "
2805 "%d %d %d %d %d "
2806 "%d %d"
2807 "\n",
2808 appctx->ctx.server_state.px->uuid, appctx->ctx.server_state.px->id,
2809 srv->puid, srv->id, srv_addr,
2810 srv->state, srv->admin, srv->uweight, srv->iweight, (long int)srv_time_since_last_change,
2811 srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state,
2812 bk_f_forced_id, srv_f_forced_id);
2813 if (bi_putchk(si_ic(si), &trash) == -1) {
2814 si_applet_cant_put(si);
2815 return 0;
2816 }
2817 }
2818 return 1;
2819}
2820
2821/* Parses backend list and simply report backend names */
2822static int stats_dump_backend_to_buffer(struct stream_interface *si)
2823{
2824 struct appctx *appctx = __objt_appctx(si->end);
2825 extern struct proxy *proxy;
2826 struct proxy *curproxy;
2827
2828 chunk_reset(&trash);
2829
2830 if (!appctx->ctx.be.px) {
2831 chunk_printf(&trash, "# name\n");
2832 if (bi_putchk(si_ic(si), &trash) == -1) {
2833 si_applet_cant_put(si);
2834 return 0;
2835 }
2836 appctx->ctx.be.px = proxy;
2837 }
2838
2839 for (; appctx->ctx.be.px != NULL; appctx->ctx.be.px = curproxy->next) {
2840 curproxy = appctx->ctx.be.px;
2841
2842 /* looking for backends only */
2843 if (!(curproxy->cap & PR_CAP_BE))
2844 continue;
2845
2846 /* we don't want to list a backend which is bound to this process */
2847 if (curproxy->bind_proc && !(curproxy->bind_proc & (1UL << (relative_pid - 1))))
2848 continue;
2849
2850 chunk_appendf(&trash, "%s\n", curproxy->id);
2851 if (bi_putchk(si_ic(si), &trash) == -1) {
2852 si_applet_cant_put(si);
2853 return 0;
2854 }
2855 }
2856
2857 return 1;
2858}
2859
2860/* Parses backend list or simply use backend name provided by the user to return
2861 * states of servers to stdout.
2862 */
2863static int stats_dump_servers_state_to_buffer(struct stream_interface *si)
2864{
2865 struct appctx *appctx = __objt_appctx(si->end);
2866 extern struct proxy *proxy;
2867 struct proxy *curproxy;
2868
2869 chunk_reset(&trash);
2870
2871 if (appctx->st2 == STAT_ST_INIT) {
2872 if (!appctx->ctx.server_state.px)
2873 appctx->ctx.server_state.px = proxy;
2874 appctx->st2 = STAT_ST_HEAD;
2875 }
2876
2877 if (appctx->st2 == STAT_ST_HEAD) {
2878 chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES);
2879 if (bi_putchk(si_ic(si), &trash) == -1) {
2880 si_applet_cant_put(si);
2881 return 0;
2882 }
2883 appctx->st2 = STAT_ST_INFO;
2884 }
2885
2886 /* STAT_ST_INFO */
2887 for (; appctx->ctx.server_state.px != NULL; appctx->ctx.server_state.px = curproxy->next) {
2888 curproxy = appctx->ctx.server_state.px;
2889 /* servers are only in backends */
2890 if (curproxy->cap & PR_CAP_BE) {
2891 if (!dump_servers_state(si, &trash))
2892 return 0;
2893
2894 if (bi_putchk(si_ic(si), &trash) == -1) {
2895 si_applet_cant_put(si);
2896 return 0;
2897 }
2898 }
2899 /* only the selected proxy is dumped */
2900 if (appctx->ctx.server_state.iid)
2901 break;
2902 }
2903
2904 return 1;
2905}
2906
2907/* This function dumps memory usage information onto the stream interface's
2908 * read buffer. It returns 0 as long as it does not complete, non-zero upon
2909 * completion. No state is used.
2910 */
2911static int stats_dump_pools_to_buffer(struct stream_interface *si)
2912{
2913 dump_pools_to_trash();
2914 if (bi_putchk(si_ic(si), &trash) == -1) {
2915 si_applet_cant_put(si);
2916 return 0;
2917 }
2918 return 1;
2919}
2920
2921static inline const char *get_conn_ctrl_name(const struct connection *conn)
2922{
2923 if (!conn_ctrl_ready(conn))
2924 return "NONE";
2925 return conn->ctrl->name;
2926}
2927
2928static inline const char *get_conn_xprt_name(const struct connection *conn)
2929{
2930 static char ptr[19];
2931
2932 if (!conn_xprt_ready(conn))
2933 return "NONE";
2934
2935 if (conn->xprt == &raw_sock)
2936 return "RAW";
2937
2938#ifdef USE_OPENSSL
2939 if (conn->xprt == &ssl_sock)
2940 return "SSL";
2941#endif
2942 snprintf(ptr, sizeof(ptr), "%p", conn->xprt);
2943 return ptr;
2944}
2945
2946static inline const char *get_conn_data_name(const struct connection *conn)
2947{
2948 static char ptr[19];
2949
2950 if (!conn->data)
2951 return "NONE";
2952
2953 if (conn->data == &sess_conn_cb)
2954 return "SESS";
2955
2956 if (conn->data == &si_conn_cb)
2957 return "STRM";
2958
2959 if (conn->data == &check_conn_cb)
2960 return "CHCK";
2961
2962 snprintf(ptr, sizeof(ptr), "%p", conn->data);
2963 return ptr;
2964}
2965
2966/* This function dumps a complete stream state onto the stream interface's
2967 * read buffer. The stream has to be set in sess->target. It returns
2968 * 0 if the output buffer is full and it needs to be called again, otherwise
2969 * non-zero. It is designed to be called from stats_dump_sess_to_buffer() below.
2970 */
2971static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess)
2972{
2973 struct appctx *appctx = __objt_appctx(si->end);
2974 struct tm tm;
2975 extern const char *monthname[12];
2976 char pn[INET6_ADDRSTRLEN];
2977 struct connection *conn;
2978 struct appctx *tmpctx;
2979
2980 chunk_reset(&trash);
2981
2982 if (appctx->ctx.sess.section > 0 && appctx->ctx.sess.uid != sess->uniq_id) {
2983 /* stream changed, no need to go any further */
2984 chunk_appendf(&trash, " *** session terminated while we were watching it ***\n");
2985 if (bi_putchk(si_ic(si), &trash) == -1) {
2986 si_applet_cant_put(si);
2987 return 0;
2988 }
2989 appctx->ctx.sess.uid = 0;
2990 appctx->ctx.sess.section = 0;
2991 return 1;
2992 }
2993
2994 switch (appctx->ctx.sess.section) {
2995 case 0: /* main status of the stream */
2996 appctx->ctx.sess.uid = sess->uniq_id;
2997 appctx->ctx.sess.section = 1;
2998 /* fall through */
2999
3000 case 1:
3001 get_localtime(sess->logs.accept_date.tv_sec, &tm);
3002 chunk_appendf(&trash,
3003 "%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s",
3004 sess,
3005 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
3006 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec),
3007 sess->uniq_id,
3008 strm_li(sess) ? strm_li(sess)->proto->name : "?");
3009
3010 conn = objt_conn(strm_orig(sess));
3011 switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
3012 case AF_INET:
3013 case AF_INET6:
3014 chunk_appendf(&trash, " source=%s:%d\n",
3015 pn, get_host_port(&conn->addr.from));
3016 break;
3017 case AF_UNIX:
3018 chunk_appendf(&trash, " source=unix:%d\n", strm_li(sess)->luid);
3019 break;
3020 default:
3021 /* no more information to print right now */
3022 chunk_appendf(&trash, "\n");
3023 break;
3024 }
3025
3026 chunk_appendf(&trash,
3027 " flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n",
3028 sess->flags, sess->si[1].conn_retries, sess->srv_conn, sess->pend_pos);
3029
3030 chunk_appendf(&trash,
3031 " frontend=%s (id=%u mode=%s), listener=%s (id=%u)",
3032 strm_fe(sess)->id, strm_fe(sess)->uuid, strm_fe(sess)->mode ? "http" : "tcp",
3033 strm_li(sess) ? strm_li(sess)->name ? strm_li(sess)->name : "?" : "?",
3034 strm_li(sess) ? strm_li(sess)->luid : 0);
3035
3036 if (conn)
3037 conn_get_to_addr(conn);
3038
3039 switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
3040 case AF_INET:
3041 case AF_INET6:
3042 chunk_appendf(&trash, " addr=%s:%d\n",
3043 pn, get_host_port(&conn->addr.to));
3044 break;
3045 case AF_UNIX:
3046 chunk_appendf(&trash, " addr=unix:%d\n", strm_li(sess)->luid);
3047 break;
3048 default:
3049 /* no more information to print right now */
3050 chunk_appendf(&trash, "\n");
3051 break;
3052 }
3053
3054 if (sess->be->cap & PR_CAP_BE)
3055 chunk_appendf(&trash,
3056 " backend=%s (id=%u mode=%s)",
3057 sess->be->id,
3058 sess->be->uuid, sess->be->mode ? "http" : "tcp");
3059 else
3060 chunk_appendf(&trash, " backend=<NONE> (id=-1 mode=-)");
3061
3062 conn = objt_conn(sess->si[1].end);
3063 if (conn)
3064 conn_get_from_addr(conn);
3065
3066 switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
3067 case AF_INET:
3068 case AF_INET6:
3069 chunk_appendf(&trash, " addr=%s:%d\n",
3070 pn, get_host_port(&conn->addr.from));
3071 break;
3072 case AF_UNIX:
3073 chunk_appendf(&trash, " addr=unix\n");
3074 break;
3075 default:
3076 /* no more information to print right now */
3077 chunk_appendf(&trash, "\n");
3078 break;
3079 }
3080
3081 if (sess->be->cap & PR_CAP_BE)
3082 chunk_appendf(&trash,
3083 " server=%s (id=%u)",
3084 objt_server(sess->target) ? objt_server(sess->target)->id : "<none>",
3085 objt_server(sess->target) ? objt_server(sess->target)->puid : 0);
3086 else
3087 chunk_appendf(&trash, " server=<NONE> (id=-1)");
3088
3089 if (conn)
3090 conn_get_to_addr(conn);
3091
3092 switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
3093 case AF_INET:
3094 case AF_INET6:
3095 chunk_appendf(&trash, " addr=%s:%d\n",
3096 pn, get_host_port(&conn->addr.to));
3097 break;
3098 case AF_UNIX:
3099 chunk_appendf(&trash, " addr=unix\n");
3100 break;
3101 default:
3102 /* no more information to print right now */
3103 chunk_appendf(&trash, "\n");
3104 break;
3105 }
3106
3107 chunk_appendf(&trash,
3108 " task=%p (state=0x%02x nice=%d calls=%d exp=%s%s",
3109 sess->task,
3110 sess->task->state,
3111 sess->task->nice, sess->task->calls,
3112 sess->task->expire ?
3113 tick_is_expired(sess->task->expire, now_ms) ? "<PAST>" :
3114 human_time(TICKS_TO_MS(sess->task->expire - now_ms),
3115 TICKS_TO_MS(1000)) : "<NEVER>",
3116 task_in_rq(sess->task) ? ", running" : "");
3117
3118 chunk_appendf(&trash,
3119 " age=%s)\n",
3120 human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1));
3121
3122 if (sess->txn)
3123 chunk_appendf(&trash,
3124 " txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s waiting=%d\n",
3125 sess->txn, sess->txn->flags, sess->txn->meth, sess->txn->status,
3126 http_msg_state_str(sess->txn->req.msg_state), http_msg_state_str(sess->txn->rsp.msg_state), !LIST_ISEMPTY(&sess->buffer_wait));
3127
3128 chunk_appendf(&trash,
3129 " si[0]=%p (state=%s flags=0x%02x endp0=%s:%p exp=%s, et=0x%03x)\n",
3130 &sess->si[0],
3131 si_state_str(sess->si[0].state),
3132 sess->si[0].flags,
3133 obj_type_name(sess->si[0].end),
3134 obj_base_ptr(sess->si[0].end),
3135 sess->si[0].exp ?
3136 tick_is_expired(sess->si[0].exp, now_ms) ? "<PAST>" :
3137 human_time(TICKS_TO_MS(sess->si[0].exp - now_ms),
3138 TICKS_TO_MS(1000)) : "<NEVER>",
3139 sess->si[0].err_type);
3140
3141 chunk_appendf(&trash,
3142 " si[1]=%p (state=%s flags=0x%02x endp1=%s:%p exp=%s, et=0x%03x)\n",
3143 &sess->si[1],
3144 si_state_str(sess->si[1].state),
3145 sess->si[1].flags,
3146 obj_type_name(sess->si[1].end),
3147 obj_base_ptr(sess->si[1].end),
3148 sess->si[1].exp ?
3149 tick_is_expired(sess->si[1].exp, now_ms) ? "<PAST>" :
3150 human_time(TICKS_TO_MS(sess->si[1].exp - now_ms),
3151 TICKS_TO_MS(1000)) : "<NEVER>",
3152 sess->si[1].err_type);
3153
3154 if ((conn = objt_conn(sess->si[0].end)) != NULL) {
3155 chunk_appendf(&trash,
3156 " co0=%p ctrl=%s xprt=%s data=%s target=%s:%p\n",
3157 conn,
3158 get_conn_ctrl_name(conn),
3159 get_conn_xprt_name(conn),
3160 get_conn_data_name(conn),
3161 obj_type_name(conn->target),
3162 obj_base_ptr(conn->target));
3163
3164 chunk_appendf(&trash, " flags=0x%08x", conn->flags);
3165
3166 if (conn->t.sock.fd >= 0) {
3167 chunk_appendf(&trash, " fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
3168 conn->t.sock.fd, fdtab[conn->t.sock.fd].state,
3169 fdtab[conn->t.sock.fd].cache, fdtab[conn->t.sock.fd].updated);
3170 }
3171 else
3172 chunk_appendf(&trash, " fd=<dead>\n");
3173 }
3174 else if ((tmpctx = objt_appctx(sess->si[0].end)) != NULL) {
3175 chunk_appendf(&trash,
3176 " app0=%p st0=%d st1=%d st2=%d applet=%s\n",
3177 tmpctx,
3178 tmpctx->st0,
3179 tmpctx->st1,
3180 tmpctx->st2,
3181 tmpctx->applet->name);
3182 }
3183
3184 if ((conn = objt_conn(sess->si[1].end)) != NULL) {
3185 chunk_appendf(&trash,
3186 " co1=%p ctrl=%s xprt=%s data=%s target=%s:%p\n",
3187 conn,
3188 get_conn_ctrl_name(conn),
3189 get_conn_xprt_name(conn),
3190 get_conn_data_name(conn),
3191 obj_type_name(conn->target),
3192 obj_base_ptr(conn->target));
3193
3194 chunk_appendf(&trash, " flags=0x%08x", conn->flags);
3195
3196 if (conn->t.sock.fd >= 0) {
3197 chunk_appendf(&trash, " fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
3198 conn->t.sock.fd, fdtab[conn->t.sock.fd].state,
3199 fdtab[conn->t.sock.fd].cache, fdtab[conn->t.sock.fd].updated);
3200 }
3201 else
3202 chunk_appendf(&trash, " fd=<dead>\n");
3203 }
3204 else if ((tmpctx = objt_appctx(sess->si[1].end)) != NULL) {
3205 chunk_appendf(&trash,
3206 " app1=%p st0=%d st1=%d st2=%d applet=%s\n",
3207 tmpctx,
3208 tmpctx->st0,
3209 tmpctx->st1,
3210 tmpctx->st2,
3211 tmpctx->applet->name);
3212 }
3213
3214 chunk_appendf(&trash,
3215 " req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
3216 " an_exp=%s",
3217 &sess->req,
3218 sess->req.flags, sess->req.analysers,
3219 sess->req.pipe ? sess->req.pipe->data : 0,
3220 sess->req.to_forward, sess->req.total,
3221 sess->req.analyse_exp ?
3222 human_time(TICKS_TO_MS(sess->req.analyse_exp - now_ms),
3223 TICKS_TO_MS(1000)) : "<NEVER>");
3224
3225 chunk_appendf(&trash,
3226 " rex=%s",
3227 sess->req.rex ?
3228 human_time(TICKS_TO_MS(sess->req.rex - now_ms),
3229 TICKS_TO_MS(1000)) : "<NEVER>");
3230
3231 chunk_appendf(&trash,
3232 " wex=%s\n"
3233 " buf=%p data=%p o=%d p=%d req.next=%d i=%d size=%d\n",
3234 sess->req.wex ?
3235 human_time(TICKS_TO_MS(sess->req.wex - now_ms),
3236 TICKS_TO_MS(1000)) : "<NEVER>",
3237 sess->req.buf,
3238 sess->req.buf->data, sess->req.buf->o,
3239 (int)(sess->req.buf->p - sess->req.buf->data),
3240 sess->txn ? sess->txn->req.next : 0, sess->req.buf->i,
3241 sess->req.buf->size);
3242
3243 chunk_appendf(&trash,
3244 " res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
3245 " an_exp=%s",
3246 &sess->res,
3247 sess->res.flags, sess->res.analysers,
3248 sess->res.pipe ? sess->res.pipe->data : 0,
3249 sess->res.to_forward, sess->res.total,
3250 sess->res.analyse_exp ?
3251 human_time(TICKS_TO_MS(sess->res.analyse_exp - now_ms),
3252 TICKS_TO_MS(1000)) : "<NEVER>");
3253
3254 chunk_appendf(&trash,
3255 " rex=%s",
3256 sess->res.rex ?
3257 human_time(TICKS_TO_MS(sess->res.rex - now_ms),
3258 TICKS_TO_MS(1000)) : "<NEVER>");
3259
3260 chunk_appendf(&trash,
3261 " wex=%s\n"
3262 " buf=%p data=%p o=%d p=%d rsp.next=%d i=%d size=%d\n",
3263 sess->res.wex ?
3264 human_time(TICKS_TO_MS(sess->res.wex - now_ms),
3265 TICKS_TO_MS(1000)) : "<NEVER>",
3266 sess->res.buf,
3267 sess->res.buf->data, sess->res.buf->o,
3268 (int)(sess->res.buf->p - sess->res.buf->data),
3269 sess->txn ? sess->txn->rsp.next : 0, sess->res.buf->i,
3270 sess->res.buf->size);
3271
3272 if (bi_putchk(si_ic(si), &trash) == -1) {
3273 si_applet_cant_put(si);
3274 return 0;
3275 }
3276
3277 /* use other states to dump the contents */
3278 }
3279 /* end of dump */
3280 appctx->ctx.sess.uid = 0;
3281 appctx->ctx.sess.section = 0;
3282 return 1;
3283}
3284
William Lallemand74c24fb2016-11-21 17:18:36 +01003285static int stats_pats_list(struct stream_interface *si)
3286{
3287 struct appctx *appctx = __objt_appctx(si->end);
3288
3289 switch (appctx->st2) {
3290 case STAT_ST_INIT:
3291 /* Display the column headers. If the message cannot be sent,
3292 * quit the fucntion with returning 0. The function is called
3293 * later and restart at the state "STAT_ST_INIT".
3294 */
3295 chunk_reset(&trash);
3296 chunk_appendf(&trash, "# id (file) description\n");
3297 if (bi_putchk(si_ic(si), &trash) == -1) {
3298 si_applet_cant_put(si);
3299 return 0;
3300 }
3301
3302 /* Now, we start the browsing of the references lists.
3303 * Note that the following call to LIST_ELEM return bad pointer. The only
3304 * available field of this pointer is <list>. It is used with the function
3305 * pat_list_get_next() for retruning the first available entry
3306 */
3307 appctx->ctx.map.ref = LIST_ELEM(&pattern_reference, struct pat_ref *, list);
3308 appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
3309 appctx->ctx.map.display_flags);
3310 appctx->st2 = STAT_ST_LIST;
3311 /* fall through */
3312
3313 case STAT_ST_LIST:
3314 while (appctx->ctx.map.ref) {
3315 chunk_reset(&trash);
3316
3317 /* Build messages. If the reference is used by another category than
3318 * the listed categorie, display the information in the massage.
3319 */
3320 chunk_appendf(&trash, "%d (%s) %s\n", appctx->ctx.map.ref->unique_id,
3321 appctx->ctx.map.ref->reference ? appctx->ctx.map.ref->reference : "",
3322 appctx->ctx.map.ref->display);
3323
3324 if (bi_putchk(si_ic(si), &trash) == -1) {
3325 /* let's try again later from this stream. We add ourselves into
3326 * this stream's users so that it can remove us upon termination.
3327 */
3328 si_applet_cant_put(si);
3329 return 0;
3330 }
3331
3332 /* get next list entry and check the end of the list */
3333 appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
3334 appctx->ctx.map.display_flags);
3335 }
3336
3337 appctx->st2 = STAT_ST_FIN;
3338 /* fall through */
3339
3340 default:
3341 appctx->st2 = STAT_ST_FIN;
3342 return 1;
3343 }
3344 return 0;
3345}
3346
3347static int stats_map_lookup(struct stream_interface *si)
3348{
3349 struct appctx *appctx = __objt_appctx(si->end);
3350 struct sample sample;
3351 struct pattern *pat;
3352 int match_method;
3353
3354 switch (appctx->st2) {
3355 case STAT_ST_INIT:
3356 /* Init to the first entry. The list cannot be change */
3357 appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, list);
3358 appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr, &appctx->ctx.map.ref->pat);
3359 appctx->st2 = STAT_ST_LIST;
3360 /* fall through */
3361
3362 case STAT_ST_LIST:
3363 /* for each lookup type */
3364 while (appctx->ctx.map.expr) {
3365 /* initialise chunk to build new message */
3366 chunk_reset(&trash);
3367
3368 /* execute pattern matching */
3369 sample.data.type = SMP_T_STR;
3370 sample.flags = SMP_F_CONST;
3371 sample.data.u.str.len = appctx->ctx.map.chunk.len;
3372 sample.data.u.str.str = appctx->ctx.map.chunk.str;
3373 if (appctx->ctx.map.expr->pat_head->match &&
3374 sample_convert(&sample, appctx->ctx.map.expr->pat_head->expect_type))
3375 pat = appctx->ctx.map.expr->pat_head->match(&sample, appctx->ctx.map.expr, 1);
3376 else
3377 pat = NULL;
3378
3379 /* build return message: set type of match */
3380 for (match_method=0; match_method<PAT_MATCH_NUM; match_method++)
3381 if (appctx->ctx.map.expr->pat_head->match == pat_match_fcts[match_method])
3382 break;
3383 if (match_method >= PAT_MATCH_NUM)
3384 chunk_appendf(&trash, "type=unknown(%p)", appctx->ctx.map.expr->pat_head->match);
3385 else
3386 chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
3387
3388 /* case sensitive */
3389 if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
3390 chunk_appendf(&trash, ", case=insensitive");
3391 else
3392 chunk_appendf(&trash, ", case=sensitive");
3393
3394 /* Display no match, and set default value */
3395 if (!pat) {
3396 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
3397 chunk_appendf(&trash, ", found=no");
3398 else
3399 chunk_appendf(&trash, ", match=no");
3400 }
3401
3402 /* Display match and match info */
3403 else {
3404 /* display match */
3405 if (appctx->ctx.map.display_flags == PAT_REF_MAP)
3406 chunk_appendf(&trash, ", found=yes");
3407 else
3408 chunk_appendf(&trash, ", match=yes");
3409
3410 /* display index mode */
3411 if (pat->sflags & PAT_SF_TREE)
3412 chunk_appendf(&trash, ", idx=tree");
3413 else
3414 chunk_appendf(&trash, ", idx=list");
3415
3416 /* display pattern */
3417 if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
3418 if (pat->ref && pat->ref->pattern)
3419 chunk_appendf(&trash, ", key=\"%s\"", pat->ref->pattern);
3420 else
3421 chunk_appendf(&trash, ", key=unknown");
3422 }
3423 else {
3424 if (pat->ref && pat->ref->pattern)
3425 chunk_appendf(&trash, ", pattern=\"%s\"", pat->ref->pattern);
3426 else
3427 chunk_appendf(&trash, ", pattern=unknown");
3428 }
3429
3430 /* display return value */
3431 if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
3432 if (pat->data && pat->ref && pat->ref->sample)
3433 chunk_appendf(&trash, ", value=\"%s\", type=\"%s\"", pat->ref->sample,
3434 smp_to_type[pat->data->type]);
3435 else
3436 chunk_appendf(&trash, ", value=none");
3437 }
3438 }
3439
3440 chunk_appendf(&trash, "\n");
3441
3442 /* display response */
3443 if (bi_putchk(si_ic(si), &trash) == -1) {
3444 /* let's try again later from this stream. We add ourselves into
3445 * this stream's users so that it can remove us upon termination.
3446 */
3447 si_applet_cant_put(si);
3448 return 0;
3449 }
3450
3451 /* get next entry */
3452 appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr,
3453 &appctx->ctx.map.ref->pat);
3454 }
3455
3456 appctx->st2 = STAT_ST_FIN;
3457 /* fall through */
3458
3459 default:
3460 appctx->st2 = STAT_ST_FIN;
3461 free(appctx->ctx.map.chunk.str);
3462 return 1;
3463 }
3464}
3465
3466static int stats_pat_list(struct stream_interface *si)
3467{
3468 struct appctx *appctx = __objt_appctx(si->end);
3469
3470 switch (appctx->st2) {
3471
3472 case STAT_ST_INIT:
3473 /* Init to the first entry. The list cannot be change */
3474 appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.ref->head,
3475 struct pat_ref_elt *, list);
3476 if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
3477 appctx->ctx.map.elt = NULL;
3478 appctx->st2 = STAT_ST_LIST;
3479 /* fall through */
3480
3481 case STAT_ST_LIST:
3482 while (appctx->ctx.map.elt) {
3483 chunk_reset(&trash);
3484
3485 /* build messages */
3486 if (appctx->ctx.map.elt->sample)
3487 chunk_appendf(&trash, "%p %s %s\n",
3488 appctx->ctx.map.elt, appctx->ctx.map.elt->pattern,
3489 appctx->ctx.map.elt->sample);
3490 else
3491 chunk_appendf(&trash, "%p %s\n",
3492 appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
3493
3494 if (bi_putchk(si_ic(si), &trash) == -1) {
3495 /* let's try again later from this stream. We add ourselves into
3496 * this stream's users so that it can remove us upon termination.
3497 */
3498 si_applet_cant_put(si);
3499 return 0;
3500 }
3501
3502 /* get next list entry and check the end of the list */
3503 appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.elt->list,
3504 struct pat_ref_elt *, list);
3505 if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
3506 break;
3507 }
3508
3509 appctx->st2 = STAT_ST_FIN;
3510 /* fall through */
3511
3512 default:
3513 appctx->st2 = STAT_ST_FIN;
3514 return 1;
3515 }
3516}
3517
3518/* This function dumps all streams' states onto the stream interface's
3519 * read buffer. It returns 0 if the output buffer is full and it needs
3520 * to be called again, otherwise non-zero. It is designed to be called
3521 * from stats_dump_sess_to_buffer() below.
3522 */
3523static int stats_dump_sess_to_buffer(struct stream_interface *si)
3524{
3525 struct appctx *appctx = __objt_appctx(si->end);
3526 struct connection *conn;
3527
3528 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
3529 /* If we're forced to shut down, we might have to remove our
3530 * reference to the last stream being dumped.
3531 */
3532 if (appctx->st2 == STAT_ST_LIST) {
3533 if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
3534 LIST_DEL(&appctx->ctx.sess.bref.users);
3535 LIST_INIT(&appctx->ctx.sess.bref.users);
3536 }
3537 }
3538 return 1;
3539 }
3540
3541 chunk_reset(&trash);
3542
3543 switch (appctx->st2) {
3544 case STAT_ST_INIT:
3545 /* the function had not been called yet, let's prepare the
3546 * buffer for a response. We initialize the current stream
3547 * pointer to the first in the global list. When a target
3548 * stream is being destroyed, it is responsible for updating
3549 * this pointer. We know we have reached the end when this
3550 * pointer points back to the head of the streams list.
3551 */
3552 LIST_INIT(&appctx->ctx.sess.bref.users);
3553 appctx->ctx.sess.bref.ref = streams.n;
3554 appctx->st2 = STAT_ST_LIST;
3555 /* fall through */
3556
3557 case STAT_ST_LIST:
3558 /* first, let's detach the back-ref from a possible previous stream */
3559 if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
3560 LIST_DEL(&appctx->ctx.sess.bref.users);
3561 LIST_INIT(&appctx->ctx.sess.bref.users);
3562 }
3563
3564 /* and start from where we stopped */
3565 while (appctx->ctx.sess.bref.ref != &streams) {
3566 char pn[INET6_ADDRSTRLEN];
3567 struct stream *curr_sess;
3568
3569 curr_sess = LIST_ELEM(appctx->ctx.sess.bref.ref, struct stream *, list);
3570
3571 if (appctx->ctx.sess.target) {
3572 if (appctx->ctx.sess.target != (void *)-1 && appctx->ctx.sess.target != curr_sess)
3573 goto next_sess;
3574
3575 LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users);
3576 /* call the proper dump() function and return if we're missing space */
3577 if (!stats_dump_full_sess_to_buffer(si, curr_sess))
3578 return 0;
3579
3580 /* stream dump complete */
3581 LIST_DEL(&appctx->ctx.sess.bref.users);
3582 LIST_INIT(&appctx->ctx.sess.bref.users);
3583 if (appctx->ctx.sess.target != (void *)-1) {
3584 appctx->ctx.sess.target = NULL;
3585 break;
3586 }
3587 else
3588 goto next_sess;
3589 }
3590
3591 chunk_appendf(&trash,
3592 "%p: proto=%s",
3593 curr_sess,
3594 strm_li(curr_sess) ? strm_li(curr_sess)->proto->name : "?");
3595
3596 conn = objt_conn(strm_orig(curr_sess));
3597 switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
3598 case AF_INET:
3599 case AF_INET6:
3600 chunk_appendf(&trash,
3601 " src=%s:%d fe=%s be=%s srv=%s",
3602 pn,
3603 get_host_port(&conn->addr.from),
3604 strm_fe(curr_sess)->id,
3605 (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
3606 objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "<none>"
3607 );
3608 break;
3609 case AF_UNIX:
3610 chunk_appendf(&trash,
3611 " src=unix:%d fe=%s be=%s srv=%s",
3612 strm_li(curr_sess)->luid,
3613 strm_fe(curr_sess)->id,
3614 (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
3615 objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "<none>"
3616 );
3617 break;
3618 }
3619
3620 chunk_appendf(&trash,
3621 " ts=%02x age=%s calls=%d",
3622 curr_sess->task->state,
3623 human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1),
3624 curr_sess->task->calls);
3625
3626 chunk_appendf(&trash,
3627 " rq[f=%06xh,i=%d,an=%02xh,rx=%s",
3628 curr_sess->req.flags,
3629 curr_sess->req.buf->i,
3630 curr_sess->req.analysers,
3631 curr_sess->req.rex ?
3632 human_time(TICKS_TO_MS(curr_sess->req.rex - now_ms),
3633 TICKS_TO_MS(1000)) : "");
3634
3635 chunk_appendf(&trash,
3636 ",wx=%s",
3637 curr_sess->req.wex ?
3638 human_time(TICKS_TO_MS(curr_sess->req.wex - now_ms),
3639 TICKS_TO_MS(1000)) : "");
3640
3641 chunk_appendf(&trash,
3642 ",ax=%s]",
3643 curr_sess->req.analyse_exp ?
3644 human_time(TICKS_TO_MS(curr_sess->req.analyse_exp - now_ms),
3645 TICKS_TO_MS(1000)) : "");
3646
3647 chunk_appendf(&trash,
3648 " rp[f=%06xh,i=%d,an=%02xh,rx=%s",
3649 curr_sess->res.flags,
3650 curr_sess->res.buf->i,
3651 curr_sess->res.analysers,
3652 curr_sess->res.rex ?
3653 human_time(TICKS_TO_MS(curr_sess->res.rex - now_ms),
3654 TICKS_TO_MS(1000)) : "");
3655
3656 chunk_appendf(&trash,
3657 ",wx=%s",
3658 curr_sess->res.wex ?
3659 human_time(TICKS_TO_MS(curr_sess->res.wex - now_ms),
3660 TICKS_TO_MS(1000)) : "");
3661
3662 chunk_appendf(&trash,
3663 ",ax=%s]",
3664 curr_sess->res.analyse_exp ?
3665 human_time(TICKS_TO_MS(curr_sess->res.analyse_exp - now_ms),
3666 TICKS_TO_MS(1000)) : "");
3667
3668 conn = objt_conn(curr_sess->si[0].end);
3669 chunk_appendf(&trash,
3670 " s0=[%d,%1xh,fd=%d,ex=%s]",
3671 curr_sess->si[0].state,
3672 curr_sess->si[0].flags,
3673 (conn && conn->t.sock.fd >= 0) ? conn->t.sock.fd : -1,
3674 curr_sess->si[0].exp ?
3675 human_time(TICKS_TO_MS(curr_sess->si[0].exp - now_ms),
3676 TICKS_TO_MS(1000)) : "");
3677
3678 conn = objt_conn(curr_sess->si[1].end);
3679 chunk_appendf(&trash,
3680 " s1=[%d,%1xh,fd=%d,ex=%s]",
3681 curr_sess->si[1].state,
3682 curr_sess->si[1].flags,
3683 (conn && conn->t.sock.fd >= 0) ? conn->t.sock.fd : -1,
3684 curr_sess->si[1].exp ?
3685 human_time(TICKS_TO_MS(curr_sess->si[1].exp - now_ms),
3686 TICKS_TO_MS(1000)) : "");
3687
3688 chunk_appendf(&trash,
3689 " exp=%s",
3690 curr_sess->task->expire ?
3691 human_time(TICKS_TO_MS(curr_sess->task->expire - now_ms),
3692 TICKS_TO_MS(1000)) : "");
3693 if (task_in_rq(curr_sess->task))
3694 chunk_appendf(&trash, " run(nice=%d)", curr_sess->task->nice);
3695
3696 chunk_appendf(&trash, "\n");
3697
3698 if (bi_putchk(si_ic(si), &trash) == -1) {
3699 /* let's try again later from this stream. We add ourselves into
3700 * this stream's users so that it can remove us upon termination.
3701 */
3702 si_applet_cant_put(si);
3703 LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users);
3704 return 0;
3705 }
3706
3707 next_sess:
3708 appctx->ctx.sess.bref.ref = curr_sess->list.n;
3709 }
3710
3711 if (appctx->ctx.sess.target && appctx->ctx.sess.target != (void *)-1) {
3712 /* specified stream not found */
3713 if (appctx->ctx.sess.section > 0)
3714 chunk_appendf(&trash, " *** session terminated while we were watching it ***\n");
3715 else
3716 chunk_appendf(&trash, "Session not found.\n");
3717
3718 if (bi_putchk(si_ic(si), &trash) == -1) {
3719 si_applet_cant_put(si);
3720 return 0;
3721 }
3722
3723 appctx->ctx.sess.target = NULL;
3724 appctx->ctx.sess.uid = 0;
3725 return 1;
3726 }
3727
3728 appctx->st2 = STAT_ST_FIN;
3729 /* fall through */
3730
3731 default:
3732 appctx->st2 = STAT_ST_FIN;
3733 return 1;
3734 }
3735}
3736
3737/* This is called when the stream interface is closed. For instance, upon an
3738 * external abort, we won't call the i/o handler anymore so we may need to
3739 * remove back references to the stream currently being dumped.
3740 */
3741static void cli_release_handler(struct appctx *appctx)
3742{
3743 if (appctx->io_release) {
3744 appctx->io_release(appctx);
3745 appctx->io_release = NULL;
3746 }
3747 if (appctx->st0 == STAT_CLI_O_SESS && appctx->st2 == STAT_ST_LIST) {
3748 if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users))
3749 LIST_DEL(&appctx->ctx.sess.bref.users);
3750 }
3751 else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) &&
3752 appctx->st2 == STAT_ST_LIST) {
3753 appctx->ctx.table.entry->ref_cnt--;
3754 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
3755 }
3756 else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
3757 free(appctx->ctx.cli.err);
3758 appctx->ctx.cli.err = NULL;
3759 }
3760 else if (appctx->st0 == STAT_CLI_O_MLOOK) {
3761 free(appctx->ctx.map.chunk.str);
3762 appctx->ctx.map.chunk.str = NULL;
3763 }
3764}
3765
3766/* This function is used to either dump tables states (when action is set
3767 * to STAT_CLI_O_TAB) or clear tables (when action is STAT_CLI_O_CLR).
3768 * It returns 0 if the output buffer is full and it needs to be called
3769 * again, otherwise non-zero.
3770 */
3771static int stats_table_request(struct stream_interface *si, int action)
3772{
3773 struct appctx *appctx = __objt_appctx(si->end);
3774 struct stream *s = si_strm(si);
3775 struct ebmb_node *eb;
3776 int dt;
3777 int skip_entry;
3778 int show = action == STAT_CLI_O_TAB;
3779
3780 /*
3781 * We have 3 possible states in appctx->st2 :
3782 * - STAT_ST_INIT : the first call
3783 * - STAT_ST_INFO : the proxy pointer points to the next table to
3784 * dump, the entry pointer is NULL ;
3785 * - STAT_ST_LIST : the proxy pointer points to the current table
3786 * and the entry pointer points to the next entry to be dumped,
3787 * and the refcount on the next entry is held ;
3788 * - STAT_ST_END : nothing left to dump, the buffer may contain some
3789 * data though.
3790 */
3791
3792 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
3793 /* in case of abort, remove any refcount we might have set on an entry */
3794 if (appctx->st2 == STAT_ST_LIST) {
3795 appctx->ctx.table.entry->ref_cnt--;
3796 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
3797 }
3798 return 1;
3799 }
3800
3801 chunk_reset(&trash);
3802
3803 while (appctx->st2 != STAT_ST_FIN) {
3804 switch (appctx->st2) {
3805 case STAT_ST_INIT:
3806 appctx->ctx.table.proxy = appctx->ctx.table.target;
3807 if (!appctx->ctx.table.proxy)
3808 appctx->ctx.table.proxy = proxy;
3809
3810 appctx->ctx.table.entry = NULL;
3811 appctx->st2 = STAT_ST_INFO;
3812 break;
3813
3814 case STAT_ST_INFO:
3815 if (!appctx->ctx.table.proxy ||
3816 (appctx->ctx.table.target &&
3817 appctx->ctx.table.proxy != appctx->ctx.table.target)) {
3818 appctx->st2 = STAT_ST_END;
3819 break;
3820 }
3821
3822 if (appctx->ctx.table.proxy->table.size) {
3823 if (show && !stats_dump_table_head_to_buffer(&trash, si, appctx->ctx.table.proxy,
3824 appctx->ctx.table.target))
3825 return 0;
3826
3827 if (appctx->ctx.table.target &&
3828 strm_li(s)->bind_conf->level >= ACCESS_LVL_OPER) {
3829 /* dump entries only if table explicitly requested */
3830 eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
3831 if (eb) {
3832 appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
3833 appctx->ctx.table.entry->ref_cnt++;
3834 appctx->st2 = STAT_ST_LIST;
3835 break;
3836 }
3837 }
3838 }
3839 appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
3840 break;
3841
3842 case STAT_ST_LIST:
3843 skip_entry = 0;
3844
3845 if (appctx->ctx.table.data_type >= 0) {
3846 /* we're filtering on some data contents */
3847 void *ptr;
3848 long long data;
3849
3850 dt = appctx->ctx.table.data_type;
3851 ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
3852 appctx->ctx.table.entry,
3853 dt);
3854
3855 data = 0;
3856 switch (stktable_data_types[dt].std_type) {
3857 case STD_T_SINT:
3858 data = stktable_data_cast(ptr, std_t_sint);
3859 break;
3860 case STD_T_UINT:
3861 data = stktable_data_cast(ptr, std_t_uint);
3862 break;
3863 case STD_T_ULL:
3864 data = stktable_data_cast(ptr, std_t_ull);
3865 break;
3866 case STD_T_FRQP:
3867 data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
3868 appctx->ctx.table.proxy->table.data_arg[dt].u);
3869 break;
3870 }
3871
3872 /* skip the entry if the data does not match the test and the value */
3873 if ((data < appctx->ctx.table.value &&
3874 (appctx->ctx.table.data_op == STD_OP_EQ ||
3875 appctx->ctx.table.data_op == STD_OP_GT ||
3876 appctx->ctx.table.data_op == STD_OP_GE)) ||
3877 (data == appctx->ctx.table.value &&
3878 (appctx->ctx.table.data_op == STD_OP_NE ||
3879 appctx->ctx.table.data_op == STD_OP_GT ||
3880 appctx->ctx.table.data_op == STD_OP_LT)) ||
3881 (data > appctx->ctx.table.value &&
3882 (appctx->ctx.table.data_op == STD_OP_EQ ||
3883 appctx->ctx.table.data_op == STD_OP_LT ||
3884 appctx->ctx.table.data_op == STD_OP_LE)))
3885 skip_entry = 1;
3886 }
3887
3888 if (show && !skip_entry &&
3889 !stats_dump_table_entry_to_buffer(&trash, si, appctx->ctx.table.proxy,
3890 appctx->ctx.table.entry))
3891 return 0;
3892
3893 appctx->ctx.table.entry->ref_cnt--;
3894
3895 eb = ebmb_next(&appctx->ctx.table.entry->key);
3896 if (eb) {
3897 struct stksess *old = appctx->ctx.table.entry;
3898 appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
3899 if (show)
3900 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
3901 else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
3902 stksess_kill(&appctx->ctx.table.proxy->table, old);
3903 appctx->ctx.table.entry->ref_cnt++;
3904 break;
3905 }
3906
3907
3908 if (show)
3909 stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
3910 else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
3911 stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
3912
3913 appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
3914 appctx->st2 = STAT_ST_INFO;
3915 break;
3916
3917 case STAT_ST_END:
3918 appctx->st2 = STAT_ST_FIN;
3919 break;
3920 }
3921 }
3922 return 1;
3923}
3924
3925/* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
3926 * <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
3927 * which is 60 chars per line. Non-printable chars \t, \n, \r and \e are
3928 * encoded in C format. Other non-printable chars are encoded "\xHH". Original
3929 * lines are respected within the limit of 70 output chars. Lines that are
3930 * continuation of a previous truncated line begin with "+" instead of " "
3931 * after the offset. The new pointer is returned.
3932 */
3933static int dump_text_line(struct chunk *out, const char *buf, int bsize, int len,
3934 int *line, int ptr)
3935{
3936 int end;
3937 unsigned char c;
3938
3939 end = out->len + 80;
3940 if (end > out->size)
3941 return ptr;
3942
3943 chunk_appendf(out, " %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
3944
3945 while (ptr < len && ptr < bsize) {
3946 c = buf[ptr];
3947 if (isprint(c) && isascii(c) && c != '\\') {
3948 if (out->len > end - 2)
3949 break;
3950 out->str[out->len++] = c;
3951 } else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\') {
3952 if (out->len > end - 3)
3953 break;
3954 out->str[out->len++] = '\\';
3955 switch (c) {
3956 case '\t': c = 't'; break;
3957 case '\n': c = 'n'; break;
3958 case '\r': c = 'r'; break;
3959 case '\e': c = 'e'; break;
3960 case '\\': c = '\\'; break;
3961 }
3962 out->str[out->len++] = c;
3963 } else {
3964 if (out->len > end - 5)
3965 break;
3966 out->str[out->len++] = '\\';
3967 out->str[out->len++] = 'x';
3968 out->str[out->len++] = hextab[(c >> 4) & 0xF];
3969 out->str[out->len++] = hextab[c & 0xF];
3970 }
3971 if (buf[ptr++] == '\n') {
3972 /* we had a line break, let's return now */
3973 out->str[out->len++] = '\n';
3974 *line = ptr;
3975 return ptr;
3976 }
3977 }
3978 /* we have an incomplete line, we return it as-is */
3979 out->str[out->len++] = '\n';
3980 return ptr;
3981}
3982
3983/* This function dumps counters from all resolvers section and associated name servers.
3984 * It returns 0 if the output buffer is full and it needs
3985 * to be called again, otherwise non-zero.
3986 */
3987static int stats_dump_resolvers_to_buffer(struct stream_interface *si)
3988{
3989 struct appctx *appctx = __objt_appctx(si->end);
3990 struct dns_resolvers *presolvers;
3991 struct dns_nameserver *pnameserver;
3992
3993 chunk_reset(&trash);
3994
3995 switch (appctx->st2) {
3996 case STAT_ST_INIT:
3997 appctx->st2 = STAT_ST_LIST; /* let's start producing data */
3998 /* fall through */
3999
4000 case STAT_ST_LIST:
4001 if (LIST_ISEMPTY(&dns_resolvers)) {
4002 chunk_appendf(&trash, "No resolvers found\n");
4003 }
4004 else {
4005 list_for_each_entry(presolvers, &dns_resolvers, list) {
4006 if (appctx->ctx.resolvers.ptr != NULL && appctx->ctx.resolvers.ptr != presolvers)
4007 continue;
4008
4009 chunk_appendf(&trash, "Resolvers section %s\n", presolvers->id);
4010 list_for_each_entry(pnameserver, &presolvers->nameserver_list, list) {
4011 chunk_appendf(&trash, " nameserver %s:\n", pnameserver->id);
4012 chunk_appendf(&trash, " sent: %ld\n", pnameserver->counters.sent);
4013 chunk_appendf(&trash, " valid: %ld\n", pnameserver->counters.valid);
4014 chunk_appendf(&trash, " update: %ld\n", pnameserver->counters.update);
4015 chunk_appendf(&trash, " cname: %ld\n", pnameserver->counters.cname);
4016 chunk_appendf(&trash, " cname_error: %ld\n", pnameserver->counters.cname_error);
4017 chunk_appendf(&trash, " any_err: %ld\n", pnameserver->counters.any_err);
4018 chunk_appendf(&trash, " nx: %ld\n", pnameserver->counters.nx);
4019 chunk_appendf(&trash, " timeout: %ld\n", pnameserver->counters.timeout);
4020 chunk_appendf(&trash, " refused: %ld\n", pnameserver->counters.refused);
4021 chunk_appendf(&trash, " other: %ld\n", pnameserver->counters.other);
4022 chunk_appendf(&trash, " invalid: %ld\n", pnameserver->counters.invalid);
4023 chunk_appendf(&trash, " too_big: %ld\n", pnameserver->counters.too_big);
4024 chunk_appendf(&trash, " truncated: %ld\n", pnameserver->counters.truncated);
4025 chunk_appendf(&trash, " outdated: %ld\n", pnameserver->counters.outdated);
4026 }
4027 }
4028 }
4029
4030 /* display response */
4031 if (bi_putchk(si_ic(si), &trash) == -1) {
4032 /* let's try again later from this session. We add ourselves into
4033 * this session's users so that it can remove us upon termination.
4034 */
4035 si->flags |= SI_FL_WAIT_ROOM;
4036 return 0;
4037 }
4038
4039 appctx->st2 = STAT_ST_FIN;
4040 /* fall through */
4041
4042 default:
4043 appctx->st2 = STAT_ST_FIN;
4044 return 1;
4045 }
4046}
4047
4048/* This function dumps all captured errors onto the stream interface's
4049 * read buffer. It returns 0 if the output buffer is full and it needs
4050 * to be called again, otherwise non-zero.
4051 */
4052static int stats_dump_errors_to_buffer(struct stream_interface *si)
4053{
4054 struct appctx *appctx = __objt_appctx(si->end);
4055 extern const char *monthname[12];
4056
4057 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
4058 return 1;
4059
4060 chunk_reset(&trash);
4061
4062 if (!appctx->ctx.errors.px) {
4063 /* the function had not been called yet, let's prepare the
4064 * buffer for a response.
4065 */
4066 struct tm tm;
4067
4068 get_localtime(date.tv_sec, &tm);
4069 chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
4070 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
4071 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
4072 error_snapshot_id);
4073
4074 if (bi_putchk(si_ic(si), &trash) == -1) {
4075 /* Socket buffer full. Let's try again later from the same point */
4076 si_applet_cant_put(si);
4077 return 0;
4078 }
4079
4080 appctx->ctx.errors.px = proxy;
4081 appctx->ctx.errors.buf = 0;
4082 appctx->ctx.errors.bol = 0;
4083 appctx->ctx.errors.ptr = -1;
4084 }
4085
4086 /* we have two inner loops here, one for the proxy, the other one for
4087 * the buffer.
4088 */
4089 while (appctx->ctx.errors.px) {
4090 struct error_snapshot *es;
4091
4092 if (appctx->ctx.errors.buf == 0)
4093 es = &appctx->ctx.errors.px->invalid_req;
4094 else
4095 es = &appctx->ctx.errors.px->invalid_rep;
4096
4097 if (!es->when.tv_sec)
4098 goto next;
4099
4100 if (appctx->ctx.errors.iid >= 0 &&
4101 appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
4102 es->oe->uuid != appctx->ctx.errors.iid)
4103 goto next;
4104
4105 if (appctx->ctx.errors.ptr < 0) {
4106 /* just print headers now */
4107
4108 char pn[INET6_ADDRSTRLEN];
4109 struct tm tm;
4110 int port;
4111
4112 get_localtime(es->when.tv_sec, &tm);
4113 chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
4114 tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
4115 tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
4116
4117 switch (addr_to_str(&es->src, pn, sizeof(pn))) {
4118 case AF_INET:
4119 case AF_INET6:
4120 port = get_host_port(&es->src);
4121 break;
4122 default:
4123 port = 0;
4124 }
4125
4126 switch (appctx->ctx.errors.buf) {
4127 case 0:
4128 chunk_appendf(&trash,
4129 " frontend %s (#%d): invalid request\n"
4130 " backend %s (#%d)",
4131 appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
4132 (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
4133 (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
4134 break;
4135 case 1:
4136 chunk_appendf(&trash,
4137 " backend %s (#%d): invalid response\n"
4138 " frontend %s (#%d)",
4139 appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
4140 es->oe->id, es->oe->uuid);
4141 break;
4142 }
4143
4144 chunk_appendf(&trash,
4145 ", server %s (#%d), event #%u\n"
4146 " src %s:%d, session #%d, session flags 0x%08x\n"
4147 " HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
4148 " HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
4149 " buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
4150 " pending %d bytes, wrapping at %d, error at position %d:\n \n",
4151 es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
4152 es->ev_id,
4153 pn, port, es->sid, es->s_flags,
4154 es->state, es->m_flags, es->t_flags,
4155 es->m_clen, es->m_blen,
4156 es->b_flags, es->b_out, es->b_tot,
4157 es->len, es->b_wrap, es->pos);
4158
4159 if (bi_putchk(si_ic(si), &trash) == -1) {
4160 /* Socket buffer full. Let's try again later from the same point */
4161 si_applet_cant_put(si);
4162 return 0;
4163 }
4164 appctx->ctx.errors.ptr = 0;
4165 appctx->ctx.errors.sid = es->sid;
4166 }
4167
4168 if (appctx->ctx.errors.sid != es->sid) {
4169 /* the snapshot changed while we were dumping it */
4170 chunk_appendf(&trash,
4171 " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
4172 if (bi_putchk(si_ic(si), &trash) == -1) {
4173 si_applet_cant_put(si);
4174 return 0;
4175 }
4176 goto next;
4177 }
4178
4179 /* OK, ptr >= 0, so we have to dump the current line */
4180 while (es->buf && appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < global.tune.bufsize) {
4181 int newptr;
4182 int newline;
4183
4184 newline = appctx->ctx.errors.bol;
4185 newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->len, &newline, appctx->ctx.errors.ptr);
4186 if (newptr == appctx->ctx.errors.ptr)
4187 return 0;
4188
4189 if (bi_putchk(si_ic(si), &trash) == -1) {
4190 /* Socket buffer full. Let's try again later from the same point */
4191 si_applet_cant_put(si);
4192 return 0;
4193 }
4194 appctx->ctx.errors.ptr = newptr;
4195 appctx->ctx.errors.bol = newline;
4196 };
4197 next:
4198 appctx->ctx.errors.bol = 0;
4199 appctx->ctx.errors.ptr = -1;
4200 appctx->ctx.errors.buf++;
4201 if (appctx->ctx.errors.buf > 1) {
4202 appctx->ctx.errors.buf = 0;
4203 appctx->ctx.errors.px = appctx->ctx.errors.px->next;
4204 }
4205 }
4206
4207 /* dump complete */
4208 return 1;
4209}
4210
4211/* This function dumps all environmnent variables to the buffer. It returns 0
4212 * if the output buffer is full and it needs to be called again, otherwise
4213 * non-zero. Dumps only one entry if st2 == STAT_ST_END.
4214 */
4215static int stats_dump_env_to_buffer(struct stream_interface *si)
4216{
4217 struct appctx *appctx = __objt_appctx(si->end);
4218
4219 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
4220 return 1;
4221
4222 chunk_reset(&trash);
4223
4224 /* we have two inner loops here, one for the proxy, the other one for
4225 * the buffer.
4226 */
4227 while (*appctx->ctx.env.var) {
4228 chunk_printf(&trash, "%s\n", *appctx->ctx.env.var);
4229
4230 if (bi_putchk(si_ic(si), &trash) == -1) {
4231 si_applet_cant_put(si);
4232 return 0;
4233 }
4234 if (appctx->st2 == STAT_ST_END)
4235 break;
4236 appctx->ctx.env.var++;
4237 }
4238
4239 /* dump complete */
4240 return 1;
4241}
4242
4243/* parse the "level" argument on the bind lines */
4244static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
4245{
4246 if (!*args[cur_arg + 1]) {
4247 memprintf(err, "'%s' : missing level", args[cur_arg]);
4248 return ERR_ALERT | ERR_FATAL;
4249 }
4250
4251 if (!strcmp(args[cur_arg+1], "user"))
4252 conf->level = ACCESS_LVL_USER;
4253 else if (!strcmp(args[cur_arg+1], "operator"))
4254 conf->level = ACCESS_LVL_OPER;
4255 else if (!strcmp(args[cur_arg+1], "admin"))
4256 conf->level = ACCESS_LVL_ADMIN;
4257 else {
4258 memprintf(err, "'%s' only supports 'user', 'operator', and 'admin' (got '%s')",
4259 args[cur_arg], args[cur_arg+1]);
4260 return ERR_ALERT | ERR_FATAL;
4261 }
4262
4263 return 0;
4264}
4265
4266static struct applet cli_applet = {
4267 .obj_type = OBJ_TYPE_APPLET,
4268 .name = "<CLI>", /* used for logging */
4269 .fct = cli_io_handler,
4270 .release = cli_release_handler,
4271};
4272
4273static struct cfg_kw_list cfg_kws = {ILH, {
4274 { CFG_GLOBAL, "stats", stats_parse_global },
4275 { 0, NULL, NULL },
4276}};
4277
4278static struct bind_kw_list bind_kws = { "STAT", { }, {
4279 { "level", bind_parse_level, 1 }, /* set the unix socket admin level */
4280 { NULL, NULL, 0 },
4281}};
4282
4283__attribute__((constructor))
4284static void __dumpstats_module_init(void)
4285{
4286 cfg_register_keywords(&cfg_kws);
4287 bind_register_keywords(&bind_kws);
4288}
4289
4290/*
4291 * Local variables:
4292 * c-indent-level: 8
4293 * c-basic-offset: 8
4294 * End:
4295 */