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 @ 1 wt . 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>
2017-04-05 16:24:59 -04:00
# include <net/if.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>
2016-11-21 11:49:11 -05:00
# include <types/cli.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>
2016-11-21 11:49:11 -05:00
# include <types/stats.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>
2016-11-21 11:49:11 -05:00
# include <proto/stats.h>
2007-10-17 11:06:05 -04:00
# 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>
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>
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>
2017-07-25 13:32:50 -04:00
# include <proto/proto_udp.h>
2007-10-17 11:06:05 -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 "
" help : this message \n "
" prompt : toggle interactive mode with prompt \n "
" quit : disconnect \n "
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 ;
}
}
2016-12-22 14:44:00 -05:00
bind_conf = bind_conf_alloc ( global . stats_fe , file , line , args [ 2 ] , xprt_get ( XPRT_RAW ) ) ;
2017-05-23 18:57:40 -04:00
bind_conf - > level & = ~ ACCESS_LVL_MASK ;
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 ;
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 ;
}
2016-11-23 11:01:39 -05:00
/* Verifies that the CLI at least has a level at least as high as <level>
* ( typically ACCESS_LVL_ADMIN ) . Returns 1 if OK , otherwise 0. In case of
* failure , an error message is prepared and the appctx ' s state is adjusted
* to print it so that a return 1 is enough to abort any processing .
*/
int cli_has_level ( struct appctx * appctx , int level )
{
struct stream_interface * si = appctx - > owner ;
struct stream * s = si_strm ( si ) ;
2017-05-23 18:57:40 -04:00
if ( ( strm_li ( s ) - > bind_conf - > level & ACCESS_LVL_MASK ) < level ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-23 11:01:39 -05:00
appctx - > ctx . cli . msg = stats_permission_denied_msg ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-23 11:01:39 -05:00
return 0 ;
}
return 1 ;
}
2017-07-20 05:59:48 -04:00
/* Returns severity_output for the current session if set, or default for the socket */
static int cli_get_severity_output ( struct appctx * appctx )
{
if ( appctx - > cli_severity_output )
return appctx - > cli_severity_output ;
return strm_li ( si_strm ( appctx - > owner ) ) - > bind_conf - > severity_output ;
}
2007-11-30 12:16:29 -05:00
2016-11-24 10:23:38 -05:00
/* Processes the CLI interpreter on the stats socket. This function is called
* from the CLI ' s IO handler running in an appctx context . The function returns 1
* if the request was understood , otherwise zero . It is called with appctx - > st0
* set to CLI_ST_GETREQ and presets - > st2 to 0 so that parsers don ' t have to do
* it . It will possilbly leave st0 to CLI_ST_CALLBACK if the keyword needs to
* have its own I / O handler called again . Most of the time , parsers will only
* set st0 to CLI_ST_PRINT and put their message to be displayed into cli . msg .
2016-12-16 11:59:25 -05:00
* If a keyword parser is NULL and an I / O handler is declared , the I / O handler
* will automatically be used .
2009-08-16 13:06:42 -04:00
*/
2016-11-24 10:23:38 -05:00
static int cli_parse_request ( struct appctx * appctx , char * line )
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 ;
2016-11-24 05:33:12 -05:00
/* unescape '\' */
2013-12-10 12:54:58 -05:00
arg = 0 ;
while ( * args [ arg ] ! = ' \0 ' ) {
j = 0 ;
for ( i = 0 ; args [ arg ] [ i ] ! = ' \0 ' ; i + + ) {
2016-11-24 05:33:12 -05:00
if ( args [ arg ] [ i ] = = ' \\ ' ) {
if ( args [ arg ] [ i + 1 ] = = ' \\ ' )
i + + ;
else
continue ;
}
2013-12-10 12:54:58 -05:00
args [ arg ] [ j ] = args [ arg ] [ i ] ;
j + + ;
}
args [ arg ] [ j ] = ' \0 ' ;
arg + + ;
}
2016-11-24 10:23:38 -05:00
appctx - > st2 = 0 ;
2016-12-16 06:37:03 -05:00
memset ( & appctx - > ctx . cli , 0 , sizeof ( appctx - > ctx . cli ) ) ;
2016-11-24 10:23:38 -05:00
kw = cli_find_kw ( args ) ;
2016-12-16 11:59:25 -05:00
if ( ! kw )
2016-11-24 10:23:38 -05:00
return 0 ;
appctx - > io_handler = kw - > io_handler ;
2017-06-29 13:54:13 -04:00
appctx - > io_release = kw - > io_release ;
/* kw->parse could set its own io_handler or ip_release handler */
2016-12-16 11:59:25 -05:00
if ( ( ! kw - > parse | | kw - > parse ( args , appctx , kw - > private ) = = 0 ) & & appctx - > io_handler ) {
2016-11-24 10:23:38 -05:00
appctx - > st0 = CLI_ST_CALLBACK ;
2009-08-16 13:06:42 -04:00
}
2016-11-24 10:23:38 -05:00
return 1 ;
2009-08-16 13:06:42 -04:00
}
2017-07-20 05:59:48 -04:00
/* prepends then outputs the argument msg with a syslog-type severity depending on severity_output value */
static int cli_output_msg ( struct channel * chn , const char * msg , int severity , int severity_output )
{
struct chunk * tmp ;
if ( likely ( severity_output = = CLI_SEVERITY_NONE ) )
return bi_putblk ( chn , msg , strlen ( msg ) ) ;
tmp = get_trash_chunk ( ) ;
chunk_reset ( tmp ) ;
if ( severity < 0 | | severity > 7 ) {
Warning ( " socket command feedback with invalid severity %d " , severity ) ;
chunk_printf ( tmp , " [%d]: " , severity ) ;
}
else {
switch ( severity_output ) {
case CLI_SEVERITY_NUMBER :
chunk_printf ( tmp , " [%d]: " , severity ) ;
break ;
case CLI_SEVERITY_STRING :
chunk_printf ( tmp , " [%s]: " , log_levels [ severity ] ) ;
break ;
default :
Warning ( " Unrecognized severity output %d " , severity_output ) ;
}
}
chunk_appendf ( tmp , " %s " , msg ) ;
return bi_putblk ( chn , tmp - > str , strlen ( tmp - > str ) ) ;
}
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
2016-11-24 09:53:53 -05:00
* CLI_ST_ * 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 ) ;
2017-07-20 05:59:48 -04:00
struct bind_conf * bind_conf = strm_li ( si_strm ( si ) ) - > bind_conf ;
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 ;
BUG/MAJOR: Fix how the list of entities waiting for a buffer is handled
When an entity tries to get a buffer, if it cannot be allocted, for example
because the number of buffers which may be allocated per process is limited,
this entity is added in a list (called <buffer_wq>) and wait for an available
buffer.
Historically, the <buffer_wq> list was logically attached to streams because it
were the only entities likely to be added in it. Now, applets can also be
waiting for a free buffer. And with filters, we could imagine to have more other
entities waiting for a buffer. So it make sense to have a generic list.
Anyway, with the current design there is a bug. When an applet failed to get a
buffer, it will wait. But we add the stream attached to the applet in
<buffer_wq>, instead of the applet itself. So when a buffer is available, we
wake up the stream and not the waiting applet. So, it is possible to have
waiting applets and never awakened.
So, now, <buffer_wq> is independant from streams. And we really add the waiting
entity in <buffer_wq>. To be generic, the entity is responsible to define the
callback used to awaken it.
In addition, applets will still request an input buffer when they become
active. But they will not be sleeped anymore if no buffer are available. So this
is the responsibility to the applet I/O handler to check if this buffer is
allocated or not. This way, an applet can decide if this buffer is required or
not and can do additional processing if not.
[wt: backport to 1.7 and 1.6]
2016-12-09 11:30:18 -05:00
/* Check if the input buffer is avalaible. */
if ( res - > buf - > size = = 0 ) {
si_applet_cant_put ( si ) ;
goto out ;
}
2009-09-22 13:31:03 -04:00
while ( 1 ) {
2016-11-24 09:53:53 -05:00
if ( appctx - > st0 = = CLI_ST_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 ) ) ;
2017-07-20 05:59:48 -04:00
/* reset severity to default at init */
appctx - > cli_severity_output = bind_conf - > severity_output ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_GETREQ ;
2009-09-22 13:31:03 -04:00
}
2016-11-24 09:53:53 -05:00
else if ( appctx - > st0 = = CLI_ST_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 ;
}
2016-11-24 09:53:53 -05:00
else if ( appctx - > st0 = = CLI_ST_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 ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 ' ) {
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 ) {
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 | |
2016-11-24 10:23:38 -05:00
! cli_parse_request ( appctx , 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 ( ) ;
2017-07-20 10:49:14 -04:00
if ( dynamic_usage_msg ) {
appctx - > ctx . cli . severity = LOG_INFO ;
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 - > ctx . cli . msg = dynamic_usage_msg ;
2017-07-20 10:49:14 -04:00
}
else {
appctx - > ctx . cli . severity = LOG_INFO ;
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 - > ctx . cli . msg = stats_sock_usage_msg ;
2017-07-20 10:49:14 -04:00
}
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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
2016-11-24 09:53:53 -05:00
* another CLI_ST_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 ( ) ;
2017-07-20 10:49:14 -04:00
if ( dynamic_usage_msg ) {
appctx - > ctx . cli . severity = LOG_INFO ;
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 - > ctx . cli . msg = dynamic_usage_msg ;
2017-07-20 10:49:14 -04:00
}
else {
appctx - > ctx . cli . severity = LOG_INFO ;
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 - > ctx . cli . msg = stats_sock_usage_msg ;
2017-07-20 10:49:14 -04:00
}
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 ) {
2016-11-24 09:53:53 -05:00
case CLI_ST_PROMPT :
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
break ;
2016-11-24 09:53:53 -05:00
case CLI_ST_PRINT :
2017-07-20 05:59:48 -04:00
if ( cli_output_msg ( res , appctx - > ctx . cli . msg , appctx - > ctx . cli . severity ,
cli_get_severity_output ( appctx ) ) ! = - 1 )
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 ;
2016-11-24 09:53:53 -05:00
case CLI_ST_PRINT_FREE :
2017-07-20 05:59:48 -04:00
if ( cli_output_msg ( res , appctx - > ctx . cli . err , LOG_ERR , cli_get_severity_output ( appctx ) ) ! = - 1 ) {
2014-01-29 13:08:49 -05:00
free ( appctx - > ctx . cli . err ) ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PROMPT ;
2014-01-29 13:08:49 -05:00
}
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 ;
2016-11-24 09:53:53 -05:00
case CLI_ST_CALLBACK : /* use custom pointer */
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 ( appctx - > io_handler )
2016-11-12 04:51:33 -05:00
if ( appctx - > io_handler ( appctx ) ) {
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 */
2016-11-24 09:53:53 -05:00
if ( appctx - > st0 = = CLI_ST_PROMPT ) {
2014-11-28 05:11:05 -05:00
if ( bi_putstr ( si_ic ( si ) , appctx - > st1 ? " \n > " : " \n " ) ! = - 1 )
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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. */
2016-11-24 09:53:53 -05:00
if ( appctx - > st0 > = CLI_ST_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 ) ) {
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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 */
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_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
}
2016-11-24 09:53:53 -05:00
if ( ( req - > flags & CF_SHUTW ) & & ( si - > state = = SI_ST_EST ) & & ( appctx - > st0 < CLI_ST_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
}
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 ;
}
2016-11-24 09:53:53 -05:00
else if ( appctx - > st0 = = CLI_ST_PRINT_FREE ) {
2014-01-29 13:08:49 -05:00
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
}
2012-11-25 20:22:40 -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
2016-12-16 11:45:44 -05:00
* non - zero . Dumps only one entry if st2 = = STAT_ST_END . It uses cli . p0 as the
* pointer to the current variable .
2016-02-16 05:27:28 -05:00
*/
2016-11-22 14:21:23 -05:00
static int cli_io_handler_show_env ( struct appctx * appctx )
2016-02-16 05:27:28 -05:00
{
2016-11-22 14:21:23 -05:00
struct stream_interface * si = appctx - > owner ;
2016-12-16 11:45:44 -05:00
char * * var = appctx - > ctx . cli . p0 ;
2016-02-16 05:27:28 -05:00
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 .
*/
2016-12-16 11:45:44 -05:00
while ( * var ) {
chunk_printf ( & trash , " %s \n " , * var ) ;
2016-02-16 05:27:28 -05:00
if ( bi_putchk ( si_ic ( si ) , & trash ) = = - 1 ) {
si_applet_cant_put ( si ) ;
return 0 ;
}
if ( appctx - > st2 = = STAT_ST_END )
break ;
2016-12-16 11:45:44 -05:00
var + + ;
appctx - > ctx . cli . p0 = var ;
2016-02-16 05:27:28 -05:00
}
/* dump complete */
return 1 ;
}
2017-07-25 13:32:50 -04:00
/* This function dumps all file descriptors states (or the requested one) 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 .
* It uses cli . i0 as the fd number to restart from .
*/
static int cli_io_handler_show_fd ( struct appctx * appctx )
{
struct stream_interface * si = appctx - > owner ;
int fd = appctx - > ctx . cli . i0 ;
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 ( fd < maxfd ) {
struct fdtab fdt ;
2017-08-09 10:35:44 -04:00
struct listener * li = NULL ;
struct server * sv = NULL ;
struct proxy * px = NULL ;
uint32_t conn_flags = 0 ;
2017-07-25 13:32:50 -04:00
fdt = fdtab [ fd ] ;
if ( fdt . iocb = = conn_fd_handler ) {
conn_flags = ( ( struct connection * ) fdt . owner ) - > flags ;
li = objt_listener ( ( ( struct connection * ) fdt . owner ) - > target ) ;
sv = objt_server ( ( ( struct connection * ) fdt . owner ) - > target ) ;
px = objt_proxy ( ( ( struct connection * ) fdt . owner ) - > target ) ;
}
else if ( fdt . iocb = = listener_accept )
li = fdt . owner ;
if ( ! fdt . owner )
goto skip ; // closed
chunk_printf ( & trash ,
" %5d : st=0x%02x(R:%c%c%c W:%c%c%c) ev=0x%02x(%c%c%c%c%c) [%c%c%c%c] cache=%u owner=%p iocb=%p(%s) " ,
fd ,
fdt . state ,
( fdt . state & FD_EV_POLLED_R ) ? ' P ' : ' p ' ,
( fdt . state & FD_EV_READY_R ) ? ' R ' : ' r ' ,
( fdt . state & FD_EV_ACTIVE_R ) ? ' A ' : ' a ' ,
( fdt . state & FD_EV_POLLED_W ) ? ' P ' : ' p ' ,
( fdt . state & FD_EV_READY_W ) ? ' R ' : ' r ' ,
( fdt . state & FD_EV_ACTIVE_W ) ? ' A ' : ' a ' ,
fdt . ev ,
( fdt . ev & FD_POLL_HUP ) ? ' H ' : ' h ' ,
( fdt . ev & FD_POLL_ERR ) ? ' E ' : ' e ' ,
( fdt . ev & FD_POLL_OUT ) ? ' O ' : ' o ' ,
( fdt . ev & FD_POLL_PRI ) ? ' P ' : ' p ' ,
( fdt . ev & FD_POLL_IN ) ? ' I ' : ' i ' ,
fdt . new ? ' N ' : ' n ' ,
fdt . updated ? ' U ' : ' u ' ,
fdt . linger_risk ? ' L ' : ' l ' ,
fdt . cloned ? ' C ' : ' c ' ,
fdt . cache ,
fdt . owner ,
fdt . iocb ,
( fdt . iocb = = conn_fd_handler ) ? " conn_fd_handler " :
( fdt . iocb = = dgram_fd_handler ) ? " dgram_fd_handler " :
( fdt . iocb = = listener_accept ) ? " listener_accept " :
" unknown " ) ;
if ( fdt . iocb = = conn_fd_handler ) {
chunk_appendf ( & trash , " cflg=0x%08x " , conn_flags ) ;
if ( px )
chunk_appendf ( & trash , " px=%s " , px - > id ) ;
else if ( sv )
chunk_appendf ( & trash , " sv=%s/%s " , sv - > id , sv - > proxy - > id ) ;
else if ( li )
chunk_appendf ( & trash , " fe=%s " , li - > bind_conf - > frontend - > id ) ;
}
else if ( fdt . iocb = = listener_accept ) {
chunk_appendf ( & trash , " l.st=%s fe=%s " ,
listener_state_str ( li ) ,
li - > bind_conf - > frontend - > id ) ;
}
chunk_appendf ( & trash , " \n " ) ;
if ( bi_putchk ( si_ic ( si ) , & trash ) = = - 1 ) {
si_applet_cant_put ( si ) ;
return 0 ;
}
skip :
if ( appctx - > st2 = = STAT_ST_END )
break ;
fd + + ;
appctx - > ctx . cli . i0 = fd ;
}
/* dump complete */
return 1 ;
}
2016-12-15 12:06:44 -05:00
/*
2016-12-16 06:58:09 -05:00
* CLI IO handler for ` show cli sockets ` .
* Uses ctx . cli . p0 to store the restart pointer .
2016-12-15 12:06:44 -05:00
*/
static int cli_io_handler_show_cli_sock ( struct appctx * appctx )
{
struct bind_conf * bind_conf ;
struct stream_interface * si = appctx - > owner ;
chunk_reset ( & trash ) ;
switch ( appctx - > st2 ) {
case STAT_ST_INIT :
chunk_printf ( & trash , " # socket lvl processes \n " ) ;
if ( bi_putchk ( si_ic ( si ) , & trash ) = = - 1 ) {
si_applet_cant_put ( si ) ;
return 0 ;
}
appctx - > st2 = STAT_ST_LIST ;
case STAT_ST_LIST :
if ( global . stats_fe ) {
list_for_each_entry ( bind_conf , & global . stats_fe - > conf . bind , by_fe ) {
struct listener * l ;
/*
2016-12-16 06:58:09 -05:00
* get the latest dumped node in appctx - > ctx . cli . p0
2016-12-15 12:06:44 -05:00
* if the current node is the first of the list
*/
2016-12-16 06:58:09 -05:00
if ( appctx - > ctx . cli . p0 & &
& bind_conf - > by_fe = = ( & global . stats_fe - > conf . bind ) - > n ) {
2016-12-15 12:06:44 -05:00
/* change the current node to the latest dumped and continue the loop */
2016-12-16 06:58:09 -05:00
bind_conf = LIST_ELEM ( appctx - > ctx . cli . p0 , typeof ( bind_conf ) , by_fe ) ;
2016-12-15 12:06:44 -05:00
continue ;
}
list_for_each_entry ( l , & bind_conf - > listeners , by_bind ) {
char addr [ 46 ] ;
char port [ 6 ] ;
if ( l - > addr . ss_family = = AF_UNIX ) {
const struct sockaddr_un * un ;
un = ( struct sockaddr_un * ) & l - > addr ;
chunk_appendf ( & trash , " %s " , un - > sun_path ) ;
} else if ( l - > addr . ss_family = = AF_INET ) {
addr_to_str ( & l - > addr , addr , sizeof ( addr ) ) ;
port_to_str ( & l - > addr , port , sizeof ( port ) ) ;
chunk_appendf ( & trash , " %s:%s " , addr , port ) ;
} else if ( l - > addr . ss_family = = AF_INET6 ) {
addr_to_str ( & l - > addr , addr , sizeof ( addr ) ) ;
port_to_str ( & l - > addr , port , sizeof ( port ) ) ;
chunk_appendf ( & trash , " [%s]:%s " , addr , port ) ;
} else
continue ;
2017-05-23 18:57:40 -04:00
if ( ( bind_conf - > level & ACCESS_LVL_MASK ) = = ACCESS_LVL_ADMIN )
2016-12-15 12:06:44 -05:00
chunk_appendf ( & trash , " admin " ) ;
2017-05-23 18:57:40 -04:00
else if ( ( bind_conf - > level & ACCESS_LVL_MASK ) = = ACCESS_LVL_OPER )
chunk_appendf ( & trash , " operator " ) ;
else if ( ( bind_conf - > level & ACCESS_LVL_MASK ) = = ACCESS_LVL_USER )
chunk_appendf ( & trash , " user " ) ;
2016-12-15 12:06:44 -05:00
else
chunk_appendf ( & trash , " " ) ;
if ( bind_conf - > bind_proc ! = 0 ) {
int pos ;
2016-12-16 06:50:55 -05:00
for ( pos = 0 ; pos < 8 * sizeof ( bind_conf - > bind_proc ) ; pos + + ) {
2016-12-16 06:56:31 -05:00
if ( bind_conf - > bind_proc & ( 1UL < < pos ) ) {
2016-12-15 12:06:44 -05:00
chunk_appendf ( & trash , " %d, " , pos + 1 ) ;
}
}
/* replace the latest comma by a newline */
trash . str [ trash . len - 1 ] = ' \n ' ;
} else {
chunk_appendf ( & trash , " all \n " ) ;
}
if ( bi_putchk ( si_ic ( si ) , & trash ) = = - 1 ) {
si_applet_cant_put ( si ) ;
return 0 ;
}
}
2016-12-16 06:58:09 -05:00
appctx - > ctx . cli . p0 = & bind_conf - > by_fe ; /* store the latest list node dumped */
2016-12-15 12:06:44 -05:00
}
}
default :
appctx - > st2 = STAT_ST_FIN ;
return 1 ;
}
}
2016-11-22 14:21:23 -05:00
/* parse a "show env" CLI request. Returns 0 if it needs to continue, 1 if it
2016-12-16 11:45:44 -05:00
* wants to stop here . It puts the variable to be dumped into cli . p0 if a single
* variable is requested otherwise puts environ there .
2016-11-22 14:21:23 -05:00
*/
static int cli_parse_show_env ( char * * args , struct appctx * appctx , void * private )
{
extern char * * environ ;
2016-12-16 11:45:44 -05:00
char * * var ;
2016-11-22 14:21:23 -05:00
if ( ! cli_has_level ( appctx , ACCESS_LVL_OPER ) )
return 1 ;
2016-12-16 11:45:44 -05:00
var = environ ;
2016-11-22 14:21:23 -05:00
if ( * args [ 2 ] ) {
int len = strlen ( args [ 2 ] ) ;
2016-12-16 11:45:44 -05:00
for ( ; * var ; var + + ) {
if ( strncmp ( * var , args [ 2 ] , len ) = = 0 & &
( * var ) [ len ] = = ' = ' )
2016-11-22 14:21:23 -05:00
break ;
}
2016-12-16 11:45:44 -05:00
if ( ! * var ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-22 14:21:23 -05:00
appctx - > ctx . cli . msg = " Variable not found \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-22 14:21:23 -05:00
return 1 ;
}
appctx - > st2 = STAT_ST_END ;
}
2016-12-16 11:45:44 -05:00
appctx - > ctx . cli . p0 = var ;
2016-11-22 14:21:23 -05:00
return 0 ;
}
2017-07-25 13:32:50 -04:00
/* parse a "show fd" CLI request. Returns 0 if it needs to continue, 1 if it
* wants to stop here . It puts the FD number into cli . i0 if a specific FD is
* requested and sets st2 to STAT_ST_END , otherwise leaves 0 in i0 .
*/
static int cli_parse_show_fd ( char * * args , struct appctx * appctx , void * private )
{
if ( ! cli_has_level ( appctx , ACCESS_LVL_OPER ) )
return 1 ;
appctx - > ctx . cli . i0 = 0 ;
if ( * args [ 2 ] ) {
appctx - > ctx . cli . i0 = atoi ( args [ 2 ] ) ;
appctx - > st2 = STAT_ST_END ;
}
return 0 ;
}
2016-11-22 14:33:32 -05:00
/* parse a "set timeout" CLI request. It always returns 1. */
static int cli_parse_set_timeout ( char * * args , struct appctx * appctx , void * private )
{
struct stream_interface * si = appctx - > owner ;
struct stream * s = si_strm ( si ) ;
if ( strcmp ( args [ 2 ] , " cli " ) = = 0 ) {
unsigned timeout ;
const char * res ;
if ( ! * args [ 3 ] ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-22 14:33:32 -05:00
appctx - > ctx . cli . msg = " Expects an integer value. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-22 14:33:32 -05:00
return 1 ;
}
res = parse_time_err ( args [ 3 ] , & timeout , TIME_UNIT_S ) ;
if ( res | | timeout < 1 ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-22 14:33:32 -05:00
appctx - > ctx . cli . msg = " Invalid timeout value. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-22 14:33:32 -05:00
return 1 ;
}
s - > req . rto = s - > res . wto = 1 + MS_TO_TICKS ( timeout * 1000 ) ;
task_wakeup ( s - > task , TASK_WOKEN_MSG ) ; // recompute timeouts
return 1 ;
}
else {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-22 14:33:32 -05:00
appctx - > ctx . cli . msg = " 'set timeout' only supports 'cli'. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-22 14:33:32 -05:00
return 1 ;
}
}
2016-11-23 05:10:59 -05:00
/* parse a "set maxconn global" command. It always returns 1. */
static int cli_parse_set_maxconn_global ( char * * args , struct appctx * appctx , void * private )
{
int v ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_ADMIN ) )
return 1 ;
if ( ! * args [ 3 ] ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-23 05:10:59 -05:00
appctx - > ctx . cli . msg = " Expects an integer value. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-23 05:10:59 -05:00
return 1 ;
}
v = atoi ( args [ 3 ] ) ;
if ( v > global . hardmaxconn ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-23 05:10:59 -05:00
appctx - > ctx . cli . msg = " Value out of range. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-23 05:10:59 -05: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 ;
}
2017-07-20 05:59:48 -04:00
static int set_severity_output ( int * target , char * argument )
{
if ( ! strcmp ( argument , " none " ) ) {
* target = CLI_SEVERITY_NONE ;
return 1 ;
}
else if ( ! strcmp ( argument , " number " ) ) {
* target = CLI_SEVERITY_NUMBER ;
return 1 ;
}
else if ( ! strcmp ( argument , " string " ) ) {
* target = CLI_SEVERITY_STRING ;
return 1 ;
}
return 0 ;
}
/* parse a "set severity-output" command. */
static int cli_parse_set_severity_output ( char * * args , struct appctx * appctx , void * private )
{
if ( * args [ 2 ] & & set_severity_output ( & appctx - > cli_severity_output , args [ 2 ] ) )
return 0 ;
appctx - > ctx . cli . severity = LOG_ERR ;
appctx - > ctx . cli . msg = " one of 'none', 'number', 'string' is a required argument " ;
appctx - > st0 = CLI_ST_PRINT ;
return 1 ;
}
2016-12-15 12:06:44 -05:00
int cli_parse_default ( char * * args , struct appctx * appctx , void * private )
{
return 0 ;
}
2016-11-24 08:51:17 -05:00
/* parse a "set rate-limit" command. It always returns 1. */
static int cli_parse_set_ratelimit ( char * * args , struct appctx * appctx , void * private )
{
int v ;
int * res ;
int mul = 1 ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_ADMIN ) )
return 1 ;
if ( strcmp ( args [ 2 ] , " connections " ) = = 0 & & strcmp ( args [ 3 ] , " global " ) = = 0 )
res = & global . cps_lim ;
else if ( strcmp ( args [ 2 ] , " sessions " ) = = 0 & & strcmp ( args [ 3 ] , " global " ) = = 0 )
res = & global . sps_lim ;
# ifdef USE_OPENSSL
else if ( strcmp ( args [ 2 ] , " ssl-sessions " ) = = 0 & & strcmp ( args [ 3 ] , " global " ) = = 0 )
res = & global . ssl_lim ;
# endif
else if ( strcmp ( args [ 2 ] , " http-compression " ) = = 0 & & strcmp ( args [ 3 ] , " global " ) = = 0 ) {
res = & global . comp_rate_lim ;
mul = 1024 ;
}
else {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-24 08:51:17 -05:00
appctx - > ctx . cli . msg =
" 'set rate-limit' only supports : \n "
" - 'connections global' to set the per-process maximum connection rate \n "
" - 'sessions global' to set the per-process maximum session rate \n "
# ifdef USE_OPENSSL
" - 'ssl-session global' to set the per-process maximum SSL session rate \n "
# endif
" - 'http-compression global' to set the per-process maximum compression speed in kB/s \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-24 08:51:17 -05:00
return 1 ;
}
if ( ! * args [ 4 ] ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-24 08:51:17 -05:00
appctx - > ctx . cli . msg = " Expects an integer value. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-24 08:51:17 -05:00
return 1 ;
}
v = atoi ( args [ 4 ] ) ;
if ( v < 0 ) {
2017-07-20 10:49:14 -04:00
appctx - > ctx . cli . severity = LOG_ERR ;
2016-11-24 08:51:17 -05:00
appctx - > ctx . cli . msg = " Value out of range. \n " ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PRINT ;
2016-11-24 08:51:17 -05:00
return 1 ;
}
* res = v * mul ;
/* Dequeues all of the listeners waiting for a resource */
if ( ! LIST_ISEMPTY ( & global_listener_queue ) )
dequeue_all_listeners ( & global_listener_queue ) ;
return 1 ;
}
2017-05-26 11:42:10 -04:00
/* parse the "expose-fd" argument on the bind lines */
static int bind_parse_expose_fd ( char * * args , int cur_arg , struct proxy * px , struct bind_conf * conf , char * * err )
{
if ( ! * args [ cur_arg + 1 ] ) {
memprintf ( err , " '%s' : missing fd type " , args [ cur_arg ] ) ;
return ERR_ALERT | ERR_FATAL ;
}
if ( ! strcmp ( args [ cur_arg + 1 ] , " listeners " ) ) {
conf - > level | = ACCESS_FD_LISTENERS ;
} else {
memprintf ( err , " '%s' only supports 'listeners' (got '%s') " ,
args [ cur_arg ] , args [ cur_arg + 1 ] ) ;
return ERR_ALERT | ERR_FATAL ;
}
return 0 ;
}
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 ;
}
2017-05-23 18:57:40 -04:00
if ( ! strcmp ( args [ cur_arg + 1 ] , " user " ) ) {
conf - > level & = ~ ACCESS_LVL_MASK ;
conf - > level | = ACCESS_LVL_USER ;
} else if ( ! strcmp ( args [ cur_arg + 1 ] , " operator " ) ) {
conf - > level & = ~ ACCESS_LVL_MASK ;
conf - > level | = ACCESS_LVL_OPER ;
} else if ( ! strcmp ( args [ cur_arg + 1 ] , " admin " ) ) {
conf - > level & = ~ ACCESS_LVL_MASK ;
conf - > level | = ACCESS_LVL_ADMIN ;
} else {
2012-09-22 13:32:35 -04:00
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 ;
}
2017-07-20 05:59:48 -04:00
static int bind_parse_severity_output ( char * * args , int cur_arg , struct proxy * px , struct bind_conf * conf , char * * err )
{
if ( ! * args [ cur_arg + 1 ] ) {
memprintf ( err , " '%s' : missing severity format " , args [ cur_arg ] ) ;
return ERR_ALERT | ERR_FATAL ;
}
if ( set_severity_output ( & conf - > severity_output , args [ cur_arg + 1 ] ) )
return 0 ;
else {
memprintf ( err , " '%s' only supports 'none', 'number', and 'string' (got '%s') " ,
args [ cur_arg ] , args [ cur_arg + 1 ] ) ;
return ERR_ALERT | ERR_FATAL ;
}
}
2017-04-05 16:24:59 -04:00
/* Send all the bound sockets, always returns 1 */
static int _getsocks ( char * * args , struct appctx * appctx , void * private )
{
char * cmsgbuf = NULL ;
unsigned char * tmpbuf = NULL ;
struct cmsghdr * cmsg ;
struct stream_interface * si = appctx - > owner ;
2017-05-26 11:42:10 -04:00
struct stream * s = si_strm ( si ) ;
2017-04-05 16:24:59 -04:00
struct connection * remote = objt_conn ( si_opposite ( si ) - > end ) ;
struct msghdr msghdr ;
struct iovec iov ;
2017-04-06 08:45:14 -04:00
struct timeval tv = { . tv_sec = 1 , . tv_usec = 0 } ;
2017-04-05 16:24:59 -04:00
int * tmpfd ;
int tot_fd_nb = 0 ;
struct proxy * px ;
int i = 0 ;
2017-08-24 08:31:19 -04:00
int fd = remote - > handle . fd ;
2017-04-05 16:24:59 -04:00
int curoff = 0 ;
int old_fcntl ;
int ret ;
/* Temporary set the FD in blocking mode, that will make our life easier */
old_fcntl = fcntl ( fd , F_GETFL ) ;
if ( old_fcntl < 0 ) {
Warning ( " Couldn't get the flags for the unix socket \n " ) ;
goto out ;
}
cmsgbuf = malloc ( CMSG_SPACE ( sizeof ( int ) * MAX_SEND_FD ) ) ;
if ( ! cmsgbuf ) {
Warning ( " Failed to allocate memory to send sockets \n " ) ;
goto out ;
}
if ( fcntl ( fd , F_SETFL , old_fcntl & ~ O_NONBLOCK ) = = - 1 ) {
Warning ( " Cannot make the unix socket blocking \n " ) ;
goto out ;
}
2017-04-06 08:45:14 -04:00
setsockopt ( fd , SOL_SOCKET , SO_RCVTIMEO , ( void * ) & tv , sizeof ( tv ) ) ;
2017-04-05 16:24:59 -04:00
iov . iov_base = & tot_fd_nb ;
iov . iov_len = sizeof ( tot_fd_nb ) ;
2017-05-26 11:42:10 -04:00
if ( ! ( strm_li ( s ) - > bind_conf - > level & ACCESS_FD_LISTENERS ) )
2017-04-05 16:24:59 -04:00
goto out ;
memset ( & msghdr , 0 , sizeof ( msghdr ) ) ;
/*
* First , calculates the total number of FD , so that we can let
* the caller know how much he should expects .
*/
px = proxy ;
while ( px ) {
struct listener * l ;
list_for_each_entry ( l , & px - > conf . listeners , by_fe ) {
2017-04-05 19:05:05 -04:00
/* Only transfer IPv4/IPv6/UNIX sockets */
if ( l - > state > = LI_ZOMBIE & &
( l - > proto - > sock_family = = AF_INET | |
2017-04-05 16:24:59 -04:00
l - > proto - > sock_family = = AF_INET6 | |
2017-04-05 19:05:05 -04:00
l - > proto - > sock_family = = AF_UNIX ) )
2017-04-05 16:24:59 -04:00
tot_fd_nb + + ;
}
px = px - > next ;
}
if ( tot_fd_nb = = 0 )
goto out ;
/* First send the total number of file descriptors, so that the
* receiving end knows what to expect .
*/
msghdr . msg_iov = & iov ;
msghdr . msg_iovlen = 1 ;
ret = sendmsg ( fd , & msghdr , 0 ) ;
if ( ret ! = sizeof ( tot_fd_nb ) ) {
Warning ( " Failed to send the number of sockets to send \n " ) ;
goto out ;
}
/* Now send the fds */
msghdr . msg_control = cmsgbuf ;
msghdr . msg_controllen = CMSG_SPACE ( sizeof ( int ) * MAX_SEND_FD ) ;
cmsg = CMSG_FIRSTHDR ( & msghdr ) ;
cmsg - > cmsg_len = CMSG_LEN ( MAX_SEND_FD * sizeof ( int ) ) ;
cmsg - > cmsg_level = SOL_SOCKET ;
cmsg - > cmsg_type = SCM_RIGHTS ;
tmpfd = ( int * ) CMSG_DATA ( cmsg ) ;
px = proxy ;
/* For each socket, e message is sent, containing the following :
* Size of the namespace name ( or 0 if none ) , as an unsigned char .
* The namespace name , if any
* Size of the interface name ( or 0 if none ) , as an unsigned char
* The interface name , if any
* Listener options , as an int .
*/
/* We will send sockets MAX_SEND_FD per MAX_SEND_FD, allocate a
* buffer big enough to store the socket informations .
*/
tmpbuf = malloc ( MAX_SEND_FD * ( 1 + NAME_MAX + 1 + IFNAMSIZ + sizeof ( int ) ) ) ;
if ( tmpbuf = = NULL ) {
Warning ( " Failed to allocate memory to transfer socket informations \n " ) ;
goto out ;
}
iov . iov_base = tmpbuf ;
while ( px ) {
struct listener * l ;
list_for_each_entry ( l , & px - > conf . listeners , by_fe ) {
int ret ;
/* Only transfer IPv4/IPv6 sockets */
2017-04-05 19:05:05 -04:00
if ( l - > state > = LI_ZOMBIE & &
2017-04-05 16:24:59 -04:00
( l - > proto - > sock_family = = AF_INET | |
l - > proto - > sock_family = = AF_INET6 | |
l - > proto - > sock_family = = AF_UNIX ) ) {
memcpy ( & tmpfd [ i % MAX_SEND_FD ] , & l - > fd , sizeof ( l - > fd ) ) ;
if ( ! l - > netns )
tmpbuf [ curoff + + ] = 0 ;
# ifdef CONFIG_HAP_NS
else {
char * name = l - > netns - > node . key ;
unsigned char len = l - > netns - > name_len ;
tmpbuf [ curoff + + ] = len ;
memcpy ( tmpbuf + curoff , name , len ) ;
curoff + = len ;
}
# endif
if ( l - > interface ) {
unsigned char len = strlen ( l - > interface ) ;
tmpbuf [ curoff + + ] = len ;
memcpy ( tmpbuf + curoff , l - > interface , len ) ;
curoff + = len ;
} else
tmpbuf [ curoff + + ] = 0 ;
memcpy ( tmpbuf + curoff , & l - > options ,
sizeof ( l - > options ) ) ;
curoff + = sizeof ( l - > options ) ;
i + + ;
} else
continue ;
if ( ( ! ( i % MAX_SEND_FD ) ) ) {
iov . iov_len = curoff ;
if ( sendmsg ( fd , & msghdr , 0 ) ! = curoff ) {
Warning ( " Failed to transfer sockets \n " ) ;
printf ( " errno %d \n " , errno ) ;
goto out ;
}
/* Wait for an ack */
do {
ret = recv ( fd , & tot_fd_nb ,
sizeof ( tot_fd_nb ) , 0 ) ;
} while ( ret = = - 1 & & errno = = EINTR ) ;
if ( ret < = 0 ) {
Warning ( " Unexpected error while transferring sockets \n " ) ;
goto out ;
}
curoff = 0 ;
}
}
px = px - > next ;
}
if ( i % MAX_SEND_FD ) {
iov . iov_len = curoff ;
cmsg - > cmsg_len = CMSG_LEN ( ( i % MAX_SEND_FD ) * sizeof ( int ) ) ;
msghdr . msg_controllen = CMSG_SPACE ( sizeof ( int ) * ( i % MAX_SEND_FD ) ) ;
if ( sendmsg ( fd , & msghdr , 0 ) ! = curoff ) {
Warning ( " Failed to transfer sockets \n " ) ;
goto out ;
}
}
out :
if ( old_fcntl > = 0 & & fcntl ( fd , F_SETFL , old_fcntl ) = = - 1 ) {
Warning ( " Cannot make the unix socket non-blocking \n " ) ;
goto out ;
}
appctx - > st0 = CLI_ST_END ;
free ( cmsgbuf ) ;
free ( tmpbuf ) ;
return 1 ;
}
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
2016-11-22 14:21:23 -05:00
/* register cli keywords */
static struct cli_kw_list cli_kws = { { } , {
2016-11-23 05:10:59 -05:00
{ { " set " , " maxconn " , " global " , NULL } , " set maxconn global : change the per-process maxconn setting " , cli_parse_set_maxconn_global , NULL } ,
2016-11-24 08:51:17 -05:00
{ { " set " , " rate-limit " , NULL } , " set rate-limit : change a rate limiting value " , cli_parse_set_ratelimit , NULL } ,
2017-07-20 05:59:48 -04:00
{ { " set " , " severity-output " , NULL } , " set severity-output [none|number|string] : set presence of severity level in feedback information " , cli_parse_set_severity_output , NULL , NULL } ,
2016-11-22 14:33:32 -05:00
{ { " set " , " timeout " , NULL } , " set timeout : change a timeout setting " , cli_parse_set_timeout , NULL , NULL } ,
2016-11-22 14:21:23 -05:00
{ { " show " , " env " , NULL } , " show env [var] : dump environment variables known to the process " , cli_parse_show_env , cli_io_handler_show_env , NULL } ,
2016-12-15 12:06:44 -05:00
{ { " show " , " cli " , " sockets " , NULL } , " show cli sockets : dump list of cli sockets " , cli_parse_default , cli_io_handler_show_cli_sock , NULL } ,
2017-07-25 13:32:50 -04:00
{ { " show " , " fd " , NULL } , " show fd [num] : dump list of file descriptors in use " , cli_parse_show_fd , cli_io_handler_show_fd , NULL } ,
2017-04-05 16:24:59 -04:00
{ { " _getsocks " , NULL } , NULL , _getsocks , NULL } ,
2016-11-22 14:21:23 -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 " , { } , {
2017-05-26 11:42:10 -04:00
{ " level " , bind_parse_level , 1 } , /* set the unix socket admin level */
{ " expose-fd " , bind_parse_expose_fd , 1 } , /* set the unix socket expose fd rights */
2017-07-20 05:59:48 -04:00
{ " severity-output " , bind_parse_severity_output , 1 } , /* set the severity output format */
2012-09-22 13:32:35 -04:00
{ NULL , NULL , 0 } ,
} } ;
2008-07-09 14:12:41 -04:00
__attribute__ ( ( constructor ) )
static void __dumpstats_module_init ( void )
{
cfg_register_keywords ( & cfg_kws ) ;
2016-11-22 14:21:23 -05:00
cli_register_kw ( & cli_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 :
*/