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