2007-10-17 11:06:05 -04:00
|
|
|
/*
|
2010-05-28 12:46:57 -04:00
|
|
|
* Functions dedicated to statistics output and the stats socket
|
2007-10-17 11:06:05 -04:00
|
|
|
*
|
2012-12-22 14:31:10 -05:00
|
|
|
* Copyright 2000-2012 Willy Tarreau <w@1wt.eu>
|
2009-09-23 16:09:24 -04:00
|
|
|
* Copyright 2007-2009 Krzysztof Piotr Oledzki <ole@ans.pl>
|
2007-10-17 11:06:05 -04:00
|
|
|
*
|
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
2007-10-18 07:53:22 -04:00
|
|
|
#include <pwd.h>
|
|
|
|
|
#include <grp.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
2008-07-09 14:12:41 -04:00
|
|
|
#include <common/cfgparse.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
#include <common/compat.h>
|
|
|
|
|
#include <common/config.h>
|
|
|
|
|
#include <common/debug.h>
|
|
|
|
|
#include <common/memory.h>
|
|
|
|
|
#include <common/mini-clist.h>
|
|
|
|
|
#include <common/standard.h>
|
2008-07-06 18:09:58 -04:00
|
|
|
#include <common/ticks.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
#include <common/time.h>
|
|
|
|
|
#include <common/uri_auth.h>
|
|
|
|
|
#include <common/version.h>
|
2014-06-16 12:36:30 -04:00
|
|
|
#include <common/base64.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
|
2015-04-13 07:24:54 -04:00
|
|
|
#include <types/applet.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
#include <types/global.h>
|
2015-05-16 18:33:24 -04:00
|
|
|
#include <types/dns.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
|
|
|
|
|
#include <proto/backend.h>
|
2012-08-24 13:22:53 -04:00
|
|
|
#include <proto/channel.h>
|
2009-09-23 16:09:24 -04:00
|
|
|
#include <proto/checks.h>
|
2012-11-20 05:25:20 -05:00
|
|
|
#include <proto/compression.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
#include <proto/dumpstats.h>
|
|
|
|
|
#include <proto/fd.h>
|
2009-03-05 12:43:00 -05:00
|
|
|
#include <proto/freq_ctr.h>
|
2015-03-13 11:14:57 -04:00
|
|
|
#include <proto/frontend.h>
|
2010-05-28 12:46:57 -04:00
|
|
|
#include <proto/log.h>
|
2013-12-11 10:55:52 -05:00
|
|
|
#include <proto/pattern.h>
|
2009-01-25 08:02:00 -05:00
|
|
|
#include <proto/pipe.h>
|
2012-09-12 16:58:11 -04:00
|
|
|
#include <proto/listener.h>
|
2013-12-11 10:55:52 -05:00
|
|
|
#include <proto/map.h>
|
2012-11-23 05:19:33 -05:00
|
|
|
#include <proto/proto_http.h>
|
2007-10-18 07:53:22 -04:00
|
|
|
#include <proto/proto_uxst.h>
|
2009-08-16 11:41:45 -04:00
|
|
|
#include <proto/proxy.h>
|
2014-02-07 06:14:54 -05:00
|
|
|
#include <proto/sample.h>
|
2015-04-04 12:50:31 -04:00
|
|
|
#include <proto/session.h>
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
#include <proto/stream.h>
|
[MEDIUM] stats: report server and backend cumulated downtime
Hello,
This patch implements new statistics for SLA calculation by adding new
field 'Dwntime' with total down time since restart (both HTTP/CSV) and
extending status field (HTTP) or inserting a new one (CSV) with time
showing how long each server/backend is in a current state. Additionaly,
down transations are also calculated and displayed for backends, so it is
possible to know how many times selected backend was down, generating "No
server is available to handle this request." error.
New information are presentetd in two different ways:
- for HTTP: a "human redable form", one of "100000d 23h", "23h 59m" or
"59m 59s"
- for CSV: seconds
I believe that seconds resolution is enough.
As there are more columns in the status page I decided to shrink some
names to make more space:
- Weight -> Wght
- Check -> Chk
- Down -> Dwn
Making described changes I also made some improvements and fixed some
small bugs:
- don't increment s->health above 's->rise + s->fall - 1'. Previously it
was incremented an then (re)set to 's->rise + s->fall - 1'.
- do not set server down if it is down already
- do not set server up if it is up already
- fix colspan in multiple places (mostly introduced by my previous patch)
- add missing "status" header to CSV
- fix order of retries/redispatches in server (CSV)
- s/Tthen/Then/
- s/server/backend/ in DATA_ST_PX_BE (dumpstats.c)
Changes from previous version:
- deal with negative time intervales
- don't relay on s->state (SRV_RUNNING)
- little reworked human_time + compacted format (no spaces). If needed it
can be used in the future for other purposes by optionally making "cnt"
as an argument
- leave set_server_down mostly unchanged
- only little reworked "process_chk: 9"
- additional fields in CSV are appended to the rigth
- fix "SEC" macro
- named arguments (human_time, be_downtime, srv_downtime)
Hope it is OK. If there are only cosmetic changes needed please fill free
to correct it, however if there are some bigger changes required I would
like to discuss it first or at last to know what exactly was changed
especially since I already put this patch into my production server. :)
Thank you,
Best regards,
Krzysztof Oledzki
2007-10-22 10:21:10 -04:00
|
|
|
#include <proto/server.h>
|
2012-08-20 11:01:35 -04:00
|
|
|
#include <proto/raw_sock.h>
|
2008-11-30 13:48:07 -05:00
|
|
|
#include <proto/stream_interface.h>
|
2009-03-07 11:25:21 -05:00
|
|
|
#include <proto/task.h>
|
2007-10-17 11:06:05 -04:00
|
|
|
|
2012-11-19 11:13:16 -05:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
#include <proto/ssl_sock.h>
|
2015-05-09 02:46:01 -04:00
|
|
|
#include <types/ssl_sock.h>
|
2012-11-19 11:13:16 -05:00
|
|
|
#endif
|
|
|
|
|
|
2016-01-11 12:12:26 -05:00
|
|
|
/* These are the field names for each INF_* field position. Please pay attention
|
|
|
|
|
* to always use the exact same name except that the strings for new names must
|
|
|
|
|
* be lower case or CamelCase while the enum entries must be upper case.
|
|
|
|
|
*/
|
|
|
|
|
const char *info_field_names[INF_TOTAL_FIELDS] = {
|
|
|
|
|
[INF_NAME] = "Name",
|
|
|
|
|
[INF_VERSION] = "Version",
|
|
|
|
|
[INF_RELEASE_DATE] = "Release_date",
|
|
|
|
|
[INF_NBPROC] = "Nbproc",
|
|
|
|
|
[INF_PROCESS_NUM] = "Process_num",
|
|
|
|
|
[INF_PID] = "Pid",
|
|
|
|
|
[INF_UPTIME] = "Uptime",
|
|
|
|
|
[INF_UPTIME_SEC] = "Uptime_sec",
|
|
|
|
|
[INF_MEMMAX_MB] = "Memmax_MB",
|
|
|
|
|
[INF_POOL_ALLOC_MB] = "PoolAlloc_MB",
|
|
|
|
|
[INF_POOL_USED_MB] = "PoolUsed_MB",
|
|
|
|
|
[INF_POOL_FAILED] = "PoolFailed",
|
|
|
|
|
[INF_ULIMIT_N] = "Ulimit-n",
|
|
|
|
|
[INF_MAXSOCK] = "Maxsock",
|
|
|
|
|
[INF_MAXCONN] = "Maxconn",
|
|
|
|
|
[INF_HARD_MAXCONN] = "Hard_maxconn",
|
|
|
|
|
[INF_CURR_CONN] = "CurrConns",
|
|
|
|
|
[INF_CUM_CONN] = "CumConns",
|
|
|
|
|
[INF_CUM_REQ] = "CumReq",
|
|
|
|
|
[INF_MAX_SSL_CONNS] = "MaxSslConns",
|
|
|
|
|
[INF_CURR_SSL_CONNS] = "CurrSslConns",
|
|
|
|
|
[INF_CUM_SSL_CONNS] = "CumSslConns",
|
|
|
|
|
[INF_MAXPIPES] = "Maxpipes",
|
|
|
|
|
[INF_PIPES_USED] = "PipesUsed",
|
|
|
|
|
[INF_PIPES_FREE] = "PipesFree",
|
|
|
|
|
[INF_CONN_RATE] = "ConnRate",
|
|
|
|
|
[INF_CONN_RATE_LIMIT] = "ConnRateLimit",
|
|
|
|
|
[INF_MAX_CONN_RATE] = "MaxConnRate",
|
|
|
|
|
[INF_SESS_RATE] = "SessRate",
|
|
|
|
|
[INF_SESS_RATE_LIMIT] = "SessRateLimit",
|
|
|
|
|
[INF_MAX_SESS_RATE] = "MaxSessRate",
|
|
|
|
|
[INF_SSL_RATE] = "SslRate",
|
|
|
|
|
[INF_SSL_RATE_LIMIT] = "SslRateLimit",
|
|
|
|
|
[INF_MAX_SSL_RATE] = "MaxSslRate",
|
|
|
|
|
[INF_SSL_FRONTEND_KEY_RATE] = "SslFrontendKeyRate",
|
|
|
|
|
[INF_SSL_FRONTEND_MAX_KEY_RATE] = "SslFrontendMaxKeyRate",
|
|
|
|
|
[INF_SSL_FRONTEND_SESSION_REUSE_PCT] = "SslFrontendSessionReuse_pct",
|
|
|
|
|
[INF_SSL_BACKEND_KEY_RATE] = "SslBackendKeyRate",
|
|
|
|
|
[INF_SSL_BACKEND_MAX_KEY_RATE] = "SslBackendMaxKeyRate",
|
|
|
|
|
[INF_SSL_CACHE_LOOKUPS] = "SslCacheLookups",
|
|
|
|
|
[INF_SSL_CACHE_MISSES] = "SslCacheMisses",
|
|
|
|
|
[INF_COMPRESS_BPS_IN] = "CompressBpsIn",
|
|
|
|
|
[INF_COMPRESS_BPS_OUT] = "CompressBpsOut",
|
|
|
|
|
[INF_COMPRESS_BPS_RATE_LIM] = "CompressBpsRateLim",
|
|
|
|
|
[INF_ZLIB_MEM_USAGE] = "ZlibMemUsage",
|
|
|
|
|
[INF_MAX_ZLIB_MEM_USAGE] = "MaxZlibMemUsage",
|
|
|
|
|
[INF_TASKS] = "Tasks",
|
|
|
|
|
[INF_RUN_QUEUE] = "Run_queue",
|
|
|
|
|
[INF_IDLE_PCT] = "Idle_pct",
|
|
|
|
|
[INF_NODE] = "node",
|
|
|
|
|
[INF_DESCRIPTION] = "description",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* one line of stats */
|
|
|
|
|
static struct field info[INF_TOTAL_FIELDS];
|
|
|
|
|
|
2015-09-18 08:49:12 -04:00
|
|
|
static int stats_dump_backend_to_buffer(struct stream_interface *si);
|
2016-02-16 05:27:28 -05:00
|
|
|
static int stats_dump_env_to_buffer(struct stream_interface *si);
|
2012-12-22 17:20:30 -05:00
|
|
|
static int stats_dump_info_to_buffer(struct stream_interface *si);
|
2015-07-03 02:01:20 -04:00
|
|
|
static int stats_dump_servers_state_to_buffer(struct stream_interface *si);
|
2014-01-28 10:49:56 -05:00
|
|
|
static int stats_dump_pools_to_buffer(struct stream_interface *si);
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess);
|
2011-06-15 02:18:44 -04:00
|
|
|
static int stats_dump_sess_to_buffer(struct stream_interface *si);
|
|
|
|
|
static int stats_dump_errors_to_buffer(struct stream_interface *si);
|
2012-12-05 17:01:12 -05:00
|
|
|
static int stats_table_request(struct stream_interface *si, int show);
|
2015-05-16 18:33:24 -04:00
|
|
|
static int stats_dump_resolvers_to_buffer(struct stream_interface *si);
|
2014-02-11 05:31:40 -05:00
|
|
|
static int stats_pats_list(struct stream_interface *si);
|
|
|
|
|
static int stats_pat_list(struct stream_interface *si);
|
2013-12-11 10:55:52 -05:00
|
|
|
static int stats_map_lookup(struct stream_interface *si);
|
2015-05-09 02:46:01 -04:00
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
static int stats_tlskeys_list(struct stream_interface *si);
|
|
|
|
|
#endif
|
2011-06-15 02:18:44 -04:00
|
|
|
|
2016-05-06 06:18:48 -04:00
|
|
|
static int dump_servers_state(struct stream_interface *si, struct chunk *buf);
|
2015-07-03 02:01:20 -04:00
|
|
|
|
2015-04-13 07:50:30 -04:00
|
|
|
static struct applet cli_applet;
|
2011-06-15 02:18:44 -04:00
|
|
|
|
|
|
|
|
static const char stats_sock_usage_msg[] =
|
2009-10-04 09:02:46 -04:00
|
|
|
"Unknown command. Please enter one of the following commands only :\n"
|
2009-10-10 09:26:26 -04:00
|
|
|
" clear counters : clear max statistics counters (add 'all' for all counters)\n"
|
2010-07-13 07:48:00 -04:00
|
|
|
" clear table : remove an entry from a table\n"
|
2009-10-04 09:02:46 -04:00
|
|
|
" help : this message\n"
|
|
|
|
|
" prompt : toggle interactive mode with prompt\n"
|
|
|
|
|
" quit : disconnect\n"
|
2015-09-18 08:49:12 -04:00
|
|
|
" show backend : list backends in the current running config\n"
|
2016-02-16 05:27:28 -05:00
|
|
|
" show env [var] : dump environment variables known to the process\n"
|
2009-10-04 09:02:46 -04:00
|
|
|
" show info : report information about the running process\n"
|
2014-01-28 10:49:56 -05:00
|
|
|
" show pools : report information about the memory pools usage\n"
|
2009-10-04 09:02:46 -04:00
|
|
|
" show stat : report counters for each proxy and server\n"
|
2016-05-06 06:18:51 -04:00
|
|
|
" show stat resolvers [id]: dumps counters from all resolvers section and\n"
|
|
|
|
|
" associated name servers\n"
|
2009-10-04 09:02:46 -04:00
|
|
|
" show errors : report last request and response errors for each proxy\n"
|
2010-03-05 11:53:32 -05:00
|
|
|
" show sess [id] : report the list of current sessions or dump this session\n"
|
2010-07-12 11:55:33 -04:00
|
|
|
" show table [id]: report table usage stats or dump this table's contents\n"
|
2015-07-03 02:01:20 -04:00
|
|
|
" show servers state [id]: dump volatile server information (for backend <id>)\n"
|
2009-10-10 12:37:29 -04:00
|
|
|
" get weight : report a server's current weight\n"
|
2009-10-10 13:30:08 -04:00
|
|
|
" set weight : change a server's weight\n"
|
2015-04-13 16:54:33 -04:00
|
|
|
" set server : change a server's state, weight or address\n"
|
2012-06-06 19:03:16 -04:00
|
|
|
" set table [id] : update or create a table entry's data\n"
|
2010-01-26 04:59:06 -05:00
|
|
|
" set timeout : change a timeout setting\n"
|
2011-08-02 05:49:05 -04:00
|
|
|
" set maxconn : change a maxconn setting\n"
|
2011-09-07 10:13:44 -04:00
|
|
|
" set rate-limit : change a rate limiting value\n"
|
2011-09-07 17:21:03 -04:00
|
|
|
" disable : put a server or frontend in maintenance mode\n"
|
|
|
|
|
" enable : re-enable a server or frontend which is in maintenance mode\n"
|
|
|
|
|
" shutdown : kill a session or a frontend (eg:to release listening ports)\n"
|
2016-05-06 06:18:50 -04:00
|
|
|
" show acl [id] : report available acls or dump an acl's contents\n"
|
2014-02-11 05:31:40 -05:00
|
|
|
" get acl : reports the patterns matching a sample for an ACL\n"
|
|
|
|
|
" add acl : add acl entry\n"
|
|
|
|
|
" del acl : delete acl entry\n"
|
|
|
|
|
" clear acl <id> : clear the content of this acl\n"
|
2016-05-06 06:18:50 -04:00
|
|
|
" show map [id] : report available maps or dump a map's contents\n"
|
2014-03-11 08:42:38 -04:00
|
|
|
" get map : reports the keys and values matching a sample for a map\n"
|
|
|
|
|
" set map : modify map entry\n"
|
|
|
|
|
" add map : add map entry\n"
|
|
|
|
|
" del map : delete map entry\n"
|
|
|
|
|
" clear map <id> : clear the content of this map\n"
|
2014-06-16 12:36:30 -04:00
|
|
|
" set ssl <stmt> : set statement for ssl\n"
|
2016-05-20 11:40:26 -04:00
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
" show tls-keys [id|*]: show tls keys references or dump tls ticket keys when id specified\n"
|
|
|
|
|
#endif
|
2009-09-22 13:31:03 -04:00
|
|
|
"";
|
2009-08-16 13:06:42 -04:00
|
|
|
|
2011-06-15 02:18:44 -04:00
|
|
|
static const char stats_permission_denied_msg[] =
|
2009-10-10 11:13:00 -04:00
|
|
|
"Permission denied\n"
|
|
|
|
|
"";
|
|
|
|
|
|
2011-03-10 05:25:07 -05:00
|
|
|
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
static char *dynamic_usage_msg = NULL;
|
|
|
|
|
|
|
|
|
|
/* List head of cli keywords */
|
2016-11-21 11:18:36 -05:00
|
|
|
static struct cli_kw_list cli_keywords = {
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
.list = LIST_HEAD_INIT(cli_keywords.list)
|
2011-03-10 05:25:07 -05:00
|
|
|
};
|
|
|
|
|
|
2012-04-04 06:57:21 -04:00
|
|
|
extern const char *stat_status_codes[];
|
|
|
|
|
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
char *cli_gen_usage_msg()
|
|
|
|
|
{
|
|
|
|
|
struct cli_kw_list *kw_list;
|
|
|
|
|
struct cli_kw *kw;
|
|
|
|
|
struct chunk *tmp = get_trash_chunk();
|
|
|
|
|
struct chunk out;
|
|
|
|
|
|
|
|
|
|
free(dynamic_usage_msg);
|
|
|
|
|
dynamic_usage_msg = NULL;
|
|
|
|
|
|
|
|
|
|
if (LIST_ISEMPTY(&cli_keywords.list))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
chunk_reset(tmp);
|
|
|
|
|
chunk_strcat(tmp, stats_sock_usage_msg);
|
|
|
|
|
list_for_each_entry(kw_list, &cli_keywords.list, list) {
|
|
|
|
|
kw = &kw_list->kw[0];
|
|
|
|
|
while (kw->usage) {
|
|
|
|
|
chunk_appendf(tmp, " %s\n", kw->usage);
|
|
|
|
|
kw++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
chunk_init(&out, NULL, 0);
|
|
|
|
|
chunk_dup(&out, tmp);
|
|
|
|
|
dynamic_usage_msg = out.str;
|
|
|
|
|
return dynamic_usage_msg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct cli_kw* cli_find_kw(char **args)
|
|
|
|
|
{
|
|
|
|
|
struct cli_kw_list *kw_list;
|
|
|
|
|
struct cli_kw *kw;/* current cli_kw */
|
|
|
|
|
char **tmp_args;
|
|
|
|
|
const char **tmp_str_kw;
|
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
|
|
if (LIST_ISEMPTY(&cli_keywords.list))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
list_for_each_entry(kw_list, &cli_keywords.list, list) {
|
|
|
|
|
kw = &kw_list->kw[0];
|
|
|
|
|
while (*kw->str_kw) {
|
|
|
|
|
tmp_args = args;
|
|
|
|
|
tmp_str_kw = kw->str_kw;
|
|
|
|
|
while (*tmp_str_kw) {
|
|
|
|
|
if (strcmp(*tmp_str_kw, *tmp_args) == 0) {
|
|
|
|
|
found = 1;
|
|
|
|
|
} else {
|
|
|
|
|
found = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
tmp_args++;
|
|
|
|
|
tmp_str_kw++;
|
|
|
|
|
}
|
|
|
|
|
if (found)
|
|
|
|
|
return (kw);
|
|
|
|
|
kw++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cli_register_kw(struct cli_kw_list *kw_list)
|
|
|
|
|
{
|
|
|
|
|
LIST_ADDQ(&cli_keywords.list, &kw_list->list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-08-17 15:48:17 -04:00
|
|
|
/* allocate a new stats frontend named <name>, and return it
|
|
|
|
|
* (or NULL in case of lack of memory).
|
|
|
|
|
*/
|
2012-09-18 14:05:00 -04:00
|
|
|
static struct proxy *alloc_stats_fe(const char *name, const char *file, int line)
|
2010-08-17 15:48:17 -04:00
|
|
|
{
|
|
|
|
|
struct proxy *fe;
|
|
|
|
|
|
2016-04-03 07:48:43 -04:00
|
|
|
fe = calloc(1, sizeof(*fe));
|
2010-08-17 15:48:17 -04:00
|
|
|
if (!fe)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2011-07-28 19:49:03 -04:00
|
|
|
init_new_proxy(fe);
|
2012-10-04 02:47:34 -04:00
|
|
|
fe->next = proxy;
|
|
|
|
|
proxy = fe;
|
2010-08-17 15:48:17 -04:00
|
|
|
fe->last_change = now.tv_sec;
|
|
|
|
|
fe->id = strdup("GLOBAL");
|
|
|
|
|
fe->cap = PR_CAP_FE;
|
2011-09-07 06:13:34 -04:00
|
|
|
fe->maxconn = 10; /* default to 10 concurrent connections */
|
|
|
|
|
fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */
|
2012-09-18 14:05:00 -04:00
|
|
|
fe->conf.file = strdup(file);
|
|
|
|
|
fe->conf.line = line;
|
2015-03-13 11:14:57 -04:00
|
|
|
fe->accept = frontend_accept;
|
2015-03-13 10:55:16 -04:00
|
|
|
fe->default_target = &cli_applet.obj_type;
|
2012-10-04 02:47:34 -04:00
|
|
|
|
|
|
|
|
/* the stats frontend is the only one able to assign ID #0 */
|
|
|
|
|
fe->conf.id.key = fe->uuid = 0;
|
|
|
|
|
eb32_insert(&used_proxy_id, &fe->conf.id);
|
2010-08-17 15:48:17 -04:00
|
|
|
return fe;
|
|
|
|
|
}
|
|
|
|
|
|
2007-10-18 07:53:22 -04:00
|
|
|
/* This function parses a "stats" statement in the "global" section. It returns
|
2012-05-08 13:47:01 -04:00
|
|
|
* -1 if there is any error, otherwise zero. If it returns -1, it will write an
|
|
|
|
|
* error message into the <err> buffer which will be preallocated. The trailing
|
|
|
|
|
* '\n' must not be written. The function must be called with <args> pointing to
|
|
|
|
|
* the first word after "stats".
|
2007-10-18 07:53:22 -04:00
|
|
|
*/
|
2008-07-09 14:12:41 -04:00
|
|
|
static int stats_parse_global(char **args, int section_type, struct proxy *curpx,
|
2012-09-18 14:02:48 -04:00
|
|
|
struct proxy *defpx, const char *file, int line,
|
|
|
|
|
char **err)
|
2007-10-18 07:53:22 -04:00
|
|
|
{
|
2012-09-20 10:48:07 -04:00
|
|
|
struct bind_conf *bind_conf;
|
2012-09-20 14:19:28 -04:00
|
|
|
struct listener *l;
|
2012-09-20 10:48:07 -04:00
|
|
|
|
2012-05-08 13:47:01 -04:00
|
|
|
if (!strcmp(args[1], "socket")) {
|
2007-10-18 07:53:22 -04:00
|
|
|
int cur_arg;
|
|
|
|
|
|
2012-05-08 13:47:01 -04:00
|
|
|
if (*args[2] == 0) {
|
2012-09-20 14:19:28 -04:00
|
|
|
memprintf(err, "'%s %s' in global section expects an address or a path to a UNIX socket", args[0], args[1]);
|
2007-10-18 07:53:22 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-08-16 11:41:45 -04:00
|
|
|
if (!global.stats_fe) {
|
2012-09-18 14:05:00 -04:00
|
|
|
if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
|
2012-05-08 13:47:01 -04:00
|
|
|
memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
|
2009-08-16 11:41:45 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 10:48:07 -04:00
|
|
|
bind_conf = bind_conf_alloc(&global.stats_fe->conf.bind, file, line, args[2]);
|
2012-09-20 12:07:14 -04:00
|
|
|
bind_conf->level = ACCESS_LVL_OPER; /* default access level */
|
2012-09-20 10:48:07 -04:00
|
|
|
|
2012-09-20 14:19:28 -04:00
|
|
|
if (!str2listener(args[2], global.stats_fe, bind_conf, file, line, err)) {
|
|
|
|
|
memprintf(err, "parsing [%s:%d] : '%s %s' : %s\n",
|
|
|
|
|
file, line, args[0], args[1], err && *err ? *err : "error");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2007-10-18 07:53:22 -04:00
|
|
|
|
2012-05-08 13:47:01 -04:00
|
|
|
cur_arg = 3;
|
2007-10-18 07:53:22 -04:00
|
|
|
while (*args[cur_arg]) {
|
2012-09-22 13:32:35 -04:00
|
|
|
static int bind_dumped;
|
|
|
|
|
struct bind_kw *kw;
|
|
|
|
|
|
|
|
|
|
kw = bind_find_kw(args[cur_arg]);
|
|
|
|
|
if (kw) {
|
|
|
|
|
if (!kw->parse) {
|
|
|
|
|
memprintf(err, "'%s %s' : '%s' option is not implemented in this version (check build options).",
|
|
|
|
|
args[0], args[1], args[cur_arg]);
|
2007-10-18 07:53:22 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2012-09-22 13:32:35 -04:00
|
|
|
|
2015-10-13 09:06:57 -04:00
|
|
|
if (kw->parse(args, cur_arg, global.stats_fe, bind_conf, err) != 0) {
|
2012-09-22 13:32:35 -04:00
|
|
|
if (err && *err)
|
|
|
|
|
memprintf(err, "'%s %s' : '%s'", args[0], args[1], *err);
|
|
|
|
|
else
|
|
|
|
|
memprintf(err, "'%s %s' : error encountered while processing '%s'",
|
|
|
|
|
args[0], args[1], args[cur_arg]);
|
2009-10-10 11:13:00 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2012-09-22 13:32:35 -04:00
|
|
|
|
|
|
|
|
cur_arg += 1 + kw->skip;
|
|
|
|
|
continue;
|
2009-10-10 11:13:00 -04:00
|
|
|
}
|
2012-09-22 13:32:35 -04:00
|
|
|
|
|
|
|
|
if (!bind_dumped) {
|
|
|
|
|
bind_dump_kws(err);
|
|
|
|
|
indent_msg(err, 4);
|
|
|
|
|
bind_dumped = 1;
|
2007-10-18 07:53:22 -04:00
|
|
|
}
|
2012-09-22 13:32:35 -04:00
|
|
|
|
|
|
|
|
memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s",
|
|
|
|
|
args[0], args[1], args[cur_arg],
|
|
|
|
|
err && *err ? " Registered keywords :" : "", err && *err ? *err : "");
|
|
|
|
|
return -1;
|
2007-10-18 07:53:22 -04:00
|
|
|
}
|
2008-12-07 10:06:43 -05:00
|
|
|
|
2012-09-20 14:19:28 -04:00
|
|
|
list_for_each_entry(l, &bind_conf->listeners, by_bind) {
|
|
|
|
|
l->maxconn = global.stats_fe->maxconn;
|
|
|
|
|
l->backlog = global.stats_fe->backlog;
|
2015-04-04 12:50:31 -04:00
|
|
|
l->accept = session_accept_fd;
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
l->handler = process_stream;
|
2015-03-13 11:43:12 -04:00
|
|
|
l->default_target = global.stats_fe->default_target;
|
2012-09-20 14:19:28 -04:00
|
|
|
l->options |= LI_O_UNLIMITED; /* don't make the peers subject to global limits */
|
|
|
|
|
l->nice = -64; /* we want to boost priority for local stats */
|
|
|
|
|
global.maxsock += l->maxconn;
|
|
|
|
|
}
|
2007-10-18 07:53:22 -04:00
|
|
|
}
|
2012-05-08 13:47:01 -04:00
|
|
|
else if (!strcmp(args[1], "timeout")) {
|
2007-12-02 16:15:14 -05:00
|
|
|
unsigned timeout;
|
2012-05-08 13:47:01 -04:00
|
|
|
const char *res = parse_time_err(args[2], &timeout, TIME_UNIT_MS);
|
2007-10-18 07:53:22 -04:00
|
|
|
|
2007-12-02 16:15:14 -05:00
|
|
|
if (res) {
|
2012-05-08 13:47:01 -04:00
|
|
|
memprintf(err, "'%s %s' : unexpected character '%c'", args[0], args[1], *res);
|
2007-12-02 16:15:14 -05:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!timeout) {
|
2012-05-08 13:47:01 -04:00
|
|
|
memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
|
2007-10-18 07:53:22 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2010-08-17 15:48:17 -04:00
|
|
|
if (!global.stats_fe) {
|
2012-09-18 14:05:00 -04:00
|
|
|
if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
|
2012-05-08 13:47:01 -04:00
|
|
|
memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
|
2010-08-17 15:48:17 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-08-16 11:41:45 -04:00
|
|
|
global.stats_fe->timeout.client = MS_TO_TICKS(timeout);
|
2007-10-18 07:53:22 -04:00
|
|
|
}
|
2012-05-08 13:47:01 -04:00
|
|
|
else if (!strcmp(args[1], "maxconn")) {
|
|
|
|
|
int maxconn = atol(args[2]);
|
2007-10-18 07:53:22 -04:00
|
|
|
|
|
|
|
|
if (maxconn <= 0) {
|
2012-05-08 13:47:01 -04:00
|
|
|
memprintf(err, "'%s %s' expects a positive value", args[0], args[1]);
|
2007-10-18 07:53:22 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2011-09-07 06:13:34 -04:00
|
|
|
|
|
|
|
|
if (!global.stats_fe) {
|
2012-09-18 14:05:00 -04:00
|
|
|
if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
|
2012-05-08 13:47:01 -04:00
|
|
|
memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
|
2011-09-07 06:13:34 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
global.stats_fe->maxconn = maxconn;
|
2007-10-18 07:53:22 -04:00
|
|
|
}
|
2012-10-22 17:17:18 -04:00
|
|
|
else if (!strcmp(args[1], "bind-process")) { /* enable the socket only on some processes */
|
|
|
|
|
int cur_arg = 2;
|
2013-01-18 05:29:29 -05:00
|
|
|
unsigned long set = 0;
|
2012-10-22 17:17:18 -04:00
|
|
|
|
2013-04-20 03:48:50 -04:00
|
|
|
if (!global.stats_fe) {
|
|
|
|
|
if ((global.stats_fe = alloc_stats_fe("GLOBAL", file, line)) == NULL) {
|
|
|
|
|
memprintf(err, "'%s %s' : out of memory trying to allocate a frontend", args[0], args[1]);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-22 17:17:18 -04:00
|
|
|
while (*args[cur_arg]) {
|
2012-11-15 11:50:01 -05:00
|
|
|
unsigned int low, high;
|
|
|
|
|
|
2012-10-22 17:17:18 -04:00
|
|
|
if (strcmp(args[cur_arg], "all") == 0) {
|
|
|
|
|
set = 0;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[cur_arg], "odd") == 0) {
|
2013-01-18 05:29:29 -05:00
|
|
|
set |= ~0UL/3UL; /* 0x555....555 */
|
2012-10-22 17:17:18 -04:00
|
|
|
}
|
|
|
|
|
else if (strcmp(args[cur_arg], "even") == 0) {
|
2013-01-18 05:29:29 -05:00
|
|
|
set |= (~0UL/3UL) << 1; /* 0xAAA...AAA */
|
2012-10-22 17:17:18 -04:00
|
|
|
}
|
2012-11-21 19:04:31 -05:00
|
|
|
else if (isdigit((int)*args[cur_arg])) {
|
2012-11-15 11:50:01 -05:00
|
|
|
char *dash = strchr(args[cur_arg], '-');
|
|
|
|
|
|
|
|
|
|
low = high = str2uic(args[cur_arg]);
|
|
|
|
|
if (dash)
|
|
|
|
|
high = str2uic(dash + 1);
|
|
|
|
|
|
|
|
|
|
if (high < low) {
|
|
|
|
|
unsigned int swap = low;
|
|
|
|
|
low = high;
|
|
|
|
|
high = swap;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-18 05:29:29 -05:00
|
|
|
if (low < 1 || high > LONGBITS) {
|
|
|
|
|
memprintf(err, "'%s %s' supports process numbers from 1 to %d.\n",
|
|
|
|
|
args[0], args[1], LONGBITS);
|
2012-10-22 17:17:18 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
2012-11-15 11:50:01 -05:00
|
|
|
while (low <= high)
|
2013-01-18 05:29:29 -05:00
|
|
|
set |= 1UL << (low++ - 1);
|
2012-11-15 11:50:01 -05:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
memprintf(err,
|
2013-01-18 05:29:29 -05:00
|
|
|
"'%s %s' expects 'all', 'odd', 'even', or a list of process ranges with numbers from 1 to %d.\n",
|
|
|
|
|
args[0], args[1], LONGBITS);
|
2012-11-15 11:50:01 -05:00
|
|
|
return -1;
|
2012-10-22 17:17:18 -04:00
|
|
|
}
|
|
|
|
|
cur_arg++;
|
|
|
|
|
}
|
|
|
|
|
global.stats_fe->bind_proc = set;
|
|
|
|
|
}
|
2007-10-18 07:53:22 -04:00
|
|
|
else {
|
2012-10-22 17:17:18 -04:00
|
|
|
memprintf(err, "'%s' only supports 'socket', 'maxconn', 'bind-process' and 'timeout' (got '%s')", args[0], args[1]);
|
2007-10-18 07:53:22 -04:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2007-11-30 12:16:29 -05:00
|
|
|
|
2011-06-15 02:18:45 -04:00
|
|
|
/* print a string of text buffer to <out>. The format is :
|
|
|
|
|
* Non-printable chars \t, \n, \r and \e are * encoded in C format.
|
2016-10-04 16:10:40 -04:00
|
|
|
* Other non-printable chars are encoded "\xHH". Space, '\', and '=' are also escaped.
|
2011-06-15 02:18:45 -04:00
|
|
|
* Print stopped if null char or <bsize> is reached, or if no more place in the chunk.
|
|
|
|
|
*/
|
|
|
|
|
static int dump_text(struct chunk *out, const char *buf, int bsize)
|
|
|
|
|
{
|
|
|
|
|
unsigned char c;
|
|
|
|
|
int ptr = 0;
|
|
|
|
|
|
|
|
|
|
while (buf[ptr] && ptr < bsize) {
|
|
|
|
|
c = buf[ptr];
|
2016-10-04 16:10:40 -04:00
|
|
|
if (isprint(c) && isascii(c) && c != '\\' && c != ' ' && c != '=') {
|
2011-06-15 02:18:45 -04:00
|
|
|
if (out->len > out->size - 1)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = c;
|
|
|
|
|
}
|
2016-10-04 16:10:40 -04:00
|
|
|
else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\' || c == ' ' || c == '=') {
|
2011-06-15 02:18:45 -04:00
|
|
|
if (out->len > out->size - 2)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = '\\';
|
|
|
|
|
switch (c) {
|
|
|
|
|
case ' ': c = ' '; break;
|
|
|
|
|
case '\t': c = 't'; break;
|
|
|
|
|
case '\n': c = 'n'; break;
|
|
|
|
|
case '\r': c = 'r'; break;
|
|
|
|
|
case '\e': c = 'e'; break;
|
|
|
|
|
case '\\': c = '\\'; break;
|
2016-10-04 16:10:40 -04:00
|
|
|
case '=': c = '='; break;
|
2011-06-15 02:18:45 -04:00
|
|
|
}
|
|
|
|
|
out->str[out->len++] = c;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (out->len > out->size - 4)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = '\\';
|
|
|
|
|
out->str[out->len++] = 'x';
|
|
|
|
|
out->str[out->len++] = hextab[(c >> 4) & 0xF];
|
|
|
|
|
out->str[out->len++] = hextab[c & 0xF];
|
|
|
|
|
}
|
|
|
|
|
ptr++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* print a buffer in hexa.
|
|
|
|
|
* Print stopped if <bsize> is reached, or if no more place in the chunk.
|
|
|
|
|
*/
|
|
|
|
|
static int dump_binary(struct chunk *out, const char *buf, int bsize)
|
|
|
|
|
{
|
|
|
|
|
unsigned char c;
|
|
|
|
|
int ptr = 0;
|
|
|
|
|
|
|
|
|
|
while (ptr < bsize) {
|
|
|
|
|
c = buf[ptr];
|
|
|
|
|
|
|
|
|
|
if (out->len > out->size - 2)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = hextab[(c >> 4) & 0xF];
|
|
|
|
|
out->str[out->len++] = hextab[c & 0xF];
|
|
|
|
|
|
|
|
|
|
ptr++;
|
|
|
|
|
}
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dump the status of a table to a stream interface's
|
|
|
|
|
* read buffer. It returns 0 if the output buffer is full
|
|
|
|
|
* and needs to be called again, otherwise non-zero.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_table_head_to_buffer(struct chunk *msg, struct stream_interface *si,
|
|
|
|
|
struct proxy *proxy, struct proxy *target)
|
|
|
|
|
{
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *s = si_strm(si);
|
2011-06-15 02:18:45 -04:00
|
|
|
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "# table: %s, type: %s, size:%d, used:%d\n",
|
2011-06-15 02:18:45 -04:00
|
|
|
proxy->id, stktable_types[proxy->table.type].kw, proxy->table.size, proxy->table.current);
|
|
|
|
|
|
|
|
|
|
/* any other information should be dumped here */
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (target && strm_li(s)->bind_conf->level < ACCESS_LVL_OPER)
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "# contents not dumped due to insufficient privileges\n");
|
2011-06-15 02:18:45 -04:00
|
|
|
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), msg) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2011-06-15 02:18:45 -04:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2011-06-15 02:18:45 -04:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Dump the a table entry to a stream interface's
|
|
|
|
|
* read buffer. It returns 0 if the output buffer is full
|
|
|
|
|
* and needs to be called again, otherwise non-zero.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_table_entry_to_buffer(struct chunk *msg, struct stream_interface *si,
|
|
|
|
|
struct proxy *proxy, struct stksess *entry)
|
|
|
|
|
{
|
|
|
|
|
int dt;
|
|
|
|
|
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "%p:", entry);
|
2011-06-15 02:18:45 -04:00
|
|
|
|
2015-07-24 02:46:42 -04:00
|
|
|
if (proxy->table.type == SMP_T_IPV4) {
|
2011-06-15 02:18:45 -04:00
|
|
|
char addr[INET_ADDRSTRLEN];
|
|
|
|
|
inet_ntop(AF_INET, (const void *)&entry->key.key, addr, sizeof(addr));
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " key=%s", addr);
|
2011-06-15 02:18:45 -04:00
|
|
|
}
|
2015-07-24 02:46:42 -04:00
|
|
|
else if (proxy->table.type == SMP_T_IPV6) {
|
2011-06-15 02:18:45 -04:00
|
|
|
char addr[INET6_ADDRSTRLEN];
|
|
|
|
|
inet_ntop(AF_INET6, (const void *)&entry->key.key, addr, sizeof(addr));
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " key=%s", addr);
|
2011-06-15 02:18:45 -04:00
|
|
|
}
|
2015-07-24 02:46:42 -04:00
|
|
|
else if (proxy->table.type == SMP_T_SINT) {
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " key=%u", *(unsigned int *)entry->key.key);
|
2011-06-15 02:18:45 -04:00
|
|
|
}
|
2015-07-24 02:46:42 -04:00
|
|
|
else if (proxy->table.type == SMP_T_STR) {
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " key=");
|
2011-06-15 02:18:45 -04:00
|
|
|
dump_text(msg, (const char *)entry->key.key, proxy->table.key_size);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " key=");
|
2011-06-15 02:18:45 -04:00
|
|
|
dump_binary(msg, (const char *)entry->key.key, proxy->table.key_size);
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " use=%d exp=%d", entry->ref_cnt - 1, tick_remain(now_ms, entry->expire));
|
2011-06-15 02:18:45 -04:00
|
|
|
|
|
|
|
|
for (dt = 0; dt < STKTABLE_DATA_TYPES; dt++) {
|
|
|
|
|
void *ptr;
|
|
|
|
|
|
|
|
|
|
if (proxy->table.data_ofs[dt] == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (stktable_data_types[dt].arg_type == ARG_T_DELAY)
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " %s(%d)=", stktable_data_types[dt].name, proxy->table.data_arg[dt].u);
|
2011-06-15 02:18:45 -04:00
|
|
|
else
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, " %s=", stktable_data_types[dt].name);
|
2011-06-15 02:18:45 -04:00
|
|
|
|
|
|
|
|
ptr = stktable_data_ptr(&proxy->table, entry, dt);
|
|
|
|
|
switch (stktable_data_types[dt].std_type) {
|
|
|
|
|
case STD_T_SINT:
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "%d", stktable_data_cast(ptr, std_t_sint));
|
2011-06-15 02:18:45 -04:00
|
|
|
break;
|
|
|
|
|
case STD_T_UINT:
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "%u", stktable_data_cast(ptr, std_t_uint));
|
2011-06-15 02:18:45 -04:00
|
|
|
break;
|
|
|
|
|
case STD_T_ULL:
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "%lld", stktable_data_cast(ptr, std_t_ull));
|
2011-06-15 02:18:45 -04:00
|
|
|
break;
|
|
|
|
|
case STD_T_FRQP:
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "%d",
|
2011-06-15 02:18:45 -04:00
|
|
|
read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
|
|
|
|
|
proxy->table.data_arg[dt].u));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(msg, "\n");
|
2011-06-15 02:18:45 -04:00
|
|
|
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), msg) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2011-06-15 02:18:45 -04:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2011-06-15 02:18:45 -04:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-06 17:37:08 -04:00
|
|
|
static void stats_sock_table_key_request(struct stream_interface *si, char **args, int action)
|
2011-06-15 02:18:46 -04:00
|
|
|
{
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *s = si_strm(si);
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
struct proxy *px = appctx->ctx.table.target;
|
2011-06-15 02:18:46 -04:00
|
|
|
struct stksess *ts;
|
2011-06-15 02:18:51 -04:00
|
|
|
uint32_t uint32_key;
|
2011-06-15 02:18:50 -04:00
|
|
|
unsigned char ip6_key[sizeof(struct in6_addr)];
|
2012-06-06 19:03:16 -04:00
|
|
|
long long value;
|
|
|
|
|
int data_type;
|
2013-08-01 15:11:42 -04:00
|
|
|
int cur_arg;
|
2012-06-06 19:03:16 -04:00
|
|
|
void *ptr;
|
|
|
|
|
struct freq_ctr_period *frqp;
|
2011-06-15 02:18:46 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_OUTPUT;
|
2011-06-15 02:18:46 -04:00
|
|
|
|
|
|
|
|
if (!*args[4]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Key value expected\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:46 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-15 02:18:50 -04:00
|
|
|
switch (px->table.type) {
|
2015-07-24 02:46:42 -04:00
|
|
|
case SMP_T_IPV4:
|
2011-06-15 02:18:51 -04:00
|
|
|
uint32_key = htonl(inetaddr_host(args[4]));
|
2012-10-29 16:56:59 -04:00
|
|
|
static_table_key->key = &uint32_key;
|
2011-06-15 02:18:50 -04:00
|
|
|
break;
|
2015-07-24 02:46:42 -04:00
|
|
|
case SMP_T_IPV6:
|
2011-06-15 02:18:50 -04:00
|
|
|
inet_pton(AF_INET6, args[4], ip6_key);
|
2012-10-29 16:56:59 -04:00
|
|
|
static_table_key->key = &ip6_key;
|
2011-06-15 02:18:50 -04:00
|
|
|
break;
|
2015-07-24 02:46:42 -04:00
|
|
|
case SMP_T_SINT:
|
2011-06-15 02:18:51 -04:00
|
|
|
{
|
|
|
|
|
char *endptr;
|
|
|
|
|
unsigned long val;
|
|
|
|
|
errno = 0;
|
|
|
|
|
val = strtoul(args[4], &endptr, 10);
|
|
|
|
|
if ((errno == ERANGE && val == ULONG_MAX) ||
|
|
|
|
|
(errno != 0 && val == 0) || endptr == args[4] ||
|
|
|
|
|
val > 0xffffffff) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Invalid key\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:51 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
uint32_key = (uint32_t) val;
|
2012-10-29 16:56:59 -04:00
|
|
|
static_table_key->key = &uint32_key;
|
2011-06-15 02:18:51 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2015-07-24 02:46:42 -04:00
|
|
|
case SMP_T_STR:
|
2012-10-29 16:56:59 -04:00
|
|
|
static_table_key->key = args[4];
|
|
|
|
|
static_table_key->key_len = strlen(args[4]);
|
2011-06-15 02:18:52 -04:00
|
|
|
break;
|
2011-06-15 02:18:50 -04:00
|
|
|
default:
|
2012-06-06 17:37:08 -04:00
|
|
|
switch (action) {
|
|
|
|
|
case STAT_CLI_O_TAB:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Showing keys from tables of type other than ip, ipv6, string and integer is not supported\n";
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
|
|
|
|
case STAT_CLI_O_CLR:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Removing keys from ip tables of type other than ip, ipv6, string and integer is not supported\n";
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
|
|
|
|
default:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Unknown action\n";
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:46 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check permissions */
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:46 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 16:56:59 -04:00
|
|
|
ts = stktable_lookup_key(&px->table, static_table_key);
|
2011-06-15 02:18:47 -04:00
|
|
|
|
2012-06-06 17:37:08 -04:00
|
|
|
switch (action) {
|
|
|
|
|
case STAT_CLI_O_TAB:
|
|
|
|
|
if (!ts)
|
|
|
|
|
return;
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_reset(&trash);
|
|
|
|
|
if (!stats_dump_table_head_to_buffer(&trash, si, px, px))
|
2011-06-15 02:18:47 -04:00
|
|
|
return;
|
2012-10-29 11:51:55 -04:00
|
|
|
stats_dump_table_entry_to_buffer(&trash, si, px, ts);
|
2011-06-15 02:18:46 -04:00
|
|
|
return;
|
2011-06-15 02:18:47 -04:00
|
|
|
|
2012-06-06 17:37:08 -04:00
|
|
|
case STAT_CLI_O_CLR:
|
|
|
|
|
if (!ts)
|
|
|
|
|
return;
|
|
|
|
|
if (ts->ref_cnt) {
|
|
|
|
|
/* don't delete an entry which is currently referenced */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Entry currently in use, cannot remove\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2012-06-06 17:37:08 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
stksess_kill(&px->table, ts);
|
|
|
|
|
break;
|
|
|
|
|
|
2012-06-06 19:03:16 -04:00
|
|
|
case STAT_CLI_O_SET:
|
|
|
|
|
if (ts)
|
|
|
|
|
stktable_touch(&px->table, ts, 1);
|
|
|
|
|
else {
|
2012-10-29 16:56:59 -04:00
|
|
|
ts = stksess_new(&px->table, static_table_key);
|
2012-06-06 19:03:16 -04:00
|
|
|
if (!ts) {
|
|
|
|
|
/* don't delete an entry which is currently referenced */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Unable to allocate a new entry\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2012-06-06 19:03:16 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
stktable_store(&px->table, ts, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-01 15:11:42 -04:00
|
|
|
for (cur_arg = 5; *args[cur_arg]; cur_arg += 2) {
|
|
|
|
|
if (strncmp(args[cur_arg], "data.", 5) != 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "\"data.<type>\" followed by a value expected\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2013-08-01 15:11:42 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
data_type = stktable_get_data_type(args[cur_arg] + 5);
|
|
|
|
|
if (data_type < 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Unknown data type\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2013-08-01 15:11:42 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!px->table.data_ofs[data_type]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Data type not stored in this table\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2013-08-01 15:11:42 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[cur_arg+1] || strl2llrc(args[cur_arg+1], strlen(args[cur_arg+1]), &value) != 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Require a valid integer value to store\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2013-08-01 15:11:42 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr = stktable_data_ptr(&px->table, ts, data_type);
|
|
|
|
|
|
|
|
|
|
switch (stktable_data_types[data_type].std_type) {
|
|
|
|
|
case STD_T_SINT:
|
|
|
|
|
stktable_data_cast(ptr, std_t_sint) = value;
|
|
|
|
|
break;
|
|
|
|
|
case STD_T_UINT:
|
|
|
|
|
stktable_data_cast(ptr, std_t_uint) = value;
|
|
|
|
|
break;
|
|
|
|
|
case STD_T_ULL:
|
|
|
|
|
stktable_data_cast(ptr, std_t_ull) = value;
|
|
|
|
|
break;
|
|
|
|
|
case STD_T_FRQP:
|
|
|
|
|
/* We set both the current and previous values. That way
|
|
|
|
|
* the reported frequency is stable during all the period
|
|
|
|
|
* then slowly fades out. This allows external tools to
|
|
|
|
|
* push measures without having to update them too often.
|
|
|
|
|
*/
|
|
|
|
|
frqp = &stktable_data_cast(ptr, std_t_frqp);
|
|
|
|
|
frqp->curr_tick = now_ms;
|
|
|
|
|
frqp->prev_ctr = 0;
|
|
|
|
|
frqp->curr_ctr = value;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2012-06-06 19:03:16 -04:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2012-06-06 17:37:08 -04:00
|
|
|
default:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Unknown action\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
2011-06-15 02:18:46 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-06 19:03:16 -04:00
|
|
|
static void stats_sock_table_data_request(struct stream_interface *si, char **args, int action)
|
2011-06-15 02:18:48 -04:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
2013-04-13 03:41:37 -04:00
|
|
|
if (action != STAT_CLI_O_TAB && action != STAT_CLI_O_CLR) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "content-based lookup is only supported with the \"show\" and \"clear\" actions";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2012-06-06 19:03:16 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-15 02:18:48 -04:00
|
|
|
/* condition on stored data value */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.data_type = stktable_get_data_type(args[3] + 5);
|
|
|
|
|
if (appctx->ctx.table.data_type < 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "Unknown data type\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:48 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (!((struct proxy *)appctx->ctx.table.target)->table.data_ofs[appctx->ctx.table.data_type]) {
|
|
|
|
|
appctx->ctx.cli.msg = "Data type not stored in this table\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:48 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.data_op = get_std_op(args[4]);
|
|
|
|
|
if (appctx->ctx.table.data_op < 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "Require and operator among \"eq\", \"ne\", \"le\", \"ge\", \"lt\", \"gt\"\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:48 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (!*args[5] || strl2llrc(args[5], strlen(args[5]), &appctx->ctx.table.value) != 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "Require a valid integer value to compare against\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:48 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-06-06 17:37:08 -04:00
|
|
|
static void stats_sock_table_request(struct stream_interface *si, char **args, int action)
|
2011-06-15 02:18:48 -04:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
|
|
|
|
appctx->ctx.table.data_type = -1;
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->ctx.table.target = NULL;
|
|
|
|
|
appctx->ctx.table.proxy = NULL;
|
|
|
|
|
appctx->ctx.table.entry = NULL;
|
|
|
|
|
appctx->st0 = action;
|
2011-06-15 02:18:48 -04:00
|
|
|
|
|
|
|
|
if (*args[2]) {
|
2015-05-26 06:08:07 -04:00
|
|
|
appctx->ctx.table.target = proxy_tbl_by_name(args[2]);
|
2013-12-01 03:15:12 -05:00
|
|
|
if (!appctx->ctx.table.target) {
|
|
|
|
|
appctx->ctx.cli.msg = "No such table\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:48 -04:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2012-06-06 17:37:08 -04:00
|
|
|
if (action != STAT_CLI_O_TAB)
|
2011-06-15 02:18:48 -04:00
|
|
|
goto err_args;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(args[3], "key") == 0)
|
2012-06-06 17:37:08 -04:00
|
|
|
stats_sock_table_key_request(si, args, action);
|
2011-06-15 02:18:49 -04:00
|
|
|
else if (strncmp(args[3], "data.", 5) == 0)
|
2012-06-06 19:03:16 -04:00
|
|
|
stats_sock_table_data_request(si, args, action);
|
2011-06-15 02:18:49 -04:00
|
|
|
else if (*args[3])
|
2011-06-15 02:18:48 -04:00
|
|
|
goto err_args;
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
err_args:
|
2012-06-06 17:37:08 -04:00
|
|
|
switch (action) {
|
|
|
|
|
case STAT_CLI_O_TAB:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Optional argument only supports \"data.<store_data_type>\" <operator> <value> and key <key>\n";
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
|
|
|
|
case STAT_CLI_O_CLR:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Required arguments: <table> \"data.<store_data_type>\" <operator> <value> or <table> key <key>\n";
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
|
|
|
|
default:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Unknown action\n";
|
2012-06-06 17:37:08 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-06-15 02:18:48 -04:00
|
|
|
}
|
|
|
|
|
|
2011-09-07 16:37:44 -04:00
|
|
|
/* Expects to find a frontend named <arg> and returns it, otherwise displays various
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* adequate error messages and returns NULL. This function also expects the stream
|
2011-09-07 16:37:44 -04:00
|
|
|
* level to be admin.
|
|
|
|
|
*/
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
static struct proxy *expect_frontend_admin(struct stream *s, struct stream_interface *si, const char *arg)
|
2011-09-07 16:37:44 -04:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2011-09-07 16:37:44 -04:00
|
|
|
struct proxy *px;
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*arg) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "A frontend name is expected.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-26 05:24:42 -04:00
|
|
|
px = proxy_fe_by_name(arg);
|
2011-09-07 16:37:44 -04:00
|
|
|
if (!px) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "No such frontend.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
return px;
|
|
|
|
|
}
|
|
|
|
|
|
2011-09-07 17:41:01 -04:00
|
|
|
/* Expects to find a backend and a server in <arg> under the form <backend>/<server>,
|
|
|
|
|
* and returns the pointer to the server. Otherwise, display adequate error messages
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* and returns NULL. This function also expects the stream level to be admin. Note:
|
2011-09-07 17:41:01 -04:00
|
|
|
* the <arg> is modified to remove the '/'.
|
|
|
|
|
*/
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
static struct server *expect_server_admin(struct stream *s, struct stream_interface *si, char *arg)
|
2011-09-07 17:41:01 -04:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2011-09-07 17:41:01 -04:00
|
|
|
struct proxy *px;
|
|
|
|
|
struct server *sv;
|
|
|
|
|
char *line;
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:41:01 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* split "backend/server" and make <line> point to server */
|
|
|
|
|
for (line = arg; *line; line++)
|
|
|
|
|
if (*line == '/') {
|
|
|
|
|
*line++ = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*line || !*arg) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Require 'backend/server'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:41:01 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!get_backend_server(arg, line, &px, &sv)) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:41:01 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (px->state == PR_STSTOPPED) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Proxy is disabled.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:41:01 -04:00
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return sv;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
/* This function is used with TLS ticket keys management. It permits to browse
|
|
|
|
|
* each reference. The variable <getnext> must contain the current node,
|
|
|
|
|
* <end> point to the root node.
|
|
|
|
|
*/
|
|
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
static inline
|
|
|
|
|
struct tls_keys_ref *tlskeys_list_get_next(struct tls_keys_ref *getnext, struct list *end)
|
|
|
|
|
{
|
|
|
|
|
struct tls_keys_ref *ref = getnext;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
|
|
|
|
/* Get next list entry. */
|
|
|
|
|
ref = LIST_NEXT(&ref->list, struct tls_keys_ref *, list);
|
|
|
|
|
|
|
|
|
|
/* If the entry is the last of the list, return NULL. */
|
|
|
|
|
if (&ref->list == end)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
return ref;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline
|
|
|
|
|
struct tls_keys_ref *tlskeys_ref_lookup_ref(const char *reference)
|
|
|
|
|
{
|
|
|
|
|
int id;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
/* If the reference starts by a '#', this is numeric id. */
|
|
|
|
|
if (reference[0] == '#') {
|
|
|
|
|
/* Try to convert the numeric id. If the conversion fails, the lookup fails. */
|
|
|
|
|
id = strtol(reference + 1, &error, 10);
|
|
|
|
|
if (*error != '\0')
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Perform the unique id lookup. */
|
|
|
|
|
return tlskeys_ref_lookupid(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perform the string lookup. */
|
|
|
|
|
return tlskeys_ref_lookup(reference);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* This function is used with map and acl management. It permits to browse
|
|
|
|
|
* each reference. The variable <getnext> must contain the current node,
|
|
|
|
|
* <end> point to the root node and the <flags> permit to filter required
|
|
|
|
|
* nodes.
|
2013-12-11 10:55:52 -05:00
|
|
|
*/
|
2014-02-11 05:31:40 -05:00
|
|
|
static inline
|
|
|
|
|
struct pat_ref *pat_list_get_next(struct pat_ref *getnext, struct list *end,
|
|
|
|
|
unsigned int flags)
|
2013-12-11 10:55:52 -05:00
|
|
|
{
|
2014-02-11 05:31:40 -05:00
|
|
|
struct pat_ref *ref = getnext;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Get next list entry. */
|
|
|
|
|
ref = LIST_NEXT(&ref->list, struct pat_ref *, list);
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* If the entry is the last of the list, return NULL. */
|
|
|
|
|
if (&ref->list == end)
|
|
|
|
|
return NULL;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* If the entry match the flag, return it. */
|
|
|
|
|
if (ref->flags & flags)
|
|
|
|
|
return ref;
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-11 09:29:22 -04:00
|
|
|
static inline
|
|
|
|
|
struct pat_ref *pat_ref_lookup_ref(const char *reference)
|
|
|
|
|
{
|
|
|
|
|
int id;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
/* If the reference starts by a '#', this is numeric id. */
|
|
|
|
|
if (reference[0] == '#') {
|
|
|
|
|
/* Try to convert the numeric id. If the conversion fails, the lookup fails. */
|
|
|
|
|
id = strtol(reference + 1, &error, 10);
|
|
|
|
|
if (*error != '\0')
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
/* Perform the unique id lookup. */
|
|
|
|
|
return pat_ref_lookupid(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Perform the string lookup. */
|
|
|
|
|
return pat_ref_lookup(reference);
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* This function is used with map and acl management. It permits to browse
|
|
|
|
|
* each reference.
|
|
|
|
|
*/
|
|
|
|
|
static inline
|
|
|
|
|
struct pattern_expr *pat_expr_get_next(struct pattern_expr *getnext, struct list *end)
|
|
|
|
|
{
|
|
|
|
|
struct pattern_expr *expr;
|
2014-01-20 08:29:33 -05:00
|
|
|
expr = LIST_NEXT(&getnext->list, struct pattern_expr *, list);
|
|
|
|
|
if (&expr->list == end)
|
2014-02-11 05:31:40 -05:00
|
|
|
return NULL;
|
|
|
|
|
return expr;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-22 13:31:03 -04:00
|
|
|
/* Processes the stats interpreter on the statistics socket. This function is
|
2009-10-04 08:22:18 -04:00
|
|
|
* called from an applet running in a stream interface. The function returns 1
|
2013-12-01 03:15:12 -05:00
|
|
|
* if the request was understood, otherwise zero. It sets appctx->st0 to a value
|
2009-10-11 17:12:51 -04:00
|
|
|
* designating the function which will have to process the request, which can
|
|
|
|
|
* also be the print function to display the return message set into cli.msg.
|
2009-08-16 13:06:42 -04:00
|
|
|
*/
|
2011-06-15 02:18:44 -04:00
|
|
|
static int stats_sock_parse_request(struct stream_interface *si, char *line)
|
2009-08-16 13:06:42 -04:00
|
|
|
{
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *s = si_strm(si);
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2009-08-16 13:06:42 -04:00
|
|
|
char *args[MAX_STATS_ARGS + 1];
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
struct cli_kw *kw;
|
2009-08-16 13:06:42 -04:00
|
|
|
int arg;
|
2013-12-10 12:54:58 -05:00
|
|
|
int i, j;
|
2009-08-16 13:06:42 -04:00
|
|
|
|
|
|
|
|
while (isspace((unsigned char)*line))
|
|
|
|
|
line++;
|
|
|
|
|
|
|
|
|
|
arg = 0;
|
|
|
|
|
args[arg] = line;
|
|
|
|
|
|
|
|
|
|
while (*line && arg < MAX_STATS_ARGS) {
|
2013-12-10 12:54:58 -05:00
|
|
|
if (*line == '\\') {
|
|
|
|
|
line++;
|
|
|
|
|
if (*line == '\0')
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (isspace((unsigned char)*line)) {
|
2009-08-16 13:06:42 -04:00
|
|
|
*line++ = '\0';
|
|
|
|
|
|
|
|
|
|
while (isspace((unsigned char)*line))
|
|
|
|
|
line++;
|
|
|
|
|
|
|
|
|
|
args[++arg] = line;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
line++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (++arg <= MAX_STATS_ARGS)
|
|
|
|
|
args[arg] = line;
|
|
|
|
|
|
2013-12-10 12:54:58 -05:00
|
|
|
/* remove \ */
|
|
|
|
|
arg = 0;
|
|
|
|
|
while (*args[arg] != '\0') {
|
|
|
|
|
j = 0;
|
|
|
|
|
for (i=0; args[arg][i] != '\0'; i++) {
|
|
|
|
|
if (args[arg][i] == '\\')
|
|
|
|
|
continue;
|
|
|
|
|
args[arg][j] = args[arg][i];
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
args[arg][j] = '\0';
|
|
|
|
|
arg++;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-04 12:07:56 -04:00
|
|
|
appctx->ctx.stats.scope_str = 0;
|
|
|
|
|
appctx->ctx.stats.scope_len = 0;
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.stats.flags = 0;
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
if ((kw = cli_find_kw(args))) {
|
|
|
|
|
if (kw->parse) {
|
2016-11-12 04:51:33 -05:00
|
|
|
if (kw->parse(args, appctx, kw->private) == 0 && kw->io_handler) {
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
appctx->st0 = STAT_CLI_O_CUSTOM;
|
|
|
|
|
appctx->io_handler = kw->io_handler;
|
2016-11-12 04:51:33 -05:00
|
|
|
appctx->io_release = kw->io_release;
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (strcmp(args[0], "show") == 0) {
|
2015-09-18 08:49:12 -04:00
|
|
|
if (strcmp(args[1], "backend") == 0) {
|
2016-05-06 06:18:49 -04:00
|
|
|
appctx->ctx.be.px = NULL;
|
2015-09-18 08:49:12 -04:00
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_BACKEND;
|
|
|
|
|
}
|
2016-02-16 05:27:28 -05:00
|
|
|
else if (strcmp(args[1], "env") == 0) {
|
|
|
|
|
extern char **environ;
|
|
|
|
|
|
|
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
|
|
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
appctx->ctx.env.var = environ;
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_ENV; // stats_dump_env_to_buffer
|
|
|
|
|
|
|
|
|
|
if (*args[2]) {
|
|
|
|
|
int len = strlen(args[2]);
|
|
|
|
|
|
|
|
|
|
for (; *appctx->ctx.env.var; appctx->ctx.env.var++) {
|
|
|
|
|
if (strncmp(*appctx->ctx.env.var, args[2], len) == 0 &&
|
|
|
|
|
(*appctx->ctx.env.var)[len] == '=')
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!*appctx->ctx.env.var) {
|
|
|
|
|
appctx->ctx.cli.msg = "Variable not found\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
appctx->st2 = STAT_ST_END;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[1], "stat") == 0) {
|
2015-05-16 18:33:24 -04:00
|
|
|
if (strcmp(args[2], "resolvers") == 0) {
|
|
|
|
|
struct dns_resolvers *presolvers;
|
|
|
|
|
|
2015-10-02 16:33:01 -04:00
|
|
|
if (*args[3]) {
|
|
|
|
|
appctx->ctx.resolvers.ptr = NULL;
|
|
|
|
|
list_for_each_entry(presolvers, &dns_resolvers, list) {
|
|
|
|
|
if (strcmp(presolvers->id, args[3]) == 0) {
|
|
|
|
|
appctx->ctx.resolvers.ptr = presolvers;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (appctx->ctx.resolvers.ptr == NULL) {
|
|
|
|
|
appctx->ctx.cli.msg = "Can't find that resolvers section\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
2015-05-16 18:33:24 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_RESOLVERS;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else if (*args[2] && *args[3] && *args[4]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.stats.flags |= STAT_BOUND;
|
|
|
|
|
appctx->ctx.stats.iid = atoi(args[2]);
|
|
|
|
|
appctx->ctx.stats.type = atoi(args[3]);
|
|
|
|
|
appctx->ctx.stats.sid = atoi(args[4]);
|
2016-01-11 12:57:53 -05:00
|
|
|
if (strcmp(args[5], "typed") == 0)
|
|
|
|
|
appctx->ctx.stats.flags |= STAT_FMT_TYPED;
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
2016-01-11 12:57:53 -05:00
|
|
|
else if (strcmp(args[2], "typed") == 0)
|
|
|
|
|
appctx->ctx.stats.flags |= STAT_FMT_TYPED;
|
2009-08-16 13:06:42 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_STAT; // stats_dump_stat_to_buffer
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
|
|
|
|
else if (strcmp(args[1], "info") == 0) {
|
2016-01-11 14:08:42 -05:00
|
|
|
if (strcmp(args[2], "typed") == 0)
|
|
|
|
|
appctx->ctx.stats.flags |= STAT_FMT_TYPED;
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
2015-07-03 02:01:20 -04:00
|
|
|
else if (strcmp(args[1], "servers") == 0 && strcmp(args[2], "state") == 0) {
|
2016-05-06 06:18:48 -04:00
|
|
|
appctx->ctx.server_state.iid = 0;
|
|
|
|
|
appctx->ctx.server_state.px = NULL;
|
|
|
|
|
appctx->ctx.server_state.sv = NULL;
|
2015-07-03 02:01:20 -04:00
|
|
|
|
|
|
|
|
/* check if a backend name has been provided */
|
|
|
|
|
if (*args[3]) {
|
|
|
|
|
/* read server state from local file */
|
2016-05-06 06:18:48 -04:00
|
|
|
appctx->ctx.server_state.px = proxy_be_by_name(args[3]);
|
2015-07-03 02:01:20 -04:00
|
|
|
|
2016-05-06 06:18:48 -04:00
|
|
|
if (!appctx->ctx.server_state.px) {
|
2015-07-03 02:01:20 -04:00
|
|
|
appctx->ctx.cli.msg = "Can't find backend.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2016-05-06 06:18:48 -04:00
|
|
|
appctx->ctx.server_state.iid = appctx->ctx.server_state.px->uuid;
|
2015-07-03 02:01:20 -04:00
|
|
|
}
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_SERVERS_STATE; // stats_dump_servers_state_to_buffer
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2014-01-28 10:49:56 -05:00
|
|
|
else if (strcmp(args[1], "pools") == 0) {
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_POOLS; // stats_dump_pools_to_buffer
|
|
|
|
|
}
|
2009-08-16 13:06:42 -04:00
|
|
|
else if (strcmp(args[1], "sess") == 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_INIT;
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-10 11:13:00 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2012-11-25 19:16:39 -05:00
|
|
|
if (*args[2] && strcmp(args[2], "all") == 0)
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.target = (void *)-1;
|
2012-11-25 19:16:39 -05:00
|
|
|
else if (*args[2])
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.target = (void *)strtoul(args[2], NULL, 0);
|
2010-03-05 11:53:32 -05:00
|
|
|
else
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.target = NULL;
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
appctx->ctx.sess.section = 0; /* start with stream status */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.pos = 0;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
|
|
|
|
else if (strcmp(args[1], "errors") == 0) {
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-10 11:13:00 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2009-08-16 13:06:42 -04:00
|
|
|
if (*args[2])
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.iid = atoi(args[2]);
|
2009-08-16 13:06:42 -04:00
|
|
|
else
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.iid = -1;
|
|
|
|
|
appctx->ctx.errors.px = NULL;
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
2010-07-12 11:55:33 -04:00
|
|
|
else if (strcmp(args[1], "table") == 0) {
|
2012-06-06 17:37:08 -04:00
|
|
|
stats_sock_table_request(si, args, STAT_CLI_O_TAB);
|
2010-07-12 11:55:33 -04:00
|
|
|
}
|
2015-05-09 02:46:01 -04:00
|
|
|
else if (strcmp(args[1], "tls-keys") == 0) {
|
|
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
2016-06-14 11:45:18 -04:00
|
|
|
appctx->ctx.tlskeys.dump_all = 0;
|
2016-05-20 11:40:26 -04:00
|
|
|
/* no parameter, shows only file list */
|
|
|
|
|
if (!*args[2]) {
|
2016-06-14 11:45:18 -04:00
|
|
|
appctx->ctx.tlskeys.dump_all = 1;
|
2016-05-20 11:40:26 -04:00
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_TLSK;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (args[2][0] == '*') {
|
|
|
|
|
/* list every TLS ticket keys */
|
|
|
|
|
appctx->ctx.tlskeys.ref = NULL;
|
2016-06-14 11:45:18 -04:00
|
|
|
appctx->ctx.tlskeys.dump_all = 1;
|
2016-05-20 11:40:26 -04:00
|
|
|
} else {
|
|
|
|
|
appctx->ctx.tlskeys.ref = tlskeys_ref_lookup_ref(args[2]);
|
|
|
|
|
if(!appctx->ctx.tlskeys.ref) {
|
|
|
|
|
appctx->ctx.cli.msg = "'show tls-keys' unable to locate referenced filename\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-09 02:46:01 -04:00
|
|
|
appctx->st2 = STAT_ST_INIT;
|
2016-05-20 11:40:26 -04:00
|
|
|
appctx->st0 = STAT_CLI_O_TLSK_ENT;
|
|
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
#else
|
|
|
|
|
appctx->ctx.cli.msg = "HAProxy was compiled against a version of OpenSSL "
|
|
|
|
|
"that doesn't support specifying TLS ticket keys\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
#endif
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2014-02-11 05:31:40 -05:00
|
|
|
else if (strcmp(args[1], "map") == 0 ||
|
|
|
|
|
strcmp(args[1], "acl") == 0) {
|
|
|
|
|
|
|
|
|
|
/* Set ACL or MAP flags. */
|
|
|
|
|
if (args[1][0] == 'm')
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_MAP;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_ACL;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2016-05-06 06:18:50 -04:00
|
|
|
/* no parameter: display all map available */
|
2013-12-11 10:55:52 -05:00
|
|
|
if (!*args[2]) {
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->st0 = STAT_CLI_O_PATS;
|
2013-12-11 10:55:52 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* lookup into the refs and check the map flag */
|
2014-03-11 09:29:22 -04:00
|
|
|
appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
|
2014-02-11 05:31:40 -05:00
|
|
|
if (!appctx->ctx.map.ref ||
|
|
|
|
|
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
|
|
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->st0 = STAT_CLI_O_PAT;
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
2012-04-02 21:57:54 -04:00
|
|
|
else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
|
2009-08-16 13:06:42 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-04 09:02:46 -04:00
|
|
|
else if (strcmp(args[0], "clear") == 0) {
|
|
|
|
|
if (strcmp(args[1], "counters") == 0) {
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
struct server *sv;
|
2009-10-04 09:43:17 -04:00
|
|
|
struct listener *li;
|
2009-10-10 09:26:26 -04:00
|
|
|
int clrall = 0;
|
|
|
|
|
|
|
|
|
|
if (strcmp(args[2], "all") == 0)
|
|
|
|
|
clrall = 1;
|
2009-10-04 09:02:46 -04:00
|
|
|
|
2009-10-10 11:13:00 -04:00
|
|
|
/* check permissions */
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER ||
|
|
|
|
|
(clrall && strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN)) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-10 11:13:00 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-04 09:02:46 -04:00
|
|
|
for (px = proxy; px; px = px->next) {
|
2011-03-10 17:25:56 -05:00
|
|
|
if (clrall) {
|
|
|
|
|
memset(&px->be_counters, 0, sizeof(px->be_counters));
|
|
|
|
|
memset(&px->fe_counters, 0, sizeof(px->fe_counters));
|
|
|
|
|
}
|
2009-10-10 09:26:26 -04:00
|
|
|
else {
|
2011-03-10 17:25:56 -05:00
|
|
|
px->be_counters.conn_max = 0;
|
|
|
|
|
px->be_counters.p.http.rps_max = 0;
|
|
|
|
|
px->be_counters.sps_max = 0;
|
|
|
|
|
px->be_counters.cps_max = 0;
|
|
|
|
|
px->be_counters.nbpend_max = 0;
|
|
|
|
|
|
|
|
|
|
px->fe_counters.conn_max = 0;
|
|
|
|
|
px->fe_counters.p.http.rps_max = 0;
|
|
|
|
|
px->fe_counters.sps_max = 0;
|
|
|
|
|
px->fe_counters.cps_max = 0;
|
|
|
|
|
px->fe_counters.nbpend_max = 0;
|
2009-10-10 09:26:26 -04:00
|
|
|
}
|
2009-10-04 09:02:46 -04:00
|
|
|
|
|
|
|
|
for (sv = px->srv; sv; sv = sv->next)
|
2009-10-10 09:26:26 -04:00
|
|
|
if (clrall)
|
|
|
|
|
memset(&sv->counters, 0, sizeof(sv->counters));
|
|
|
|
|
else {
|
|
|
|
|
sv->counters.cur_sess_max = 0;
|
|
|
|
|
sv->counters.nbpend_max = 0;
|
|
|
|
|
sv->counters.sps_max = 0;
|
|
|
|
|
}
|
2009-10-04 09:43:17 -04:00
|
|
|
|
2012-09-26 15:03:11 -04:00
|
|
|
list_for_each_entry(li, &px->conf.listeners, by_fe)
|
2009-10-10 09:26:26 -04:00
|
|
|
if (li->counters) {
|
|
|
|
|
if (clrall)
|
|
|
|
|
memset(li->counters, 0, sizeof(*li->counters));
|
|
|
|
|
else
|
|
|
|
|
li->counters->conn_max = 0;
|
|
|
|
|
}
|
2009-10-04 09:02:46 -04:00
|
|
|
}
|
|
|
|
|
|
2011-09-07 09:17:21 -04:00
|
|
|
global.cps_max = 0;
|
2013-10-07 12:51:07 -04:00
|
|
|
global.sps_max = 0;
|
2009-10-04 09:02:46 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2010-07-13 07:48:00 -04:00
|
|
|
else if (strcmp(args[1], "table") == 0) {
|
2012-06-06 17:37:08 -04:00
|
|
|
stats_sock_table_request(si, args, STAT_CLI_O_CLR);
|
2010-07-13 07:48:00 -04:00
|
|
|
/* end of processing */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2014-02-11 05:31:40 -05:00
|
|
|
else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
|
|
|
|
/* Set ACL or MAP flags. */
|
|
|
|
|
if (args[1][0] == 'm')
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_MAP;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_ACL;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
/* no parameter */
|
|
|
|
|
if (!*args[2]) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
|
|
|
|
appctx->ctx.cli.msg = "Missing map identifier.\n";
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.cli.msg = "Missing ACL identifier.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* lookup into the refs and check the map flag */
|
2014-03-11 09:29:22 -04:00
|
|
|
appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
|
2014-02-11 05:31:40 -05:00
|
|
|
if (!appctx->ctx.map.ref ||
|
|
|
|
|
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
|
|
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Clear all. */
|
|
|
|
|
pat_ref_prune(appctx->ctx.map.ref);
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
/* return response */
|
2014-12-18 09:28:01 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2014-02-11 05:31:40 -05:00
|
|
|
return 1;
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
2009-10-04 09:02:46 -04:00
|
|
|
else {
|
2010-07-13 07:48:00 -04:00
|
|
|
/* unknown "clear" argument */
|
2009-10-04 09:02:46 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-10 12:37:29 -04:00
|
|
|
else if (strcmp(args[0], "get") == 0) {
|
|
|
|
|
if (strcmp(args[1], "weight") == 0) {
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
struct server *sv;
|
|
|
|
|
|
|
|
|
|
/* split "backend/server" and make <line> point to server */
|
|
|
|
|
for (line = args[2]; *line; line++)
|
|
|
|
|
if (*line == '/') {
|
|
|
|
|
*line++ = '\0';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*line) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Require 'backend/server'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-10 12:37:29 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!get_backend_server(args[2], line, &px, &sv)) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = px ? "No such server.\n" : "No such backend.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-10 12:37:29 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* return server's effective weight at the moment */
|
2012-10-29 11:51:55 -04:00
|
|
|
snprintf(trash.str, trash.size, "%d (initial %d)\n", sv->uweight, sv->iweight);
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putstr(si_ic(si), trash.str) == -1)
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2015-03-13 09:00:47 -04:00
|
|
|
|
2009-10-10 12:37:29 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2014-02-11 05:31:40 -05:00
|
|
|
else if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
|
|
|
|
/* Set flags. */
|
|
|
|
|
if (args[1][0] == 'm')
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_MAP;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_ACL;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* No parameter. */
|
2013-12-11 10:55:52 -05:00
|
|
|
if (!*args[2] || !*args[3]) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
|
|
|
|
appctx->ctx.cli.msg = "Missing map identifier and/or key.\n";
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.cli.msg = "Missing ACL identifier and/or key.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* lookup into the maps */
|
2014-03-11 09:29:22 -04:00
|
|
|
appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
|
2013-12-11 10:55:52 -05:00
|
|
|
if (!appctx->ctx.map.ref) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* copy input string. The string must be allocated because
|
|
|
|
|
* it may be used over multiple iterations. It's released
|
|
|
|
|
* at the end and upon abort anyway.
|
|
|
|
|
*/
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->ctx.map.chunk.len = strlen(args[3]);
|
|
|
|
|
appctx->ctx.map.chunk.size = appctx->ctx.map.chunk.len + 1;
|
|
|
|
|
appctx->ctx.map.chunk.str = strdup(args[3]);
|
|
|
|
|
if (!appctx->ctx.map.chunk.str) {
|
|
|
|
|
appctx->ctx.cli.msg = "Out of memory error.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* prepare response */
|
|
|
|
|
appctx->st2 = STAT_ST_INIT;
|
|
|
|
|
appctx->st0 = STAT_CLI_O_MLOOK;
|
|
|
|
|
}
|
2009-10-10 12:37:29 -04:00
|
|
|
else { /* not "get weight" */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-10 13:30:08 -04:00
|
|
|
else if (strcmp(args[0], "set") == 0) {
|
|
|
|
|
if (strcmp(args[1], "weight") == 0) {
|
|
|
|
|
struct server *sv;
|
2013-02-11 20:45:51 -05:00
|
|
|
const char *warning;
|
2009-10-10 13:30:08 -04:00
|
|
|
|
2011-09-07 17:41:01 -04:00
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
2009-10-10 13:30:08 -04:00
|
|
|
return 1;
|
|
|
|
|
|
2013-02-11 20:45:51 -05:00
|
|
|
warning = server_parse_weight_change_request(sv, args[3]);
|
|
|
|
|
if (warning) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = warning;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-10 13:30:08 -04:00
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2014-05-22 12:42:35 -04:00
|
|
|
else if (strcmp(args[1], "server") == 0) {
|
|
|
|
|
struct server *sv;
|
|
|
|
|
const char *warning;
|
|
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (strcmp(args[3], "weight") == 0) {
|
|
|
|
|
warning = server_parse_weight_change_request(sv, args[4]);
|
|
|
|
|
if (warning) {
|
|
|
|
|
appctx->ctx.cli.msg = warning;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[3], "state") == 0) {
|
|
|
|
|
if (strcmp(args[4], "ready") == 0)
|
|
|
|
|
srv_adm_set_ready(sv);
|
|
|
|
|
else if (strcmp(args[4], "drain") == 0)
|
|
|
|
|
srv_adm_set_drain(sv);
|
|
|
|
|
else if (strcmp(args[4], "maint") == 0)
|
|
|
|
|
srv_adm_set_maint(sv);
|
|
|
|
|
else {
|
|
|
|
|
appctx->ctx.cli.msg = "'set server <srv> state' expects 'ready', 'drain' and 'maint'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[3], "health") == 0) {
|
|
|
|
|
if (sv->track) {
|
|
|
|
|
appctx->ctx.cli.msg = "cannot change health on a tracking server.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[4], "up") == 0) {
|
|
|
|
|
sv->check.health = sv->check.rise + sv->check.fall - 1;
|
|
|
|
|
srv_set_running(sv, "changed from CLI");
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[4], "stopping") == 0) {
|
|
|
|
|
sv->check.health = sv->check.rise + sv->check.fall - 1;
|
|
|
|
|
srv_set_stopping(sv, "changed from CLI");
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[4], "down") == 0) {
|
|
|
|
|
sv->check.health = 0;
|
|
|
|
|
srv_set_stopped(sv, "changed from CLI");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
appctx->ctx.cli.msg = "'set server <srv> health' expects 'up', 'stopping', or 'down'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[3], "agent") == 0) {
|
|
|
|
|
if (!(sv->agent.state & CHK_ST_ENABLED)) {
|
|
|
|
|
appctx->ctx.cli.msg = "agent checks are not enabled on this server.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[4], "up") == 0) {
|
|
|
|
|
sv->agent.health = sv->agent.rise + sv->agent.fall - 1;
|
|
|
|
|
srv_set_running(sv, "changed from CLI");
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[4], "down") == 0) {
|
|
|
|
|
sv->agent.health = 0;
|
|
|
|
|
srv_set_stopped(sv, "changed from CLI");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
appctx->ctx.cli.msg = "'set server <srv> agent' expects 'up' or 'down'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-08-31 17:26:29 -04:00
|
|
|
else if (strcmp(args[3], "check-port") == 0) {
|
|
|
|
|
int i = 0;
|
|
|
|
|
if (strl2irc(args[4], strlen(args[4]), &i) != 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "'set server <srv> check-port' expects an integer as argument.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
if ((i < 0) || (i > 65535)) {
|
|
|
|
|
appctx->ctx.cli.msg = "provided port is not valid.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
/* prevent the update of port to 0 if MAPPORTS are in use */
|
|
|
|
|
if ((sv->flags & SRV_F_MAPPORTS) && (i == 0)) {
|
|
|
|
|
appctx->ctx.cli.msg = "can't unset 'port' since MAPPORTS is in use.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
sv->check.port = i;
|
|
|
|
|
appctx->ctx.cli.msg = "health check port updated.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
2015-04-13 16:54:33 -04:00
|
|
|
else if (strcmp(args[3], "addr") == 0) {
|
2016-08-03 16:34:12 -04:00
|
|
|
char *addr = NULL;
|
|
|
|
|
char *port = NULL;
|
|
|
|
|
if (strlen(args[4]) == 0) {
|
2016-11-04 13:21:32 -04:00
|
|
|
appctx->ctx.cli.msg = "set server <b>/<s> addr requires an address and optionally a port.\n";
|
2016-08-03 16:34:12 -04:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
addr = args[4];
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(args[5], "port") == 0) {
|
|
|
|
|
port = args[6];
|
|
|
|
|
}
|
|
|
|
|
warning = update_server_addr_port(sv, addr, port, "stats socket command");
|
2015-04-13 16:54:33 -04:00
|
|
|
if (warning) {
|
|
|
|
|
appctx->ctx.cli.msg = warning;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
2016-11-04 13:21:32 -04:00
|
|
|
srv_clr_admin_flag(sv, SRV_ADMF_RMAINT);
|
2015-04-13 16:54:33 -04:00
|
|
|
}
|
2014-05-22 12:42:35 -04:00
|
|
|
else {
|
2016-08-31 17:26:29 -04:00
|
|
|
appctx->ctx.cli.msg = "'set server <srv>' only supports 'agent', 'health', 'state', 'weight', 'addr' and 'check-port'.\n";
|
2014-05-22 12:42:35 -04:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2010-01-26 04:59:06 -05:00
|
|
|
else if (strcmp(args[1], "timeout") == 0) {
|
|
|
|
|
if (strcmp(args[2], "cli") == 0) {
|
|
|
|
|
unsigned timeout;
|
|
|
|
|
const char *res;
|
|
|
|
|
|
|
|
|
|
if (!*args[3]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Expects an integer value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2010-01-26 04:59:06 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
res = parse_time_err(args[3], &timeout, TIME_UNIT_S);
|
|
|
|
|
if (res || timeout < 1) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Invalid timeout value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2010-01-26 04:59:06 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 14:45:39 -05:00
|
|
|
s->req.rto = s->res.wto = 1 + MS_TO_TICKS(timeout*1000);
|
2016-11-24 09:35:16 -05:00
|
|
|
task_wakeup(s->task, TASK_WOKEN_MSG); // recompute timeouts
|
2010-01-26 04:59:06 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "'set timeout' only supports 'cli'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2010-01-26 04:59:06 -05:00
|
|
|
return 1;
|
2011-08-02 05:49:05 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[1], "maxconn") == 0) {
|
|
|
|
|
if (strcmp(args[2], "frontend") == 0) {
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
struct listener *l;
|
|
|
|
|
int v;
|
|
|
|
|
|
2011-09-07 16:37:44 -04:00
|
|
|
px = expect_frontend_admin(s, si, args[3]);
|
|
|
|
|
if (!px)
|
2011-08-02 05:49:05 -04:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (!*args[4]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Integer value expected.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-08-02 05:49:05 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v = atoi(args[4]);
|
2012-09-26 15:07:15 -04:00
|
|
|
if (v < 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Value out of range.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-08-02 05:49:05 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK, the value is fine, so we assign it to the proxy and to all of
|
|
|
|
|
* its listeners. The blocked ones will be dequeued.
|
|
|
|
|
*/
|
|
|
|
|
px->maxconn = v;
|
2012-09-26 15:03:11 -04:00
|
|
|
list_for_each_entry(l, &px->conf.listeners, by_fe) {
|
2011-08-02 05:49:05 -04:00
|
|
|
l->maxconn = v;
|
|
|
|
|
if (l->state == LI_FULL)
|
|
|
|
|
resume_listener(l);
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-24 09:25:39 -05:00
|
|
|
if (px->maxconn > px->feconn && !LIST_ISEMPTY(&px->listener_queue))
|
|
|
|
|
dequeue_all_listeners(&px->listener_queue);
|
2011-08-02 05:49:05 -04:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2015-10-27 17:46:25 -04:00
|
|
|
else if (strcmp(args[2], "server") == 0) {
|
|
|
|
|
struct server *sv;
|
2016-04-24 17:10:07 -04:00
|
|
|
const char *warning;
|
2015-10-27 17:46:25 -04:00
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[3]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
2016-04-24 17:10:07 -04:00
|
|
|
warning = server_parse_maxconn_change_request(sv, args[4]);
|
|
|
|
|
if (warning) {
|
|
|
|
|
appctx->ctx.cli.msg = warning;
|
2015-10-27 17:46:25 -04:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2011-09-07 08:38:31 -04:00
|
|
|
else if (strcmp(args[2], "global") == 0) {
|
|
|
|
|
int v;
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 08:38:31 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[3]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Expects an integer value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 08:38:31 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v = atoi(args[3]);
|
|
|
|
|
if (v > global.hardmaxconn) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Value out of range.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 08:38:31 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* check for unlimited values */
|
|
|
|
|
if (v <= 0)
|
|
|
|
|
v = global.hardmaxconn;
|
|
|
|
|
|
|
|
|
|
global.maxconn = v;
|
|
|
|
|
|
|
|
|
|
/* Dequeues all of the listeners waiting for a resource */
|
|
|
|
|
if (!LIST_ISEMPTY(&global_listener_queue))
|
|
|
|
|
dequeue_all_listeners(&global_listener_queue);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2011-08-02 05:49:05 -04:00
|
|
|
else {
|
2015-10-27 17:46:25 -04:00
|
|
|
appctx->ctx.cli.msg = "'set maxconn' only supports 'frontend', 'server', and 'global'.\n";
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-08-02 05:49:05 -04:00
|
|
|
return 1;
|
2010-01-26 04:59:06 -05:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-07 10:13:44 -04:00
|
|
|
else if (strcmp(args[1], "rate-limit") == 0) {
|
|
|
|
|
if (strcmp(args[2], "connections") == 0) {
|
|
|
|
|
if (strcmp(args[3], "global") == 0) {
|
|
|
|
|
int v;
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 10:13:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[4]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Expects an integer value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 10:13:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v = atoi(args[4]);
|
|
|
|
|
if (v < 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Value out of range.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 10:13:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.cps_lim = v;
|
|
|
|
|
|
|
|
|
|
/* Dequeues all of the listeners waiting for a resource */
|
|
|
|
|
if (!LIST_ISEMPTY(&global_listener_queue))
|
|
|
|
|
dequeue_all_listeners(&global_listener_queue);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "'set rate-limit connections' only supports 'global'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 10:13:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-10-07 12:51:07 -04:00
|
|
|
else if (strcmp(args[2], "sessions") == 0) {
|
|
|
|
|
if (strcmp(args[3], "global") == 0) {
|
|
|
|
|
int v;
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-10-07 12:51:07 -04:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[4]) {
|
|
|
|
|
appctx->ctx.cli.msg = "Expects an integer value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v = atoi(args[4]);
|
|
|
|
|
if (v < 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "Value out of range.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.sps_lim = v;
|
|
|
|
|
|
|
|
|
|
/* Dequeues all of the listeners waiting for a resource */
|
|
|
|
|
if (!LIST_ISEMPTY(&global_listener_queue))
|
|
|
|
|
dequeue_all_listeners(&global_listener_queue);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
appctx->ctx.cli.msg = "'set rate-limit sessions' only supports 'global'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-10-07 14:01:52 -04:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
else if (strcmp(args[2], "ssl-sessions") == 0) {
|
|
|
|
|
if (strcmp(args[3], "global") == 0) {
|
|
|
|
|
int v;
|
|
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-10-07 14:01:52 -04:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[4]) {
|
|
|
|
|
appctx->ctx.cli.msg = "Expects an integer value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
v = atoi(args[4]);
|
|
|
|
|
if (v < 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "Value out of range.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
global.ssl_lim = v;
|
|
|
|
|
|
|
|
|
|
/* Dequeues all of the listeners waiting for a resource */
|
|
|
|
|
if (!LIST_ISEMPTY(&global_listener_queue))
|
|
|
|
|
dequeue_all_listeners(&global_listener_queue);
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
appctx->ctx.cli.msg = "'set rate-limit ssl-sessions' only supports 'global'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2012-11-09 11:05:39 -05:00
|
|
|
else if (strcmp(args[2], "http-compression") == 0) {
|
|
|
|
|
if (strcmp(args[3], "global") == 0) {
|
|
|
|
|
int v;
|
|
|
|
|
|
2015-11-26 12:32:39 -05:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
|
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-20 18:29:50 -05:00
|
|
|
if (!*args[4]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Expects a maximum input byte rate in kB/s.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2012-11-20 18:29:50 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-09 11:05:39 -05:00
|
|
|
v = atoi(args[4]);
|
|
|
|
|
global.comp_rate_lim = v * 1024; /* Kilo to bytes. */
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "'set rate-limit http-compression' only supports 'global'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2012-11-09 11:05:39 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-07 10:13:44 -04:00
|
|
|
else {
|
2013-10-07 14:01:52 -04:00
|
|
|
appctx->ctx.cli.msg = "'set rate-limit' supports 'connections', 'sessions', 'ssl-sessions', and 'http-compression'.\n";
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 10:13:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-06-06 19:03:16 -04:00
|
|
|
else if (strcmp(args[1], "table") == 0) {
|
|
|
|
|
stats_sock_table_request(si, args, STAT_CLI_O_SET);
|
|
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
else if (strcmp(args[1], "map") == 0) {
|
2014-01-29 13:08:49 -05:00
|
|
|
char *err;
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Set flags. */
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_MAP;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
/* Expect three parameters: map name, key and new value. */
|
|
|
|
|
if (!*args[2] || !*args[3] || !*args[4]) {
|
2014-03-11 08:52:44 -04:00
|
|
|
appctx->ctx.cli.msg = "'set map' expects three parameters: map identifier, key and value.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lookup the reference in the maps. */
|
2014-03-11 09:29:22 -04:00
|
|
|
appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
|
2013-12-11 10:55:52 -05:00
|
|
|
if (!appctx->ctx.map.ref) {
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 09:55:37 -05:00
|
|
|
/* If the entry identifier start with a '#', it is considered as
|
|
|
|
|
* pointer id
|
|
|
|
|
*/
|
|
|
|
|
if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
|
|
|
|
|
struct pat_ref_elt *ref;
|
|
|
|
|
long long int conv;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
/* Convert argument to integer value. */
|
|
|
|
|
conv = strtoll(&args[3][1], &error, 16);
|
|
|
|
|
if (*error != '\0') {
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
|
2014-01-28 09:55:37 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert and check integer to pointer. */
|
|
|
|
|
ref = (struct pat_ref_elt *)(long)conv;
|
|
|
|
|
if ((long long int)(long)ref != conv) {
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
|
2014-01-28 09:55:37 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Try to delete the entry. */
|
2014-01-29 13:08:49 -05:00
|
|
|
err = NULL;
|
|
|
|
|
if (!pat_ref_set_by_id(appctx->ctx.map.ref, ref, args[4], &err)) {
|
|
|
|
|
if (err)
|
|
|
|
|
memprintf(&err, "%s.\n", err);
|
|
|
|
|
appctx->ctx.cli.err = err;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT_FREE;
|
2014-01-28 09:55:37 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Else, use the entry identifier as pattern
|
|
|
|
|
* string, and update the value.
|
|
|
|
|
*/
|
2014-01-29 13:08:49 -05:00
|
|
|
err = NULL;
|
|
|
|
|
if (!pat_ref_set(appctx->ctx.map.ref, args[3], args[4], &err)) {
|
|
|
|
|
if (err)
|
|
|
|
|
memprintf(&err, "%s.\n", err);
|
|
|
|
|
appctx->ctx.cli.err = err;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT_FREE;
|
2014-01-28 09:55:37 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The set is done, send message. */
|
2014-12-18 09:28:01 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2013-12-11 10:55:52 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2014-06-16 12:36:30 -04:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
else if (strcmp(args[1], "ssl") == 0) {
|
|
|
|
|
if (strcmp(args[2], "ocsp-response") == 0) {
|
2014-12-09 10:32:51 -05:00
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
|
2014-06-16 12:36:30 -04:00
|
|
|
char *err = NULL;
|
|
|
|
|
|
2014-06-19 08:10:45 -04:00
|
|
|
/* Expect one parameter: the new response in base64 encoding */
|
2014-06-16 12:36:30 -04:00
|
|
|
if (!*args[3]) {
|
|
|
|
|
appctx->ctx.cli.msg = "'set ssl ocsp-response' expects response in base64 encoding.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trash.len = base64dec(args[3], strlen(args[3]), trash.str, trash.size);
|
|
|
|
|
if (trash.len < 0) {
|
|
|
|
|
appctx->ctx.cli.msg = "'set ssl ocsp-response' received invalid base64 encoded response.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ssl_sock_update_ocsp_response(&trash, &err)) {
|
|
|
|
|
if (err) {
|
|
|
|
|
memprintf(&err, "%s.\n", err);
|
|
|
|
|
appctx->ctx.cli.err = err;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT_FREE;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
appctx->ctx.cli.msg = "OCSP Response updated!";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
#else
|
|
|
|
|
appctx->ctx.cli.msg = "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
2015-05-09 02:46:01 -04:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[2], "tls-key") == 0) {
|
|
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
/* Expect two parameters: the filename and the new new TLS key in encoding */
|
|
|
|
|
if (!*args[3] || !*args[4]) {
|
|
|
|
|
appctx->ctx.cli.msg = "'set ssl tls-key' expects a filename and the new TLS key in base64 encoding.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->ctx.tlskeys.ref = tlskeys_ref_lookup_ref(args[3]);
|
|
|
|
|
if(!appctx->ctx.tlskeys.ref) {
|
|
|
|
|
appctx->ctx.cli.msg = "'set ssl tls-key' unable to locate referenced filename\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trash.len = base64dec(args[4], strlen(args[4]), trash.str, trash.size);
|
|
|
|
|
if (trash.len != sizeof(struct tls_sess_key)) {
|
|
|
|
|
appctx->ctx.cli.msg = "'set ssl tls-key' received invalid base64 encoded TLS key.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-20 08:55:17 -04:00
|
|
|
memcpy(appctx->ctx.tlskeys.ref->tlskeys + ((appctx->ctx.tlskeys.ref->tls_ticket_enc_index + 2) % TLS_TICKETS_NO), trash.str, trash.len);
|
|
|
|
|
appctx->ctx.tlskeys.ref->tls_ticket_enc_index = (appctx->ctx.tlskeys.ref->tls_ticket_enc_index + 1) % TLS_TICKETS_NO;
|
2015-05-09 02:46:01 -04:00
|
|
|
|
|
|
|
|
appctx->ctx.cli.msg = "TLS ticket key updated!";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
#else
|
|
|
|
|
appctx->ctx.cli.msg = "HAProxy was compiled against a version of OpenSSL "
|
|
|
|
|
"that doesn't support specifying TLS ticket keys\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
2014-06-16 12:36:30 -04:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
appctx->ctx.cli.msg = "'set ssl' only supports 'ocsp-response'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2010-01-26 04:59:06 -05:00
|
|
|
else { /* unknown "set" parameter */
|
2009-10-10 13:30:08 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-31 16:34:03 -05:00
|
|
|
else if (strcmp(args[0], "enable") == 0) {
|
2013-11-24 20:46:39 -05:00
|
|
|
if (strcmp(args[1], "agent") == 0) {
|
|
|
|
|
struct server *sv;
|
|
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
2013-12-11 14:11:55 -05:00
|
|
|
if (!(sv->agent.state & CHK_ST_CONFIGURED)) {
|
|
|
|
|
appctx->ctx.cli.msg = "Agent was not configured on this server, cannot enable.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sv->agent.state |= CHK_ST_ENABLED;
|
2013-11-24 20:46:39 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2014-05-23 05:53:10 -04:00
|
|
|
else if (strcmp(args[1], "health") == 0) {
|
|
|
|
|
struct server *sv;
|
|
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (!(sv->check.state & CHK_ST_CONFIGURED)) {
|
|
|
|
|
appctx->ctx.cli.msg = "Health checks are not configured on this server, cannot enable.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sv->check.state |= CHK_ST_ENABLED;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[1], "server") == 0) {
|
2010-01-31 16:34:03 -05:00
|
|
|
struct server *sv;
|
|
|
|
|
|
2011-09-07 17:41:01 -04:00
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
2010-01-31 16:34:03 -05:00
|
|
|
return 1;
|
2011-03-03 14:49:04 -05:00
|
|
|
|
2014-05-22 10:14:34 -04:00
|
|
|
srv_adm_set_ready(sv);
|
2010-01-31 16:34:03 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2011-09-07 16:37:44 -04:00
|
|
|
else if (strcmp(args[1], "frontend") == 0) {
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
|
|
|
|
|
px = expect_frontend_admin(s, si, args[2]);
|
|
|
|
|
if (!px)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (px->state == PR_STSTOPPED) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Frontend was previously shut down, cannot enable.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (px->state != PR_STPAUSED) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Frontend is already enabled.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!resume_proxy(px)) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Failed to resume frontend, check logs for precise cause (port conflict?).\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2010-01-31 16:34:03 -05:00
|
|
|
else { /* unknown "enable" parameter */
|
2014-05-23 05:53:10 -04:00
|
|
|
appctx->ctx.cli.msg = "'enable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
2010-01-31 16:34:03 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[0], "disable") == 0) {
|
2013-11-24 20:46:39 -05:00
|
|
|
if (strcmp(args[1], "agent") == 0) {
|
|
|
|
|
struct server *sv;
|
|
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
2013-12-11 14:11:55 -05:00
|
|
|
sv->agent.state &= ~CHK_ST_ENABLED;
|
2013-11-24 20:46:39 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2014-05-23 05:53:10 -04:00
|
|
|
else if (strcmp(args[1], "health") == 0) {
|
|
|
|
|
struct server *sv;
|
|
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
sv->check.state &= ~CHK_ST_ENABLED;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2013-11-24 20:46:39 -05:00
|
|
|
else if (strcmp(args[1], "server") == 0) {
|
2010-01-31 16:34:03 -05:00
|
|
|
struct server *sv;
|
|
|
|
|
|
2011-09-07 17:41:01 -04:00
|
|
|
sv = expect_server_admin(s, si, args[2]);
|
|
|
|
|
if (!sv)
|
2010-01-31 16:34:03 -05:00
|
|
|
return 1;
|
2011-03-03 14:49:04 -05:00
|
|
|
|
2014-05-22 10:14:34 -04:00
|
|
|
srv_adm_set_maint(sv);
|
2010-01-31 16:34:03 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2011-09-07 16:37:44 -04:00
|
|
|
else if (strcmp(args[1], "frontend") == 0) {
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
|
|
|
|
|
px = expect_frontend_admin(s, si, args[2]);
|
|
|
|
|
if (!px)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (px->state == PR_STSTOPPED) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Frontend was previously shut down, cannot disable.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (px->state == PR_STPAUSED) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Frontend is already disabled.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!pause_proxy(px)) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Failed to pause frontend, check logs for precise cause.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2010-01-31 16:34:03 -05:00
|
|
|
else { /* unknown "disable" parameter */
|
2014-05-23 05:53:10 -04:00
|
|
|
appctx->ctx.cli.msg = "'disable' only supports 'agent', 'frontend', 'health', and 'server'.\n";
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[0], "shutdown") == 0) {
|
|
|
|
|
if (strcmp(args[1], "frontend") == 0) {
|
|
|
|
|
struct proxy *px;
|
|
|
|
|
|
|
|
|
|
px = expect_frontend_admin(s, si, args[2]);
|
|
|
|
|
if (!px)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
if (px->state == PR_STSTOPPED) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Frontend was already shut down.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Warning("Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
|
|
|
|
|
px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
|
|
|
|
|
send_log(px, LOG_WARNING, "Proxy %s stopped (FE: %lld conns, BE: %lld conns).\n",
|
|
|
|
|
px->id, px->fe_counters.cum_conn, px->be_counters.cum_conn);
|
|
|
|
|
stop_proxy(px);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2011-09-07 17:21:03 -04:00
|
|
|
else if (strcmp(args[1], "session") == 0) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *sess, *ptr;
|
2011-09-07 17:21:03 -04:00
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
if (strm_li(s)->bind_conf->level < ACCESS_LVL_ADMIN) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = stats_permission_denied_msg;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:21:03 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!*args[2]) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "Session pointer expected (use 'show sess').\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:21:03 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ptr = (void *)strtoul(args[2], NULL, 0);
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* first, look for the requested stream in the stream table */
|
|
|
|
|
list_for_each_entry(sess, &streams, list) {
|
2011-09-07 17:21:03 -04:00
|
|
|
if (sess == ptr)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* do we have the stream ? */
|
2011-09-07 17:21:03 -04:00
|
|
|
if (sess != ptr) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "No such session (use 'show sess').\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:21:03 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-02 19:14:29 -04:00
|
|
|
stream_shutdown(sess, SF_ERR_KILLED);
|
2011-09-07 17:21:03 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
2011-09-07 17:48:48 -04:00
|
|
|
else if (strcmp(args[1], "sessions") == 0) {
|
|
|
|
|
if (strcmp(args[2], "server") == 0) {
|
|
|
|
|
struct server *sv;
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *sess, *sess_bck;
|
2011-09-07 17:48:48 -04:00
|
|
|
|
|
|
|
|
sv = expect_server_admin(s, si, args[3]);
|
|
|
|
|
if (!sv)
|
|
|
|
|
return 1;
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* kill all the stream that are on this server */
|
2011-09-07 17:48:48 -04:00
|
|
|
list_for_each_entry_safe(sess, sess_bck, &sv->actconns, by_srv)
|
|
|
|
|
if (sess->srv_conn == sv)
|
2015-04-02 19:14:29 -04:00
|
|
|
stream_shutdown(sess, SF_ERR_KILLED);
|
2011-09-07 17:48:48 -04:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "'shutdown sessions' only supports 'server'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 17:48:48 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-09-07 16:37:44 -04:00
|
|
|
else { /* unknown "disable" parameter */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.cli.msg = "'shutdown' only supports 'frontend', 'session' and 'sessions'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2011-09-07 16:37:44 -04:00
|
|
|
return 1;
|
2010-01-31 16:34:03 -05:00
|
|
|
}
|
|
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
else if (strcmp(args[0], "del") == 0) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (strcmp(args[1], "map") == 0 || strcmp(args[1], "acl") == 0) {
|
|
|
|
|
if (args[1][0] == 'm')
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_MAP;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_ACL;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
/* Expect two parameters: map name and key. */
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
|
|
|
|
|
if (!*args[2] || !*args[3]) {
|
|
|
|
|
appctx->ctx.cli.msg = "This command expects two parameters: map identifier and key.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
if (!*args[2] || !*args[3]) {
|
|
|
|
|
appctx->ctx.cli.msg = "This command expects two parameters: ACL identifier and key.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Lookup the reference in the maps. */
|
2014-03-11 09:29:22 -04:00
|
|
|
appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
|
2014-02-11 05:31:40 -05:00
|
|
|
if (!appctx->ctx.map.ref ||
|
|
|
|
|
!(appctx->ctx.map.ref->flags & appctx->ctx.map.display_flags)) {
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 09:55:37 -05:00
|
|
|
/* If the entry identifier start with a '#', it is considered as
|
|
|
|
|
* pointer id
|
|
|
|
|
*/
|
|
|
|
|
if (args[3][0] == '#' && args[3][1] == '0' && args[3][2] == 'x') {
|
|
|
|
|
struct pat_ref_elt *ref;
|
|
|
|
|
long long int conv;
|
|
|
|
|
char *error;
|
|
|
|
|
|
|
|
|
|
/* Convert argument to integer value. */
|
|
|
|
|
conv = strtoll(&args[3][1], &error, 16);
|
|
|
|
|
if (*error != '\0') {
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
|
2014-01-28 09:55:37 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert and check integer to pointer. */
|
|
|
|
|
ref = (struct pat_ref_elt *)(long)conv;
|
|
|
|
|
if ((long long int)(long)ref != conv) {
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Malformed identifier. Please use #<id> or <file>.\n";
|
2014-01-28 09:55:37 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Try to delete the entry. */
|
|
|
|
|
if (!pat_ref_delete_by_id(appctx->ctx.map.ref, ref)) {
|
|
|
|
|
/* The entry is not found, send message. */
|
|
|
|
|
appctx->ctx.cli.msg = "Key not found.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Else, use the entry identifier as pattern
|
|
|
|
|
* string and try to delete the entry.
|
|
|
|
|
*/
|
|
|
|
|
if (!pat_ref_delete(appctx->ctx.map.ref, args[3])) {
|
|
|
|
|
/* The entry is not found, send message. */
|
|
|
|
|
appctx->ctx.cli.msg = "Key not found.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The deletion is done, send message. */
|
2014-12-18 09:28:01 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2013-12-11 10:55:52 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else { /* unknown "del" parameter */
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->ctx.cli.msg = "'del' only supports 'map' or 'acl'.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (strcmp(args[0], "add") == 0) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (strcmp(args[1], "map") == 0 ||
|
|
|
|
|
strcmp(args[1], "acl") == 0) {
|
|
|
|
|
int ret;
|
2014-01-29 13:08:49 -05:00
|
|
|
char *err;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Set flags. */
|
|
|
|
|
if (args[1][0] == 'm')
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_MAP;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.map.display_flags = PAT_REF_ACL;
|
|
|
|
|
|
|
|
|
|
/* If the keywork is "map", we expect three parameters, if it
|
|
|
|
|
* is "acl", we expect only two parameters
|
|
|
|
|
*/
|
|
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
|
|
|
|
|
if (!*args[2] || !*args[3] || !*args[4]) {
|
|
|
|
|
appctx->ctx.cli.msg = "'add map' expects three parameters: map identifier, key and value.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
if (!*args[2] || !*args[3]) {
|
|
|
|
|
appctx->ctx.cli.msg = "'add acl' expects two parameters: ACL identifier and pattern.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Lookup for the reference. */
|
2014-03-11 09:29:22 -04:00
|
|
|
appctx->ctx.map.ref = pat_ref_lookup_ref(args[2]);
|
2013-12-11 10:55:52 -05:00
|
|
|
if (!appctx->ctx.map.ref) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown map identifier. Please use #<id> or <file>.\n";
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-03-20 06:42:45 -04:00
|
|
|
appctx->ctx.cli.msg = "Unknown ACL identifier. Please use #<id> or <file>.\n";
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-29 14:02:36 -05:00
|
|
|
/* The command "add acl" is prohibited if the reference
|
|
|
|
|
* use samples.
|
|
|
|
|
*/
|
|
|
|
|
if ((appctx->ctx.map.display_flags & PAT_REF_ACL) &&
|
|
|
|
|
(appctx->ctx.map.ref->flags & PAT_REF_SMP)) {
|
|
|
|
|
appctx->ctx.cli.msg = "This ACL is shared with a map containing samples. "
|
|
|
|
|
"You must use the command 'add map' to add values.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Add value. */
|
2014-01-29 13:08:49 -05:00
|
|
|
err = NULL;
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
2014-04-28 05:18:57 -04:00
|
|
|
ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-04-28 05:18:57 -04:00
|
|
|
ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
|
2014-02-11 05:31:40 -05:00
|
|
|
if (!ret) {
|
2014-01-29 13:08:49 -05:00
|
|
|
if (err)
|
|
|
|
|
memprintf(&err, "%s.\n", err);
|
|
|
|
|
appctx->ctx.cli.err = err;
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT_FREE;
|
2013-12-11 10:55:52 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The add is done, send message. */
|
2014-12-18 09:28:01 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2013-12-11 10:55:52 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
else { /* unknown "del" parameter */
|
|
|
|
|
appctx->ctx.cli.msg = "'add' only supports 'map'.\n";
|
|
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-01-31 16:34:03 -05:00
|
|
|
else { /* not "show" nor "clear" nor "get" nor "set" nor "enable" nor "disable" */
|
2009-08-16 13:06:42 -04:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-22 13:31:03 -04:00
|
|
|
/* This I/O handler runs as an applet embedded in a stream interface. It is
|
2009-10-04 08:22:18 -04:00
|
|
|
* used to processes I/O from/to the stats unix socket. The system relies on a
|
|
|
|
|
* state machine handling requests and various responses. We read a request,
|
|
|
|
|
* then we process it and send the response, and we possibly display a prompt.
|
2013-12-01 03:15:12 -05:00
|
|
|
* Then we can read again. The state is stored in appctx->st0 and is one of the
|
|
|
|
|
* STAT_CLI_* constants. appctx->st1 is used to indicate whether prompt is enabled
|
2009-10-04 08:22:18 -04:00
|
|
|
* or not.
|
2009-08-16 13:06:42 -04:00
|
|
|
*/
|
2015-04-13 06:05:19 -04:00
|
|
|
static void cli_io_handler(struct appctx *appctx)
|
2009-08-16 13:06:42 -04:00
|
|
|
{
|
2015-04-13 06:05:19 -04:00
|
|
|
struct stream_interface *si = appctx->owner;
|
2014-11-28 05:11:05 -05:00
|
|
|
struct channel *req = si_oc(si);
|
|
|
|
|
struct channel *res = si_ic(si);
|
2009-09-22 13:31:03 -04:00
|
|
|
int reql;
|
|
|
|
|
int len;
|
|
|
|
|
|
|
|
|
|
if (unlikely(si->state == SI_ST_DIS || si->state == SI_ST_CLO))
|
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
|
|
while (1) {
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->st0 == STAT_CLI_INIT) {
|
2009-09-22 13:31:03 -04:00
|
|
|
/* Stats output not initialized yet */
|
2013-12-01 03:15:12 -05:00
|
|
|
memset(&appctx->ctx.stats, 0, sizeof(appctx->ctx.stats));
|
|
|
|
|
appctx->st0 = STAT_CLI_GETREQ;
|
2009-09-22 13:31:03 -04:00
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
else if (appctx->st0 == STAT_CLI_END) {
|
2009-10-04 08:22:18 -04:00
|
|
|
/* Let's close for real now. We just close the request
|
|
|
|
|
* side, the conditions below will complete if needed.
|
|
|
|
|
*/
|
2012-05-21 10:31:45 -04:00
|
|
|
si_shutw(si);
|
2009-10-04 08:22:18 -04:00
|
|
|
break;
|
|
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
else if (appctx->st0 == STAT_CLI_GETREQ) {
|
2009-10-11 17:35:10 -04:00
|
|
|
/* ensure we have some output room left in the event we
|
|
|
|
|
* would want to return some info right after parsing.
|
|
|
|
|
*/
|
2014-11-28 06:18:45 -05:00
|
|
|
if (buffer_almost_full(si_ib(si))) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2009-10-11 17:35:10 -04:00
|
|
|
break;
|
MAJOR: channel: add a new flag CF_WAKE_WRITE to notify the task of writes
Since commit 6b66f3e ([MAJOR] implement autonomous inter-socket forwarding)
introduced in 1.3.16-rc1, we've been relying on a stupid mechanism to wake
up the task after a write, which was an exact copy-paste of the reader side.
The principle was that if we empty a buffer and there's no forwarding
scheduled or if the *producer* is not in a connected state, then we wake
the task up.
That does not make any sense. It happens to wake up too late sometimes (eg,
when the request analyser waits for some room in the buffer to start to
work), and leads to unneeded wakeups in client-side keep-alive, because
the task is woken up when the response is sent, while the analysers are
simply waiting for a new request.
In order to fix this, we introduce a new channel flag : CF_WAKE_WRITE. It
is designed so that an analyser can explicitly request being notified when
some data were written. It is used only when the HTTP request or response
analysers need to wait for more room in the buffers. It is automatically
cleared upon wake up.
The flag is also automatically set by the functions which try to write into
a buffer from an applet when they fail (bi_putblk() etc...).
That allows us to remove the stupid condition above and avoid some wakeups.
In http-server-close and in http-keep-alive modes, this reduces from 4 to 3
the average number of wakeups per request, and increases the overall
performance by about 1.5%.
2013-12-31 11:26:25 -05:00
|
|
|
}
|
2009-10-11 17:35:10 -04:00
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
reql = bo_getline(si_oc(si), trash.str, trash.size);
|
2009-09-22 13:31:03 -04:00
|
|
|
if (reql <= 0) { /* closed or EOL not found */
|
|
|
|
|
if (reql == 0)
|
|
|
|
|
break;
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_END;
|
2009-09-22 13:31:03 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-26 16:42:25 -04:00
|
|
|
/* seek for a possible unescaped semi-colon. If we find
|
|
|
|
|
* one, we replace it with an LF and skip only this part.
|
2009-09-22 13:31:03 -04:00
|
|
|
*/
|
2016-05-26 16:42:25 -04:00
|
|
|
for (len = 0; len < reql; len++) {
|
|
|
|
|
if (trash.str[len] == '\\') {
|
|
|
|
|
len++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-10-29 11:51:55 -04:00
|
|
|
if (trash.str[len] == ';') {
|
|
|
|
|
trash.str[len] = '\n';
|
2009-09-22 13:31:03 -04:00
|
|
|
reql = len + 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-05-26 16:42:25 -04:00
|
|
|
}
|
2009-08-16 13:06:42 -04:00
|
|
|
|
2009-10-04 01:36:58 -04:00
|
|
|
/* now it is time to check that we have a full line,
|
|
|
|
|
* remove the trailing \n and possibly \r, then cut the
|
|
|
|
|
* line.
|
|
|
|
|
*/
|
|
|
|
|
len = reql - 1;
|
2012-10-29 11:51:55 -04:00
|
|
|
if (trash.str[len] != '\n') {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_END;
|
2009-09-22 13:31:03 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
if (len && trash.str[len-1] == '\r')
|
2009-10-04 01:36:58 -04:00
|
|
|
len--;
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
trash.str[len] = '\0';
|
2009-09-22 13:31:03 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2009-09-22 13:31:03 -04:00
|
|
|
if (len) {
|
2012-10-29 11:51:55 -04:00
|
|
|
if (strcmp(trash.str, "quit") == 0) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_END;
|
2009-09-22 13:31:03 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
2012-10-29 11:51:55 -04:00
|
|
|
else if (strcmp(trash.str, "prompt") == 0)
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st1 = !appctx->st1;
|
2012-10-29 11:51:55 -04:00
|
|
|
else if (strcmp(trash.str, "help") == 0 ||
|
|
|
|
|
!stats_sock_parse_request(si, trash.str)) {
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
cli_gen_usage_msg();
|
|
|
|
|
if (dynamic_usage_msg)
|
|
|
|
|
appctx->ctx.cli.msg = dynamic_usage_msg;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.cli.msg = stats_sock_usage_msg;
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-10-11 17:12:51 -04:00
|
|
|
}
|
2009-10-04 08:22:18 -04:00
|
|
|
/* NB: stats_sock_parse_request() may have put
|
2013-12-01 03:15:12 -05:00
|
|
|
* another STAT_CLI_O_* into appctx->st0.
|
2009-10-04 08:22:18 -04:00
|
|
|
*/
|
2009-09-22 13:31:03 -04:00
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
else if (!appctx->st1) {
|
2009-09-22 13:31:03 -04:00
|
|
|
/* if prompt is disabled, print help on empty lines,
|
|
|
|
|
* so that the user at least knows how to enable
|
|
|
|
|
* prompt and find help.
|
|
|
|
|
*/
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
cli_gen_usage_msg();
|
|
|
|
|
if (dynamic_usage_msg)
|
|
|
|
|
appctx->ctx.cli.msg = dynamic_usage_msg;
|
|
|
|
|
else
|
|
|
|
|
appctx->ctx.cli.msg = stats_sock_usage_msg;
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PRINT;
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
2009-09-22 13:31:03 -04:00
|
|
|
|
|
|
|
|
/* re-adjust req buffer */
|
2014-11-28 05:11:05 -05:00
|
|
|
bo_skip(si_oc(si), reql);
|
2012-08-27 17:14:58 -04:00
|
|
|
req->flags |= CF_READ_DONTWAIT; /* we plan to read small requests */
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
2015-09-25 13:21:19 -04:00
|
|
|
else { /* output functions */
|
2013-12-01 03:15:12 -05:00
|
|
|
switch (appctx->st0) {
|
BUG/MINOR: Handle interactive mode in cli handler
A previous commit broke the interactive stats cli prompt. Specifically,
it was not clear that we could be in STAT_CLI_PROMPT when we get to
the output functions for the cli handler, and the switch statement did
not handle this case. We would then fall through to the default
statement, which was recently changed to set error flags on the socket.
This in turn causes the socket to be closed, which is not what we wanted
in this specific case.
To fix, we add a case for STAT_CLI_PROMPT, and simply break out of the
switch statement.
Testing:
- Connected to unix stats socket, issued 'prompt', observed that I
could issue multiple consecutive commands.
- Connected to unix stats socket, issued 'prompt', observed that socket
timed out after inactivity expired.
- Connected to unix stats socket, issued 'prompt' then 'set timeout cli
5', observed that socket timed out after 5 seconds expired.
- Connected to unix stats socket, issued invalid commands, received
usage output.
- Connected to unix stats socket, issued 'show info', received info
output and socket disconnected.
- Connected to unix stats socket, issued 'show stat', received stats
output and socket disconnected.
- Repeated above tests with TCP stats socket.
[wt: no backport needed, this was introduced during the applet rework in 1.6]
2015-10-02 11:08:10 -04:00
|
|
|
case STAT_CLI_PROMPT:
|
|
|
|
|
break;
|
2009-10-11 17:12:51 -04:00
|
|
|
case STAT_CLI_PRINT:
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putstr(si_ic(si), appctx->ctx.cli.msg) != -1)
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2015-03-13 09:00:47 -04:00
|
|
|
else
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2009-09-22 13:31:03 -04:00
|
|
|
break;
|
2014-01-29 13:08:49 -05:00
|
|
|
case STAT_CLI_PRINT_FREE:
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putstr(si_ic(si), appctx->ctx.cli.err) != -1) {
|
2014-01-29 13:08:49 -05:00
|
|
|
free(appctx->ctx.cli.err);
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
}
|
2015-03-13 09:00:47 -04:00
|
|
|
else
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2014-01-29 13:08:49 -05:00
|
|
|
break;
|
2015-09-18 08:49:12 -04:00
|
|
|
case STAT_CLI_O_BACKEND:
|
|
|
|
|
if (stats_dump_backend_to_buffer(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2009-10-04 08:22:18 -04:00
|
|
|
case STAT_CLI_O_INFO:
|
2012-12-22 17:20:30 -05:00
|
|
|
if (stats_dump_info_to_buffer(si))
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2012-12-22 14:31:10 -05:00
|
|
|
break;
|
2015-07-03 02:01:20 -04:00
|
|
|
case STAT_CLI_O_SERVERS_STATE:
|
|
|
|
|
if (stats_dump_servers_state_to_buffer(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2012-12-22 14:31:10 -05:00
|
|
|
case STAT_CLI_O_STAT:
|
2012-12-22 17:20:30 -05:00
|
|
|
if (stats_dump_stat_to_buffer(si, NULL))
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2009-09-22 13:31:03 -04:00
|
|
|
break;
|
2015-05-16 18:33:24 -04:00
|
|
|
case STAT_CLI_O_RESOLVERS:
|
|
|
|
|
if (stats_dump_resolvers_to_buffer(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2009-10-04 08:22:18 -04:00
|
|
|
case STAT_CLI_O_SESS:
|
2011-02-13 09:27:22 -05:00
|
|
|
if (stats_dump_sess_to_buffer(si))
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2009-09-22 13:31:03 -04:00
|
|
|
break;
|
2009-10-04 08:22:18 -04:00
|
|
|
case STAT_CLI_O_ERR: /* errors dump */
|
2011-02-13 09:27:22 -05:00
|
|
|
if (stats_dump_errors_to_buffer(si))
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2009-09-22 13:31:03 -04:00
|
|
|
break;
|
2010-07-12 11:55:33 -04:00
|
|
|
case STAT_CLI_O_TAB:
|
2011-06-15 02:18:49 -04:00
|
|
|
case STAT_CLI_O_CLR:
|
2013-12-01 03:15:12 -05:00
|
|
|
if (stats_table_request(si, appctx->st0))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
2014-02-11 05:31:40 -05:00
|
|
|
case STAT_CLI_O_PATS:
|
|
|
|
|
if (stats_pats_list(si))
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2014-02-11 05:31:40 -05:00
|
|
|
case STAT_CLI_O_PAT:
|
|
|
|
|
if (stats_pat_list(si))
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
|
|
|
|
case STAT_CLI_O_MLOOK:
|
|
|
|
|
if (stats_map_lookup(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2014-01-29 06:13:39 -05:00
|
|
|
break;
|
2014-01-28 10:49:56 -05:00
|
|
|
case STAT_CLI_O_POOLS:
|
|
|
|
|
if (stats_dump_pools_to_buffer(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2015-05-09 02:46:01 -04:00
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
case STAT_CLI_O_TLSK:
|
|
|
|
|
if (stats_tlskeys_list(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2016-05-20 11:40:26 -04:00
|
|
|
case STAT_CLI_O_TLSK_ENT:
|
|
|
|
|
if (stats_tlskeys_list(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
2015-05-09 02:46:01 -04:00
|
|
|
#endif
|
2016-02-16 05:27:28 -05:00
|
|
|
case STAT_CLI_O_ENV: /* environment dump */
|
|
|
|
|
if (stats_dump_env_to_buffer(si))
|
|
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
|
|
|
|
break;
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
case STAT_CLI_O_CUSTOM: /* use custom pointer */
|
|
|
|
|
if (appctx->io_handler)
|
2016-11-12 04:51:33 -05:00
|
|
|
if (appctx->io_handler(appctx)) {
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
appctx->st0 = STAT_CLI_PROMPT;
|
2016-11-12 04:51:33 -05:00
|
|
|
if (appctx->io_release) {
|
|
|
|
|
appctx->io_release(appctx);
|
|
|
|
|
appctx->io_release = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
MEDIUM: cli: register CLI keywords with cli_register_kw()
To register a new cli keyword, you need to declare a cli_kw_list
structure in your source file:
static struct cli_kw_list cli_kws = {{ },{
{ { "test", "list", NULL }, "test list : do some tests on the cli", test_parsing, NULL },
{ { NULL }, NULL, NULL, NULL, NULL }
}};
And then register it:
cli_register_kw(&cli_kws);
The first field is an array of 5 elements, where you declare the
keywords combination which will match, it must be ended by a NULL
element.
The second field is used as a usage message, it will appear in the help
of the cli, you can set it to NULL if you don't want to show it, it's a
good idea if you want to overwrite some existing keywords.
The two last fields are callbacks.
The first one is used at parsing time, you can use it to parse the
arguments of your keywords and print small messages. The function must
return 1 in case of a failure, otherwise 0:
#include <proto/dumpstats.h>
static int test_parsing(char **args, struct appctx *appctx)
{
struct chunk out;
if (!*args[2]) {
appctx->ctx.cli.msg = "Error: the 3rd argument is mandatory !";
appctx->st0 = STAT_CLI_PRINT;
return 1;
}
chunk_reset(&trash);
chunk_printf(&trash, "arg[3]: %s\n", args[2]);
chunk_init(&out, NULL, 0);
chunk_dup(&out, &trash);
appctx->ctx.cli.err = out.str;
appctx->st0 = STAT_CLI_PRINT_FREE; /* print and free in the default cli_io_handler */
return 0;
}
The last field is the IO handler callback, it can be set to NULL if you
want to use the default cli_io_handler() otherwise you can write your
own. You can use the private pointer in the appctx if you need to store
a context or some data. stats_dump_sess_to_buffer() is a good example of
IO handler, IO handlers often use the appctx->st2 variable for the state
machine. The handler must return 0 in case it have to be recall later
otherwise 1.
2016-10-13 11:57:55 -04:00
|
|
|
break;
|
2009-10-04 08:22:18 -04:00
|
|
|
default: /* abnormal state */
|
2015-09-25 14:08:51 -04:00
|
|
|
si->flags |= SI_FL_ERR;
|
2009-09-22 13:31:03 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-04 08:22:18 -04:00
|
|
|
/* The post-command prompt is either LF alone or LF + '> ' in interactive mode */
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->st0 == STAT_CLI_PROMPT) {
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putstr(si_ic(si), appctx->st1 ? "\n> " : "\n") != -1)
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_GETREQ;
|
2015-03-13 09:00:47 -04:00
|
|
|
else
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2009-09-22 13:31:03 -04:00
|
|
|
}
|
2009-08-16 13:06:42 -04:00
|
|
|
|
2009-10-04 08:22:18 -04:00
|
|
|
/* If the output functions are still there, it means they require more room. */
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->st0 >= STAT_CLI_OUTPUT)
|
2009-09-22 13:31:03 -04:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
/* Now we close the output if one of the writers did so,
|
|
|
|
|
* or if we're not in interactive mode and the request
|
|
|
|
|
* buffer is empty. This still allows pipelined requests
|
|
|
|
|
* to be sent in non-interactive mode.
|
|
|
|
|
*/
|
2013-12-01 03:15:12 -05:00
|
|
|
if ((res->flags & (CF_SHUTW|CF_SHUTW_NOW)) || (!appctx->st1 && !req->buf->o)) {
|
|
|
|
|
appctx->st0 = STAT_CLI_END;
|
2009-09-22 13:31:03 -04:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-04 08:22:18 -04:00
|
|
|
/* switch state back to GETREQ to read next requests */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st0 = STAT_CLI_GETREQ;
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
2009-09-22 13:31:03 -04:00
|
|
|
}
|
2009-08-16 13:06:42 -04:00
|
|
|
|
2015-09-25 14:06:08 -04:00
|
|
|
if ((res->flags & CF_SHUTR) && (si->state == SI_ST_EST)) {
|
2009-09-22 13:31:03 -04:00
|
|
|
DPRINTF(stderr, "%s@%d: si to buf closed. req=%08x, res=%08x, st=%d\n",
|
|
|
|
|
__FUNCTION__, __LINE__, req->flags, res->flags, si->state);
|
2012-04-02 21:57:54 -04:00
|
|
|
/* Other side has closed, let's abort if we have no more processing to do
|
2009-09-22 13:31:03 -04:00
|
|
|
* and nothing more to consume. This is comparable to a broken pipe, so
|
|
|
|
|
* we forward the close to the request side so that it flows upstream to
|
|
|
|
|
* the client.
|
|
|
|
|
*/
|
2012-05-21 10:31:45 -04:00
|
|
|
si_shutw(si);
|
2009-09-22 13:31:03 -04:00
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if ((req->flags & CF_SHUTW) && (si->state == SI_ST_EST) && (appctx->st0 < STAT_CLI_OUTPUT)) {
|
2009-09-22 13:31:03 -04:00
|
|
|
DPRINTF(stderr, "%s@%d: buf to si closed. req=%08x, res=%08x, st=%d\n",
|
|
|
|
|
__FUNCTION__, __LINE__, req->flags, res->flags, si->state);
|
|
|
|
|
/* We have no more processing to do, and nothing more to send, and
|
|
|
|
|
* the client side has closed. So we'll forward this state downstream
|
|
|
|
|
* on the response buffer.
|
|
|
|
|
*/
|
2012-05-21 10:31:45 -04:00
|
|
|
si_shutr(si);
|
2012-08-27 17:14:58 -04:00
|
|
|
res->flags |= CF_READ_NULL;
|
2009-09-22 13:31:03 -04:00
|
|
|
}
|
|
|
|
|
|
2015-04-19 11:20:03 -04:00
|
|
|
out:
|
2012-03-01 12:19:58 -05:00
|
|
|
DPRINTF(stderr, "%s@%d: st=%d, rqf=%x, rpf=%x, rqh=%d, rqs=%d, rh=%d, rs=%d\n",
|
2009-09-22 13:31:03 -04:00
|
|
|
__FUNCTION__, __LINE__,
|
2012-10-12 17:49:43 -04:00
|
|
|
si->state, req->flags, res->flags, req->buf->i, req->buf->o, res->buf->i, res->buf->o);
|
2009-08-16 13:06:42 -04:00
|
|
|
}
|
|
|
|
|
|
2016-01-11 12:27:29 -05:00
|
|
|
/* Dump all fields from <info> into <out> using the "show info" format (name: value) */
|
|
|
|
|
static int stats_dump_info_fields(struct chunk *out, const struct field *info)
|
|
|
|
|
{
|
|
|
|
|
int field;
|
|
|
|
|
|
|
|
|
|
for (field = 0; field < INF_TOTAL_FIELDS; field++) {
|
|
|
|
|
if (!field_format(info, field))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!chunk_appendf(out, "%s: ", info_field_names[field]))
|
|
|
|
|
return 0;
|
|
|
|
|
if (!stats_emit_raw_data_field(out, &info[field]))
|
|
|
|
|
return 0;
|
|
|
|
|
if (!chunk_strcat(out, "\n"))
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-11 14:08:42 -05:00
|
|
|
/* Dump all fields from <info> into <out> using the "show info typed" format */
|
|
|
|
|
static int stats_dump_typed_info_fields(struct chunk *out, const struct field *info)
|
|
|
|
|
{
|
|
|
|
|
int field;
|
|
|
|
|
|
|
|
|
|
for (field = 0; field < INF_TOTAL_FIELDS; field++) {
|
|
|
|
|
if (!field_format(info, field))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (!chunk_appendf(out, "%d.%s.%u:", field, info_field_names[field], info[INF_PROCESS_NUM].u.u32))
|
|
|
|
|
return 0;
|
|
|
|
|
if (!stats_emit_field_tags(out, &info[field], ':'))
|
|
|
|
|
return 0;
|
|
|
|
|
if (!stats_emit_typed_data_field(out, &info[field]))
|
|
|
|
|
return 0;
|
|
|
|
|
if (!chunk_strcat(out, "\n"))
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-25 03:19:23 -04:00
|
|
|
/* Fill <info> with HAProxy global info. <info> is preallocated
|
|
|
|
|
* array of length <len>. The length of the aray must be
|
|
|
|
|
* INF_TOTAL_FIELDS. If this length is less then this value, the
|
|
|
|
|
* function returns 0, otherwise, it returns 1.
|
2012-12-22 14:31:10 -05:00
|
|
|
*/
|
2016-03-25 03:19:23 -04:00
|
|
|
int stats_fill_info(struct field *info, int len)
|
2012-12-22 14:31:10 -05:00
|
|
|
{
|
|
|
|
|
unsigned int up = (now.tv_sec - start_date.tv_sec);
|
2016-01-11 12:17:07 -05:00
|
|
|
struct chunk *out = get_trash_chunk();
|
2012-12-22 14:31:10 -05:00
|
|
|
|
2014-05-28 06:28:58 -04:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
int ssl_sess_rate = read_freq_ctr(&global.ssl_per_sec);
|
|
|
|
|
int ssl_key_rate = read_freq_ctr(&global.ssl_fe_keys_per_sec);
|
|
|
|
|
int ssl_reuse = 0;
|
|
|
|
|
|
|
|
|
|
if (ssl_key_rate < ssl_sess_rate) {
|
|
|
|
|
/* count the ssl reuse ratio and avoid overflows in both directions */
|
|
|
|
|
ssl_reuse = 100 - (100 * ssl_key_rate + (ssl_sess_rate - 1) / 2) / ssl_sess_rate;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2016-03-25 03:19:23 -04:00
|
|
|
if (len < INF_TOTAL_FIELDS)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2016-01-11 12:17:07 -05:00
|
|
|
chunk_reset(out);
|
2016-03-25 03:19:23 -04:00
|
|
|
memset(info, 0, sizeof(*info) * len);
|
2016-01-11 12:17:07 -05:00
|
|
|
|
|
|
|
|
info[INF_NAME] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, PRODUCT_NAME);
|
|
|
|
|
info[INF_VERSION] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, HAPROXY_VERSION);
|
|
|
|
|
info[INF_RELEASE_DATE] = mkf_str(FO_PRODUCT|FN_OUTPUT|FS_SERVICE, HAPROXY_DATE);
|
|
|
|
|
|
|
|
|
|
info[INF_NBPROC] = mkf_u32(FO_CONFIG|FS_SERVICE, global.nbproc);
|
|
|
|
|
info[INF_PROCESS_NUM] = mkf_u32(FO_KEY, relative_pid);
|
|
|
|
|
info[INF_PID] = mkf_u32(FO_STATUS, pid);
|
|
|
|
|
|
|
|
|
|
info[INF_UPTIME] = mkf_str(FN_DURATION, chunk_newstr(out));
|
|
|
|
|
chunk_appendf(out, "%ud %uh%02um%02us", up / 86400, (up % 86400) / 3600, (up % 3600) / 60, (up % 60));
|
|
|
|
|
|
|
|
|
|
info[INF_UPTIME_SEC] = mkf_u32(FN_DURATION, up);
|
|
|
|
|
info[INF_MEMMAX_MB] = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_memmax);
|
|
|
|
|
info[INF_POOL_ALLOC_MB] = mkf_u32(0, (unsigned)(pool_total_allocated() / 1048576L));
|
|
|
|
|
info[INF_POOL_USED_MB] = mkf_u32(0, (unsigned)(pool_total_used() / 1048576L));
|
|
|
|
|
info[INF_POOL_FAILED] = mkf_u32(FN_COUNTER, pool_total_failures());
|
|
|
|
|
info[INF_ULIMIT_N] = mkf_u32(FO_CONFIG|FN_LIMIT, global.rlimit_nofile);
|
|
|
|
|
info[INF_MAXSOCK] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxsock);
|
|
|
|
|
info[INF_MAXCONN] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxconn);
|
|
|
|
|
info[INF_HARD_MAXCONN] = mkf_u32(FO_CONFIG|FN_LIMIT, global.hardmaxconn);
|
|
|
|
|
info[INF_CURR_CONN] = mkf_u32(0, actconn);
|
|
|
|
|
info[INF_CUM_CONN] = mkf_u32(FN_COUNTER, totalconn);
|
|
|
|
|
info[INF_CUM_REQ] = mkf_u32(FN_COUNTER, global.req_count);
|
|
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
info[INF_MAX_SSL_CONNS] = mkf_u32(FN_MAX, global.maxsslconn);
|
|
|
|
|
info[INF_CURR_SSL_CONNS] = mkf_u32(0, sslconns);
|
|
|
|
|
info[INF_CUM_SSL_CONNS] = mkf_u32(FN_COUNTER, totalsslconns);
|
|
|
|
|
#endif
|
|
|
|
|
info[INF_MAXPIPES] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxpipes);
|
|
|
|
|
info[INF_PIPES_USED] = mkf_u32(0, pipes_used);
|
|
|
|
|
info[INF_PIPES_FREE] = mkf_u32(0, pipes_free);
|
|
|
|
|
info[INF_CONN_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.conn_per_sec));
|
|
|
|
|
info[INF_CONN_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.cps_lim);
|
|
|
|
|
info[INF_MAX_CONN_RATE] = mkf_u32(FN_MAX, global.cps_max);
|
|
|
|
|
info[INF_SESS_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.sess_per_sec));
|
|
|
|
|
info[INF_SESS_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.sps_lim);
|
|
|
|
|
info[INF_MAX_SESS_RATE] = mkf_u32(FN_RATE, global.sps_max);
|
|
|
|
|
|
|
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
info[INF_SSL_RATE] = mkf_u32(FN_RATE, ssl_sess_rate);
|
|
|
|
|
info[INF_SSL_RATE_LIMIT] = mkf_u32(FO_CONFIG|FN_LIMIT, global.ssl_lim);
|
|
|
|
|
info[INF_MAX_SSL_RATE] = mkf_u32(FN_MAX, global.ssl_max);
|
|
|
|
|
info[INF_SSL_FRONTEND_KEY_RATE] = mkf_u32(0, ssl_key_rate);
|
|
|
|
|
info[INF_SSL_FRONTEND_MAX_KEY_RATE] = mkf_u32(FN_MAX, global.ssl_fe_keys_max);
|
|
|
|
|
info[INF_SSL_FRONTEND_SESSION_REUSE_PCT] = mkf_u32(0, ssl_reuse);
|
|
|
|
|
info[INF_SSL_BACKEND_KEY_RATE] = mkf_u32(FN_RATE, read_freq_ctr(&global.ssl_be_keys_per_sec));
|
|
|
|
|
info[INF_SSL_BACKEND_MAX_KEY_RATE] = mkf_u32(FN_MAX, global.ssl_be_keys_max);
|
|
|
|
|
info[INF_SSL_CACHE_LOOKUPS] = mkf_u32(FN_COUNTER, global.shctx_lookups);
|
|
|
|
|
info[INF_SSL_CACHE_MISSES] = mkf_u32(FN_COUNTER, global.shctx_misses);
|
|
|
|
|
#endif
|
|
|
|
|
info[INF_COMPRESS_BPS_IN] = mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_in));
|
|
|
|
|
info[INF_COMPRESS_BPS_OUT] = mkf_u32(FN_RATE, read_freq_ctr(&global.comp_bps_out));
|
|
|
|
|
info[INF_COMPRESS_BPS_RATE_LIM] = mkf_u32(FO_CONFIG|FN_LIMIT, global.comp_rate_lim);
|
|
|
|
|
#ifdef USE_ZLIB
|
|
|
|
|
info[INF_ZLIB_MEM_USAGE] = mkf_u32(0, zlib_used_memory);
|
|
|
|
|
info[INF_MAX_ZLIB_MEM_USAGE] = mkf_u32(FO_CONFIG|FN_LIMIT, global.maxzlibmem);
|
|
|
|
|
#endif
|
|
|
|
|
info[INF_TASKS] = mkf_u32(0, nb_tasks_cur);
|
|
|
|
|
info[INF_RUN_QUEUE] = mkf_u32(0, run_queue_cur);
|
|
|
|
|
info[INF_IDLE_PCT] = mkf_u32(FN_AVG, idle_pct);
|
|
|
|
|
info[INF_NODE] = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.node);
|
|
|
|
|
if (global.desc)
|
|
|
|
|
info[INF_DESCRIPTION] = mkf_str(FO_CONFIG|FN_OUTPUT|FS_SERVICE, global.desc);
|
|
|
|
|
|
2016-03-25 03:19:23 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function dumps information onto the stream interface's read buffer.
|
|
|
|
|
* It returns 0 as long as it does not complete, non-zero upon completion.
|
|
|
|
|
* No state is used.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_info_to_buffer(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
|
|
|
|
if (!stats_fill_info(info, INF_TOTAL_FIELDS))
|
|
|
|
|
return 0;
|
|
|
|
|
|
2016-01-11 12:29:04 -05:00
|
|
|
chunk_reset(&trash);
|
2016-01-11 14:08:42 -05:00
|
|
|
|
|
|
|
|
if (appctx->ctx.stats.flags & STAT_FMT_TYPED)
|
|
|
|
|
stats_dump_typed_info_fields(&trash, info);
|
|
|
|
|
else
|
|
|
|
|
stats_dump_info_fields(&trash, info);
|
2012-12-22 14:31:10 -05:00
|
|
|
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2012-12-22 14:31:10 -05:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2012-12-22 14:31:10 -05:00
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-03 02:01:20 -04:00
|
|
|
/* dumps server state information into <buf> for all the servers found in <backend>
|
|
|
|
|
* These information are all the parameters which may change during HAProxy runtime.
|
|
|
|
|
* By default, we only export to the last known server state file format.
|
|
|
|
|
* These information can be used at next startup to recover same level of server state.
|
|
|
|
|
*/
|
2016-05-06 06:18:48 -04:00
|
|
|
static int dump_servers_state(struct stream_interface *si, struct chunk *buf)
|
2015-07-03 02:01:20 -04:00
|
|
|
{
|
2016-05-06 06:18:48 -04:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2015-07-03 02:01:20 -04:00
|
|
|
struct server *srv;
|
|
|
|
|
char srv_addr[INET6_ADDRSTRLEN + 1];
|
|
|
|
|
time_t srv_time_since_last_change;
|
|
|
|
|
int bk_f_forced_id, srv_f_forced_id;
|
|
|
|
|
|
|
|
|
|
|
2016-05-06 06:18:48 -04:00
|
|
|
/* we don't want to report any state if the backend is not enabled on this process */
|
|
|
|
|
if (appctx->ctx.server_state.px->bind_proc && !(appctx->ctx.server_state.px->bind_proc & (1UL << (relative_pid - 1))))
|
|
|
|
|
return 1;
|
2015-07-03 02:01:20 -04:00
|
|
|
|
2016-05-26 18:06:45 -04:00
|
|
|
if (!appctx->ctx.server_state.sv)
|
|
|
|
|
appctx->ctx.server_state.sv = appctx->ctx.server_state.px->srv;
|
|
|
|
|
|
2016-05-06 06:18:48 -04:00
|
|
|
for (; appctx->ctx.server_state.sv != NULL; appctx->ctx.server_state.sv = srv->next) {
|
|
|
|
|
srv = appctx->ctx.server_state.sv;
|
2015-07-03 02:01:20 -04:00
|
|
|
srv_addr[0] = '\0';
|
|
|
|
|
|
|
|
|
|
switch (srv->addr.ss_family) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
inet_ntop(srv->addr.ss_family, &((struct sockaddr_in *)&srv->addr)->sin_addr,
|
|
|
|
|
srv_addr, INET_ADDRSTRLEN + 1);
|
|
|
|
|
break;
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
inet_ntop(srv->addr.ss_family, &((struct sockaddr_in6 *)&srv->addr)->sin6_addr,
|
|
|
|
|
srv_addr, INET6_ADDRSTRLEN + 1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
srv_time_since_last_change = now.tv_sec - srv->last_change;
|
2016-05-06 06:18:48 -04:00
|
|
|
bk_f_forced_id = appctx->ctx.server_state.px->options & PR_O_FORCED_ID ? 1 : 0;
|
2015-07-03 02:01:20 -04:00
|
|
|
srv_f_forced_id = srv->flags & SRV_F_FORCED_ID ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
chunk_appendf(buf,
|
|
|
|
|
"%d %s "
|
|
|
|
|
"%d %s %s "
|
|
|
|
|
"%d %d %d %d %ld "
|
|
|
|
|
"%d %d %d %d %d "
|
|
|
|
|
"%d %d"
|
|
|
|
|
"\n",
|
2016-05-06 06:18:48 -04:00
|
|
|
appctx->ctx.server_state.px->uuid, appctx->ctx.server_state.px->id,
|
2015-07-03 02:01:20 -04:00
|
|
|
srv->puid, srv->id, srv_addr,
|
2015-11-18 01:10:22 -05:00
|
|
|
srv->state, srv->admin, srv->uweight, srv->iweight, (long int)srv_time_since_last_change,
|
2015-07-03 02:01:20 -04:00
|
|
|
srv->check.status, srv->check.result, srv->check.health, srv->check.state, srv->agent.state,
|
|
|
|
|
bk_f_forced_id, srv_f_forced_id);
|
2016-05-06 06:18:48 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2015-07-03 02:01:20 -04:00
|
|
|
}
|
2016-05-06 06:18:48 -04:00
|
|
|
return 1;
|
2015-07-03 02:01:20 -04:00
|
|
|
}
|
|
|
|
|
|
2015-09-18 08:49:12 -04:00
|
|
|
/* Parses backend list and simply report backend names */
|
|
|
|
|
static int stats_dump_backend_to_buffer(struct stream_interface *si)
|
|
|
|
|
{
|
2016-05-06 06:18:49 -04:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2015-09-18 08:49:12 -04:00
|
|
|
extern struct proxy *proxy;
|
|
|
|
|
struct proxy *curproxy;
|
|
|
|
|
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
2016-05-06 06:18:49 -04:00
|
|
|
if (!appctx->ctx.be.px) {
|
|
|
|
|
chunk_printf(&trash, "# name\n");
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
appctx->ctx.be.px = proxy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (; appctx->ctx.be.px != NULL; appctx->ctx.be.px = curproxy->next) {
|
|
|
|
|
curproxy = appctx->ctx.be.px;
|
|
|
|
|
|
2015-09-18 08:49:12 -04:00
|
|
|
/* looking for backends only */
|
|
|
|
|
if (!(curproxy->cap & PR_CAP_BE))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* we don't want to list a backend which is bound to this process */
|
|
|
|
|
if (curproxy->bind_proc && !(curproxy->bind_proc & (1UL << (relative_pid - 1))))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
chunk_appendf(&trash, "%s\n", curproxy->id);
|
2016-05-06 06:18:49 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2015-09-18 08:49:12 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-03 02:01:20 -04:00
|
|
|
/* Parses backend list or simply use backend name provided by the user to return
|
|
|
|
|
* states of servers to stdout.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_servers_state_to_buffer(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
extern struct proxy *proxy;
|
|
|
|
|
struct proxy *curproxy;
|
|
|
|
|
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
2016-06-22 08:51:40 -04:00
|
|
|
if (appctx->st2 == STAT_ST_INIT) {
|
|
|
|
|
if (!appctx->ctx.server_state.px)
|
|
|
|
|
appctx->ctx.server_state.px = proxy;
|
|
|
|
|
appctx->st2 = STAT_ST_HEAD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (appctx->st2 == STAT_ST_HEAD) {
|
2016-05-06 06:18:48 -04:00
|
|
|
chunk_printf(&trash, "%d\n# %s\n", SRV_STATE_FILE_VERSION, SRV_STATE_FILE_FIELD_NAMES);
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
2015-07-03 02:01:20 -04:00
|
|
|
}
|
2016-06-22 08:51:40 -04:00
|
|
|
appctx->st2 = STAT_ST_INFO;
|
2015-07-03 02:01:20 -04:00
|
|
|
}
|
|
|
|
|
|
2016-06-22 08:51:40 -04:00
|
|
|
/* STAT_ST_INFO */
|
2016-05-06 06:18:48 -04:00
|
|
|
for (; appctx->ctx.server_state.px != NULL; appctx->ctx.server_state.px = curproxy->next) {
|
|
|
|
|
curproxy = appctx->ctx.server_state.px;
|
|
|
|
|
/* servers are only in backends */
|
|
|
|
|
if (curproxy->cap & PR_CAP_BE) {
|
|
|
|
|
if (!dump_servers_state(si, &trash))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* only the selected proxy is dumped */
|
|
|
|
|
if (appctx->ctx.server_state.iid)
|
|
|
|
|
break;
|
2015-07-03 02:01:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 10:49:56 -05:00
|
|
|
/* This function dumps memory usage information onto the stream interface's
|
|
|
|
|
* read buffer. It returns 0 as long as it does not complete, non-zero upon
|
|
|
|
|
* completion. No state is used.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_pools_to_buffer(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
dump_pools_to_trash();
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2014-01-28 10:49:56 -05:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2014-01-28 10:49:56 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
static inline const char *get_conn_ctrl_name(const struct connection *conn)
|
2016-01-04 13:04:18 -05:00
|
|
|
{
|
2016-11-21 11:18:36 -05:00
|
|
|
if (!conn_ctrl_ready(conn))
|
|
|
|
|
return "NONE";
|
|
|
|
|
return conn->ctrl->name;
|
2016-01-04 13:04:18 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
static inline const char *get_conn_xprt_name(const struct connection *conn)
|
2016-01-11 12:57:53 -05:00
|
|
|
{
|
2016-11-21 11:18:36 -05:00
|
|
|
static char ptr[19];
|
2016-01-11 12:57:53 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (!conn_xprt_ready(conn))
|
|
|
|
|
return "NONE";
|
2016-01-11 12:57:53 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (conn->xprt == &raw_sock)
|
|
|
|
|
return "RAW";
|
2016-01-11 12:57:53 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
#ifdef USE_OPENSSL
|
|
|
|
|
if (conn->xprt == &ssl_sock)
|
|
|
|
|
return "SSL";
|
|
|
|
|
#endif
|
|
|
|
|
snprintf(ptr, sizeof(ptr), "%p", conn->xprt);
|
|
|
|
|
return ptr;
|
2016-01-11 12:57:53 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
static inline const char *get_conn_data_name(const struct connection *conn)
|
2010-10-11 18:14:35 -04:00
|
|
|
{
|
2016-11-21 11:18:36 -05:00
|
|
|
static char ptr[19];
|
2010-10-11 18:14:35 -04:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (!conn->data)
|
|
|
|
|
return "NONE";
|
2012-12-22 14:31:10 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (conn->data == &sess_conn_cb)
|
|
|
|
|
return "SESS";
|
2016-01-08 11:20:51 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (conn->data == &si_conn_cb)
|
|
|
|
|
return "STRM";
|
2016-01-08 11:20:51 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (conn->data == &check_conn_cb)
|
|
|
|
|
return "CHCK";
|
2016-01-08 11:15:39 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
snprintf(ptr, sizeof(ptr), "%p", conn->data);
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
2016-01-08 11:15:39 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
/* This function dumps a complete stream state onto the stream interface's
|
|
|
|
|
* read buffer. The stream has to be set in sess->target. It returns
|
|
|
|
|
* 0 if the output buffer is full and it needs to be called again, otherwise
|
|
|
|
|
* non-zero. It is designed to be called from stats_dump_sess_to_buffer() below.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess)
|
|
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
struct tm tm;
|
|
|
|
|
extern const char *monthname[12];
|
|
|
|
|
char pn[INET6_ADDRSTRLEN];
|
|
|
|
|
struct connection *conn;
|
|
|
|
|
struct appctx *tmpctx;
|
2010-10-11 18:14:35 -04:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
chunk_reset(&trash);
|
2013-12-01 03:15:12 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (appctx->ctx.sess.section > 0 && appctx->ctx.sess.uid != sess->uniq_id) {
|
|
|
|
|
/* stream changed, no need to go any further */
|
|
|
|
|
chunk_appendf(&trash, " *** session terminated while we were watching it ***\n");
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
2016-01-08 10:59:56 -05:00
|
|
|
}
|
2016-11-21 11:18:36 -05:00
|
|
|
appctx->ctx.sess.uid = 0;
|
|
|
|
|
appctx->ctx.sess.section = 0;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2016-01-08 10:59:56 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
switch (appctx->ctx.sess.section) {
|
|
|
|
|
case 0: /* main status of the stream */
|
|
|
|
|
appctx->ctx.sess.uid = sess->uniq_id;
|
|
|
|
|
appctx->ctx.sess.section = 1;
|
|
|
|
|
/* fall through */
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
case 1:
|
|
|
|
|
get_localtime(sess->logs.accept_date.tv_sec, &tm);
|
|
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
"%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s",
|
|
|
|
|
sess,
|
|
|
|
|
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
|
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec),
|
|
|
|
|
sess->uniq_id,
|
|
|
|
|
strm_li(sess) ? strm_li(sess)->proto->name : "?");
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
conn = objt_conn(strm_orig(sess));
|
|
|
|
|
switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
chunk_appendf(&trash, " source=%s:%d\n",
|
|
|
|
|
pn, get_host_port(&conn->addr.from));
|
|
|
|
|
break;
|
|
|
|
|
case AF_UNIX:
|
|
|
|
|
chunk_appendf(&trash, " source=unix:%d\n", strm_li(sess)->luid);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* no more information to print right now */
|
|
|
|
|
chunk_appendf(&trash, "\n");
|
|
|
|
|
break;
|
2016-01-08 11:23:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n",
|
|
|
|
|
sess->flags, sess->si[1].conn_retries, sess->srv_conn, sess->pend_pos);
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" frontend=%s (id=%u mode=%s), listener=%s (id=%u)",
|
|
|
|
|
strm_fe(sess)->id, strm_fe(sess)->uuid, strm_fe(sess)->mode ? "http" : "tcp",
|
|
|
|
|
strm_li(sess) ? strm_li(sess)->name ? strm_li(sess)->name : "?" : "?",
|
|
|
|
|
strm_li(sess) ? strm_li(sess)->luid : 0);
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (conn)
|
|
|
|
|
conn_get_to_addr(conn);
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
chunk_appendf(&trash, " addr=%s:%d\n",
|
|
|
|
|
pn, get_host_port(&conn->addr.to));
|
|
|
|
|
break;
|
|
|
|
|
case AF_UNIX:
|
|
|
|
|
chunk_appendf(&trash, " addr=unix:%d\n", strm_li(sess)->luid);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* no more information to print right now */
|
|
|
|
|
chunk_appendf(&trash, "\n");
|
|
|
|
|
break;
|
2016-01-08 11:23:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (sess->be->cap & PR_CAP_BE)
|
|
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" backend=%s (id=%u mode=%s)",
|
|
|
|
|
sess->be->id,
|
|
|
|
|
sess->be->uuid, sess->be->mode ? "http" : "tcp");
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, " backend=<NONE> (id=-1 mode=-)");
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
conn = objt_conn(sess->si[1].end);
|
|
|
|
|
if (conn)
|
|
|
|
|
conn_get_from_addr(conn);
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
chunk_appendf(&trash, " addr=%s:%d\n",
|
|
|
|
|
pn, get_host_port(&conn->addr.from));
|
|
|
|
|
break;
|
|
|
|
|
case AF_UNIX:
|
|
|
|
|
chunk_appendf(&trash, " addr=unix\n");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* no more information to print right now */
|
|
|
|
|
chunk_appendf(&trash, "\n");
|
|
|
|
|
break;
|
2016-01-08 11:23:28 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
if (sess->be->cap & PR_CAP_BE)
|
|
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" server=%s (id=%u)",
|
|
|
|
|
objt_server(sess->target) ? objt_server(sess->target)->id : "<none>",
|
|
|
|
|
objt_server(sess->target) ? objt_server(sess->target)->puid : 0);
|
2016-01-08 11:23:28 -05:00
|
|
|
else
|
2016-11-21 11:18:36 -05:00
|
|
|
chunk_appendf(&trash, " server=<NONE> (id=-1)");
|
|
|
|
|
|
|
|
|
|
if (conn)
|
|
|
|
|
conn_get_to_addr(conn);
|
2016-01-08 11:25:50 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
chunk_appendf(&trash, " addr=%s:%d\n",
|
|
|
|
|
pn, get_host_port(&conn->addr.to));
|
|
|
|
|
break;
|
|
|
|
|
case AF_UNIX:
|
|
|
|
|
chunk_appendf(&trash, " addr=unix\n");
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
/* no more information to print right now */
|
|
|
|
|
chunk_appendf(&trash, "\n");
|
|
|
|
|
break;
|
2016-01-08 11:25:50 -05:00
|
|
|
}
|
|
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" task=%p (state=0x%02x nice=%d calls=%d exp=%s%s",
|
|
|
|
|
sess->task,
|
|
|
|
|
sess->task->state,
|
|
|
|
|
sess->task->nice, sess->task->calls,
|
|
|
|
|
sess->task->expire ?
|
|
|
|
|
tick_is_expired(sess->task->expire, now_ms) ? "<PAST>" :
|
|
|
|
|
human_time(TICKS_TO_MS(sess->task->expire - now_ms),
|
|
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
|
|
|
|
task_in_rq(sess->task) ? ", running" : "");
|
2016-01-08 11:23:28 -05:00
|
|
|
|
2016-11-21 11:18:36 -05:00
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" age=%s)\n",
|
|
|
|
|
human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1));
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2015-04-03 17:46:31 -04:00
|
|
|
if (sess->txn)
|
|
|
|
|
chunk_appendf(&trash,
|
2014-11-26 12:05:38 -05:00
|
|
|
" txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s waiting=%d\n",
|
2015-04-03 17:46:31 -04:00
|
|
|
sess->txn, sess->txn->flags, sess->txn->meth, sess->txn->status,
|
|
|
|
|
http_msg_state_str(sess->txn->req.msg_state), http_msg_state_str(sess->txn->rsp.msg_state), !LIST_ISEMPTY(&sess->buffer_wait));
|
2012-12-08 11:48:47 -05:00
|
|
|
|
|
|
|
|
chunk_appendf(&trash,
|
2013-10-11 13:34:20 -04:00
|
|
|
" si[0]=%p (state=%s flags=0x%02x endp0=%s:%p exp=%s, et=0x%03x)\n",
|
2010-03-05 11:53:32 -05:00
|
|
|
&sess->si[0],
|
2012-12-08 11:48:47 -05:00
|
|
|
si_state_str(sess->si[0].state),
|
2010-03-05 11:53:32 -05:00
|
|
|
sess->si[0].flags,
|
2013-09-29 11:19:56 -04:00
|
|
|
obj_type_name(sess->si[0].end),
|
|
|
|
|
obj_base_ptr(sess->si[0].end),
|
2010-03-05 11:53:32 -05:00
|
|
|
sess->si[0].exp ?
|
|
|
|
|
tick_is_expired(sess->si[0].exp, now_ms) ? "<PAST>" :
|
|
|
|
|
human_time(TICKS_TO_MS(sess->si[0].exp - now_ms),
|
|
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
|
|
|
|
sess->si[0].err_type);
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2013-10-11 13:34:20 -04:00
|
|
|
" si[1]=%p (state=%s flags=0x%02x endp1=%s:%p exp=%s, et=0x%03x)\n",
|
2010-03-05 11:53:32 -05:00
|
|
|
&sess->si[1],
|
2012-12-08 11:48:47 -05:00
|
|
|
si_state_str(sess->si[1].state),
|
2010-03-05 11:53:32 -05:00
|
|
|
sess->si[1].flags,
|
2013-09-29 11:19:56 -04:00
|
|
|
obj_type_name(sess->si[1].end),
|
|
|
|
|
obj_base_ptr(sess->si[1].end),
|
2010-03-05 11:53:32 -05:00
|
|
|
sess->si[1].exp ?
|
|
|
|
|
tick_is_expired(sess->si[1].exp, now_ms) ? "<PAST>" :
|
|
|
|
|
human_time(TICKS_TO_MS(sess->si[1].exp - now_ms),
|
|
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
|
|
|
|
sess->si[1].err_type);
|
|
|
|
|
|
2013-12-01 14:45:00 -05:00
|
|
|
if ((conn = objt_conn(sess->si[0].end)) != NULL) {
|
2013-09-29 11:19:56 -04:00
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" co0=%p ctrl=%s xprt=%s data=%s target=%s:%p\n",
|
2013-10-01 04:45:07 -04:00
|
|
|
conn,
|
|
|
|
|
get_conn_ctrl_name(conn),
|
|
|
|
|
get_conn_xprt_name(conn),
|
|
|
|
|
get_conn_data_name(conn),
|
|
|
|
|
obj_type_name(conn->target),
|
|
|
|
|
obj_base_ptr(conn->target));
|
2012-11-25 21:04:41 -05:00
|
|
|
|
2016-11-17 08:22:52 -05:00
|
|
|
chunk_appendf(&trash, " flags=0x%08x", conn->flags);
|
|
|
|
|
|
|
|
|
|
if (conn->t.sock.fd >= 0) {
|
|
|
|
|
chunk_appendf(&trash, " fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
|
|
|
|
|
conn->t.sock.fd, fdtab[conn->t.sock.fd].state,
|
|
|
|
|
fdtab[conn->t.sock.fd].cache, fdtab[conn->t.sock.fd].updated);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, " fd=<dead>\n");
|
2013-09-29 11:19:56 -04:00
|
|
|
}
|
2013-12-01 14:45:00 -05:00
|
|
|
else if ((tmpctx = objt_appctx(sess->si[0].end)) != NULL) {
|
|
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" app0=%p st0=%d st1=%d st2=%d applet=%s\n",
|
|
|
|
|
tmpctx,
|
|
|
|
|
tmpctx->st0,
|
|
|
|
|
tmpctx->st1,
|
|
|
|
|
tmpctx->st2,
|
|
|
|
|
tmpctx->applet->name);
|
|
|
|
|
}
|
2012-11-19 10:10:32 -05:00
|
|
|
|
2013-12-01 14:45:00 -05:00
|
|
|
if ((conn = objt_conn(sess->si[1].end)) != NULL) {
|
2013-09-29 11:19:56 -04:00
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" co1=%p ctrl=%s xprt=%s data=%s target=%s:%p\n",
|
2013-10-01 04:45:07 -04:00
|
|
|
conn,
|
|
|
|
|
get_conn_ctrl_name(conn),
|
|
|
|
|
get_conn_xprt_name(conn),
|
|
|
|
|
get_conn_data_name(conn),
|
|
|
|
|
obj_type_name(conn->target),
|
|
|
|
|
obj_base_ptr(conn->target));
|
2012-11-25 21:04:41 -05:00
|
|
|
|
2016-11-17 08:22:52 -05:00
|
|
|
chunk_appendf(&trash, " flags=0x%08x", conn->flags);
|
|
|
|
|
|
|
|
|
|
if (conn->t.sock.fd >= 0) {
|
|
|
|
|
chunk_appendf(&trash, " fd=%d fd.state=%02x fd.cache=%d updt=%d\n",
|
|
|
|
|
conn->t.sock.fd, fdtab[conn->t.sock.fd].state,
|
|
|
|
|
fdtab[conn->t.sock.fd].cache, fdtab[conn->t.sock.fd].updated);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, " fd=<dead>\n");
|
2013-09-29 11:19:56 -04:00
|
|
|
}
|
2013-12-01 14:45:00 -05:00
|
|
|
else if ((tmpctx = objt_appctx(sess->si[1].end)) != NULL) {
|
|
|
|
|
chunk_appendf(&trash,
|
|
|
|
|
" app1=%p st0=%d st1=%d st2=%d applet=%s\n",
|
|
|
|
|
tmpctx,
|
|
|
|
|
tmpctx->st0,
|
|
|
|
|
tmpctx->st1,
|
|
|
|
|
tmpctx->st2,
|
|
|
|
|
tmpctx->applet->name);
|
|
|
|
|
}
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2012-11-25 21:04:41 -05:00
|
|
|
" req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
|
2010-03-05 11:53:32 -05:00
|
|
|
" an_exp=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
&sess->req,
|
|
|
|
|
sess->req.flags, sess->req.analysers,
|
|
|
|
|
sess->req.pipe ? sess->req.pipe->data : 0,
|
|
|
|
|
sess->req.to_forward, sess->req.total,
|
|
|
|
|
sess->req.analyse_exp ?
|
|
|
|
|
human_time(TICKS_TO_MS(sess->req.analyse_exp - now_ms),
|
2010-03-05 11:53:32 -05:00
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2010-03-05 11:53:32 -05:00
|
|
|
" rex=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->req.rex ?
|
|
|
|
|
human_time(TICKS_TO_MS(sess->req.rex - now_ms),
|
2010-03-05 11:53:32 -05:00
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2010-03-05 11:53:32 -05:00
|
|
|
" wex=%s\n"
|
2012-11-25 21:04:41 -05:00
|
|
|
" buf=%p data=%p o=%d p=%d req.next=%d i=%d size=%d\n",
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->req.wex ?
|
|
|
|
|
human_time(TICKS_TO_MS(sess->req.wex - now_ms),
|
2010-03-05 11:53:32 -05:00
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->req.buf,
|
|
|
|
|
sess->req.buf->data, sess->req.buf->o,
|
|
|
|
|
(int)(sess->req.buf->p - sess->req.buf->data),
|
2015-04-03 17:46:31 -04:00
|
|
|
sess->txn ? sess->txn->req.next : 0, sess->req.buf->i,
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->req.buf->size);
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2012-11-25 21:04:41 -05:00
|
|
|
" res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n"
|
2010-03-05 11:53:32 -05:00
|
|
|
" an_exp=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
&sess->res,
|
|
|
|
|
sess->res.flags, sess->res.analysers,
|
|
|
|
|
sess->res.pipe ? sess->res.pipe->data : 0,
|
|
|
|
|
sess->res.to_forward, sess->res.total,
|
|
|
|
|
sess->res.analyse_exp ?
|
|
|
|
|
human_time(TICKS_TO_MS(sess->res.analyse_exp - now_ms),
|
2010-03-05 11:53:32 -05:00
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2010-03-05 11:53:32 -05:00
|
|
|
" rex=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->res.rex ?
|
|
|
|
|
human_time(TICKS_TO_MS(sess->res.rex - now_ms),
|
2010-03-05 11:53:32 -05:00
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2010-03-05 11:53:32 -05:00
|
|
|
" wex=%s\n"
|
2012-11-25 21:04:41 -05:00
|
|
|
" buf=%p data=%p o=%d p=%d rsp.next=%d i=%d size=%d\n",
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->res.wex ?
|
|
|
|
|
human_time(TICKS_TO_MS(sess->res.wex - now_ms),
|
2010-03-05 11:53:32 -05:00
|
|
|
TICKS_TO_MS(1000)) : "<NEVER>",
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->res.buf,
|
|
|
|
|
sess->res.buf->data, sess->res.buf->o,
|
|
|
|
|
(int)(sess->res.buf->p - sess->res.buf->data),
|
2015-04-03 17:46:31 -04:00
|
|
|
sess->txn ? sess->txn->rsp.next : 0, sess->res.buf->i,
|
2014-11-27 14:45:39 -05:00
|
|
|
sess->res.buf->size);
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2010-03-05 11:53:32 -05:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2010-03-05 11:53:32 -05:00
|
|
|
|
|
|
|
|
/* use other states to dump the contents */
|
|
|
|
|
}
|
|
|
|
|
/* end of dump */
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.uid = 0;
|
|
|
|
|
appctx->ctx.sess.section = 0;
|
2010-03-05 11:53:32 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
#if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0)
|
|
|
|
|
static int stats_tlskeys_list(struct stream_interface *si) {
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
|
|
|
|
switch (appctx->st2) {
|
|
|
|
|
case STAT_ST_INIT:
|
|
|
|
|
/* Display the column headers. If the message cannot be sent,
|
|
|
|
|
* quit the fucntion with returning 0. The function is called
|
|
|
|
|
* later and restart at the state "STAT_ST_INIT".
|
|
|
|
|
*/
|
|
|
|
|
chunk_reset(&trash);
|
2016-05-20 11:40:26 -04:00
|
|
|
|
|
|
|
|
if (appctx->st0 == STAT_CLI_O_TLSK_ENT)
|
|
|
|
|
chunk_appendf(&trash, "# id secret\n");
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, "# id (file)\n");
|
|
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-14 12:58:55 -04:00
|
|
|
appctx->ctx.tlskeys.dump_keys_index = 0;
|
2016-05-20 11:40:26 -04:00
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
/* Now, we start the browsing of the references lists.
|
|
|
|
|
* Note that the following call to LIST_ELEM return bad pointer. The only
|
2016-05-06 06:18:50 -04:00
|
|
|
* available field of this pointer is <list>. It is used with the function
|
|
|
|
|
* tlskeys_list_get_next() for retruning the first available entry
|
2015-05-09 02:46:01 -04:00
|
|
|
*/
|
2016-06-14 11:45:18 -04:00
|
|
|
if (appctx->ctx.tlskeys.ref == NULL) {
|
|
|
|
|
appctx->ctx.tlskeys.ref = LIST_ELEM(&tlskeys_reference, struct tls_keys_ref *, list);
|
|
|
|
|
appctx->ctx.tlskeys.ref = tlskeys_list_get_next(appctx->ctx.tlskeys.ref, &tlskeys_reference);
|
2016-05-20 11:40:26 -04:00
|
|
|
}
|
2015-05-09 02:46:01 -04:00
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_LIST;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case STAT_ST_LIST:
|
2016-06-14 11:45:18 -04:00
|
|
|
while (appctx->ctx.tlskeys.ref) {
|
|
|
|
|
int head = appctx->ctx.tlskeys.ref->tls_ticket_enc_index;
|
2015-05-09 02:46:01 -04:00
|
|
|
|
2016-05-20 11:40:26 -04:00
|
|
|
chunk_reset(&trash);
|
2016-06-14 12:58:55 -04:00
|
|
|
if (appctx->st0 == STAT_CLI_O_TLSK_ENT && appctx->ctx.tlskeys.dump_keys_index == 0)
|
2016-05-20 11:40:26 -04:00
|
|
|
chunk_appendf(&trash, "# ");
|
2016-06-14 12:58:55 -04:00
|
|
|
if (appctx->ctx.tlskeys.dump_keys_index == 0)
|
|
|
|
|
chunk_appendf(&trash, "%d (%s)\n", appctx->ctx.tlskeys.ref->unique_id,
|
|
|
|
|
appctx->ctx.tlskeys.ref->filename);
|
2016-05-20 11:40:26 -04:00
|
|
|
if (appctx->st0 == STAT_CLI_O_TLSK_ENT) {
|
2016-06-14 12:58:55 -04:00
|
|
|
while (appctx->ctx.tlskeys.dump_keys_index < TLS_TICKETS_NO) {
|
2016-05-20 11:40:26 -04:00
|
|
|
struct chunk *t2 = get_trash_chunk();
|
|
|
|
|
|
|
|
|
|
chunk_reset(t2);
|
2016-06-14 12:58:55 -04:00
|
|
|
/* should never fail here because we dump only a key in the t2 buffer */
|
|
|
|
|
t2->len = a2base64((char *)(appctx->ctx.tlskeys.ref->tlskeys + (head + 2 + appctx->ctx.tlskeys.dump_keys_index) % TLS_TICKETS_NO),
|
2016-05-20 11:40:26 -04:00
|
|
|
sizeof(struct tls_sess_key), t2->str, t2->size);
|
2016-06-14 12:58:55 -04:00
|
|
|
chunk_appendf(&trash, "%d.%d %s\n", appctx->ctx.tlskeys.ref->unique_id, appctx->ctx.tlskeys.dump_keys_index, t2->str);
|
|
|
|
|
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
/* let's try again later from this stream. We add ourselves into
|
|
|
|
|
* this stream's users so that it can remove us upon termination.
|
|
|
|
|
*/
|
|
|
|
|
si_applet_cant_put(si);
|
2016-05-20 11:40:26 -04:00
|
|
|
return 0;
|
2016-06-14 12:58:55 -04:00
|
|
|
}
|
|
|
|
|
appctx->ctx.tlskeys.dump_keys_index++;
|
2016-05-20 11:40:26 -04:00
|
|
|
}
|
2016-06-14 12:58:55 -04:00
|
|
|
appctx->ctx.tlskeys.dump_keys_index = 0;
|
2016-05-20 11:40:26 -04:00
|
|
|
}
|
2015-05-09 02:46:01 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
/* let's try again later from this stream. We add ourselves into
|
|
|
|
|
* this stream's users so that it can remove us upon termination.
|
|
|
|
|
*/
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-14 11:45:18 -04:00
|
|
|
if (appctx->ctx.tlskeys.dump_all == 0) /* don't display everything if not necessary */
|
2016-05-20 11:40:26 -04:00
|
|
|
break;
|
|
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
/* get next list entry and check the end of the list */
|
2016-06-14 11:45:18 -04:00
|
|
|
appctx->ctx.tlskeys.ref = tlskeys_list_get_next(appctx->ctx.tlskeys.ref, &tlskeys_reference);
|
2016-05-20 11:40:26 -04:00
|
|
|
|
2015-05-09 02:46:01 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
static int stats_pats_list(struct stream_interface *si)
|
2013-12-11 10:55:52 -05:00
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
|
|
|
|
switch (appctx->st2) {
|
|
|
|
|
case STAT_ST_INIT:
|
2014-03-11 09:29:22 -04:00
|
|
|
/* Display the column headers. If the message cannot be sent,
|
|
|
|
|
* quit the fucntion with returning 0. The function is called
|
|
|
|
|
* later and restart at the state "STAT_ST_INIT".
|
|
|
|
|
*/
|
|
|
|
|
chunk_reset(&trash);
|
2014-03-20 06:42:45 -04:00
|
|
|
chunk_appendf(&trash, "# id (file) description\n");
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2014-03-11 09:29:22 -04:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2014-02-11 05:31:40 -05:00
|
|
|
|
|
|
|
|
/* Now, we start the browsing of the references lists.
|
|
|
|
|
* Note that the following call to LIST_ELEM return bad pointer. The only
|
2016-05-06 06:18:50 -04:00
|
|
|
* available field of this pointer is <list>. It is used with the function
|
|
|
|
|
* pat_list_get_next() for retruning the first available entry
|
2014-02-11 05:31:40 -05:00
|
|
|
*/
|
|
|
|
|
appctx->ctx.map.ref = LIST_ELEM(&pattern_reference, struct pat_ref *, list);
|
|
|
|
|
appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
|
|
|
|
|
appctx->ctx.map.display_flags);
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st2 = STAT_ST_LIST;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case STAT_ST_LIST:
|
|
|
|
|
while (appctx->ctx.map.ref) {
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Build messages. If the reference is used by another category than
|
|
|
|
|
* the listed categorie, display the information in the massage.
|
|
|
|
|
*/
|
2014-03-20 06:45:47 -04:00
|
|
|
chunk_appendf(&trash, "%d (%s) %s\n", appctx->ctx.map.ref->unique_id,
|
2014-02-10 21:31:34 -05:00
|
|
|
appctx->ctx.map.ref->reference ? appctx->ctx.map.ref->reference : "",
|
|
|
|
|
appctx->ctx.map.ref->display);
|
2014-03-11 09:29:22 -04:00
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* let's try again later from this stream. We add ourselves into
|
|
|
|
|
* this stream's users so that it can remove us upon termination.
|
2013-12-11 10:55:52 -05:00
|
|
|
*/
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2013-12-11 10:55:52 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get next list entry and check the end of the list */
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->ctx.map.ref = pat_list_get_next(appctx->ctx.map.ref, &pattern_reference,
|
|
|
|
|
appctx->ctx.map.display_flags);
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2014-02-11 05:31:40 -05:00
|
|
|
return 0;
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int stats_map_lookup(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
struct sample sample;
|
|
|
|
|
struct pattern *pat;
|
2014-02-11 05:31:40 -05:00
|
|
|
int match_method;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
switch (appctx->st2) {
|
|
|
|
|
case STAT_ST_INIT:
|
2014-02-11 05:31:40 -05:00
|
|
|
/* Init to the first entry. The list cannot be change */
|
2014-01-20 08:29:33 -05:00
|
|
|
appctx->ctx.map.expr = LIST_ELEM(&appctx->ctx.map.ref->pat, struct pattern_expr *, list);
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr, &appctx->ctx.map.ref->pat);
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st2 = STAT_ST_LIST;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case STAT_ST_LIST:
|
|
|
|
|
/* for each lookup type */
|
2014-02-11 05:31:40 -05:00
|
|
|
while (appctx->ctx.map.expr) {
|
2013-12-11 10:55:52 -05:00
|
|
|
/* initialise chunk to build new message */
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
|
|
|
|
/* execute pattern matching */
|
2015-08-19 03:00:18 -04:00
|
|
|
sample.data.type = SMP_T_STR;
|
2016-03-03 13:32:25 -05:00
|
|
|
sample.flags = SMP_F_CONST;
|
2015-08-19 03:07:19 -04:00
|
|
|
sample.data.u.str.len = appctx->ctx.map.chunk.len;
|
|
|
|
|
sample.data.u.str.str = appctx->ctx.map.chunk.str;
|
2014-01-27 08:19:53 -05:00
|
|
|
if (appctx->ctx.map.expr->pat_head->match &&
|
|
|
|
|
sample_convert(&sample, appctx->ctx.map.expr->pat_head->expect_type))
|
2014-02-11 05:31:40 -05:00
|
|
|
pat = appctx->ctx.map.expr->pat_head->match(&sample, appctx->ctx.map.expr, 1);
|
|
|
|
|
else
|
|
|
|
|
pat = NULL;
|
2013-12-11 10:55:52 -05:00
|
|
|
|
|
|
|
|
/* build return message: set type of match */
|
2014-02-11 05:31:40 -05:00
|
|
|
for (match_method=0; match_method<PAT_MATCH_NUM; match_method++)
|
|
|
|
|
if (appctx->ctx.map.expr->pat_head->match == pat_match_fcts[match_method])
|
|
|
|
|
break;
|
|
|
|
|
if (match_method >= PAT_MATCH_NUM)
|
|
|
|
|
chunk_appendf(&trash, "type=unknown(%p)", appctx->ctx.map.expr->pat_head->match);
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-04-28 05:18:57 -04:00
|
|
|
/* case sensitive */
|
|
|
|
|
if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
|
|
|
|
|
chunk_appendf(&trash, ", case=insensitive");
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, ", case=sensitive");
|
|
|
|
|
|
2013-12-11 10:55:52 -05:00
|
|
|
/* Display no match, and set default value */
|
2014-01-17 09:25:13 -05:00
|
|
|
if (!pat) {
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
|
|
|
|
chunk_appendf(&trash, ", found=no");
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, ", match=no");
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Display match and match info */
|
|
|
|
|
else {
|
|
|
|
|
/* display match */
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
|
|
|
|
|
chunk_appendf(&trash, ", found=yes");
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, ", match=yes");
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-01-17 09:25:13 -05:00
|
|
|
/* display index mode */
|
2014-04-28 05:18:57 -04:00
|
|
|
if (pat->sflags & PAT_SF_TREE)
|
2014-03-11 13:48:17 -04:00
|
|
|
chunk_appendf(&trash, ", idx=tree");
|
2013-12-11 10:55:52 -05:00
|
|
|
else
|
2014-03-11 13:48:17 -04:00
|
|
|
chunk_appendf(&trash, ", idx=list");
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-03-11 13:37:58 -04:00
|
|
|
/* display pattern */
|
|
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
|
|
|
|
|
if (pat->ref && pat->ref->pattern)
|
|
|
|
|
chunk_appendf(&trash, ", key=\"%s\"", pat->ref->pattern);
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, ", key=unknown");
|
2014-02-11 05:31:40 -05:00
|
|
|
}
|
|
|
|
|
else {
|
2014-03-11 13:37:58 -04:00
|
|
|
if (pat->ref && pat->ref->pattern)
|
|
|
|
|
chunk_appendf(&trash, ", pattern=\"%s\"", pat->ref->pattern);
|
|
|
|
|
else
|
|
|
|
|
chunk_appendf(&trash, ", pattern=unknown");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* display return value */
|
|
|
|
|
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
|
2015-08-19 02:35:43 -04:00
|
|
|
if (pat->data && pat->ref && pat->ref->sample)
|
2015-07-23 12:33:41 -04:00
|
|
|
chunk_appendf(&trash, ", value=\"%s\", type=\"%s\"", pat->ref->sample,
|
2015-08-19 03:05:25 -04:00
|
|
|
smp_to_type[pat->data->type]);
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-03-11 13:37:58 -04:00
|
|
|
chunk_appendf(&trash, ", value=none");
|
2014-02-11 05:31:40 -05:00
|
|
|
}
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
2014-03-11 13:48:17 -04:00
|
|
|
chunk_appendf(&trash, "\n");
|
|
|
|
|
|
2013-12-11 10:55:52 -05:00
|
|
|
/* display response */
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* let's try again later from this stream. We add ourselves into
|
|
|
|
|
* this stream's users so that it can remove us upon termination.
|
2013-12-11 10:55:52 -05:00
|
|
|
*/
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2013-12-11 10:55:52 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get next entry */
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->ctx.map.expr = pat_expr_get_next(appctx->ctx.map.expr,
|
|
|
|
|
&appctx->ctx.map.ref->pat);
|
2013-12-11 10:55:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
free(appctx->ctx.map.chunk.str);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-11 05:31:40 -05:00
|
|
|
static int stats_pat_list(struct stream_interface *si)
|
2013-12-11 10:55:52 -05:00
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
|
|
|
|
switch (appctx->st2) {
|
|
|
|
|
|
|
|
|
|
case STAT_ST_INIT:
|
|
|
|
|
/* Init to the first entry. The list cannot be change */
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.ref->head,
|
|
|
|
|
struct pat_ref_elt *, list);
|
|
|
|
|
if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
|
|
|
|
|
appctx->ctx.map.elt = NULL;
|
2013-12-11 10:55:52 -05:00
|
|
|
appctx->st2 = STAT_ST_LIST;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case STAT_ST_LIST:
|
2014-02-11 05:31:40 -05:00
|
|
|
while (appctx->ctx.map.elt) {
|
2013-12-11 10:55:52 -05:00
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
|
|
|
|
/* build messages */
|
2014-02-11 05:31:40 -05:00
|
|
|
if (appctx->ctx.map.elt->sample)
|
2014-01-28 09:55:37 -05:00
|
|
|
chunk_appendf(&trash, "%p %s %s\n",
|
|
|
|
|
appctx->ctx.map.elt, appctx->ctx.map.elt->pattern,
|
|
|
|
|
appctx->ctx.map.elt->sample);
|
2014-02-11 05:31:40 -05:00
|
|
|
else
|
2014-01-28 09:55:37 -05:00
|
|
|
chunk_appendf(&trash, "%p %s\n",
|
|
|
|
|
appctx->ctx.map.elt, appctx->ctx.map.elt->pattern);
|
2013-12-11 10:55:52 -05:00
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* let's try again later from this stream. We add ourselves into
|
|
|
|
|
* this stream's users so that it can remove us upon termination.
|
2013-12-11 10:55:52 -05:00
|
|
|
*/
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2013-12-11 10:55:52 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get next list entry and check the end of the list */
|
2014-02-11 05:31:40 -05:00
|
|
|
appctx->ctx.map.elt = LIST_NEXT(&appctx->ctx.map.elt->list,
|
|
|
|
|
struct pat_ref_elt *, list);
|
|
|
|
|
if (&appctx->ctx.map.elt->list == &appctx->ctx.map.ref->head)
|
2013-12-11 10:55:52 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* This function dumps all streams' states onto the stream interface's
|
2013-07-08 09:53:06 -04:00
|
|
|
* read buffer. It returns 0 if the output buffer is full and it needs
|
2011-02-13 09:27:22 -05:00
|
|
|
* to be called again, otherwise non-zero. It is designed to be called
|
|
|
|
|
* from stats_dump_sess_to_buffer() below.
|
2008-12-07 16:29:48 -05:00
|
|
|
*/
|
2011-06-15 02:18:44 -04:00
|
|
|
static int stats_dump_sess_to_buffer(struct stream_interface *si)
|
2008-12-07 16:29:48 -05:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2013-10-01 04:45:07 -04:00
|
|
|
struct connection *conn;
|
|
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
|
2008-12-07 16:29:48 -05:00
|
|
|
/* If we're forced to shut down, we might have to remove our
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* reference to the last stream being dumped.
|
2008-12-07 16:29:48 -05:00
|
|
|
*/
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->st2 == STAT_ST_LIST) {
|
|
|
|
|
if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
|
|
|
|
|
LIST_DEL(&appctx->ctx.sess.bref.users);
|
|
|
|
|
LIST_INIT(&appctx->ctx.sess.bref.users);
|
2009-02-22 09:17:24 -05:00
|
|
|
}
|
2008-12-07 16:29:48 -05:00
|
|
|
}
|
2009-10-03 17:55:14 -04:00
|
|
|
return 1;
|
2008-12-07 16:29:48 -05:00
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_reset(&trash);
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
switch (appctx->st2) {
|
2011-03-10 05:25:07 -05:00
|
|
|
case STAT_ST_INIT:
|
2008-12-07 16:29:48 -05:00
|
|
|
/* the function had not been called yet, let's prepare the
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* buffer for a response. We initialize the current stream
|
2009-02-22 09:17:24 -05:00
|
|
|
* pointer to the first in the global list. When a target
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* stream is being destroyed, it is responsible for updating
|
2009-02-22 09:17:24 -05:00
|
|
|
* this pointer. We know we have reached the end when this
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* pointer points back to the head of the streams list.
|
2008-12-07 16:29:48 -05:00
|
|
|
*/
|
2013-12-01 03:15:12 -05:00
|
|
|
LIST_INIT(&appctx->ctx.sess.bref.users);
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
appctx->ctx.sess.bref.ref = streams.n;
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_LIST;
|
2008-12-07 16:29:48 -05:00
|
|
|
/* fall through */
|
|
|
|
|
|
2011-03-10 05:25:07 -05:00
|
|
|
case STAT_ST_LIST:
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* first, let's detach the back-ref from a possible previous stream */
|
2013-12-01 03:15:12 -05:00
|
|
|
if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) {
|
|
|
|
|
LIST_DEL(&appctx->ctx.sess.bref.users);
|
|
|
|
|
LIST_INIT(&appctx->ctx.sess.bref.users);
|
2009-02-22 09:17:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* and start from where we stopped */
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
while (appctx->ctx.sess.bref.ref != &streams) {
|
2010-11-01 14:26:02 -04:00
|
|
|
char pn[INET6_ADDRSTRLEN];
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *curr_sess;
|
2008-12-07 16:29:48 -05:00
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
curr_sess = LIST_ELEM(appctx->ctx.sess.bref.ref, struct stream *, list);
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.sess.target) {
|
|
|
|
|
if (appctx->ctx.sess.target != (void *)-1 && appctx->ctx.sess.target != curr_sess)
|
2010-03-05 11:53:32 -05:00
|
|
|
goto next_sess;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users);
|
2010-03-05 11:53:32 -05:00
|
|
|
/* call the proper dump() function and return if we're missing space */
|
2012-11-25 19:16:39 -05:00
|
|
|
if (!stats_dump_full_sess_to_buffer(si, curr_sess))
|
2010-03-05 11:53:32 -05:00
|
|
|
return 0;
|
|
|
|
|
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* stream dump complete */
|
2013-12-01 03:15:12 -05:00
|
|
|
LIST_DEL(&appctx->ctx.sess.bref.users);
|
|
|
|
|
LIST_INIT(&appctx->ctx.sess.bref.users);
|
|
|
|
|
if (appctx->ctx.sess.target != (void *)-1) {
|
|
|
|
|
appctx->ctx.sess.target = NULL;
|
2012-11-25 19:16:39 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
goto next_sess;
|
2010-03-05 11:53:32 -05:00
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2008-12-07 16:29:48 -05:00
|
|
|
"%p: proto=%s",
|
|
|
|
|
curr_sess,
|
2015-09-23 06:16:43 -04:00
|
|
|
strm_li(curr_sess) ? strm_li(curr_sess)->proto->name : "?");
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2015-04-03 20:10:38 -04:00
|
|
|
conn = objt_conn(strm_orig(curr_sess));
|
2013-10-01 04:45:07 -04:00
|
|
|
switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) {
|
2011-09-04 18:36:48 -04:00
|
|
|
case AF_INET:
|
2008-12-07 16:29:48 -05:00
|
|
|
case AF_INET6:
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2008-12-07 16:29:48 -05:00
|
|
|
" src=%s:%d fe=%s be=%s srv=%s",
|
|
|
|
|
pn,
|
2013-10-01 04:45:07 -04:00
|
|
|
get_host_port(&conn->addr.from),
|
2015-04-03 20:10:38 -04:00
|
|
|
strm_fe(curr_sess)->id,
|
2011-09-02 11:33:05 -04:00
|
|
|
(curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
|
2012-11-11 18:42:33 -05:00
|
|
|
objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "<none>"
|
2008-12-07 16:29:48 -05:00
|
|
|
);
|
|
|
|
|
break;
|
|
|
|
|
case AF_UNIX:
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2010-10-22 10:19:01 -04:00
|
|
|
" src=unix:%d fe=%s be=%s srv=%s",
|
2015-04-03 20:10:38 -04:00
|
|
|
strm_li(curr_sess)->luid,
|
|
|
|
|
strm_fe(curr_sess)->id,
|
2011-09-02 11:33:05 -04:00
|
|
|
(curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "<NONE>",
|
2012-11-11 18:42:33 -05:00
|
|
|
objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "<none>"
|
2010-10-22 10:19:01 -04:00
|
|
|
);
|
2008-12-07 16:29:48 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-10-04 08:24:59 -04:00
|
|
|
" ts=%02x age=%s calls=%d",
|
|
|
|
|
curr_sess->task->state,
|
2009-03-28 12:54:35 -04:00
|
|
|
human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1),
|
|
|
|
|
curr_sess->task->calls);
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2012-03-01 12:19:58 -05:00
|
|
|
" rq[f=%06xh,i=%d,an=%02xh,rx=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
curr_sess->req.flags,
|
|
|
|
|
curr_sess->req.buf->i,
|
|
|
|
|
curr_sess->req.analysers,
|
|
|
|
|
curr_sess->req.rex ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->req.rex - now_ms),
|
2009-03-28 19:18:14 -04:00
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
",wx=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
curr_sess->req.wex ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->req.wex - now_ms),
|
2009-03-28 19:18:14 -04:00
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
",ax=%s]",
|
2014-11-27 14:45:39 -05:00
|
|
|
curr_sess->req.analyse_exp ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->req.analyse_exp - now_ms),
|
2009-03-28 19:18:14 -04:00
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2012-03-01 12:19:58 -05:00
|
|
|
" rp[f=%06xh,i=%d,an=%02xh,rx=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
curr_sess->res.flags,
|
|
|
|
|
curr_sess->res.buf->i,
|
|
|
|
|
curr_sess->res.analysers,
|
|
|
|
|
curr_sess->res.rex ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->res.rex - now_ms),
|
2009-03-28 19:18:14 -04:00
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
",wx=%s",
|
2014-11-27 14:45:39 -05:00
|
|
|
curr_sess->res.wex ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->res.wex - now_ms),
|
2009-03-28 19:18:14 -04:00
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
",ax=%s]",
|
2014-11-27 14:45:39 -05:00
|
|
|
curr_sess->res.analyse_exp ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->res.analyse_exp - now_ms),
|
2009-03-28 19:18:14 -04:00
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2013-10-01 04:45:07 -04:00
|
|
|
conn = objt_conn(curr_sess->si[0].end);
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
" s0=[%d,%1xh,fd=%d,ex=%s]",
|
|
|
|
|
curr_sess->si[0].state,
|
|
|
|
|
curr_sess->si[0].flags,
|
2016-11-17 08:22:52 -05:00
|
|
|
(conn && conn->t.sock.fd >= 0) ? conn->t.sock.fd : -1,
|
2009-03-28 19:18:14 -04:00
|
|
|
curr_sess->si[0].exp ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->si[0].exp - now_ms),
|
|
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2013-10-01 04:45:07 -04:00
|
|
|
conn = objt_conn(curr_sess->si[1].end);
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
" s1=[%d,%1xh,fd=%d,ex=%s]",
|
|
|
|
|
curr_sess->si[1].state,
|
|
|
|
|
curr_sess->si[1].flags,
|
2016-11-17 08:22:52 -05:00
|
|
|
(conn && conn->t.sock.fd >= 0) ? conn->t.sock.fd : -1,
|
2009-03-28 19:18:14 -04:00
|
|
|
curr_sess->si[1].exp ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->si[1].exp - now_ms),
|
|
|
|
|
TICKS_TO_MS(1000)) : "");
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-28 19:18:14 -04:00
|
|
|
" exp=%s",
|
|
|
|
|
curr_sess->task->expire ?
|
|
|
|
|
human_time(TICKS_TO_MS(curr_sess->task->expire - now_ms),
|
|
|
|
|
TICKS_TO_MS(1000)) : "");
|
2009-03-07 11:25:21 -05:00
|
|
|
if (task_in_rq(curr_sess->task))
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash, " run(nice=%d)", curr_sess->task->nice);
|
2009-03-28 19:18:14 -04:00
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash, "\n");
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* let's try again later from this stream. We add ourselves into
|
|
|
|
|
* this stream's users so that it can remove us upon termination.
|
2009-02-22 09:17:24 -05:00
|
|
|
*/
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2013-12-01 03:15:12 -05:00
|
|
|
LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users);
|
2009-10-03 17:55:14 -04:00
|
|
|
return 0;
|
2008-12-07 16:29:48 -05:00
|
|
|
}
|
|
|
|
|
|
2010-03-05 11:53:32 -05:00
|
|
|
next_sess:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.bref.ref = curr_sess->list.n;
|
2008-12-07 16:29:48 -05:00
|
|
|
}
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.sess.target && appctx->ctx.sess.target != (void *)-1) {
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
/* specified stream not found */
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.sess.section > 0)
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash, " *** session terminated while we were watching it ***\n");
|
2010-03-05 11:53:32 -05:00
|
|
|
else
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash, "Session not found.\n");
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2010-03-05 11:53:32 -05:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2010-03-05 11:53:32 -05:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.sess.target = NULL;
|
|
|
|
|
appctx->ctx.sess.uid = 0;
|
2010-03-05 11:53:32 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_FIN;
|
2008-12-07 16:29:48 -05:00
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
default:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_FIN;
|
2009-10-03 17:55:14 -04:00
|
|
|
return 1;
|
2008-12-07 16:29:48 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-25 20:22:40 -05:00
|
|
|
/* This is called when the stream interface is closed. For instance, upon an
|
|
|
|
|
* external abort, we won't call the i/o handler anymore so we may need to
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
* remove back references to the stream currently being dumped.
|
2012-11-25 20:22:40 -05:00
|
|
|
*/
|
2015-04-13 06:05:19 -04:00
|
|
|
static void cli_release_handler(struct appctx *appctx)
|
2012-11-25 20:22:40 -05:00
|
|
|
{
|
2016-11-12 04:51:33 -05:00
|
|
|
if (appctx->io_release) {
|
|
|
|
|
appctx->io_release(appctx);
|
|
|
|
|
appctx->io_release = NULL;
|
|
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->st0 == STAT_CLI_O_SESS && appctx->st2 == STAT_ST_LIST) {
|
|
|
|
|
if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users))
|
|
|
|
|
LIST_DEL(&appctx->ctx.sess.bref.users);
|
2012-11-25 20:22:40 -05:00
|
|
|
}
|
2016-11-18 13:17:40 -05:00
|
|
|
else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) &&
|
|
|
|
|
appctx->st2 == STAT_ST_LIST) {
|
|
|
|
|
appctx->ctx.table.entry->ref_cnt--;
|
|
|
|
|
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
|
|
|
|
}
|
2014-01-29 13:08:49 -05:00
|
|
|
else if (appctx->st0 == STAT_CLI_PRINT_FREE) {
|
|
|
|
|
free(appctx->ctx.cli.err);
|
2015-10-07 14:00:24 -04:00
|
|
|
appctx->ctx.cli.err = NULL;
|
2014-01-29 13:08:49 -05:00
|
|
|
}
|
2014-02-11 05:31:40 -05:00
|
|
|
else if (appctx->st0 == STAT_CLI_O_MLOOK) {
|
|
|
|
|
free(appctx->ctx.map.chunk.str);
|
2015-10-07 14:00:24 -04:00
|
|
|
appctx->ctx.map.chunk.str = NULL;
|
2014-02-11 05:31:40 -05:00
|
|
|
}
|
2012-11-25 20:22:40 -05:00
|
|
|
}
|
|
|
|
|
|
2013-04-13 03:22:25 -04:00
|
|
|
/* This function is used to either dump tables states (when action is set
|
|
|
|
|
* to STAT_CLI_O_TAB) or clear tables (when action is STAT_CLI_O_CLR).
|
|
|
|
|
* It returns 0 if the output buffer is full and it needs to be called
|
|
|
|
|
* again, otherwise non-zero.
|
2010-07-12 11:55:33 -04:00
|
|
|
*/
|
2013-04-13 03:22:25 -04:00
|
|
|
static int stats_table_request(struct stream_interface *si, int action)
|
2010-07-12 11:55:33 -04:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
REORG/MAJOR: session: rename the "session" entity to "stream"
With HTTP/2, we'll have to support multiplexed streams. A stream is in
fact the largest part of what we currently call a session, it has buffers,
logs, etc.
In order to catch any error, this commit removes any reference to the
struct session and tries to rename most "session" occurrences in function
names to "stream" and "sess" to "strm" when that's related to a session.
The files stream.{c,h} were added and session.{c,h} removed.
The session will be reintroduced later and a few parts of the stream
will progressively be moved overthere. It will more or less contain
only what we need in an embryonic session.
Sample fetch functions and converters will have to change a bit so
that they'll use an L5 (session) instead of what's currently called
"L4" which is in fact L6 for now.
Once all changes are completed, we should see approximately this :
L7 - http_txn
L6 - stream
L5 - session
L4 - connection | applet
There will be at most one http_txn per stream, and a same session will
possibly be referenced by multiple streams. A connection will point to
a session and to a stream. The session will hold all the information
we need to keep even when we don't yet have a stream.
Some more cleanup is needed because some code was already far from
being clean. The server queue management still refers to sessions at
many places while comments talk about connections. This will have to
be cleaned up once we have a server-side connection pool manager.
Stream flags "SN_*" still need to be renamed, it doesn't seem like
any of them will need to move to the session.
2015-04-02 18:22:06 -04:00
|
|
|
struct stream *s = si_strm(si);
|
2010-07-12 11:55:33 -04:00
|
|
|
struct ebmb_node *eb;
|
|
|
|
|
int dt;
|
2012-12-05 17:01:12 -05:00
|
|
|
int skip_entry;
|
2013-04-13 03:22:25 -04:00
|
|
|
int show = action == STAT_CLI_O_TAB;
|
2010-07-12 11:55:33 -04:00
|
|
|
|
|
|
|
|
/*
|
2013-12-01 03:15:12 -05:00
|
|
|
* We have 3 possible states in appctx->st2 :
|
2011-03-10 05:25:07 -05:00
|
|
|
* - STAT_ST_INIT : the first call
|
|
|
|
|
* - STAT_ST_INFO : the proxy pointer points to the next table to
|
2010-07-12 11:55:33 -04:00
|
|
|
* dump, the entry pointer is NULL ;
|
2011-03-10 05:25:07 -05:00
|
|
|
* - STAT_ST_LIST : the proxy pointer points to the current table
|
2010-07-12 11:55:33 -04:00
|
|
|
* and the entry pointer points to the next entry to be dumped,
|
|
|
|
|
* and the refcount on the next entry is held ;
|
2011-03-10 05:25:07 -05:00
|
|
|
* - STAT_ST_END : nothing left to dump, the buffer may contain some
|
2010-07-12 11:55:33 -04:00
|
|
|
* data though.
|
|
|
|
|
*/
|
|
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) {
|
2010-07-12 11:55:33 -04:00
|
|
|
/* in case of abort, remove any refcount we might have set on an entry */
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->st2 == STAT_ST_LIST) {
|
|
|
|
|
appctx->ctx.table.entry->ref_cnt--;
|
|
|
|
|
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
2010-08-03 14:34:06 -04:00
|
|
|
}
|
2010-07-12 11:55:33 -04:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_reset(&trash);
|
2010-07-12 11:55:33 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
while (appctx->st2 != STAT_ST_FIN) {
|
|
|
|
|
switch (appctx->st2) {
|
2011-03-10 05:25:07 -05:00
|
|
|
case STAT_ST_INIT:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.proxy = appctx->ctx.table.target;
|
|
|
|
|
if (!appctx->ctx.table.proxy)
|
|
|
|
|
appctx->ctx.table.proxy = proxy;
|
2010-07-12 11:55:33 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.entry = NULL;
|
|
|
|
|
appctx->st2 = STAT_ST_INFO;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
|
2011-03-10 05:25:07 -05:00
|
|
|
case STAT_ST_INFO:
|
2013-12-01 03:15:12 -05:00
|
|
|
if (!appctx->ctx.table.proxy ||
|
|
|
|
|
(appctx->ctx.table.target &&
|
|
|
|
|
appctx->ctx.table.proxy != appctx->ctx.table.target)) {
|
|
|
|
|
appctx->st2 = STAT_ST_END;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.table.proxy->table.size) {
|
|
|
|
|
if (show && !stats_dump_table_head_to_buffer(&trash, si, appctx->ctx.table.proxy,
|
|
|
|
|
appctx->ctx.table.target))
|
2010-07-12 11:55:33 -04:00
|
|
|
return 0;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.table.target &&
|
2015-04-03 20:10:38 -04:00
|
|
|
strm_li(s)->bind_conf->level >= ACCESS_LVL_OPER) {
|
2010-07-12 11:55:33 -04:00
|
|
|
/* dump entries only if table explicitly requested */
|
2013-12-01 03:15:12 -05:00
|
|
|
eb = ebmb_first(&appctx->ctx.table.proxy->table.keys);
|
2010-07-12 11:55:33 -04:00
|
|
|
if (eb) {
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
|
|
|
|
appctx->ctx.table.entry->ref_cnt++;
|
|
|
|
|
appctx->st2 = STAT_ST_LIST;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
|
2011-03-10 05:25:07 -05:00
|
|
|
case STAT_ST_LIST:
|
2012-12-05 17:01:12 -05:00
|
|
|
skip_entry = 0;
|
2011-06-15 02:18:49 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.table.data_type >= 0) {
|
2010-07-18 05:46:20 -04:00
|
|
|
/* we're filtering on some data contents */
|
|
|
|
|
void *ptr;
|
|
|
|
|
long long data;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
dt = appctx->ctx.table.data_type;
|
|
|
|
|
ptr = stktable_data_ptr(&appctx->ctx.table.proxy->table,
|
|
|
|
|
appctx->ctx.table.entry,
|
2010-07-18 05:46:20 -04:00
|
|
|
dt);
|
|
|
|
|
|
|
|
|
|
data = 0;
|
|
|
|
|
switch (stktable_data_types[dt].std_type) {
|
|
|
|
|
case STD_T_SINT:
|
|
|
|
|
data = stktable_data_cast(ptr, std_t_sint);
|
|
|
|
|
break;
|
|
|
|
|
case STD_T_UINT:
|
|
|
|
|
data = stktable_data_cast(ptr, std_t_uint);
|
|
|
|
|
break;
|
|
|
|
|
case STD_T_ULL:
|
|
|
|
|
data = stktable_data_cast(ptr, std_t_ull);
|
|
|
|
|
break;
|
|
|
|
|
case STD_T_FRQP:
|
|
|
|
|
data = read_freq_ctr_period(&stktable_data_cast(ptr, std_t_frqp),
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.proxy->table.data_arg[dt].u);
|
2010-07-18 05:46:20 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* skip the entry if the data does not match the test and the value */
|
2013-12-01 03:15:12 -05:00
|
|
|
if ((data < appctx->ctx.table.value &&
|
|
|
|
|
(appctx->ctx.table.data_op == STD_OP_EQ ||
|
|
|
|
|
appctx->ctx.table.data_op == STD_OP_GT ||
|
|
|
|
|
appctx->ctx.table.data_op == STD_OP_GE)) ||
|
|
|
|
|
(data == appctx->ctx.table.value &&
|
|
|
|
|
(appctx->ctx.table.data_op == STD_OP_NE ||
|
|
|
|
|
appctx->ctx.table.data_op == STD_OP_GT ||
|
|
|
|
|
appctx->ctx.table.data_op == STD_OP_LT)) ||
|
|
|
|
|
(data > appctx->ctx.table.value &&
|
|
|
|
|
(appctx->ctx.table.data_op == STD_OP_EQ ||
|
|
|
|
|
appctx->ctx.table.data_op == STD_OP_LT ||
|
|
|
|
|
appctx->ctx.table.data_op == STD_OP_LE)))
|
2012-12-05 17:01:12 -05:00
|
|
|
skip_entry = 1;
|
2010-07-18 05:46:20 -04:00
|
|
|
}
|
|
|
|
|
|
2011-06-15 02:18:49 -04:00
|
|
|
if (show && !skip_entry &&
|
2013-12-01 03:15:12 -05:00
|
|
|
!stats_dump_table_entry_to_buffer(&trash, si, appctx->ctx.table.proxy,
|
|
|
|
|
appctx->ctx.table.entry))
|
2011-06-15 02:18:45 -04:00
|
|
|
return 0;
|
2010-07-12 11:55:33 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.entry->ref_cnt--;
|
2010-07-12 11:55:33 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
eb = ebmb_next(&appctx->ctx.table.entry->key);
|
2010-07-12 11:55:33 -04:00
|
|
|
if (eb) {
|
2013-12-01 03:15:12 -05:00
|
|
|
struct stksess *old = appctx->ctx.table.entry;
|
|
|
|
|
appctx->ctx.table.entry = ebmb_entry(eb, struct stksess, key);
|
2012-01-09 05:50:03 -05:00
|
|
|
if (show)
|
2013-12-01 03:15:12 -05:00
|
|
|
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, old);
|
|
|
|
|
else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
|
|
|
|
|
stksess_kill(&appctx->ctx.table.proxy->table, old);
|
|
|
|
|
appctx->ctx.table.entry->ref_cnt++;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2011-06-15 02:18:49 -04:00
|
|
|
|
|
|
|
|
if (show)
|
2013-12-01 03:15:12 -05:00
|
|
|
stksess_kill_if_expired(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
|
|
|
|
else if (!skip_entry && !appctx->ctx.table.entry->ref_cnt)
|
|
|
|
|
stksess_kill(&appctx->ctx.table.proxy->table, appctx->ctx.table.entry);
|
2011-06-15 02:18:49 -04:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.table.proxy = appctx->ctx.table.proxy->next;
|
|
|
|
|
appctx->st2 = STAT_ST_INFO;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
|
2011-03-10 05:25:07 -05:00
|
|
|
case STAT_ST_END:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->st2 = STAT_ST_FIN;
|
2010-07-12 11:55:33 -04:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-05 08:58:26 -05:00
|
|
|
/* print a line of text buffer (limited to 70 bytes) to <out>. The format is :
|
2009-03-04 09:53:18 -05:00
|
|
|
* <2 spaces> <offset=5 digits> <space or plus> <space> <70 chars max> <\n>
|
|
|
|
|
* which is 60 chars per line. Non-printable chars \t, \n, \r and \e are
|
|
|
|
|
* encoded in C format. Other non-printable chars are encoded "\xHH". Original
|
|
|
|
|
* lines are respected within the limit of 70 output chars. Lines that are
|
|
|
|
|
* continuation of a previous truncated line begin with "+" instead of " "
|
|
|
|
|
* after the offset. The new pointer is returned.
|
|
|
|
|
*/
|
2010-03-05 08:58:26 -05:00
|
|
|
static int dump_text_line(struct chunk *out, const char *buf, int bsize, int len,
|
|
|
|
|
int *line, int ptr)
|
2009-03-04 09:53:18 -05:00
|
|
|
{
|
|
|
|
|
int end;
|
|
|
|
|
unsigned char c;
|
|
|
|
|
|
|
|
|
|
end = out->len + 80;
|
2009-09-27 07:23:20 -04:00
|
|
|
if (end > out->size)
|
2009-03-04 09:53:18 -05:00
|
|
|
return ptr;
|
|
|
|
|
|
2012-10-29 11:14:26 -04:00
|
|
|
chunk_appendf(out, " %05d%c ", ptr, (ptr == *line) ? ' ' : '+');
|
2009-03-04 09:53:18 -05:00
|
|
|
|
2010-03-05 08:58:26 -05:00
|
|
|
while (ptr < len && ptr < bsize) {
|
|
|
|
|
c = buf[ptr];
|
2009-03-12 03:18:33 -04:00
|
|
|
if (isprint(c) && isascii(c) && c != '\\') {
|
2009-03-04 09:53:18 -05:00
|
|
|
if (out->len > end - 2)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = c;
|
2009-03-12 03:18:33 -04:00
|
|
|
} else if (c == '\t' || c == '\n' || c == '\r' || c == '\e' || c == '\\') {
|
2009-03-04 09:53:18 -05:00
|
|
|
if (out->len > end - 3)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = '\\';
|
|
|
|
|
switch (c) {
|
|
|
|
|
case '\t': c = 't'; break;
|
|
|
|
|
case '\n': c = 'n'; break;
|
|
|
|
|
case '\r': c = 'r'; break;
|
|
|
|
|
case '\e': c = 'e'; break;
|
2009-03-12 03:18:33 -04:00
|
|
|
case '\\': c = '\\'; break;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
|
|
|
|
out->str[out->len++] = c;
|
|
|
|
|
} else {
|
|
|
|
|
if (out->len > end - 5)
|
|
|
|
|
break;
|
|
|
|
|
out->str[out->len++] = '\\';
|
|
|
|
|
out->str[out->len++] = 'x';
|
|
|
|
|
out->str[out->len++] = hextab[(c >> 4) & 0xF];
|
|
|
|
|
out->str[out->len++] = hextab[c & 0xF];
|
|
|
|
|
}
|
2010-03-05 08:58:26 -05:00
|
|
|
if (buf[ptr++] == '\n') {
|
2009-03-04 09:53:18 -05:00
|
|
|
/* we had a line break, let's return now */
|
|
|
|
|
out->str[out->len++] = '\n';
|
|
|
|
|
*line = ptr;
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* we have an incomplete line, we return it as-is */
|
|
|
|
|
out->str[out->len++] = '\n';
|
|
|
|
|
return ptr;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-16 18:33:24 -04:00
|
|
|
/* This function dumps counters from all resolvers section and associated name servers.
|
|
|
|
|
* It returns 0 if the output buffer is full and it needs
|
|
|
|
|
* to be called again, otherwise non-zero.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_resolvers_to_buffer(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
struct dns_resolvers *presolvers;
|
|
|
|
|
struct dns_nameserver *pnameserver;
|
|
|
|
|
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
|
|
|
|
switch (appctx->st2) {
|
|
|
|
|
case STAT_ST_INIT:
|
|
|
|
|
appctx->st2 = STAT_ST_LIST; /* let's start producing data */
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
case STAT_ST_LIST:
|
2015-10-02 16:33:01 -04:00
|
|
|
if (LIST_ISEMPTY(&dns_resolvers)) {
|
|
|
|
|
chunk_appendf(&trash, "No resolvers found\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
list_for_each_entry(presolvers, &dns_resolvers, list) {
|
|
|
|
|
if (appctx->ctx.resolvers.ptr != NULL && appctx->ctx.resolvers.ptr != presolvers)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
chunk_appendf(&trash, "Resolvers section %s\n", presolvers->id);
|
|
|
|
|
list_for_each_entry(pnameserver, &presolvers->nameserver_list, list) {
|
|
|
|
|
chunk_appendf(&trash, " nameserver %s:\n", pnameserver->id);
|
|
|
|
|
chunk_appendf(&trash, " sent: %ld\n", pnameserver->counters.sent);
|
|
|
|
|
chunk_appendf(&trash, " valid: %ld\n", pnameserver->counters.valid);
|
|
|
|
|
chunk_appendf(&trash, " update: %ld\n", pnameserver->counters.update);
|
|
|
|
|
chunk_appendf(&trash, " cname: %ld\n", pnameserver->counters.cname);
|
|
|
|
|
chunk_appendf(&trash, " cname_error: %ld\n", pnameserver->counters.cname_error);
|
|
|
|
|
chunk_appendf(&trash, " any_err: %ld\n", pnameserver->counters.any_err);
|
|
|
|
|
chunk_appendf(&trash, " nx: %ld\n", pnameserver->counters.nx);
|
|
|
|
|
chunk_appendf(&trash, " timeout: %ld\n", pnameserver->counters.timeout);
|
|
|
|
|
chunk_appendf(&trash, " refused: %ld\n", pnameserver->counters.refused);
|
|
|
|
|
chunk_appendf(&trash, " other: %ld\n", pnameserver->counters.other);
|
|
|
|
|
chunk_appendf(&trash, " invalid: %ld\n", pnameserver->counters.invalid);
|
|
|
|
|
chunk_appendf(&trash, " too_big: %ld\n", pnameserver->counters.too_big);
|
|
|
|
|
chunk_appendf(&trash, " truncated: %ld\n", pnameserver->counters.truncated);
|
|
|
|
|
chunk_appendf(&trash, " outdated: %ld\n", pnameserver->counters.outdated);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-16 18:33:24 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* display response */
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
/* let's try again later from this session. We add ourselves into
|
|
|
|
|
* this session's users so that it can remove us upon termination.
|
|
|
|
|
*/
|
|
|
|
|
si->flags |= SI_FL_WAIT_ROOM;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
appctx->st2 = STAT_ST_FIN;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
REORG: connection: rename the data layer the "transport layer"
While working on the changes required to make the health checks use the
new connections, it started to become obvious that some naming was not
logical at all in the connections. Specifically, it is not logical to
call the "data layer" the layer which is in charge for all the handshake
and which does not yet provide a data layer once established until a
session has allocated all the required buffers.
In fact, it's more a transport layer, which makes much more sense. The
transport layer offers a medium on which data can transit, and it offers
the functions to move these data when the upper layer requests this. And
it is the upper layer which iterates over the transport layer's functions
to move data which should be called the data layer.
The use case where it's obvious is with embryonic sessions : an incoming
SSL connection is accepted. Only the connection is allocated, not the
buffers nor stream interface, etc... The connection handles the SSL
handshake by itself. Once this handshake is complete, we can't use the
data functions because the buffers and stream interface are not there
yet. Hence we have to first call a specific function to complete the
session initialization, after which we'll be able to use the data
functions. This clearly proves that SSL here is only a transport layer
and that the stream interface constitutes the data layer.
A similar change will be performed to rename app_cb => data, but the
two could not be in the same commit for obvious reasons.
2012-10-02 18:19:48 -04:00
|
|
|
/* This function dumps all captured errors onto the stream interface's
|
2013-07-08 09:53:06 -04:00
|
|
|
* read buffer. It returns 0 if the output buffer is full and it needs
|
2011-02-13 09:27:22 -05:00
|
|
|
* to be called again, otherwise non-zero.
|
2009-03-04 09:53:18 -05:00
|
|
|
*/
|
2011-06-15 02:18:44 -04:00
|
|
|
static int stats_dump_errors_to_buffer(struct stream_interface *si)
|
2009-03-04 09:53:18 -05:00
|
|
|
{
|
2013-12-01 03:15:12 -05:00
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
2009-03-04 09:53:18 -05:00
|
|
|
extern const char *monthname[12];
|
|
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
|
2009-10-03 17:49:35 -04:00
|
|
|
return 1;
|
2009-03-04 09:53:18 -05:00
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_reset(&trash);
|
2009-03-04 09:53:18 -05:00
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (!appctx->ctx.errors.px) {
|
2009-03-04 09:53:18 -05:00
|
|
|
/* the function had not been called yet, let's prepare the
|
|
|
|
|
* buffer for a response.
|
|
|
|
|
*/
|
2010-12-12 08:00:34 -05:00
|
|
|
struct tm tm;
|
|
|
|
|
|
|
|
|
|
get_localtime(date.tv_sec, &tm);
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
|
2010-12-12 08:00:34 -05:00
|
|
|
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
|
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
|
|
|
|
|
error_snapshot_id);
|
|
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2010-12-12 08:00:34 -05:00
|
|
|
/* Socket buffer full. Let's try again later from the same point */
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2010-12-12 08:00:34 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.px = proxy;
|
|
|
|
|
appctx->ctx.errors.buf = 0;
|
|
|
|
|
appctx->ctx.errors.bol = 0;
|
|
|
|
|
appctx->ctx.errors.ptr = -1;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we have two inner loops here, one for the proxy, the other one for
|
|
|
|
|
* the buffer.
|
|
|
|
|
*/
|
2013-12-01 03:15:12 -05:00
|
|
|
while (appctx->ctx.errors.px) {
|
2009-03-04 09:53:18 -05:00
|
|
|
struct error_snapshot *es;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.errors.buf == 0)
|
|
|
|
|
es = &appctx->ctx.errors.px->invalid_req;
|
2009-03-04 09:53:18 -05:00
|
|
|
else
|
2013-12-01 03:15:12 -05:00
|
|
|
es = &appctx->ctx.errors.px->invalid_rep;
|
2009-03-04 09:53:18 -05:00
|
|
|
|
|
|
|
|
if (!es->when.tv_sec)
|
|
|
|
|
goto next;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.errors.iid >= 0 &&
|
|
|
|
|
appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
|
|
|
|
|
es->oe->uuid != appctx->ctx.errors.iid)
|
2009-03-04 09:53:18 -05:00
|
|
|
goto next;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.errors.ptr < 0) {
|
2009-03-04 09:53:18 -05:00
|
|
|
/* just print headers now */
|
|
|
|
|
|
|
|
|
|
char pn[INET6_ADDRSTRLEN];
|
|
|
|
|
struct tm tm;
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
int port;
|
2009-03-04 09:53:18 -05:00
|
|
|
|
|
|
|
|
get_localtime(es->when.tv_sec, &tm);
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
|
2009-03-04 09:53:18 -05:00
|
|
|
tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
|
2009-04-03 08:49:12 -04:00
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
|
2009-03-04 09:53:18 -05:00
|
|
|
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
switch (addr_to_str(&es->src, pn, sizeof(pn))) {
|
|
|
|
|
case AF_INET:
|
|
|
|
|
case AF_INET6:
|
|
|
|
|
port = get_host_port(&es->src);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
port = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
switch (appctx->ctx.errors.buf) {
|
2009-03-04 09:53:18 -05:00
|
|
|
case 0:
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-04 09:53:18 -05:00
|
|
|
" frontend %s (#%d): invalid request\n"
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
" backend %s (#%d)",
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
(es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
|
|
|
|
|
(es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
|
2009-03-04 09:53:18 -05:00
|
|
|
break;
|
|
|
|
|
case 1:
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2014-09-08 05:23:00 -04:00
|
|
|
" backend %s (#%d): invalid response\n"
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
" frontend %s (#%d)",
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
es->oe->id, es->oe->uuid);
|
2009-03-04 09:53:18 -05:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
MEDIUM: http: improve error capture reports
A number of important information were missing from the error captures, so
let's improve them. Now we also log source port, session flags, transaction
flags, message flags, pending output bytes, expected buffer wrapping position,
total bytes transferred, message chunk length, and message body length.
As such, the output format has slightly evolved and the source address moved
to the third line :
[08/May/2012:11:14:36.341] frontend echo (#1): invalid request
backend echo (#1), server <NONE> (#-1), event #1
src 127.0.0.1:40616, session #4, session flags 0x00000000
HTTP msg state 26, msg flags 0x00000000, tx flags 0x00000000
HTTP chunk len 0 bytes, HTTP body len 0 bytes
buffer flags 0x00909002, out 0 bytes, total 28 bytes
pending 28 bytes, wrapping at 8030, error at position 7:
00000 GET / /?t=20000 HTTP/1.1\r\n
00026 \r\n
[08/May/2012:11:13:13.426] backend echo (#1) : invalid response
frontend echo (#1), server local (#1), event #0
src 127.0.0.1:40615, session #1, session flags 0x0000044e
HTTP msg state 32, msg flags 0x0000000e, tx flags 0x08200000
HTTP chunk len 0 bytes, HTTP body len 20 bytes
buffer flags 0x00008002, out 81 bytes, total 92 bytes
pending 11 bytes, wrapping at 7949, error at position 9:
00000 Foo: bar\r\r\n
2012-05-08 05:03:10 -04:00
|
|
|
", server %s (#%d), event #%u\n"
|
|
|
|
|
" src %s:%d, session #%d, session flags 0x%08x\n"
|
|
|
|
|
" HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
|
|
|
|
|
" HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
|
|
|
|
|
" buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
|
|
|
|
|
" pending %d bytes, wrapping at %d, error at position %d:\n \n",
|
|
|
|
|
es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
|
|
|
|
|
es->ev_id,
|
|
|
|
|
pn, port, es->sid, es->s_flags,
|
|
|
|
|
es->state, es->m_flags, es->t_flags,
|
|
|
|
|
es->m_clen, es->m_blen,
|
|
|
|
|
es->b_flags, es->b_out, es->b_tot,
|
|
|
|
|
es->len, es->b_wrap, es->pos);
|
|
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2009-03-04 09:53:18 -05:00
|
|
|
/* Socket buffer full. Let's try again later from the same point */
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2009-10-03 17:49:35 -04:00
|
|
|
return 0;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.ptr = 0;
|
|
|
|
|
appctx->ctx.errors.sid = es->sid;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
if (appctx->ctx.errors.sid != es->sid) {
|
2009-03-04 09:53:18 -05:00
|
|
|
/* the snapshot changed while we were dumping it */
|
2012-10-29 11:51:55 -04:00
|
|
|
chunk_appendf(&trash,
|
2009-03-04 09:53:18 -05:00
|
|
|
" WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
|
2015-03-13 09:00:47 -04:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2009-10-03 17:49:35 -04:00
|
|
|
return 0;
|
2015-03-13 09:00:47 -04:00
|
|
|
}
|
2009-03-04 09:53:18 -05:00
|
|
|
goto next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* OK, ptr >= 0, so we have to dump the current line */
|
2016-03-31 07:45:10 -04:00
|
|
|
while (es->buf && appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < global.tune.bufsize) {
|
2009-03-04 09:53:18 -05:00
|
|
|
int newptr;
|
|
|
|
|
int newline;
|
|
|
|
|
|
2013-12-01 03:15:12 -05:00
|
|
|
newline = appctx->ctx.errors.bol;
|
2016-03-31 07:45:10 -04:00
|
|
|
newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->len, &newline, appctx->ctx.errors.ptr);
|
2013-12-01 03:15:12 -05:00
|
|
|
if (newptr == appctx->ctx.errors.ptr)
|
2009-10-03 17:49:35 -04:00
|
|
|
return 0;
|
2009-03-04 09:53:18 -05:00
|
|
|
|
2014-11-28 05:11:05 -05:00
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
2009-03-04 09:53:18 -05:00
|
|
|
/* Socket buffer full. Let's try again later from the same point */
|
2015-04-21 13:23:39 -04:00
|
|
|
si_applet_cant_put(si);
|
2009-10-03 17:49:35 -04:00
|
|
|
return 0;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.ptr = newptr;
|
|
|
|
|
appctx->ctx.errors.bol = newline;
|
2009-03-04 09:53:18 -05:00
|
|
|
};
|
|
|
|
|
next:
|
2013-12-01 03:15:12 -05:00
|
|
|
appctx->ctx.errors.bol = 0;
|
|
|
|
|
appctx->ctx.errors.ptr = -1;
|
|
|
|
|
appctx->ctx.errors.buf++;
|
|
|
|
|
if (appctx->ctx.errors.buf > 1) {
|
|
|
|
|
appctx->ctx.errors.buf = 0;
|
|
|
|
|
appctx->ctx.errors.px = appctx->ctx.errors.px->next;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* dump complete */
|
2009-10-03 17:49:35 -04:00
|
|
|
return 1;
|
2009-03-04 09:53:18 -05:00
|
|
|
}
|
|
|
|
|
|
2016-02-16 05:27:28 -05:00
|
|
|
/* This function dumps all environmnent variables to the buffer. It returns 0
|
|
|
|
|
* if the output buffer is full and it needs to be called again, otherwise
|
|
|
|
|
* non-zero. Dumps only one entry if st2 == STAT_ST_END.
|
|
|
|
|
*/
|
|
|
|
|
static int stats_dump_env_to_buffer(struct stream_interface *si)
|
|
|
|
|
{
|
|
|
|
|
struct appctx *appctx = __objt_appctx(si->end);
|
|
|
|
|
|
|
|
|
|
if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
chunk_reset(&trash);
|
|
|
|
|
|
|
|
|
|
/* we have two inner loops here, one for the proxy, the other one for
|
|
|
|
|
* the buffer.
|
|
|
|
|
*/
|
|
|
|
|
while (*appctx->ctx.env.var) {
|
|
|
|
|
chunk_printf(&trash, "%s\n", *appctx->ctx.env.var);
|
|
|
|
|
|
|
|
|
|
if (bi_putchk(si_ic(si), &trash) == -1) {
|
|
|
|
|
si_applet_cant_put(si);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (appctx->st2 == STAT_ST_END)
|
|
|
|
|
break;
|
|
|
|
|
appctx->ctx.env.var++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* dump complete */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-22 13:32:35 -04:00
|
|
|
/* parse the "level" argument on the bind lines */
|
|
|
|
|
static int bind_parse_level(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err)
|
|
|
|
|
{
|
|
|
|
|
if (!*args[cur_arg + 1]) {
|
|
|
|
|
memprintf(err, "'%s' : missing level", args[cur_arg]);
|
|
|
|
|
return ERR_ALERT | ERR_FATAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!strcmp(args[cur_arg+1], "user"))
|
|
|
|
|
conf->level = ACCESS_LVL_USER;
|
|
|
|
|
else if (!strcmp(args[cur_arg+1], "operator"))
|
|
|
|
|
conf->level = ACCESS_LVL_OPER;
|
|
|
|
|
else if (!strcmp(args[cur_arg+1], "admin"))
|
|
|
|
|
conf->level = ACCESS_LVL_ADMIN;
|
|
|
|
|
else {
|
|
|
|
|
memprintf(err, "'%s' only supports 'user', 'operator', and 'admin' (got '%s')",
|
|
|
|
|
args[cur_arg], args[cur_arg+1]);
|
|
|
|
|
return ERR_ALERT | ERR_FATAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-13 07:50:30 -04:00
|
|
|
static struct applet cli_applet = {
|
2012-11-11 18:42:33 -05:00
|
|
|
.obj_type = OBJ_TYPE_APPLET,
|
2011-02-13 07:16:36 -05:00
|
|
|
.name = "<CLI>", /* used for logging */
|
|
|
|
|
.fct = cli_io_handler,
|
2012-11-25 20:22:40 -05:00
|
|
|
.release = cli_release_handler,
|
2011-02-13 07:16:36 -05:00
|
|
|
};
|
2008-12-07 16:29:48 -05:00
|
|
|
|
2013-06-21 17:16:39 -04:00
|
|
|
static struct cfg_kw_list cfg_kws = {ILH, {
|
2008-07-09 14:12:41 -04:00
|
|
|
{ CFG_GLOBAL, "stats", stats_parse_global },
|
|
|
|
|
{ 0, NULL, NULL },
|
|
|
|
|
}};
|
|
|
|
|
|
2012-09-22 13:32:35 -04:00
|
|
|
static struct bind_kw_list bind_kws = { "STAT", { }, {
|
|
|
|
|
{ "level", bind_parse_level, 1 }, /* set the unix socket admin level */
|
|
|
|
|
{ NULL, NULL, 0 },
|
|
|
|
|
}};
|
|
|
|
|
|
2008-07-09 14:12:41 -04:00
|
|
|
__attribute__((constructor))
|
|
|
|
|
static void __dumpstats_module_init(void)
|
|
|
|
|
{
|
|
|
|
|
cfg_register_keywords(&cfg_kws);
|
2012-09-22 13:32:35 -04:00
|
|
|
bind_register_keywords(&bind_kws);
|
2008-07-09 14:12:41 -04:00
|
|
|
}
|
|
|
|
|
|
2007-10-17 11:06:05 -04:00
|
|
|
/*
|
|
|
|
|
* Local variables:
|
|
|
|
|
* c-indent-level: 8
|
|
|
|
|
* c-basic-offset: 8
|
|
|
|
|
* End:
|
|
|
|
|
*/
|