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>
2020-05-27 06:58:42 -04:00
# include <haproxy/api.h>
2022-04-04 05:29:28 -04:00
# include <haproxy/applet.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/base64.h>
2020-06-04 18:00:29 -04:00
# include <haproxy/cfgparse.h>
2020-06-04 15:07:02 -04:00
# include <haproxy/channel.h>
2020-06-04 12:21:56 -04:00
# include <haproxy/check.h>
2020-06-04 14:19:54 -04:00
# include <haproxy/cli.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/compression.h>
2020-06-04 04:53:16 -04:00
# include <haproxy/dns-t.h>
2020-06-05 11:27:29 -04:00
# include <haproxy/errors.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/fd.h>
# include <haproxy/freq_ctr.h>
2020-06-04 05:23:07 -04:00
# include <haproxy/frontend.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/global.h>
2020-05-27 12:01:47 -04:00
# include <haproxy/list.h>
2020-06-04 08:58:24 -04:00
# include <haproxy/listener.h>
2020-06-04 16:01:04 -04:00
# include <haproxy/log.h>
2021-11-10 04:57:18 -05:00
# include <haproxy/mworker.h>
2020-06-04 08:07:37 -04:00
# include <haproxy/mworker-t.h>
2020-06-04 09:06:28 -04:00
# include <haproxy/pattern-t.h>
2020-06-04 12:38:21 -04:00
# include <haproxy/peers.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/pipe.h>
# include <haproxy/protocol.h>
2020-06-04 16:29:18 -04:00
# include <haproxy/proxy.h>
2023-05-11 11:06:22 -04:00
# include <haproxy/quic_sock.h>
2020-06-04 09:33:47 -04:00
# include <haproxy/sample-t.h>
2022-05-27 03:25:10 -04:00
# include <haproxy/sc_strm.h>
2020-06-04 17:20:13 -04:00
# include <haproxy/server.h>
2020-06-04 12:58:52 -04:00
# include <haproxy/session.h>
2020-10-15 15:29:49 -04:00
# include <haproxy/sock.h>
2020-06-04 13:58:55 -04:00
# include <haproxy/stats-t.h>
2022-05-27 03:47:12 -04:00
# include <haproxy/stconn.h>
2020-06-04 17:46:14 -04:00
# include <haproxy/stream.h>
2024-11-20 06:02:39 -05:00
# include <haproxy/systemd.h>
2020-06-04 11:25:40 -04:00
# include <haproxy/task.h>
2020-06-02 12:15:32 -04:00
# include <haproxy/ticks.h>
2020-06-01 05:05:15 -04:00
# include <haproxy/time.h>
2020-06-09 03:07:15 -04:00
# include <haproxy/tools.h>
2020-05-27 09:59:00 -04:00
# include <haproxy/version.h>
2007-10-17 11:06:05 -04:00
2018-04-18 07:26:46 -04:00
# define PAYLOAD_PATTERN "<<"
2015-04-13 07:50:30 -04:00
static struct applet cli_applet ;
2020-11-05 04:28:53 -05:00
static struct applet mcli_applet ;
2011-06-15 02:18:44 -04:00
2021-03-13 05:00:33 -05:00
static const char cli_permission_denied_msg [ ] =
2009-10-10 11:13:00 -04:00
" Permission denied \n "
" " ;
2011-03-10 05:25:07 -05:00
2017-10-29 15:14:08 -04:00
static THREAD_LOCAL char * dynamic_usage_msg = 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
/* 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 [ ] ;
2021-03-16 10:12:17 -04:00
struct proxy * mworker_proxy ; /* CLI proxy of the master */
2022-09-24 09:56:25 -04:00
struct bind_conf * mcli_reload_bind_conf ;
2018-10-26 08:47:35 -04:00
2022-05-05 11:45:52 -04:00
/* CLI context for the "show env" command */
struct show_env_ctx {
char * * var ; /* first variable to show */
int show_one ; /* stop after showing the first one */
} ;
2022-05-05 11:56:58 -04:00
/* CLI context for the "show fd" command */
2023-03-31 10:33:53 -04:00
/* flags for show_fd_ctx->show_mask */
# define CLI_SHOWFD_F_PI 0x00000001 /* pipes */
# define CLI_SHOWFD_F_LI 0x00000002 /* listeners */
# define CLI_SHOWFD_F_FE 0x00000004 /* frontend conns */
# define CLI_SHOWFD_F_SV 0x00000010 /* server-only conns */
# define CLI_SHOWFD_F_PX 0x00000020 /* proxy-only conns */
# define CLI_SHOWFD_F_BE 0x00000030 /* backend: srv+px */
# define CLI_SHOWFD_F_CO 0x00000034 /* conn: be+fe */
# define CLI_SHOWFD_F_ANY 0x0000003f /* any type */
2022-05-05 11:56:58 -04:00
struct show_fd_ctx {
int fd ; /* first FD to show */
int show_one ; /* stop after showing one FD */
2023-03-31 10:33:53 -04:00
uint show_mask ; /* CLI_SHOWFD_F_xxx */
2022-05-05 11:56:58 -04:00
} ;
2022-05-05 13:11:05 -04:00
/* CLI context for the "show cli sockets" command */
struct show_sock_ctx {
struct bind_conf * bind_conf ;
struct listener * listener ;
} ;
2021-05-09 15:45:29 -04:00
static int cmp_kw_entries ( const void * a , const void * b )
{
const struct cli_kw * l = * ( const struct cli_kw * * ) a ;
const struct cli_kw * r = * ( const struct cli_kw * * ) b ;
return strcmp ( l - > usage ? l - > usage : " " , r - > usage ? r - > usage : " " ) ;
}
2021-03-12 11:13:28 -05:00
/* This will show the help message and list the commands supported at the
* current level that match all of the first words of < args > if args is not
* NULL , or all args if none matches or if args is null .
*/
static char * cli_gen_usage_msg ( struct appctx * appctx , char * const * args )
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
{
2021-05-09 15:45:29 -04:00
struct cli_kw * entries [ CLI_MAX_HELP_ENTRIES ] ;
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_list * kw_list ;
struct cli_kw * kw ;
2018-07-13 05:56:34 -04:00
struct buffer * tmp = get_trash_chunk ( ) ;
struct buffer out ;
2021-03-12 12:24:46 -05:00
struct { struct cli_kw * kw ; int dist ; } matches [ CLI_MAX_MATCHES ] , swp ;
2021-03-12 11:13:28 -05:00
int idx ;
2021-05-09 14:59:23 -04:00
int ishelp = 0 ;
2021-03-12 11:13:28 -05:00
int length = 0 ;
2021-05-09 15:45:29 -04:00
int help_entries = 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
2021-02-20 04:46:51 -05:00
ha_free ( & dynamic_usage_msg ) ;
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
2021-05-09 14:59:23 -04:00
if ( args & & * args & & strcmp ( * args , " help " ) = = 0 ) {
args + + ;
ishelp = 1 ;
}
2021-03-12 11:13:28 -05:00
/* first, let's measure the longest match */
list_for_each_entry ( kw_list , & cli_keywords . list , list ) {
for ( kw = & kw_list - > kw [ 0 ] ; kw - > str_kw [ 0 ] ; kw + + ) {
2021-03-18 10:32:53 -04:00
if ( kw - > level & ~ appctx - > cli_level & ( ACCESS_MASTER_ONLY | ACCESS_EXPERT | ACCESS_EXPERIMENTAL ) )
2021-03-12 11:13:28 -05:00
continue ;
2022-02-02 05:43:20 -05:00
if ( ! ( appctx - > cli_level & ACCESS_MCLI_DEBUG ) & &
( appctx - > cli_level & ~ kw - > level & ( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) = =
2021-03-12 11:13:28 -05:00
( ACCESS_MASTER_ONLY | ACCESS_MASTER ) )
continue ;
/* OK this command is visible */
for ( idx = 0 ; idx < CLI_PREFIX_KW_NB ; idx + + ) {
if ( ! kw - > str_kw [ idx ] )
break ; // end of keyword
if ( ! args | | ! args [ idx ] | | ! * args [ idx ] )
break ; // end of command line
if ( strcmp ( kw - > str_kw [ idx ] , args [ idx ] ) ! = 0 )
break ;
if ( idx + 1 > length )
length = idx + 1 ;
}
}
}
2021-03-12 12:24:46 -05:00
/* now <length> equals the number of exactly matching words */
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
chunk_reset ( tmp ) ;
2021-05-09 14:59:23 -04:00
if ( ishelp ) // this is the help message.
2021-03-12 11:13:28 -05:00
chunk_strcat ( tmp , " The following commands are valid at this level: \n " ) ;
MINOR: cli: print parsed command when not found
It is useful because when we're passing data to runtime API, specially
via code, we can mistakenly send newlines leading to some lines being
wrongly interpretted as commands.
This is analogous to how it's done in a shell, example bash:
$ not_found arg1
bash: not_found: command not found...
$
Real world example: Following the official docs to add a cert:
$ echo -e "set ssl cert ./cert.pem <<\n$(cat ./cert.pem)\n" | socat stdio tcp4-connect:127.0.0.1:9999
Note, how the payload is sent via '<<\n$(cat ./cert.pem)\n'. If cert.pem
contains a newline between various PEM blocks, which is valid, the above
command would generate a flood of 'Unknown command' messages for every
line sent after the first newline. As a new user, this detail is not
clearly visible as socket API doesn't say what exactly what was 'unknown'
about it. The cli interface should be obvious around guiding user on
"what do do next".
This commit changes that by printing the parsed cmd in output like
'Unknown command: "<cmd>"' so the user gets clear "next steps", like
bash, regarding what indeed was the wrong command that HAproxy couldn't
interpret.
Previously:
$ echo -e "show version\nhelpp"| socat ./haproxy.sock - | head -n4
2.7-dev6
Unknown command, but maybe one of the following ones is a better match:
add map [@<ver>] <map> <key> <val> : add a map entry (payload supported instead of key/val)
Now:
$ echo -e "show version\nhelpp"| socat ./haproxy.sock - | head -n4
2.7-dev8-737bb7-156
Unknown command: 'helpp', but maybe one of the following ones is a better match:
add map [@<ver>] <map> <key> <val> : add a map entry (payload supported instead of key/val)
2022-11-17 07:42:38 -05:00
else {
chunk_strcat ( tmp , " Unknown command: ' " ) ;
if ( args & & * args )
chunk_strcat ( tmp , * args ) ;
chunk_strcat ( tmp , " ' " ) ;
if ( ! length & & ( ! args | | ! * args | | ! * * args ) ) // no match
chunk_strcat ( tmp , " . Please enter one of the following commands only: \n " ) ;
else // partial match
chunk_strcat ( tmp , " , but maybe one of the following ones is a better match: \n " ) ;
}
2021-03-12 11:13:28 -05:00
2021-03-12 12:24:46 -05:00
for ( idx = 0 ; idx < CLI_MAX_MATCHES ; idx + + ) {
matches [ idx ] . kw = NULL ;
matches [ idx ] . dist = INT_MAX ;
}
/* In case of partial match we'll look for the best matching entries
* starting from position < length >
*/
2021-03-13 06:25:43 -05:00
if ( args & & args [ length ] & & * args [ length ] ) {
2021-03-12 12:24:46 -05:00
list_for_each_entry ( kw_list , & cli_keywords . list , list ) {
for ( kw = & kw_list - > kw [ 0 ] ; kw - > str_kw [ 0 ] ; kw + + ) {
2021-03-18 10:32:53 -04:00
if ( kw - > level & ~ appctx - > cli_level & ( ACCESS_MASTER_ONLY | ACCESS_EXPERT | ACCESS_EXPERIMENTAL ) )
2021-03-12 12:24:46 -05:00
continue ;
2022-02-02 05:43:20 -05:00
if ( ! ( appctx - > cli_level & ACCESS_MCLI_DEBUG ) & &
( ( appctx - > cli_level & ~ kw - > level & ( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) = =
( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) )
2021-03-12 12:24:46 -05:00
continue ;
for ( idx = 0 ; idx < length ; idx + + ) {
if ( ! kw - > str_kw [ idx ] )
break ; // end of keyword
if ( ! args | | ! args [ idx ] | | ! * args [ idx ] )
break ; // end of command line
if ( strcmp ( kw - > str_kw [ idx ] , args [ idx ] ) ! = 0 )
break ;
}
/* extra non-matching words are fuzzy-matched */
if ( kw - > usage & & idx = = length & & args [ idx ] & & * args [ idx ] ) {
uint8_t word_sig [ 1024 ] ;
uint8_t list_sig [ 1024 ] ;
int dist = 0 ;
int totlen = 0 ;
2021-03-15 05:00:29 -04:00
int i ;
2021-03-12 12:24:46 -05:00
/* this one matches, let's compute the distance between the two
2021-03-15 05:00:29 -04:00
* on the remaining words . For this we ' re computing the signature
* of everything that remains and the cumulated length of the
* strings .
2021-03-12 12:24:46 -05:00
*/
2021-03-12 13:01:59 -05:00
memset ( word_sig , 0 , sizeof ( word_sig ) ) ;
2021-03-15 05:00:29 -04:00
for ( i = idx ; i < CLI_PREFIX_KW_NB & & args [ i ] & & * args [ i ] ; i + + ) {
update_word_fingerprint ( word_sig , args [ i ] ) ;
totlen + = strlen ( args [ i ] ) ;
}
2021-03-12 13:01:59 -05:00
2021-03-15 05:00:29 -04:00
memset ( list_sig , 0 , sizeof ( list_sig ) ) ;
for ( i = idx ; i < CLI_PREFIX_KW_NB & & kw - > str_kw [ i ] ; i + + ) {
update_word_fingerprint ( list_sig , kw - > str_kw [ i ] ) ;
totlen + = strlen ( kw - > str_kw [ i ] ) ;
2021-03-12 12:24:46 -05:00
}
2021-03-15 05:00:29 -04:00
2021-03-12 13:01:59 -05:00
dist = word_fingerprint_distance ( word_sig , list_sig ) ;
2021-03-12 12:24:46 -05:00
/* insert this one at its place if relevant, in order to keep only
* the best matches .
*/
swp . kw = kw ; swp . dist = dist ;
2021-03-15 05:00:29 -04:00
if ( dist < 5 * totlen / 2 & & dist < matches [ CLI_MAX_MATCHES - 1 ] . dist ) {
2021-03-12 12:24:46 -05:00
matches [ CLI_MAX_MATCHES - 1 ] = swp ;
for ( idx = CLI_MAX_MATCHES - 1 ; - - idx > = 0 ; ) {
if ( matches [ idx + 1 ] . dist > = matches [ idx ] . dist )
break ;
matches [ idx + 1 ] = matches [ idx ] ;
matches [ idx ] = swp ;
}
}
}
}
}
}
2021-03-15 05:35:04 -04:00
if ( matches [ 0 ] . kw ) {
/* we have fuzzy matches, let's propose them */
for ( idx = 0 ; idx < CLI_MAX_MATCHES ; idx + + ) {
kw = matches [ idx ] . kw ;
if ( ! kw )
break ;
/* stop the dump if some words look very unlikely candidates */
if ( matches [ idx ] . dist > 5 * matches [ 0 ] . dist / 2 )
break ;
2021-05-09 15:45:29 -04:00
if ( help_entries < CLI_MAX_HELP_ENTRIES )
entries [ help_entries + + ] = kw ;
2021-03-15 05:35:04 -04: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
list_for_each_entry ( kw_list , & cli_keywords . list , list ) {
2021-03-15 05:35:04 -04:00
/* no full dump if we've already found nice candidates */
if ( matches [ 0 ] . kw )
break ;
2021-03-12 09:20:39 -05:00
for ( kw = & kw_list - > kw [ 0 ] ; kw - > str_kw [ 0 ] ; kw + + ) {
2018-10-26 08:47:37 -04:00
2021-03-12 09:20:39 -05:00
/* in a worker or normal process, don't display master-only commands
2021-03-18 10:32:53 -04:00
* nor expert / experimental mode commands if not in this mode .
2021-03-12 09:20:39 -05:00
*/
2021-03-18 10:32:53 -04:00
if ( kw - > level & ~ appctx - > cli_level & ( ACCESS_MASTER_ONLY | ACCESS_EXPERT | ACCESS_EXPERIMENTAL ) )
2021-03-12 09:20:39 -05:00
continue ;
2018-10-26 08:47:37 -04:00
2022-02-02 05:43:20 -05:00
/* in master, if the CLI don't have the
* ACCESS_MCLI_DEBUG don ' t display commands that have
* neither the master bit nor the master - only bit .
2021-03-12 09:20:39 -05:00
*/
2022-02-02 05:43:20 -05:00
if ( ! ( appctx - > cli_level & ACCESS_MCLI_DEBUG ) & &
( ( appctx - > cli_level & ~ kw - > level & ( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) = =
( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) )
2021-03-12 09:20:39 -05:00
continue ;
2019-10-24 11:55:53 -04:00
2021-03-12 11:13:28 -05:00
for ( idx = 0 ; idx < length ; idx + + ) {
if ( ! kw - > str_kw [ idx ] )
break ; // end of keyword
if ( ! args | | ! args [ idx ] | | ! * args [ idx ] )
break ; // end of command line
if ( strcmp ( kw - > str_kw [ idx ] , args [ idx ] ) ! = 0 )
break ;
}
2021-05-09 15:45:29 -04:00
if ( kw - > usage & & idx = = length & & help_entries < CLI_MAX_HELP_ENTRIES )
entries [ help_entries + + ] = kw ;
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
}
}
2021-03-12 11:13:28 -05:00
2021-05-09 15:45:29 -04:00
qsort ( entries , help_entries , sizeof ( * entries ) , cmp_kw_entries ) ;
for ( idx = 0 ; idx < help_entries ; idx + + )
chunk_appendf ( tmp , " %s \n " , entries [ idx ] - > usage ) ;
2021-03-12 11:13:28 -05:00
/* always show the prompt/help/quit commands */
chunk_strcat ( tmp ,
2021-05-09 14:59:23 -04:00
" help [<command>] : list matching or all commands \n "
2023-05-04 08:22:36 -04:00
" prompt [timed] : toggle interactive mode with prompt \n "
2021-05-07 05:38:37 -04:00
" quit : disconnect \n " ) ;
2021-03-12 11:13:28 -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
chunk_init ( & out , NULL , 0 ) ;
chunk_dup ( & out , tmp ) ;
2018-07-13 04:54:26 -04:00
dynamic_usage_msg = out . area ;
2018-04-18 07:26:46 -04:00
2022-05-06 11:16:35 -04:00
cli_msg ( appctx , LOG_INFO , dynamic_usage_msg ) ;
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
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 ;
}
2020-11-28 14:10:08 -05:00
struct cli_kw * cli_find_kw_exact ( char * * args )
{
struct cli_kw_list * kw_list ;
int found = 0 ;
int i ;
int j ;
if ( LIST_ISEMPTY ( & cli_keywords . list ) )
return NULL ;
list_for_each_entry ( kw_list , & cli_keywords . list , list ) {
for ( i = 0 ; kw_list - > kw [ i ] . str_kw [ 0 ] ; i + + ) {
found = 1 ;
for ( j = 0 ; j < CLI_PREFIX_KW_NB ; j + + ) {
if ( args [ j ] = = NULL & & kw_list - > kw [ i ] . str_kw [ j ] = = NULL ) {
break ;
}
if ( args [ j ] = = NULL | | kw_list - > kw [ i ] . str_kw [ j ] = = NULL ) {
found = 0 ;
break ;
}
if ( strcmp ( args [ j ] , kw_list - > kw [ i ] . str_kw [ j ] ) ! = 0 ) {
found = 0 ;
break ;
}
}
if ( found )
return & kw_list - > kw [ i ] ;
}
}
return 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
void cli_register_kw ( struct cli_kw_list * kw_list )
{
2021-04-21 01:32:39 -04:00
LIST_APPEND ( & cli_keywords . list , & kw_list - > list ) ;
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
}
2022-03-29 09:25:30 -04:00
/* list all known keywords on stdout, one per line */
void cli_list_keywords ( void )
{
struct cli_kw_list * kw_list ;
2022-03-30 06:02:35 -04:00
struct cli_kw * kwp , * kwn , * kw ;
2022-03-29 09:25:30 -04:00
int idx ;
2022-03-30 06:02:35 -04:00
for ( kwn = kwp = NULL ; ; kwp = kwn ) {
list_for_each_entry ( kw_list , & cli_keywords . list , list ) {
/* note: we sort based on the usage message when available,
* otherwise we fall back to the first keyword .
*/
for ( kw = & kw_list - > kw [ 0 ] ; kw - > str_kw [ 0 ] ; kw + + ) {
if ( strordered ( kwp ? kwp - > usage ? kwp - > usage : kwp - > str_kw [ 0 ] : NULL ,
kw - > usage ? kw - > usage : kw - > str_kw [ 0 ] ,
kwn ! = kwp ? kwn - > usage ? kwn - > usage : kwn - > str_kw [ 0 ] : NULL ) )
kwn = kw ;
2022-03-29 09:25:30 -04:00
}
}
2022-03-30 06:02:35 -04:00
if ( kwn = = kwp )
break ;
for ( idx = 0 ; kwn - > str_kw [ idx ] ; idx + + ) {
printf ( " %s " , kwn - > str_kw [ idx ] ) ;
}
if ( kwn - > level & ( ACCESS_MASTER_ONLY | ACCESS_MASTER ) )
printf ( " [MASTER] " ) ;
if ( ! ( kwn - > level & ACCESS_MASTER_ONLY ) )
printf ( " [WORKER] " ) ;
if ( kwn - > level & ACCESS_EXPERT )
printf ( " [EXPERT] " ) ;
if ( kwn - > level & ACCESS_EXPERIMENTAL )
printf ( " [EXPERIM] " ) ;
printf ( " \n " ) ;
2022-03-29 09:25:30 -04: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
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 ) .
*/
2021-03-13 05:00:33 -05:00
static struct proxy * cli_alloc_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 ) ;
2017-11-24 10:54:05 -05:00
fe - > next = proxies_list ;
proxies_list = fe ;
2024-04-30 06:04:57 -04:00
fe - > fe_counters . last_change = ns_to_sec ( now_ns ) ;
2010-08-17 15:48:17 -04:00
fe - > id = strdup ( " GLOBAL " ) ;
2021-08-13 09:31:33 -04:00
fe - > cap = PR_CAP_FE | PR_CAP_INT ;
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 */
2024-09-21 14:08:06 -04:00
fe - > conf . file = copy_file_name ( file ) ;
2012-09-18 14:05:00 -04:00
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
*/
2021-03-13 05:00:33 -05:00
static int cli_parse_global ( char * * args , int section_type , struct proxy * curpx ,
const 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
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ 1 ] , " socket " ) = = 0 ) {
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 ;
}
2021-03-13 05:00:33 -05:00
if ( ! global . cli_fe ) {
if ( ( global . cli_fe = cli_alloc_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 ;
}
}
2021-03-13 05:00:33 -05:00
bind_conf = bind_conf_alloc ( global . cli_fe , file , line , args [ 2 ] , xprt_get ( XPRT_RAW ) ) ;
2021-04-12 10:56:37 -04:00
if ( ! bind_conf ) {
memprintf ( err , " '%s %s' : out of memory trying to allocate a bind_conf " , args [ 0 ] , args [ 1 ] ) ;
return - 1 ;
}
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
2021-03-13 05:00:33 -05:00
if ( ! str2listener ( args [ 2 ] , global . cli_fe , bind_conf , file , line , err ) ) {
2012-09-20 14:19:28 -04:00
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
struct bind_kw * kw ;
2021-03-12 04:14:07 -05:00
const char * best ;
2021-11-20 14:10:41 -05:00
int code ;
2012-09-22 13:32:35 -04:00
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
2021-11-20 14:10:41 -05:00
code = kw - > parse ( args , cur_arg , global . cli_fe , bind_conf , err ) ;
/* FIXME: this is ugly, we don't have a way to collect warnings,
* yet some important bind keywords may report warnings that we
* must display .
*/
if ( ( ( code & ( ERR_WARN | ERR_FATAL | ERR_ALERT ) ) = = ERR_WARN ) & & err & & * err ) {
indent_msg ( err , 2 ) ;
ha_warning ( " parsing [%s:%d] : '%s %s' : %s \n " , file , line , args [ 0 ] , args [ 1 ] , * err ) ;
ha_free ( err ) ;
}
if ( code & ~ ERR_WARN ) {
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
2021-03-12 04:14:07 -05:00
best = bind_find_best_kw ( args [ cur_arg ] ) ;
if ( best )
memprintf ( err , " '%s %s' : unknown keyword '%s'. Did you mean '%s' maybe ? " ,
args [ 0 ] , args [ 1 ] , args [ cur_arg ] , best ) ;
else
memprintf ( err , " '%s %s' : unknown keyword '%s'. " ,
args [ 0 ] , args [ 1 ] , args [ cur_arg ] ) ;
2012-09-22 13:32:35 -04:00
return - 1 ;
2007-10-18 07:53:22 -04:00
}
2008-12-07 10:06:43 -05:00
2023-01-12 13:10:17 -05:00
bind_conf - > accept = session_accept_fd ;
2023-01-12 13:32:45 -05:00
bind_conf - > nice = - 64 ; /* we want to boost priority for local stats */
2023-01-12 13:58:42 -05:00
bind_conf - > options | = BC_O_UNLIMITED ; /* don't make the peers subject to global limits */
2023-01-12 13:18:34 -05:00
2012-09-20 14:19:28 -04:00
list_for_each_entry ( l , & bind_conf - > listeners , by_bind ) {
2019-02-27 10:25:28 -05:00
global . maxsock + + ; /* for the listening socket */
2012-09-20 14:19:28 -04:00
}
2007-10-18 07:53:22 -04:00
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
else if ( strcmp ( args [ 1 ] , " timeout " ) = = 0 ) {
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
2019-06-07 13:00:37 -04:00
if ( res = = PARSE_TIME_OVER ) {
memprintf ( err , " timer overflow in argument '%s' to '%s %s' (maximum value is 2147483647 ms or ~24.8 days) " ,
args [ 2 ] , args [ 0 ] , args [ 1 ] ) ;
return - 1 ;
}
else if ( res = = PARSE_TIME_UNDER ) {
memprintf ( err , " timer underflow in argument '%s' to '%s %s' (minimum non-null value is 1 ms) " ,
args [ 2 ] , args [ 0 ] , args [ 1 ] ) ;
return - 1 ;
}
else 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 ;
}
2021-03-13 05:00:33 -05:00
if ( ! global . cli_fe ) {
if ( ( global . cli_fe = cli_alloc_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 ;
}
}
2021-03-13 05:00:33 -05:00
global . cli_fe - > timeout . client = MS_TO_TICKS ( timeout ) ;
2007-10-18 07:53:22 -04:00
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
else if ( strcmp ( args [ 1 ] , " maxconn " ) = = 0 ) {
2012-05-08 13:47:01 -04:00
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
2021-03-13 05:00:33 -05:00
if ( ! global . cli_fe ) {
if ( ( global . cli_fe = cli_alloc_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 ;
}
}
2021-03-13 05:00:33 -05:00
global . cli_fe - > maxconn = maxconn ;
2007-10-18 07:53:22 -04:00
}
2022-07-15 11:14:40 -04:00
else if ( strcmp ( args [ 1 ] , " bind-process " ) = = 0 ) {
2023-04-23 03:40:56 -04:00
memprintf ( err , " '%s %s' is not supported anymore. " , args [ 0 ] , args [ 1 ] ) ;
2022-07-15 11:14:40 -04:00
return - 1 ;
2012-10-22 17:17:18 -04:00
}
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 ;
}
2019-04-01 05:30:06 -04:00
/*
2019-04-12 10:09:24 -04:00
* This function exports the bound addresses of a < frontend > in the environment
* variable < varname > . Those addresses are separated by semicolons and prefixed
* with their type ( abns @ , unix @ , sockpair @ etc )
* Return - 1 upon error , 0 otherwise
2019-04-01 05:30:06 -04:00
*/
2019-04-12 10:09:24 -04:00
int listeners_setenv ( struct proxy * frontend , const char * varname )
2019-04-01 05:30:06 -04:00
{
struct buffer * trash = get_trash_chunk ( ) ;
struct bind_conf * bind_conf ;
2019-04-12 10:09:24 -04:00
if ( frontend ) {
list_for_each_entry ( bind_conf , & frontend - > conf . bind , by_fe ) {
2019-04-01 05:30:06 -04:00
struct listener * l ;
list_for_each_entry ( l , & bind_conf - > listeners , by_bind ) {
char addr [ 46 ] ;
char port [ 6 ] ;
2024-08-09 15:12:23 -04:00
if ( l - > rx . addr . ss_family = = AF_UNIX | |
l - > rx . addr . ss_family = = AF_CUST_ABNS | |
l - > rx . addr . ss_family = = AF_CUST_ABNSZ ) {
2019-04-01 05:30:06 -04:00
const struct sockaddr_un * un ;
2020-08-27 01:48:42 -04:00
un = ( struct sockaddr_un * ) & l - > rx . addr ;
2024-08-09 15:12:23 -04:00
if ( l - > rx . addr . ss_family = = AF_CUST_ABNS | |
l - > rx . addr . ss_family = = AF_CUST_ABNSZ ) {
2024-11-12 16:43:49 -05:00
chunk_appendf ( trash , " %sabns@%s " , ( trash - > data ? " ; " : " " ) , un - > sun_path + 1 ) ;
2019-04-01 05:30:06 -04:00
} else {
2024-11-12 16:43:49 -05:00
chunk_appendf ( trash , " %sunix@%s " , ( trash - > data ? " ; " : " " ) , un - > sun_path ) ;
2019-04-01 05:30:06 -04:00
}
2020-08-27 01:48:42 -04:00
} else if ( l - > rx . addr . ss_family = = AF_INET ) {
addr_to_str ( & l - > rx . addr , addr , sizeof ( addr ) ) ;
port_to_str ( & l - > rx . addr , port , sizeof ( port ) ) ;
2024-11-12 16:43:49 -05:00
chunk_appendf ( trash , " %sipv4@%s:%s " , ( trash - > data ? " ; " : " " ) , addr , port ) ;
2020-08-27 01:48:42 -04:00
} else if ( l - > rx . addr . ss_family = = AF_INET6 ) {
addr_to_str ( & l - > rx . addr , addr , sizeof ( addr ) ) ;
port_to_str ( & l - > rx . addr , port , sizeof ( port ) ) ;
2024-11-12 16:43:49 -05:00
chunk_appendf ( trash , " %sipv6@[%s]:%s " , ( trash - > data ? " ; " : " " ) , addr , port ) ;
2019-04-01 05:30:06 -04:00
}
2024-11-12 16:43:49 -05:00
/* AF_CUST_SOCKPAIR is explicitly skipped, we don't want to show reload and shared
* master CLI sockpairs in HAPROXY_CLI and HAPROXY_MASTER_CLI
*/
2019-04-01 05:30:06 -04:00
}
}
trash - > area [ trash - > data + + ] = ' \0 ' ;
2019-04-12 10:09:24 -04:00
if ( setenv ( varname , trash - > area , 1 ) < 0 )
2019-04-01 05:30:06 -04:00
return - 1 ;
}
return 0 ;
}
2019-04-12 10:09:24 -04:00
int cli_socket_setenv ( )
{
2021-03-13 05:00:33 -05:00
if ( listeners_setenv ( global . cli_fe , " HAPROXY_CLI " ) < 0 )
2019-04-12 10:09:24 -04:00
return - 1 ;
if ( listeners_setenv ( mworker_proxy , " HAPROXY_MASTER_CLI " ) < 0 )
return - 1 ;
return 0 ;
}
2019-04-01 05:30:06 -04:00
REGISTER_CONFIG_POSTPARSER ( " cli " , cli_socket_setenv ) ;
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 )
{
2018-12-13 03:05:44 -05:00
if ( ( appctx - > cli_level & ACCESS_LVL_MASK ) < level ) {
2021-03-13 05:00:33 -05:00
cli_err ( appctx , cli_permission_denied_msg ) ;
2016-11-23 11:01:39 -05:00
return 0 ;
}
return 1 ;
}
2018-12-13 03:05:47 -05:00
/* same as cli_has_level but for the CLI proxy and without error message */
int pcli_has_level ( struct stream * s , int level )
{
if ( ( s - > pcli_flags & ACCESS_LVL_MASK ) < level ) {
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 ;
2022-05-11 08:09:57 -04:00
return strm_li ( appctx_strm ( appctx ) ) - > bind_conf - > severity_output ;
2017-07-20 05:59:48 -04:00
}
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
2021-03-12 09:57:47 -05:00
* from the CLI ' s IO handler running in an appctx context . The function returns
* 1 if the request was understood , otherwise zero ( in which case an error
* message will be displayed ) . It is called with appctx - > st0
2016-11-24 10:23:38 -05:00
* 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
*/
2018-04-18 07:26:46 -04:00
static int cli_parse_request ( struct appctx * appctx )
2009-08-16 13:06:42 -04:00
{
2021-03-13 04:59:23 -05:00
char * args [ MAX_CLI_ARGS + 1 ] , * p , * end , * payload = NULL ;
2018-04-18 07:26:46 -04:00
int i = 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
struct cli_kw * kw ;
2009-08-16 13:06:42 -04:00
2024-02-20 02:47:38 -05:00
p = b_head ( & appctx - > inbuf ) ;
end = b_tail ( & appctx - > inbuf ) ;
2018-04-18 07:26:46 -04:00
/*
* Get pointers on words .
* One extra slot is reserved to store a pointer on a null byte .
*/
2021-03-13 04:59:23 -05:00
while ( i < MAX_CLI_ARGS & & p < end ) {
2018-04-18 07:26:46 -04:00
int j , k ;
2009-08-16 13:06:42 -04:00
2018-04-18 07:26:46 -04:00
/* skip leading spaces/tabs */
p + = strspn ( p , " \t " ) ;
if ( ! * p )
break ;
2009-08-16 13:06:42 -04:00
2023-11-27 10:04:20 -05:00
/* first check if the '<<' is present, but this is not enough
* because we don ' t know if this is the end of the string */
if ( strncmp ( p , PAYLOAD_PATTERN , strlen ( PAYLOAD_PATTERN ) ) = = 0 ) {
int pat_len = strlen ( appctx - > cli_payload_pat ) ;
/* then if the customized pattern is empty, check if the next character is '\0' */
if ( pat_len = = 0 & & p [ strlen ( PAYLOAD_PATTERN ) ] = = ' \0 ' ) {
payload = p + strlen ( PAYLOAD_PATTERN ) + 1 ;
break ;
}
/* else if we found the customized pattern at the end of the string */
if ( strcmp ( p + strlen ( PAYLOAD_PATTERN ) , appctx - > cli_payload_pat ) = = 0 ) {
payload = p + strlen ( PAYLOAD_PATTERN ) + pat_len + 1 ;
break ;
}
2021-09-17 05:07:45 -04:00
}
2018-04-18 07:26:46 -04:00
args [ i ] = p ;
2020-06-08 10:08:06 -04:00
while ( 1 ) {
p + = strcspn ( p , " \t \\ " ) ;
/* escaped chars using backlashes (\) */
if ( * p = = ' \\ ' ) {
if ( ! * + + p )
break ;
if ( ! * + + p )
break ;
} else {
break ;
}
}
2018-04-18 07:26:46 -04:00
* p + + = 0 ;
2009-08-16 13:06:42 -04:00
2018-04-18 07:26:46 -04:00
/* unescape backslashes (\) */
for ( j = 0 , k = 0 ; args [ i ] [ k ] ; k + + ) {
if ( args [ i ] [ k ] = = ' \\ ' ) {
if ( args [ i ] [ k + 1 ] = = ' \\ ' )
k + + ;
2016-11-24 05:33:12 -05:00
else
continue ;
}
2018-04-18 07:26:46 -04:00
args [ i ] [ j ] = args [ i ] [ k ] ;
2013-12-10 12:54:58 -05:00
j + + ;
}
2018-04-18 07:26:46 -04:00
args [ i ] [ j ] = 0 ;
2013-12-10 12:54:58 -05:00
2018-04-18 07:26:46 -04:00
i + + ;
}
/* fill unused slots */
2024-02-20 02:47:38 -05:00
p = b_tail ( & appctx - > inbuf ) ;
2021-03-13 04:59:23 -05:00
for ( ; i < MAX_CLI_ARGS + 1 ; i + + )
2018-04-18 07:26:46 -04:00
args [ i ] = p ;
2016-11-24 10:23:38 -05:00
2023-05-11 10:32:56 -04:00
if ( ! * * args )
return 0 ;
2016-11-24 10:23:38 -05:00
kw = cli_find_kw ( args ) ;
2021-03-12 09:57:47 -05:00
if ( ! kw | |
( kw - > level & ~ appctx - > cli_level & ACCESS_MASTER_ONLY ) | |
2022-02-02 05:43:20 -05:00
( ! ( appctx - > cli_level & ACCESS_MCLI_DEBUG ) & &
( appctx - > cli_level & ~ kw - > level & ( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) = = ( ACCESS_MASTER_ONLY | ACCESS_MASTER ) ) ) {
2021-03-12 09:57:47 -05:00
/* keyword not found in this mode */
2021-03-12 11:13:28 -05:00
cli_gen_usage_msg ( appctx , args ) ;
2018-10-26 08:47:37 -04:00
return 0 ;
2021-03-12 09:57:47 -05:00
}
2018-10-26 08:47:37 -04:00
2021-03-12 09:57:47 -05:00
/* don't handle expert mode commands if not in this mode. */
if ( kw - > level & ~ appctx - > cli_level & ACCESS_EXPERT ) {
cli_err ( appctx , " This command is restricted to expert mode only. \n " ) ;
2019-10-24 11:55:53 -04:00
return 0 ;
2021-03-12 09:57:47 -05:00
}
2019-10-24 11:55:53 -04:00
2021-03-18 10:32:53 -04:00
if ( kw - > level & ~ appctx - > cli_level & ACCESS_EXPERIMENTAL ) {
cli_err ( appctx , " This command is restricted to experimental mode only. \n " ) ;
return 0 ;
}
2021-05-05 10:29:23 -04:00
if ( kw - > level = = ACCESS_EXPERT )
mark_tainted ( TAINTED_CLI_EXPERT_MODE ) ;
else if ( kw - > level = = ACCESS_EXPERIMENTAL )
mark_tainted ( TAINTED_CLI_EXPERIMENTAL_MODE ) ;
2016-11-24 10:23:38 -05:00
appctx - > io_handler = kw - > io_handler ;
2017-06-29 13:54:13 -04:00
appctx - > io_release = kw - > io_release ;
2019-10-25 15:10:14 -04:00
if ( kw - > parse & & kw - > parse ( args , payload , appctx , kw - > private ) ! = 0 )
goto fail ;
/* kw->parse could set its own io_handler or io_release handler */
if ( ! appctx - > io_handler )
goto fail ;
appctx - > st0 = CLI_ST_CALLBACK ;
return 1 ;
fail :
appctx - > io_handler = NULL ;
appctx - > io_release = NULL ;
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 */
2023-05-05 04:56:00 -04:00
static int cli_output_msg ( struct appctx * appctx , const char * msg , int severity , int severity_output )
2017-07-20 05:59:48 -04:00
{
2018-07-13 05:56:34 -04:00
struct buffer * tmp ;
2024-02-08 12:15:23 -05:00
struct ist imsg ;
2017-07-20 05:59:48 -04:00
tmp = get_trash_chunk ( ) ;
chunk_reset ( tmp ) ;
2024-02-08 12:15:23 -05:00
if ( likely ( severity_output = = CLI_SEVERITY_NONE ) )
goto send_it ;
2017-07-20 05:59:48 -04:00
if ( severity < 0 | | severity > 7 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " socket command feedback with invalid severity %d " , severity ) ;
2017-07-20 05:59:48 -04:00
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 :
2017-11-24 10:50:31 -05:00
ha_warning ( " Unrecognized severity output %d " , severity_output ) ;
2017-07-20 05:59:48 -04:00
}
}
2024-02-08 12:15:23 -05:00
send_it :
/* the vast majority of messages have their trailing LF but a few are
* still missing it , and very rare ones might even have two . For this
* reason , we ' ll first delete the trailing LFs if present , then
* systematically append one .
*/
for ( imsg = ist ( msg ) ; imsg . len > 0 & & imsg . ptr [ imsg . len - 1 ] = = ' \n ' ; imsg . len - - )
;
chunk_istcat ( tmp , imsg ) ;
chunk_istcat ( tmp , ist ( " \n " ) ) ;
2017-07-20 05:59:48 -04:00
2023-05-05 04:56:00 -04:00
return applet_putchk ( appctx , tmp ) ;
2017-07-20 05:59:48 -04:00
}
2024-02-20 02:47:38 -05:00
int cli_init ( struct appctx * appctx )
{
struct stconn * sc = appctx_sc ( appctx ) ;
struct bind_conf * bind_conf = strm_li ( __sc_strm ( sc ) ) - > bind_conf ;
appctx - > cli_severity_output = bind_conf - > severity_output ;
applet_reset_svcctx ( appctx ) ;
appctx - > st0 = CLI_ST_GETREQ ;
appctx - > cli_level = bind_conf - > level ;
/* Wakeup the applet ASAP. */
applet_need_more_data ( appctx ) ;
return 0 ;
}
size_t cli_snd_buf ( struct appctx * appctx , struct buffer * buf , size_t count , unsigned flags )
{
char * str ;
size_t len , ret = 0 ;
int lf = 0 ;
if ( appctx - > st0 = = CLI_ST_INIT )
cli_init ( appctx ) ;
2025-02-17 08:49:34 -05:00
else if ( appctx - > st0 = = CLI_ST_END ) {
/* drop all data on END state */
ret = count ;
b_del ( buf , ret ) ;
goto end ;
}
2024-02-20 02:47:38 -05:00
else if ( appctx - > st0 ! = CLI_ST_GETREQ )
goto end ;
if ( b_space_wraps ( & appctx - > inbuf ) )
b_slow_realign ( & appctx - > inbuf , trash . area , b_data ( & appctx - > inbuf ) ) ;
while ( 1 ) {
/* payload doesn't take escapes nor does it end on semi-colons,
* so we use the regular getline . Normal mode however must stop
* on LFs and semi - colons that are not prefixed by a backslash .
* Note we reserve one byte at the end to insert a trailing nul
* byte .
*/
str = b_tail ( & appctx - > inbuf ) ;
if ( ! ( appctx - > st1 & APPCTX_CLI_ST1_PAYLOAD ) )
len = b_getdelim ( buf , ret , count , str , b_room ( & appctx - > inbuf ) - 1 , " \n ; " , ' \\ ' ) ;
else
len = b_getline ( buf , ret , count , str , b_room ( & appctx - > inbuf ) - 1 ) ;
if ( ! len ) {
2024-04-08 01:41:17 -04:00
if ( ! b_room ( buf ) | | ( count > b_room ( & appctx - > inbuf ) - 1 ) ) {
2024-02-20 02:47:38 -05:00
cli_err ( appctx , " The command is too big for the buffer size. Please change tune.bufsize in the configuration to use a bigger command. \n " ) ;
applet_set_error ( appctx ) ;
b_reset ( & appctx - > inbuf ) ;
}
2024-04-08 01:41:17 -04:00
else if ( flags & CO_SFL_LAST_DATA ) {
applet_set_eos ( appctx ) ;
applet_set_error ( appctx ) ;
b_reset ( & appctx - > inbuf ) ;
}
2024-02-20 02:47:38 -05:00
break ;
}
ret + = len ;
count - = len ;
if ( str [ len - 1 ] = = ' \n ' )
lf = 1 ;
len - - ;
2024-12-12 13:38:38 -05:00
2024-02-20 02:47:38 -05:00
if ( appctx - > st1 & APPCTX_CLI_ST1_PAYLOAD ) {
str [ len + 1 ] = ' \0 ' ;
b_add ( & appctx - > inbuf , len + 1 ) ;
}
else {
2024-12-12 13:38:38 -05:00
/* Remove the trailing \r, if any and add a null byte at the
* end . For normal mode , the trailing \ n is removed , but we
* conserve \ r \ n or \ n sequences for payload mode .
*/
if ( len & & str [ len - 1 ] = = ' \r ' )
len - - ;
2024-02-20 02:47:38 -05:00
str [ len ] = ' \0 ' ;
b_add ( & appctx - > inbuf , len ) ;
}
if ( appctx - > st1 & APPCTX_CLI_ST1_PAYLOAD ) {
/* look for a pattern */
if ( len = = strlen ( appctx - > cli_payload_pat ) ) {
/* here use 'len' because str still contains the \n */
if ( strncmp ( str , appctx - > cli_payload_pat , len ) = = 0 ) {
/* remove the last two \n */
b_sub ( & appctx - > inbuf , strlen ( appctx - > cli_payload_pat ) + 2 ) ;
* b_tail ( & appctx - > inbuf ) = ' \0 ' ;
appctx - > st1 & = ~ APPCTX_CLI_ST1_PAYLOAD ;
if ( ! ( appctx - > st1 & APPCTX_CLI_ST1_PROMPT ) & & lf )
appctx - > st1 | = APPCTX_CLI_ST1_LASTCMD ;
}
}
}
else {
char * last_arg ;
/*
* Look for the " payload start " pattern at the end of a
* line Its location is not remembered here , this is
* just to switch to a gathering mode .
*
* The pattern must start by < < followed by 0 to 7
* characters , and finished by the end of the command
* ( \ n or ; ) .
*/
/* look for the first space starting by the end of the line */
for ( last_arg = b_tail ( & appctx - > inbuf ) ; last_arg ! = b_head ( & appctx - > inbuf ) ; last_arg - - ) {
if ( * last_arg = = ' ' | | * last_arg = = ' \t ' ) {
last_arg + + ;
break ;
}
}
if ( strncmp ( last_arg , PAYLOAD_PATTERN , strlen ( PAYLOAD_PATTERN ) ) = = 0 ) {
ssize_t pat_len = strlen ( last_arg + strlen ( PAYLOAD_PATTERN ) ) ;
/* A customized pattern can't be more than 7 characters
* if it ' s more , don ' t make it a payload
*/
if ( pat_len < sizeof ( appctx - > cli_payload_pat ) ) {
appctx - > st1 | = APPCTX_CLI_ST1_PAYLOAD ;
/* copy the customized pattern, don't store the << */
strncpy ( appctx - > cli_payload_pat , last_arg + strlen ( PAYLOAD_PATTERN ) , sizeof ( appctx - > cli_payload_pat ) - 1 ) ;
appctx - > cli_payload_pat [ sizeof ( appctx - > cli_payload_pat ) - 1 ] = ' \0 ' ;
b_add ( & appctx - > inbuf , 1 ) ; // keep the trailing \0 after the pattern
}
}
else {
if ( ! ( appctx - > st1 & APPCTX_CLI_ST1_PROMPT ) & & lf )
appctx - > st1 | = APPCTX_CLI_ST1_LASTCMD ;
}
}
if ( ! ( appctx - > st1 & APPCTX_CLI_ST1_PAYLOAD ) | | ( appctx - > st1 & APPCTX_CLI_ST1_PROMPT ) ) {
appctx - > st0 = CLI_ST_PARSEREQ ;
break ;
}
}
b_del ( buf , ret ) ;
end :
return ret ;
}
2022-05-17 13:07:51 -04:00
/* This I/O handler runs as an applet embedded in a stream connector. 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
*/
2025-03-13 12:28:12 -04:00
void cli_io_handler ( struct appctx * appctx )
2009-08-16 13:06:42 -04:00
{
2024-02-15 07:34:05 -05:00
if ( applet_fl_test ( appctx , APPCTX_FL_OUTBLK_ALLOC | APPCTX_FL_OUTBLK_FULL ) )
goto out ;
if ( ! appctx_get_buf ( appctx , & appctx - > outbuf ) ) {
2009-09-22 13:31:03 -04:00
goto out ;
2023-04-07 12:07:51 -04:00
}
2009-09-22 13:31:03 -04:00
2024-02-15 07:34:05 -05:00
if ( unlikely ( applet_fl_test ( appctx , APPCTX_FL_EOS | APPCTX_FL_ERROR ) ) ) {
appctx - > st0 = CLI_ST_END ;
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
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 ) {
2017-07-20 05:59:48 -04:00
/* reset severity to default at init */
2024-02-20 02:47:38 -05:00
cli_init ( appctx ) ;
2009-09-22 13:31:03 -04:00
}
2016-11-24 09:53:53 -05:00
else if ( appctx - > st0 = = CLI_ST_END ) {
2024-02-15 07:34:05 -05:00
applet_set_eos ( appctx ) ;
2009-10-04 08:22:18 -04:00
break ;
}
2016-11-24 09:53:53 -05:00
else if ( appctx - > st0 = = CLI_ST_GETREQ ) {
2024-02-20 02:47:38 -05:00
/* Now we close the output 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 .
*/
if ( se_fl_test ( appctx - > sedesc , SE_FL_SHW ) ) {
appctx - > st0 = CLI_ST_END ;
continue ;
2018-04-18 07:26:46 -04:00
}
2024-02-20 02:47:38 -05:00
break ;
}
else if ( appctx - > st0 = = CLI_ST_PARSEREQ ) {
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 .
*/
2024-02-15 07:34:05 -05:00
if ( buffer_almost_full ( & appctx - > outbuf ) ) {
applet_fl_set ( appctx , APPCTX_FL_OUTBLK_FULL ) ;
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
2024-02-08 14:53:31 -05:00
appctx - > t - > expire = TICK_ETERNITY ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PROMPT ;
2018-04-18 07:26:46 -04:00
2024-02-20 02:47:38 -05:00
if ( ! ( appctx - > st1 & APPCTX_CLI_ST1_PAYLOAD ) ) {
cli_parse_request ( appctx ) ;
b_reset ( & appctx - > inbuf ) ;
2009-08-16 13:06:42 -04:00
}
}
2015-09-25 13:21:19 -04:00
else { /* output functions */
2022-05-06 11:16:35 -04:00
struct cli_print_ctx * ctx ;
2019-08-09 03:57:36 -04:00
const char * msg ;
int sev ;
2024-03-28 09:52:29 -04:00
cli_output :
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 ;
2019-08-09 03:57:36 -04:00
case CLI_ST_PRINT : /* print const message in msg */
case CLI_ST_PRINT_ERR : /* print const error in msg */
case CLI_ST_PRINT_DYN : /* print dyn message in msg, free */
2022-11-10 05:47:36 -05:00
case CLI_ST_PRINT_DYNERR : /* print dyn error in err, free */
2022-11-10 08:24:51 -05:00
case CLI_ST_PRINT_UMSG : /* print usermsgs_ctx and reset it */
case CLI_ST_PRINT_UMSGERR : /* print usermsgs_ctx as error and reset it */
2022-05-06 11:16:35 -04:00
/* the message is in the svcctx */
ctx = applet_reserve_svcctx ( appctx , sizeof ( * ctx ) ) ;
2019-08-09 03:57:36 -04:00
if ( appctx - > st0 = = CLI_ST_PRINT | | appctx - > st0 = = CLI_ST_PRINT_ERR ) {
sev = appctx - > st0 = = CLI_ST_PRINT_ERR ?
2022-05-06 11:16:35 -04:00
LOG_ERR : ctx - > severity ;
msg = ctx - > msg ;
2019-08-09 03:57:36 -04:00
}
2022-11-10 05:47:36 -05:00
else if ( appctx - > st0 = = CLI_ST_PRINT_DYN | | appctx - > st0 = = CLI_ST_PRINT_DYNERR ) {
sev = appctx - > st0 = = CLI_ST_PRINT_DYNERR ?
2022-05-06 11:16:35 -04:00
LOG_ERR : ctx - > severity ;
msg = ctx - > err ;
2019-08-09 03:57:36 -04:00
if ( ! msg ) {
sev = LOG_ERR ;
msg = " Out of memory. \n " ;
}
}
2022-11-10 08:24:51 -05:00
else if ( appctx - > st0 = = CLI_ST_PRINT_UMSG | |
appctx - > st0 = = CLI_ST_PRINT_UMSGERR ) {
sev = appctx - > st0 = = CLI_ST_PRINT_UMSGERR ?
LOG_ERR : ctx - > severity ;
msg = usermsgs_str ( ) ;
}
2019-08-09 03:57:36 -04:00
else {
sev = LOG_ERR ;
msg = " Internal error. \n " ;
}
2018-04-16 12:50:19 -04:00
2023-05-05 04:56:00 -04:00
if ( cli_output_msg ( appctx , msg , sev , cli_get_severity_output ( appctx ) ) ! = - 1 ) {
2022-11-10 05:47:36 -05:00
if ( appctx - > st0 = = CLI_ST_PRINT_DYN | |
appctx - > st0 = = CLI_ST_PRINT_DYNERR ) {
2022-05-06 11:16:35 -04:00
ha_free ( & ctx - > err ) ;
2019-08-09 03:57:36 -04:00
}
2022-11-10 08:24:51 -05:00
else if ( appctx - > st0 = = CLI_ST_PRINT_UMSG | |
appctx - > st0 = = CLI_ST_PRINT_UMSGERR ) {
usermsgs_clr ( NULL ) ;
}
2024-02-08 14:53:31 -05:00
appctx - > t - > expire = TICK_ETERNITY ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_PROMPT ;
2014-01-29 13:08:49 -05:00
}
2024-02-20 02:47:38 -05:00
if ( applet_fl_test ( appctx , APPCTX_FL_ERR_PENDING ) ) {
appctx - > st0 = CLI_ST_END ;
continue ;
}
2014-01-29 13:08:49 -05:00
break ;
2019-08-09 03:57:36 -04:00
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 ) ) {
2024-02-08 14:53:31 -05:00
appctx - > t - > expire = TICK_ETERNITY ;
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 ;
2024-02-08 11:22:41 -05:00
/* some release handlers might have
* pending output to print .
*/
continue ;
2016-11-12 04:51:33 -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
break ;
2009-10-04 08:22:18 -04:00
default : /* abnormal state */
2025-02-06 09:15:27 -05:00
applet_set_error ( appctx ) ;
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 ) {
2023-05-04 08:22:36 -04:00
char prompt_buf [ 20 ] ;
2018-04-18 07:26:46 -04:00
const char * prompt = " " ;
if ( appctx - > st1 & APPCTX_CLI_ST1_PROMPT ) {
/*
* when entering a payload with interactive mode , change the prompt
* to emphasize that more data can still be sent
*/
2024-02-20 02:47:38 -05:00
if ( b_data ( & appctx - > inbuf ) & & appctx - > st1 & APPCTX_CLI_ST1_PAYLOAD )
2018-04-18 07:26:46 -04:00
prompt = " + " ;
2023-05-04 08:22:36 -04:00
else if ( appctx - > st1 & APPCTX_CLI_ST1_TIMED ) {
uint up = ns_to_sec ( now_ns - start_time_ns ) ;
snprintf ( prompt_buf , sizeof ( prompt_buf ) ,
" \n [%u:%02u:%02u:%02u]> " ,
( up / 86400 ) , ( up / 3600 ) % 24 , ( up / 60 ) % 60 , up % 60 ) ;
prompt = prompt_buf ;
}
2018-04-18 07:26:46 -04:00
else
prompt = " \n > " ;
}
else {
2019-07-01 04:56:15 -04:00
if ( ! ( appctx - > st1 & ( APPCTX_CLI_ST1_PAYLOAD | APPCTX_CLI_ST1_NOLF ) ) )
2018-04-18 07:26:46 -04:00
prompt = " \n " ;
}
2022-08-18 12:04:37 -04:00
if ( applet_putstr ( appctx , prompt ) ! = - 1 ) {
applet_reset_svcctx ( appctx ) ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_GETREQ ;
2022-08-18 12:04:37 -04:00
}
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. */
2022-06-01 11:25:42 -04:00
if ( appctx - > st0 > = CLI_ST_OUTPUT ) {
applet_wont_consume ( appctx ) ;
2009-09-22 13:31:03 -04:00
break ;
2022-06-01 11:25:42 -04:00
}
2009-09-22 13:31:03 -04:00
2023-03-31 04:25:07 -04:00
/* Now we close the output 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 .
2009-09-22 13:31:03 -04:00
*/
2024-02-20 12:39:50 -05:00
if ( ( appctx - > st1 & ( APPCTX_CLI_ST1_PROMPT | APPCTX_CLI_ST1_PAYLOAD | APPCTX_CLI_ST1_LASTCMD ) ) = = APPCTX_CLI_ST1_LASTCMD ) {
2024-02-15 07:34:05 -05:00
applet_set_eoi ( appctx ) ;
appctx - > st0 = CLI_ST_END ;
continue ;
}
2009-09-22 13:31:03 -04:00
2009-10-04 08:22:18 -04:00
/* switch state back to GETREQ to read next requests */
2022-08-18 12:04:37 -04:00
applet_reset_svcctx ( appctx ) ;
2016-11-24 09:53:53 -05:00
appctx - > st0 = CLI_ST_GETREQ ;
2022-06-01 11:25:42 -04:00
applet_will_consume ( appctx ) ;
2023-09-06 03:17:33 -04:00
applet_expect_data ( appctx ) ;
2022-06-01 11:25:42 -04:00
2019-07-01 04:56:15 -04:00
/* reactivate the \n at the end of the response for the next command */
appctx - > st1 & = ~ APPCTX_CLI_ST1_NOLF ;
MEDIUM: cli: yield between each pipelined command
Pipelining commands on the CLI is sometimes needed for batched operations
such as map deletion etc, but it causes two problems:
- some possibly long-running commands will be run in series without
yielding, possibly causing extremely long latencies that will affect
quality of service and even trigger the watchdog, as seen in github
issue #1515.
- short commands that end on a buffer size boundary, when not run in
interactive mode, will often cause the socket to be closed when
the last command is parsed, because the buffer is empty.
This patch proposes a small change to this: by yielding in the CLI applet
after processing a command when there are data left, we significantly
reduce the latency, since only one command is executed per call, and
we leave an opportunity for the I/O layers to refill the request buffer
with more commands, hence to execute all of them much more often.
With this change there's no more watchdog triggered on long series of
"del map" on large map files, and the operations are much less disturbed.
It would be desirable to backport this patch to stable versions after some
period of observation in recent versions.
2022-01-19 11:11:36 -05:00
/* this forces us to yield between pipelined commands and
* avoid extremely long latencies ( e . g . " del map " etc ) . In
* addition this increases the likelihood that the stream
* refills the buffer with new bytes in non - interactive
* mode , avoiding to close on apparently empty commands .
*/
2024-02-20 02:47:38 -05:00
break ;
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-04-19 11:20:03 -04:00
out :
2024-02-15 07:34:05 -05:00
if ( appctx - > st0 = = CLI_ST_END ) {
/* eat the whole request */
b_reset ( & appctx - > inbuf ) ;
applet_fl_clr ( appctx , APPCTX_FL_INBLK_FULL ) ;
}
2023-04-18 12:36:43 -04:00
return ;
2009-08-16 13:06:42 -04:00
}
2022-05-17 13:07:51 -04:00
/* This is called when the stream connector is closed. For instance, upon an
2012-11-25 20:22:40 -05:00
* 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 ;
}
2022-11-10 05:47:36 -05:00
else if ( appctx - > st0 = = CLI_ST_PRINT_DYN | | appctx - > st0 = = CLI_ST_PRINT_DYNERR ) {
2022-05-06 11:16:35 -04:00
struct cli_print_ctx * ctx = applet_reserve_svcctx ( appctx , sizeof ( * ctx ) ) ;
ha_free ( & ctx - > err ) ;
2014-01-29 13:08:49 -05:00
}
2022-11-10 08:24:51 -05:00
else if ( appctx - > st0 = = CLI_ST_PRINT_UMSG | | appctx - > st0 = = CLI_ST_PRINT_UMSGERR ) {
usermsgs_clr ( NULL ) ;
}
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
2022-05-05 11:45:52 -04:00
* non - zero . It takes its context from the show_env_ctx in svcctx , and will
* start from - > var and dump only one variable if - > show_one is set .
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
{
2022-05-05 11:45:52 -04:00
struct show_env_ctx * ctx = appctx - > svcctx ;
char * * var = ctx - > var ;
2016-02-16 05:27:28 -05:00
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
2022-05-18 09:07:19 -04:00
if ( applet_putchk ( appctx , & trash ) = = - 1 )
2016-02-16 05:27:28 -05:00
return 0 ;
2022-05-18 09:07:19 -04:00
2022-05-05 11:45:52 -04:00
if ( ctx - > show_one )
2016-02-16 05:27:28 -05:00
break ;
2016-12-16 11:45:44 -05:00
var + + ;
2022-05-05 11:45:52 -04:00
ctx - > var = 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
2022-05-05 11:56:58 -04:00
* called again , otherwise non - zero . It takes its context from the show_fd_ctx
* in svcctx , only dumps one entry if - > show_one is non - zero , and ( re ) starts
* from - > fd .
2017-07-25 13:32:50 -04:00
*/
static int cli_io_handler_show_fd ( struct appctx * appctx )
{
2022-05-05 11:56:58 -04:00
struct show_fd_ctx * fdctx = appctx - > svcctx ;
2023-03-31 10:33:53 -04:00
uint match = fdctx - > show_mask ;
2022-05-05 11:56:58 -04:00
int fd = fdctx - > fd ;
2018-12-18 09:45:11 -05:00
int ret = 1 ;
2017-07-25 13:32:50 -04:00
chunk_reset ( & trash ) ;
2018-12-18 09:45:11 -05:00
/* isolate the threads once per round. We're limited to a buffer worth
* of output anyway , it cannot last very long .
*/
thread_isolate ( ) ;
2017-07-25 13:32:50 -04:00
/* we have two inner loops here, one for the proxy, the other one for
* the buffer .
*/
2018-03-09 12:51:16 -05:00
while ( fd > = 0 & & fd < global . maxsock ) {
2017-07-25 13:32:50 -04:00
struct fdtab fdt ;
2021-01-20 08:13:46 -05:00
const struct listener * li = NULL ;
const struct server * sv = NULL ;
const struct proxy * px = NULL ;
const struct connection * conn = NULL ;
2018-03-29 07:19:37 -04:00
const struct mux_ops * mux = NULL ;
2021-01-20 08:40:04 -05:00
const struct xprt_ops * xprt = NULL ;
2021-01-20 08:13:46 -05:00
const void * ctx = NULL ;
2021-01-20 08:40:04 -05:00
const void * xprt_ctx = NULL ;
2023-05-11 11:06:22 -04:00
const struct quic_conn * qc = NULL ;
2017-08-09 10:35:44 -04:00
uint32_t conn_flags = 0 ;
2023-03-14 10:48:06 -04:00
uint8_t conn_err = 0 ;
2018-12-19 12:40:58 -05:00
int is_back = 0 ;
2021-01-21 02:26:06 -05:00
int suspicious = 0 ;
2017-07-25 13:32:50 -04:00
fdt = fdtab [ fd ] ;
2020-06-23 04:04:54 -04:00
/* When DEBUG_FD is set, we also report closed FDs that have a
* non - null event count to detect stuck ones .
*/
2020-06-29 08:23:31 -04:00
if ( ! fdt . owner ) {
2020-06-23 04:04:54 -04:00
# ifdef DEBUG_FD
2020-06-29 08:23:31 -04:00
if ( ! fdt . event_count )
2020-06-23 04:04:54 -04:00
# endif
2020-06-29 08:23:31 -04:00
goto skip ; // closed
}
2020-12-11 09:54:36 -05:00
else if ( fdt . iocb = = sock_conn_iocb ) {
2021-01-20 08:13:46 -05:00
conn = ( const struct connection * ) fdt . owner ;
conn_flags = conn - > flags ;
2023-03-14 10:48:06 -04:00
conn_err = conn - > err_code ;
2021-01-20 08:13:46 -05:00
mux = conn - > mux ;
ctx = conn - > ctx ;
2021-01-20 08:40:04 -05:00
xprt = conn - > xprt ;
xprt_ctx = conn - > xprt_ctx ;
2021-01-20 08:13:46 -05:00
li = objt_listener ( conn - > target ) ;
sv = objt_server ( conn - > target ) ;
px = objt_proxy ( conn - > target ) ;
is_back = conn_is_back ( conn ) ;
2021-01-21 03:07:29 -05:00
if ( atleast2 ( fdt . thread_mask ) )
suspicious = 1 ;
if ( conn - > handle . fd ! = fd )
suspicious = 1 ;
2017-07-25 13:32:50 -04:00
}
2023-05-11 11:06:22 -04:00
# if defined(USE_QUIC)
else if ( fdt . iocb = = quic_conn_sock_fd_iocb ) {
qc = fdtab [ fd ] . owner ;
li = qc ? qc - > li : NULL ;
xprt_ctx = qc ? qc - > xprt_ctx : NULL ;
conn = qc ? qc - > conn : NULL ;
xprt = conn ? conn - > xprt : NULL ; // in fact it's &ssl_quic
mux = conn ? conn - > mux : NULL ;
/* quic_conns don't always have a connection but they
* always have an xprt_ctx .
*/
}
else if ( fdt . iocb = = quic_lstnr_sock_fd_iocb ) {
li = objt_listener ( fdtab [ fd ] . owner ) ;
}
# endif
2020-10-15 15:29:49 -04:00
else if ( fdt . iocb = = sock_accept_iocb )
2017-07-25 13:32:50 -04:00
li = fdt . owner ;
2023-05-11 11:06:22 -04:00
if ( ! ( ( ( conn | | xprt_ctx ) & &
2023-03-31 10:33:53 -04:00
( ( match & CLI_SHOWFD_F_SV & & sv ) | |
( match & CLI_SHOWFD_F_PX & & px ) | |
( match & CLI_SHOWFD_F_FE & & li ) ) ) | |
( ! conn & &
( ( match & CLI_SHOWFD_F_LI & & li ) | |
( match & CLI_SHOWFD_F_PI & & ! li /* only pipes match this */ ) ) ) ) ) {
/* not a desired type */
goto skip ;
}
2021-01-21 03:07:29 -05:00
if ( ! fdt . thread_mask )
suspicious = 1 ;
2017-07-25 13:32:50 -04:00
chunk_printf ( & trash ,
2025-01-30 10:25:40 -05:00
" %5d : st=0x%06x(%c%c %c%c%c%c%c W:%c%c%c R:%c%c%c) ref=%#x gid=%d tmask=0x%lx umask=0x%lx prmsk=0x%lx pwmsk=0x%lx owner=%p gen=%u tkov=%u iocb=%p( " ,
2017-07-25 13:32:50 -04:00
fd ,
fdt . state ,
2021-04-07 02:48:12 -04:00
( fdt . state & FD_CLONED ) ? ' C ' : ' c ' ,
( fdt . state & FD_LINGER_RISK ) ? ' L ' : ' l ' ,
2021-04-06 11:23:40 -04:00
( fdt . state & FD_POLL_HUP ) ? ' H ' : ' h ' ,
( fdt . state & FD_POLL_ERR ) ? ' E ' : ' e ' ,
( fdt . state & FD_POLL_OUT ) ? ' O ' : ' o ' ,
( fdt . state & FD_POLL_PRI ) ? ' P ' : ' p ' ,
( fdt . state & FD_POLL_IN ) ? ' I ' : ' i ' ,
2021-04-07 02:48:12 -04:00
( fdt . state & FD_EV_SHUT_W ) ? ' S ' : ' s ' ,
( fdt . state & FD_EV_READY_W ) ? ' R ' : ' r ' ,
( fdt . state & FD_EV_ACTIVE_W ) ? ' A ' : ' a ' ,
( fdt . state & FD_EV_SHUT_R ) ? ' S ' : ' s ' ,
( fdt . state & FD_EV_READY_R ) ? ' R ' : ' r ' ,
( fdt . state & FD_EV_ACTIVE_R ) ? ' A ' : ' a ' ,
2022-07-08 04:23:01 -04:00
( fdt . refc_tgid > > 4 ) & 0xffff ,
( fdt . refc_tgid ) & 0xffff ,
2018-03-30 09:00:15 -04:00
fdt . thread_mask , fdt . update_mask ,
2023-03-02 09:05:31 -05:00
polled_mask [ fd ] . poll_recv ,
polled_mask [ fd ] . poll_send ,
2017-07-25 13:32:50 -04:00
fdt . owner ,
2025-01-30 10:25:40 -05:00
fdt . generation ,
2025-01-30 09:59:11 -05:00
fdt . nb_takeover ,
2020-03-03 11:29:58 -05:00
fdt . iocb ) ;
resolve_sym_name ( & trash , NULL , fdt . iocb ) ;
2017-07-25 13:32:50 -04:00
2020-06-23 04:04:54 -04:00
if ( ! fdt . owner ) {
chunk_appendf ( & trash , " ) " ) ;
}
2023-05-11 11:06:22 -04:00
else if ( conn ) {
2023-03-14 10:48:06 -04:00
chunk_appendf ( & trash , " ) back=%d cflg=0x%08x cerr=%d " , is_back , conn_flags , conn_err ) ;
2021-01-21 03:07:29 -05:00
2023-05-11 11:06:22 -04:00
if ( ! ( conn - > flags & CO_FL_FDLESS ) & & conn - > handle . fd ! = fd ) {
2021-01-21 03:07:29 -05:00
chunk_appendf ( & trash , " fd=%d(BOGUS) " , conn - > handle . fd ) ;
suspicious = 1 ;
2023-05-11 11:06:22 -04:00
} else if ( ( conn - > flags & CO_FL_FDLESS ) & & ( qc ! = conn - > handle . qc ) ) {
chunk_appendf ( & trash , " qc=%p(BOGUS) " , conn - > handle . qc ) ;
suspicious = 1 ;
2021-02-05 04:54:52 -05:00
} else {
struct sockaddr_storage sa ;
socklen_t salen ;
salen = sizeof ( sa ) ;
if ( getsockname ( fd , ( struct sockaddr * ) & sa , & salen ) ! = - 1 ) {
MEDIUM: protocol: make abns a custom unix socket address family
This is a pre-requisite to adding the abnsz socket address family:
in this patch we make use of protocol API rework started by 732913f
("MINOR: protocol: properly assign the sock_domain and sock_family") in
order to implement a dedicated address family for ABNS sockets (based on
UNIX parent family).
Thanks to this, it will become trivial to implement a new ABNSZ (for abns
zero) family which is essentially the same as ABNS but with a slight
difference when it comes to path handling (ABNS uses the whole sun_path
length, while ABNSZ's path is zero terminated and evaluation stops at 0)
It was verified that this patch doesn't break reg-tests and behaves
properly (tests performed on the CLI with show sess and show fd).
Anywhere relevant, AF_CUST_ABNS is handled alongside AF_UNIX. If no
distinction needs to be made, real_family() is used to fetch the proper
real family type to handle it properly.
Both stream and dgram were converted, so no functional change should be
expected for this "internal" rework, except that proto will be displayed
as "abns_{stream,dgram}" instead of "unix_{stream,dgram}".
Before ("show sess" output):
0x64c35528aab0: proto=unix_stream src=unix:1 fe=GLOBAL be=<NONE> srv=<none> ts=00 epoch=0 age=0s calls=1 rate=0 cpu=0 lat=0 rq[f=848000h,i=0,an=00h,ax=] rp[f=80008000h,i=0,an=00h,ax=] scf=[8,0h,fd=21,rex=10s,wex=] scb=[8,1h,fd=-1,rex=,wex=] exp=10s rc=0 c_exp=
After:
0x619da7ad74c0: proto=abns_stream src=unix:1 fe=GLOBAL be=<NONE> srv=<none> ts=00 epoch=0 age=0s calls=1 rate=0 cpu=0 lat=0 rq[f=848000h,i=0,an=00h,ax=] rp[f=80008000h,i=0,an=00h,ax=] scf=[8,0h,fd=22,rex=10s,wex=] scb=[8,1h,fd=-1,rex=,wex=] exp=10s rc=0 c_exp=
Co-authored-by: Aurelien DARRAGON <adarragon@haproxy.com>
2024-08-09 12:48:14 -04:00
/* only real address families in .ss_family (as provided by getsockname) */
2021-02-05 04:54:52 -05:00
if ( sa . ss_family = = AF_INET )
chunk_appendf ( & trash , " fam=ipv4 lport=%d " , ntohs ( ( ( const struct sockaddr_in * ) & sa ) - > sin_port ) ) ;
else if ( sa . ss_family = = AF_INET6 )
chunk_appendf ( & trash , " fam=ipv6 lport=%d " , ntohs ( ( ( const struct sockaddr_in6 * ) & sa ) - > sin6_port ) ) ;
else if ( sa . ss_family = = AF_UNIX )
chunk_appendf ( & trash , " fam=unix " ) ;
}
salen = sizeof ( sa ) ;
if ( getpeername ( fd , ( struct sockaddr * ) & sa , & salen ) ! = - 1 ) {
if ( sa . ss_family = = AF_INET )
chunk_appendf ( & trash , " rport=%d " , ntohs ( ( ( const struct sockaddr_in * ) & sa ) - > sin_port ) ) ;
else if ( sa . ss_family = = AF_INET6 )
chunk_appendf ( & trash , " rport=%d " , ntohs ( ( ( const struct sockaddr_in6 * ) & sa ) - > sin6_port ) ) ;
}
2021-01-21 03:07:29 -05:00
}
2017-07-25 13:32:50 -04:00
if ( px )
chunk_appendf ( & trash , " px=%s " , px - > id ) ;
else if ( sv )
2021-07-06 05:41:10 -04:00
chunk_appendf ( & trash , " sv=%s/%s " , sv - > proxy - > id , sv - > id ) ;
2017-07-25 13:32:50 -04:00
else if ( li )
chunk_appendf ( & trash , " fe=%s " , li - > bind_conf - > frontend - > id ) ;
2018-03-28 12:41:30 -04:00
2018-03-30 08:41:19 -04:00
if ( mux ) {
2018-12-19 08:12:10 -05:00
chunk_appendf ( & trash , " mux=%s ctx=%p " , mux - > name , ctx ) ;
2023-05-11 11:06:22 -04:00
if ( ! ctx & & ! qc )
2021-01-21 03:07:29 -05:00
suspicious = 1 ;
2018-03-30 08:41:19 -04:00
if ( mux - > show_fd )
2021-01-21 02:26:06 -05:00
suspicious | = mux - > show_fd ( & trash , fdt . owner ) ;
2018-03-30 08:41:19 -04:00
}
2018-03-28 12:41:30 -04:00
else
chunk_appendf ( & trash , " nomux " ) ;
2021-01-20 08:40:04 -05:00
chunk_appendf ( & trash , " xprt=%s " , xprt ? xprt - > name : " " ) ;
2021-01-20 09:30:56 -05:00
if ( xprt ) {
if ( xprt_ctx | | xprt - > show_fd )
chunk_appendf ( & trash , " xprt_ctx=%p " , xprt_ctx ) ;
if ( xprt - > show_fd )
2021-01-21 02:26:06 -05:00
suspicious | = xprt - > show_fd ( & trash , conn , xprt_ctx ) ;
2021-01-20 09:30:56 -05:00
}
2017-07-25 13:32:50 -04:00
}
2023-05-11 11:06:22 -04:00
else if ( li & & ! xprt_ctx ) {
2021-02-05 04:54:52 -05:00
struct sockaddr_storage sa ;
socklen_t salen ;
2020-03-03 11:29:58 -05:00
chunk_appendf ( & trash , " ) l.st=%s fe=%s " ,
2017-07-25 13:32:50 -04:00
listener_state_str ( li ) ,
li - > bind_conf - > frontend - > id ) ;
2021-02-05 04:54:52 -05:00
salen = sizeof ( sa ) ;
if ( getsockname ( fd , ( struct sockaddr * ) & sa , & salen ) ! = - 1 ) {
MEDIUM: protocol: make abns a custom unix socket address family
This is a pre-requisite to adding the abnsz socket address family:
in this patch we make use of protocol API rework started by 732913f
("MINOR: protocol: properly assign the sock_domain and sock_family") in
order to implement a dedicated address family for ABNS sockets (based on
UNIX parent family).
Thanks to this, it will become trivial to implement a new ABNSZ (for abns
zero) family which is essentially the same as ABNS but with a slight
difference when it comes to path handling (ABNS uses the whole sun_path
length, while ABNSZ's path is zero terminated and evaluation stops at 0)
It was verified that this patch doesn't break reg-tests and behaves
properly (tests performed on the CLI with show sess and show fd).
Anywhere relevant, AF_CUST_ABNS is handled alongside AF_UNIX. If no
distinction needs to be made, real_family() is used to fetch the proper
real family type to handle it properly.
Both stream and dgram were converted, so no functional change should be
expected for this "internal" rework, except that proto will be displayed
as "abns_{stream,dgram}" instead of "unix_{stream,dgram}".
Before ("show sess" output):
0x64c35528aab0: proto=unix_stream src=unix:1 fe=GLOBAL be=<NONE> srv=<none> ts=00 epoch=0 age=0s calls=1 rate=0 cpu=0 lat=0 rq[f=848000h,i=0,an=00h,ax=] rp[f=80008000h,i=0,an=00h,ax=] scf=[8,0h,fd=21,rex=10s,wex=] scb=[8,1h,fd=-1,rex=,wex=] exp=10s rc=0 c_exp=
After:
0x619da7ad74c0: proto=abns_stream src=unix:1 fe=GLOBAL be=<NONE> srv=<none> ts=00 epoch=0 age=0s calls=1 rate=0 cpu=0 lat=0 rq[f=848000h,i=0,an=00h,ax=] rp[f=80008000h,i=0,an=00h,ax=] scf=[8,0h,fd=22,rex=10s,wex=] scb=[8,1h,fd=-1,rex=,wex=] exp=10s rc=0 c_exp=
Co-authored-by: Aurelien DARRAGON <adarragon@haproxy.com>
2024-08-09 12:48:14 -04:00
/* only real address families in .ss_family (as provided by getsockname) */
2021-02-05 04:54:52 -05:00
if ( sa . ss_family = = AF_INET )
chunk_appendf ( & trash , " fam=ipv4 lport=%d " , ntohs ( ( ( const struct sockaddr_in * ) & sa ) - > sin_port ) ) ;
else if ( sa . ss_family = = AF_INET6 )
chunk_appendf ( & trash , " fam=ipv6 lport=%d " , ntohs ( ( ( const struct sockaddr_in6 * ) & sa ) - > sin6_port ) ) ;
else if ( sa . ss_family = = AF_UNIX )
chunk_appendf ( & trash , " fam=unix " ) ;
}
2017-07-25 13:32:50 -04:00
}
2021-01-20 08:13:46 -05:00
else
chunk_appendf ( & trash , " ) " ) ;
2017-07-25 13:32:50 -04:00
2020-06-23 04:04:54 -04:00
# ifdef DEBUG_FD
chunk_appendf ( & trash , " evcnt=%u " , fdtab [ fd ] . event_count ) ;
2021-01-21 03:07:29 -05:00
if ( fdtab [ fd ] . event_count > = 1000000 )
suspicious = 1 ;
2020-06-23 04:04:54 -04:00
# endif
2021-01-21 02:26:06 -05:00
chunk_appendf ( & trash , " %s \n " , suspicious ? " ! " : " " ) ;
2017-07-25 13:32:50 -04:00
2022-05-18 09:07:19 -04:00
if ( applet_putchk ( appctx , & trash ) = = - 1 ) {
2022-05-05 11:56:58 -04:00
fdctx - > fd = fd ;
2018-12-18 09:45:11 -05:00
ret = 0 ;
break ;
2017-07-25 13:32:50 -04:00
}
skip :
2022-05-05 11:56:58 -04:00
if ( fdctx - > show_one )
2017-07-25 13:32:50 -04:00
break ;
fd + + ;
}
2018-12-18 09:45:11 -05:00
end :
2017-07-25 13:32:50 -04:00
/* dump complete */
2018-12-18 09:45:11 -05:00
thread_release ( ) ;
return ret ;
2017-07-25 13:32:50 -04:00
}
2016-12-15 12:06:44 -05:00
/*
2016-12-16 06:58:09 -05:00
* CLI IO handler for ` show cli sockets ` .
2022-05-05 13:11:05 -04:00
* Uses the svcctx as a show_sock_ctx to store / retrieve the bind_conf and the
* listener pointers .
2016-12-15 12:06:44 -05:00
*/
static int cli_io_handler_show_cli_sock ( struct appctx * appctx )
{
2022-05-05 13:11:05 -04:00
struct show_sock_ctx * ctx = applet_reserve_svcctx ( appctx , sizeof ( * ctx ) ) ;
struct bind_conf * bind_conf = ctx - > bind_conf ;
2016-12-15 12:06:44 -05:00
2022-05-05 12:52:36 -04:00
if ( ! global . cli_fe )
goto done ;
2016-12-15 12:06:44 -05:00
chunk_reset ( & trash ) ;
2022-05-05 12:52:36 -04:00
if ( ! bind_conf ) {
/* first call */
2022-05-18 09:07:19 -04:00
if ( applet_putstr ( appctx , " # socket lvl processes \n " ) = = - 1 )
2022-05-05 12:52:36 -04:00
goto full ;
bind_conf = LIST_ELEM ( global . cli_fe - > conf . bind . n , typeof ( bind_conf ) , by_fe ) ;
}
list_for_each_entry_from ( bind_conf , & global . cli_fe - > conf . bind , by_fe ) {
2022-05-05 13:11:05 -04:00
struct listener * l = ctx - > listener ;
2022-05-05 12:52:36 -04:00
if ( ! l )
l = LIST_ELEM ( bind_conf - > listeners . n , typeof ( l ) , by_bind ) ;
list_for_each_entry_from ( l , & bind_conf - > listeners , by_bind ) {
char addr [ 46 ] ;
char port [ 6 ] ;
2024-08-09 15:12:23 -04:00
if ( l - > rx . addr . ss_family = = AF_UNIX | |
l - > rx . addr . ss_family = = AF_CUST_ABNS | |
l - > rx . addr . ss_family = = AF_CUST_ABNSZ ) {
2022-05-05 12:52:36 -04:00
const struct sockaddr_un * un ;
un = ( struct sockaddr_un * ) & l - > rx . addr ;
2024-08-09 15:12:23 -04:00
if ( l - > rx . addr . ss_family = = AF_CUST_ABNS | |
l - > rx . addr . ss_family = = AF_CUST_ABNSZ ) {
2022-05-05 12:52:36 -04:00
chunk_appendf ( & trash , " abns@%s " , un - > sun_path + 1 ) ;
} else {
chunk_appendf ( & trash , " unix@%s " , un - > sun_path ) ;
2016-12-15 12:06:44 -05:00
}
2022-05-05 12:52:36 -04:00
} else if ( l - > rx . addr . ss_family = = AF_INET ) {
addr_to_str ( & l - > rx . addr , addr , sizeof ( addr ) ) ;
port_to_str ( & l - > rx . addr , port , sizeof ( port ) ) ;
chunk_appendf ( & trash , " ipv4@%s:%s " , addr , port ) ;
} else if ( l - > rx . addr . ss_family = = AF_INET6 ) {
addr_to_str ( & l - > rx . addr , addr , sizeof ( addr ) ) ;
port_to_str ( & l - > rx . addr , port , sizeof ( port ) ) ;
chunk_appendf ( & trash , " ipv6@[%s]:%s " , addr , port ) ;
} else if ( l - > rx . addr . ss_family = = AF_CUST_SOCKPAIR ) {
chunk_appendf ( & trash , " sockpair@%d " , ( ( struct sockaddr_in * ) & l - > rx . addr ) - > sin_addr . s_addr ) ;
} else
chunk_appendf ( & trash , " unknown " ) ;
if ( ( bind_conf - > level & ACCESS_LVL_MASK ) = = ACCESS_LVL_ADMIN )
chunk_appendf ( & trash , " admin " ) ;
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 " ) ;
else
chunk_appendf ( & trash , " " ) ;
chunk_appendf ( & trash , " all \n " ) ;
2022-05-18 09:07:19 -04:00
if ( applet_putchk ( appctx , & trash ) = = - 1 ) {
2022-05-05 13:11:05 -04:00
ctx - > bind_conf = bind_conf ;
ctx - > listener = l ;
2022-05-05 12:52:36 -04:00
goto full ;
2016-12-15 12:06:44 -05:00
}
2022-05-05 12:52:36 -04:00
}
2016-12-15 12:06:44 -05:00
}
2022-05-05 12:52:36 -04:00
done :
return 1 ;
full :
return 0 ;
2016-12-15 12:06:44 -05:00
}
2016-11-22 14:21:23 -05:00
/* parse a "show env" CLI request. Returns 0 if it needs to continue, 1 if it
2024-11-18 04:46:33 -05:00
* wants to stop here . It reserves a show_env_ctx where it puts the variable to
2022-05-05 11:45:52 -04:00
* be dumped as well as a flag if a single variable is requested , otherwise puts
* environ there .
2016-11-22 14:21:23 -05:00
*/
2018-04-18 07:26:46 -04:00
static int cli_parse_show_env ( char * * args , char * payload , struct appctx * appctx , void * private )
2016-11-22 14:21:23 -05:00
{
2022-05-05 11:45:52 -04:00
struct show_env_ctx * ctx = applet_reserve_svcctx ( appctx , sizeof ( * ctx ) ) ;
2016-11-22 14:21:23 -05:00
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 ;
}
2019-08-09 05:21:01 -04:00
if ( ! * var )
return cli_err ( appctx , " Variable not found \n " ) ;
2022-05-05 11:45:52 -04:00
ctx - > show_one = 1 ;
2016-11-22 14:21:23 -05:00
}
2022-05-05 11:45:52 -04:00
ctx - > var = 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
2022-05-05 11:56:58 -04:00
* wants to stop here . It sets a show_fd_ctx context where , if a specific fd is
* requested , it puts the FD number into - > fd and sets - > show_one to 1.
2017-07-25 13:32:50 -04:00
*/
2018-04-18 07:26:46 -04:00
static int cli_parse_show_fd ( char * * args , char * payload , struct appctx * appctx , void * private )
2017-07-25 13:32:50 -04:00
{
2022-05-05 11:56:58 -04:00
struct show_fd_ctx * ctx = applet_reserve_svcctx ( appctx , sizeof ( * ctx ) ) ;
2023-03-31 10:33:53 -04:00
const char * c ;
int arg ;
2022-05-05 11:56:58 -04:00
2017-07-25 13:32:50 -04:00
if ( ! cli_has_level ( appctx , ACCESS_LVL_OPER ) )
return 1 ;
2023-03-31 10:33:53 -04:00
arg = 2 ;
/* when starting with an inversion we preset every flag */
if ( * args [ arg ] = = ' ! ' | | * args [ arg ] = = ' - ' )
ctx - > show_mask = CLI_SHOWFD_F_ANY ;
while ( * args [ arg ] & & ! isdigit ( ( uchar ) * args [ arg ] ) ) {
uint flag = 0 , inv = 0 ;
c = args [ arg ] ;
while ( * c ) {
switch ( * c ) {
case ' ! ' : inv = ! inv ; break ;
case ' - ' : inv = ! inv ; break ;
case ' p ' : flag = CLI_SHOWFD_F_PI ; break ;
case ' l ' : flag = CLI_SHOWFD_F_LI ; break ;
case ' c ' : flag = CLI_SHOWFD_F_CO ; break ;
case ' f ' : flag = CLI_SHOWFD_F_FE ; break ;
case ' b ' : flag = CLI_SHOWFD_F_BE ; break ;
case ' s ' : flag = CLI_SHOWFD_F_SV ; break ;
case ' d ' : flag = CLI_SHOWFD_F_PX ; break ;
default : return cli_err ( appctx , " Invalid FD type \n " ) ;
}
c + + ;
if ( ! inv )
ctx - > show_mask | = flag ;
else
ctx - > show_mask & = ~ flag ;
}
arg + + ;
}
/* default mask is to show everything */
if ( ! ctx - > show_mask )
ctx - > show_mask = CLI_SHOWFD_F_ANY ;
if ( * args [ arg ] ) {
2022-05-05 11:56:58 -04:00
ctx - > fd = atoi ( args [ 2 ] ) ;
ctx - > show_one = 1 ;
2017-07-25 13:32:50 -04:00
}
2023-03-31 10:33:53 -04:00
2017-07-25 13:32:50 -04:00
return 0 ;
}
2016-11-22 14:33:32 -05:00
/* parse a "set timeout" CLI request. It always returns 1. */
2018-04-18 07:26:46 -04:00
static int cli_parse_set_timeout ( char * * args , char * payload , struct appctx * appctx , void * private )
2016-11-22 14:33:32 -05:00
{
2022-05-11 08:09:57 -04:00
struct stream * s = appctx_strm ( appctx ) ;
2016-11-22 14:33:32 -05:00
if ( strcmp ( args [ 2 ] , " cli " ) = = 0 ) {
unsigned timeout ;
const char * res ;
2019-08-09 05:21:01 -04:00
if ( ! * args [ 3 ] )
return cli_err ( appctx , " Expects an integer value. \n " ) ;
2016-11-22 14:33:32 -05:00
res = parse_time_err ( args [ 3 ] , & timeout , TIME_UNIT_S ) ;
2019-08-09 05:21:01 -04:00
if ( res | | timeout < 1 )
return cli_err ( appctx , " Invalid timeout value. \n " ) ;
2016-11-22 14:33:32 -05:00
2023-02-15 02:13:33 -05:00
s - > scf - > ioto = 1 + MS_TO_TICKS ( timeout * 1000 ) ;
2016-11-22 14:33:32 -05:00
task_wakeup ( s - > task , TASK_WOKEN_MSG ) ; // recompute timeouts
return 1 ;
}
2019-08-09 05:21:01 -04:00
return cli_err ( appctx , " 'set timeout' only supports 'cli'. \n " ) ;
2016-11-22 14:33:32 -05:00
}
2016-11-23 05:10:59 -05:00
/* parse a "set maxconn global" command. It always returns 1. */
2018-04-18 07:26:46 -04:00
static int cli_parse_set_maxconn_global ( char * * args , char * payload , struct appctx * appctx , void * private )
2016-11-23 05:10:59 -05:00
{
int v ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_ADMIN ) )
return 1 ;
2019-08-09 05:21:01 -04:00
if ( ! * args [ 3 ] )
return cli_err ( appctx , " Expects an integer value. \n " ) ;
2016-11-23 05:10:59 -05:00
v = atoi ( args [ 3 ] ) ;
2019-08-09 05:21:01 -04:00
if ( v > global . hardmaxconn )
return cli_err ( appctx , " Value out of range. \n " ) ;
2016-11-23 05:10:59 -05:00
/* check for unlimited values */
if ( v < = 0 )
v = global . hardmaxconn ;
global . maxconn = v ;
/* Dequeues all of the listeners waiting for a resource */
2019-12-10 08:10:52 -05:00
dequeue_all_listeners ( ) ;
2016-11-23 05:10:59 -05:00
return 1 ;
}
2017-07-20 05:59:48 -04:00
static int set_severity_output ( int * target , char * argument )
{
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( argument , " none " ) = = 0 ) {
2017-07-20 05:59:48 -04:00
* target = CLI_SEVERITY_NONE ;
return 1 ;
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
else if ( strcmp ( argument , " number " ) = = 0 ) {
2017-07-20 05:59:48 -04:00
* target = CLI_SEVERITY_NUMBER ;
return 1 ;
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
else if ( strcmp ( argument , " string " ) = = 0 ) {
2017-07-20 05:59:48 -04:00
* target = CLI_SEVERITY_STRING ;
return 1 ;
}
return 0 ;
}
/* parse a "set severity-output" command. */
2018-04-18 07:26:46 -04:00
static int cli_parse_set_severity_output ( char * * args , char * payload , struct appctx * appctx , void * private )
2017-07-20 05:59:48 -04:00
{
2023-12-06 05:15:01 -05:00
/* this will ask the applet to not output a \n after the command */
if ( strcmp ( args [ 3 ] , " - " ) = = 0 )
appctx - > st1 | = APPCTX_CLI_ST1_NOLF ;
2017-07-20 05:59:48 -04:00
if ( * args [ 2 ] & & set_severity_output ( & appctx - > cli_severity_output , args [ 2 ] ) )
return 0 ;
2019-08-09 05:21:01 -04:00
return cli_err ( appctx , " one of 'none', 'number', 'string' is a required argument \n " ) ;
2017-07-20 05:59:48 -04:00
}
2016-12-15 12:06:44 -05:00
2018-12-13 03:05:45 -05:00
/* show the level of the current CLI session */
static int cli_parse_show_lvl ( char * * args , char * payload , struct appctx * appctx , void * private )
{
if ( ( appctx - > cli_level & ACCESS_LVL_MASK ) = = ACCESS_LVL_ADMIN )
2019-08-09 05:21:01 -04:00
return cli_msg ( appctx , LOG_INFO , " admin \n " ) ;
2018-12-13 03:05:45 -05:00
else if ( ( appctx - > cli_level & ACCESS_LVL_MASK ) = = ACCESS_LVL_OPER )
2019-08-09 05:21:01 -04:00
return cli_msg ( appctx , LOG_INFO , " operator \n " ) ;
2018-12-13 03:05:45 -05:00
else if ( ( appctx - > cli_level & ACCESS_LVL_MASK ) = = ACCESS_LVL_USER )
2019-08-09 05:21:01 -04:00
return cli_msg ( appctx , LOG_INFO , " user \n " ) ;
2018-12-13 03:05:45 -05:00
else
2019-08-09 05:21:01 -04:00
return cli_msg ( appctx , LOG_INFO , " unknown \n " ) ;
2018-12-13 03:05:45 -05:00
}
/* parse and set the CLI level dynamically */
static int cli_parse_set_lvl ( char * * args , char * payload , struct appctx * appctx , void * private )
{
2019-07-01 04:56:15 -04:00
/* this will ask the applet to not output a \n after the command */
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ 1 ] , " - " ) = = 0 )
2019-07-01 04:56:15 -04:00
appctx - > st1 | = APPCTX_CLI_ST1_NOLF ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ 0 ] , " operator " ) = = 0 ) {
2018-12-13 03:05:45 -05:00
if ( ! cli_has_level ( appctx , ACCESS_LVL_OPER ) ) {
return 1 ;
}
appctx - > cli_level & = ~ ACCESS_LVL_MASK ;
appctx - > cli_level | = ACCESS_LVL_OPER ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( args [ 0 ] , " user " ) = = 0 ) {
2018-12-13 03:05:45 -05:00
if ( ! cli_has_level ( appctx , ACCESS_LVL_USER ) ) {
return 1 ;
}
appctx - > cli_level & = ~ ACCESS_LVL_MASK ;
appctx - > cli_level | = ACCESS_LVL_USER ;
}
2021-03-18 10:32:53 -04:00
appctx - > cli_level & = ~ ( ACCESS_EXPERT | ACCESS_EXPERIMENTAL ) ;
2019-10-24 11:55:53 -04:00
return 1 ;
}
2021-03-18 10:32:53 -04:00
/* parse and set the CLI expert/experimental-mode dynamically */
static int cli_parse_expert_experimental_mode ( char * * args , char * payload , struct appctx * appctx , void * private )
2019-10-24 11:55:53 -04:00
{
2021-03-18 10:32:53 -04:00
int level ;
char * level_str ;
char * output = NULL ;
2022-02-01 10:08:50 -05:00
/* this will ask the applet to not output a \n after the command */
if ( * args [ 1 ] & & * args [ 2 ] & & strcmp ( args [ 2 ] , " - " ) = = 0 )
appctx - > st1 | = APPCTX_CLI_ST1_NOLF ;
2019-10-24 11:55:53 -04:00
if ( ! cli_has_level ( appctx , ACCESS_LVL_ADMIN ) )
return 1 ;
2021-10-16 11:48:15 -04:00
if ( strcmp ( args [ 0 ] , " expert-mode " ) = = 0 ) {
2021-03-18 10:32:53 -04:00
level = ACCESS_EXPERT ;
level_str = " expert-mode " ;
}
2021-10-16 11:48:15 -04:00
else if ( strcmp ( args [ 0 ] , " experimental-mode " ) = = 0 ) {
2021-03-18 10:32:53 -04:00
level = ACCESS_EXPERIMENTAL ;
level_str = " experimental-mode " ;
}
2022-02-02 05:43:20 -05:00
else if ( strcmp ( args [ 0 ] , " mcli-debug-mode " ) = = 0 ) {
level = ACCESS_MCLI_DEBUG ;
level_str = " mcli-debug-mode " ;
}
2021-03-18 10:32:53 -04:00
else {
return 1 ;
}
if ( ! * args [ 1 ] ) {
memprintf ( & output , " %s is %s \n " , level_str ,
( appctx - > cli_level & level ) ? " ON " : " OFF " ) ;
return cli_dynmsg ( appctx , LOG_INFO , output ) ;
}
2019-10-24 11:55:53 -04:00
2021-03-18 10:32:53 -04:00
appctx - > cli_level & = ~ level ;
2019-10-24 11:55:53 -04:00
if ( strcmp ( args [ 1 ] , " on " ) = = 0 )
2021-03-18 10:32:53 -04:00
appctx - > cli_level | = level ;
2018-12-13 03:05:45 -05:00
return 1 ;
}
2021-12-14 09:22:29 -05:00
/* shows HAProxy version */
static int cli_parse_show_version ( char * * args , char * payload , struct appctx * appctx , void * private )
{
char * msg = NULL ;
return cli_dynmsg ( appctx , LOG_INFO , memprintf ( & msg , " %s \n " , haproxy_version ) ) ;
}
2018-04-18 07:26:46 -04:00
int cli_parse_default ( char * * args , char * payload , struct appctx * appctx , void * private )
2016-12-15 12:06:44 -05:00
{
return 0 ;
}
2022-09-14 11:24:22 -04:00
/* enable or disable the anonymized mode, it returns 1 when it works or displays an error message if it doesn't. */
static int cli_parse_set_anon ( char * * args , char * payload , struct appctx * appctx , void * private )
{
uint32_t tmp ;
long long key ;
if ( strcmp ( args [ 2 ] , " on " ) = = 0 ) {
2022-09-28 11:04:29 -04:00
if ( * args [ 3 ] ) {
key = atoll ( args [ 3 ] ) ;
if ( key < 1 | | key > UINT_MAX )
return cli_err ( appctx , " Value out of range (1 to 4294967295 expected) . \ n " ) ;
appctx - > cli_anon_key = key ;
}
2022-09-14 11:24:22 -04:00
else {
2022-09-28 11:04:29 -04:00
tmp = HA_ATOMIC_LOAD ( & global . anon_key ) ;
if ( tmp ! = 0 )
appctx - > cli_anon_key = tmp ;
else
appctx - > cli_anon_key = ha_random32 ( ) ;
2022-09-14 11:24:22 -04:00
}
}
else if ( strcmp ( args [ 2 ] , " off " ) = = 0 ) {
2022-09-28 11:04:29 -04:00
if ( * args [ 3 ] ) {
2022-09-14 11:24:22 -04:00
return cli_err ( appctx , " Key can't be added while disabling anonymized mode \n " ) ;
}
else {
appctx - > cli_anon_key = 0 ;
}
}
else {
return cli_err ( appctx ,
" 'set anon' only supports : \n "
" - 'on' [key] to enable the anonymized mode \n "
" - 'off' to disable the anonymized mode " ) ;
}
return 1 ;
}
2022-09-14 11:24:22 -04:00
/* This function set the global anonyzing key, restricted to level 'admin' */
static int cli_parse_set_global_key ( char * * args , char * payload , struct appctx * appctx , void * private )
{
long long key ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_ADMIN ) )
return cli_err ( appctx , " Permission denied \n " ) ;
if ( ! * args [ 2 ] )
return cli_err ( appctx , " Expects an integer value. \n " ) ;
key = atoll ( args [ 2 ] ) ;
if ( key < 0 | | key > UINT_MAX )
return cli_err ( appctx , " Value out of range (0 to 4294967295 expected) . \ n " ) ;
HA_ATOMIC_STORE ( & global . anon_key , key ) ;
return 1 ;
}
2022-09-14 11:24:22 -04:00
/* shows the anonymized mode state to everyone, and the key except for users, it always returns 1. */
static int cli_parse_show_anon ( char * * args , char * payload , struct appctx * appctx , void * private )
{
char * msg = NULL ;
char * anon_mode = NULL ;
uint32_t c_key = appctx - > cli_anon_key ;
if ( ! c_key )
anon_mode = " Anonymized mode disabled " ;
else
anon_mode = " Anonymized mode enabled " ;
if ( ! ( ( appctx - > cli_level & ACCESS_LVL_MASK ) < ACCESS_LVL_OPER ) & & c_key ! = 0 ) {
cli_dynmsg ( appctx , LOG_INFO , memprintf ( & msg , " %s \n Key : %u \n " , anon_mode , c_key ) ) ;
}
else {
cli_dynmsg ( appctx , LOG_INFO , memprintf ( & msg , " %s \n " , anon_mode ) ) ;
}
return 1 ;
}
2016-11-24 08:51:17 -05:00
/* parse a "set rate-limit" command. It always returns 1. */
2018-04-18 07:26:46 -04:00
static int cli_parse_set_ratelimit ( char * * args , char * payload , struct appctx * appctx , void * private )
2016-11-24 08:51:17 -05:00
{
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 {
2019-08-09 05:21:01 -04:00
return cli_err ( appctx ,
2016-11-24 08:51:17 -05:00
" '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
2018-03-11 11:55:02 -04:00
" - 'ssl-sessions global' to set the per-process maximum SSL session rate \n "
2016-11-24 08:51:17 -05:00
# endif
2019-08-09 05:21:01 -04:00
" - 'http-compression global' to set the per-process maximum compression speed in kB/s \n " ) ;
2016-11-24 08:51:17 -05:00
}
2019-08-09 05:21:01 -04:00
if ( ! * args [ 4 ] )
return cli_err ( appctx , " Expects an integer value. \n " ) ;
2016-11-24 08:51:17 -05:00
v = atoi ( args [ 4 ] ) ;
2019-08-09 05:21:01 -04:00
if ( v < 0 )
return cli_err ( appctx , " Value out of range. \n " ) ;
2016-11-24 08:51:17 -05:00
* res = v * mul ;
/* Dequeues all of the listeners waiting for a resource */
2019-12-10 08:10:52 -05:00
dequeue_all_listeners ( ) ;
2016-11-24 08:51:17 -05:00
return 1 ;
}
2024-02-08 15:45:22 -05:00
/* Parse a "wait <time>" command.
* It uses a " cli_wait_ctx " struct for its context .
* Returns 0 if the server deletion has been successfully scheduled , 1 on failure .
*/
static int cli_parse_wait ( char * * args , char * payload , struct appctx * appctx , void * private )
{
struct cli_wait_ctx * ctx = applet_reserve_svcctx ( appctx , sizeof ( * ctx ) ) ;
uint wait_ms ;
const char * err ;
if ( ! cli_has_level ( appctx , ACCESS_LVL_ADMIN ) )
return 1 ;
if ( ! * args [ 1 ] )
return cli_err ( appctx , " Expects a duration in milliseconds. \n " ) ;
err = parse_time_err ( args [ 1 ] , & wait_ms , TIME_UNIT_MS ) ;
2024-02-09 11:40:43 -05:00
if ( err | | wait_ms < 1 ) {
/* in case -h is passed as the first option, continue to the next test */
if ( strcmp ( args [ 1 ] , " -h " ) = = 0 )
args - - ;
else
return cli_err ( appctx , " Invalid duration. \n " ) ;
}
2024-04-27 03:12:34 -04:00
if ( strcmp ( args [ 2 ] , " srv-removable " ) = = 0 ) {
2024-02-09 14:35:52 -05:00
struct ist be_name , sv_name ;
if ( ! * args [ 3 ] )
return cli_err ( appctx , " Missing server name (<backend>/<server>) . \ n " ) ;
sv_name = ist ( args [ 3 ] ) ;
be_name = istsplit ( & sv_name , ' / ' ) ;
if ( ! istlen ( sv_name ) )
return cli_err ( appctx , " Require 'backend/server'. \n " ) ;
be_name = istdup ( be_name ) ;
sv_name = istdup ( sv_name ) ;
if ( ! isttest ( be_name ) | | ! isttest ( sv_name ) ) {
free ( istptr ( be_name ) ) ;
free ( istptr ( sv_name ) ) ;
return cli_err ( appctx , " Out of memory trying to clone the server name. \n " ) ;
}
ctx - > args [ 0 ] = ist0 ( be_name ) ;
ctx - > args [ 1 ] = ist0 ( sv_name ) ;
ctx - > cond = CLI_WAIT_COND_SRV_UNUSED ;
}
else if ( * args [ 2 ] ) {
2024-02-09 11:40:43 -05:00
/* show the command's help either upon request (-h) or error */
err = " Usage: wait {-h|<duration>} [condition [args...]] \n "
" - '-h' displays this help \n "
" - <duration> is the maximum wait time, optionally suffixed by the unit among \n "
" 'us', 'ms', 's', 'm', 'h', and 'd'. ; the default unit is milliseconds. \n "
2024-02-09 14:35:52 -05:00
" - <condition> indicates what to wait for, no longer than the specified \n "
" duration. Supported conditions are: \n "
" - <none> : by default, just sleep for the specified duration. \n "
2024-04-27 03:12:34 -04:00
" - srv-removable <px>/<sv> : wait for this server to become removable. \n "
2024-02-09 14:35:52 -05:00
" " ;
2024-02-09 11:40:43 -05:00
if ( strcmp ( args [ 2 ] , " -h " ) = = 0 )
return cli_msg ( appctx , LOG_INFO , err ) ;
else
return cli_err ( appctx , err ) ;
}
2024-02-08 15:45:22 -05:00
ctx - > start = now_ms ;
ctx - > deadline = tick_add ( now_ms , wait_ms ) ;
/* proceed with the I/O handler */
return 0 ;
}
/* Execute a "wait" condition. The delay is exponentially incremented between
* now_ms and ctx - > deadline in powers of 1.5 and with a bound set to 10 % of the
* programmed wait time , so that in a few wakeups we can later check a condition
* with reasonable accuracy . Shutdowns and other errors are handled as well and
* terminate the operation , but not new inputs so that it remains possible to
* chain other commands after it . Returns 0 if not finished , 1 if finished .
*/
static int cli_io_handler_wait ( struct appctx * appctx )
{
struct cli_wait_ctx * ctx = appctx - > svcctx ;
uint total , elapsed , left , wait ;
2024-02-09 14:35:52 -05:00
int ret ;
2024-02-08 15:45:22 -05:00
/* note: upon first invocation, the timeout is not set */
if ( tick_isset ( appctx - > t - > expire ) & &
! tick_is_expired ( appctx - > t - > expire , now_ms ) )
goto wait ;
/* here we should evaluate our waiting conditions, if any */
2024-02-09 14:35:52 -05:00
if ( ctx - > cond = = CLI_WAIT_COND_SRV_UNUSED ) {
/* check if the server in args[0]/args[1] can be released now */
ret = srv_check_for_deletion ( ctx - > args [ 0 ] , ctx - > args [ 1 ] , NULL , NULL , NULL ) ;
if ( ret < 0 ) {
/* unrecoverable failure */
ctx - > error = CLI_WAIT_ERR_FAIL ;
return 1 ;
} else if ( ret > 0 ) {
/* immediate success */
ctx - > error = CLI_WAIT_ERR_DONE ;
return 1 ;
}
/* let's check the timer */
}
2024-02-08 15:45:22 -05:00
/* and here we recalculate the new wait time or abort */
left = tick_remain ( now_ms , ctx - > deadline ) ;
if ( ! left ) {
/* let the release handler know we've expired. When there is no
* wait condition , it ' s a simple sleep so we declare we ' re done .
*/
if ( ctx - > cond = = CLI_WAIT_COND_NONE )
ctx - > error = CLI_WAIT_ERR_DONE ;
else
ctx - > error = CLI_WAIT_ERR_EXP ;
return 1 ;
}
total = tick_remain ( ctx - > start , ctx - > deadline ) ;
elapsed = total - left ;
wait = elapsed / 2 + 1 ;
if ( wait > left )
wait = left ;
else if ( wait > total / 10 )
wait = total / 10 ;
appctx - > t - > expire = tick_add ( now_ms , wait ) ;
wait :
/* Stop waiting upon close/abort/error */
2024-02-15 07:34:05 -05:00
if ( unlikely ( se_fl_test ( appctx - > sedesc , SE_FL_SHW ) ) & & ! b_data ( & appctx - > inbuf ) ) {
2024-02-08 15:45:22 -05:00
ctx - > error = CLI_WAIT_ERR_INTR ;
return 1 ;
}
2024-02-12 16:29:06 -05:00
2024-02-08 15:45:22 -05:00
return 0 ;
}
2024-02-15 07:34:05 -05:00
2024-02-08 15:45:22 -05:00
/* release structs allocated by "delete server" */
static void cli_release_wait ( struct appctx * appctx )
{
struct cli_wait_ctx * ctx = appctx - > svcctx ;
const char * msg ;
2024-02-09 14:09:59 -05:00
int i ;
2024-02-08 15:45:22 -05:00
switch ( ctx - > error ) {
case CLI_WAIT_ERR_EXP : msg = " Wait delay expired. \n " ; break ;
case CLI_WAIT_ERR_INTR : msg = " Interrupted. \n " ; break ;
2024-02-09 14:05:14 -05:00
case CLI_WAIT_ERR_FAIL : msg = ctx - > msg ? ctx - > msg : " Failed. \n " ; break ;
2024-02-08 15:45:22 -05:00
default : msg = " Done. \n " ; break ;
}
2024-02-09 14:09:59 -05:00
for ( i = 0 ; i < sizeof ( ctx - > args ) / sizeof ( ctx - > args [ 0 ] ) ; i + + )
ha_free ( & ctx - > args [ i ] ) ;
2024-02-08 15:45:22 -05:00
if ( ctx - > error = = CLI_WAIT_ERR_DONE )
cli_msg ( appctx , LOG_INFO , msg ) ;
else
cli_err ( appctx , msg ) ;
}
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 ;
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ cur_arg + 1 ] , " listeners " ) = = 0 ) {
2017-05-26 11:42:10 -04:00
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 ;
}
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
if ( strcmp ( args [ cur_arg + 1 ] , " user " ) = = 0 ) {
2017-05-23 18:57:40 -04:00
conf - > level & = ~ ACCESS_LVL_MASK ;
conf - > level | = ACCESS_LVL_USER ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( args [ cur_arg + 1 ] , " operator " ) = = 0 ) {
2017-05-23 18:57:40 -04:00
conf - > level & = ~ ACCESS_LVL_MASK ;
conf - > level | = ACCESS_LVL_OPER ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( args [ cur_arg + 1 ] , " admin " ) = = 0 ) {
2017-05-23 18:57:40 -04:00
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 */
2018-04-18 07:26:46 -04:00
static int _getsocks ( char * * args , char * payload , struct appctx * appctx , void * private )
2017-04-05 16:24:59 -04:00
{
2022-07-28 09:33:41 -04:00
static int already_sent = 0 ;
2017-04-05 16:24:59 -04:00
char * cmsgbuf = NULL ;
unsigned char * tmpbuf = NULL ;
struct cmsghdr * cmsg ;
2022-05-27 05:08:15 -04:00
struct stconn * sc = appctx_sc ( appctx ) ;
2022-05-27 04:26:46 -04:00
struct stream * s = __sc_strm ( sc ) ;
struct connection * remote = sc_conn ( sc_opposite ( sc ) ) ;
2017-04-05 16:24:59 -04:00
struct msghdr msghdr ;
struct iovec iov ;
2017-04-06 08:45:14 -04:00
struct timeval tv = { . tv_sec = 1 , . tv_usec = 0 } ;
2020-08-19 11:03:55 -04:00
const char * ns_name , * if_name ;
unsigned char ns_nlen , if_nlen ;
int nb_queued ;
int cur_fd = 0 ;
2017-04-05 16:24:59 -04:00
int * tmpfd ;
int tot_fd_nb = 0 ;
2018-09-20 05:22:29 -04:00
int fd = - 1 ;
2017-04-05 16:24:59 -04:00
int curoff = 0 ;
2018-09-20 05:22:29 -04:00
int old_fcntl = - 1 ;
2017-04-05 16:24:59 -04:00
int ret ;
2018-09-20 05:22:29 -04:00
if ( ! remote ) {
ha_warning ( " Only works on real connections \n " ) ;
goto out ;
}
fd = remote - > handle . fd ;
2017-04-05 16:24:59 -04:00
/* Temporary set the FD in blocking mode, that will make our life easier */
old_fcntl = fcntl ( fd , F_GETFL ) ;
if ( old_fcntl < 0 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Couldn't get the flags for the unix socket \n " ) ;
2017-04-05 16:24:59 -04:00
goto out ;
}
cmsgbuf = malloc ( CMSG_SPACE ( sizeof ( int ) * MAX_SEND_FD ) ) ;
if ( ! cmsgbuf ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to allocate memory to send sockets \n " ) ;
2017-04-05 16:24:59 -04:00
goto out ;
}
if ( fcntl ( fd , F_SETFL , old_fcntl & ~ O_NONBLOCK ) = = - 1 ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Cannot make the unix socket blocking \n " ) ;
2017-04-05 16:24:59 -04:00
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
2020-07-22 19:59:40 -04:00
* the caller know how much it should expect .
2017-04-05 16:24:59 -04:00
*/
2020-08-19 11:03:55 -04:00
for ( cur_fd = 0 ; cur_fd < global . maxsock ; cur_fd + + )
2021-04-06 12:09:06 -04:00
tot_fd_nb + = ! ! ( fdtab [ cur_fd ] . state & FD_EXPORTED ) ;
2020-01-16 09:32:08 -05:00
2022-07-28 09:33:41 -04:00
if ( tot_fd_nb = = 0 ) {
if ( already_sent )
ha_warning ( " _getsocks: attempt to get sockets but they were already sent and closed in this process! \n " ) ;
2017-04-05 16:24:59 -04:00
goto out ;
2022-07-28 09:33:41 -04:00
}
2017-04-05 16:24:59 -04:00
/* 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 ) ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to send the number of sockets to send \n " ) ;
2017-04-05 16:24:59 -04:00
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 ) ;
/* 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
2020-08-26 04:30:09 -04:00
* 32 bits of zeroes ( used to be listener options ) .
2017-04-05 16:24:59 -04:00
*/
/* We will send sockets MAX_SEND_FD per MAX_SEND_FD, allocate a
2020-04-07 16:07:56 -04:00
* buffer big enough to store the socket information .
2017-04-05 16:24:59 -04:00
*/
2017-11-04 10:13:01 -04:00
tmpbuf = malloc ( MAX_SEND_FD * ( 1 + MAXPATHLEN + 1 + IFNAMSIZ + sizeof ( int ) ) ) ;
2017-04-05 16:24:59 -04:00
if ( tmpbuf = = NULL ) {
2020-04-07 16:07:56 -04:00
ha_warning ( " Failed to allocate memory to transfer socket information \n " ) ;
2017-04-05 16:24:59 -04:00
goto out ;
}
2020-08-19 11:03:55 -04:00
nb_queued = 0 ;
2017-04-05 16:24:59 -04:00
iov . iov_base = tmpbuf ;
2020-08-19 11:03:55 -04:00
for ( cur_fd = 0 ; cur_fd < global . maxsock ; cur_fd + + ) {
2021-04-06 12:09:06 -04:00
if ( ! ( fdtab [ cur_fd ] . state & FD_EXPORTED ) )
2020-08-19 11:03:55 -04:00
continue ;
BUG/MEDIUM: fd: mark FD transferred to another process as FD_CLONED
The crappy epoll API stroke again with reloads and transferred FDs.
Indeed, when listening sockets are retrieved by a new worker from a
previous one, and the old one finally stops listening on them, it
closes the FDs. But in this case, since the sockets themselves were
not closed, epoll will not unregister them and will continue to
report new activity for these in the old process, which can only
observe, count an fd_poll_drop event and not unregister them since
they're not reachable anymore.
The unfortunate effect is that long-lasting old processes are woken
up at the same rate as the new process when accepting new connections,
and can waste a lot of CPU. Accept rates divided by 8 were observed on
a small test involving a slow transfer on 10 connections facing a reload
every second so that 10 processes were busy dealing with them while
another process was hammering the service with new connections.
Fortunately, years ago we implemented a flag FD_CLONED exactly for
similar purposes. Let's simply mark transferred FDs with FD_CLONED
so that the process knows that these ones require special treatment
and have to be manually unregistered before being closed. This does
the job fine, now old processes correctly unregister the FD before
closing it and no longer receive accept events for the new process.
This needs to be backported to all stable versions. It only affects
epoll, as usual, and this time in combination with transferred FDs
(typically reloads in master-worker mode). Thanks to Damien Claisse
for providing all detailed measurements and statistics allowing to
understand and reproduce the problem.
2025-02-12 10:25:13 -05:00
/* this FD is now shared between processes */
HA_ATOMIC_OR ( & fdtab [ cur_fd ] . state , FD_CLONED ) ;
2020-08-19 11:03:55 -04:00
ns_name = if_name = " " ;
ns_nlen = if_nlen = 0 ;
/* for now we can only retrieve namespaces and interfaces from
* pure listeners .
*/
2020-10-15 15:29:49 -04:00
if ( fdtab [ cur_fd ] . iocb = = sock_accept_iocb ) {
2020-08-19 11:03:55 -04:00
const struct listener * l = fdtab [ cur_fd ] . owner ;
2020-09-03 01:50:19 -04:00
if ( l - > rx . settings - > interface ) {
if_name = l - > rx . settings - > interface ;
2020-08-19 11:03:55 -04:00
if_nlen = strlen ( if_name ) ;
}
# ifdef USE_NS
2020-09-03 01:50:19 -04:00
if ( l - > rx . settings - > netns ) {
ns_name = l - > rx . settings - > netns - > node . key ;
ns_nlen = l - > rx . settings - > netns - > name_len ;
2020-08-19 11:03:55 -04:00
}
# endif
}
/* put the FD into the CMSG_DATA */
tmpfd [ nb_queued + + ] = cur_fd ;
/* first block is <ns_name_len> <ns_name> */
tmpbuf [ curoff + + ] = ns_nlen ;
if ( ns_nlen )
memcpy ( tmpbuf + curoff , ns_name , ns_nlen ) ;
curoff + = ns_nlen ;
/* second block is <if_name_len> <if_name> */
tmpbuf [ curoff + + ] = if_nlen ;
if ( if_nlen )
memcpy ( tmpbuf + curoff , if_name , if_nlen ) ;
curoff + = if_nlen ;
/* we used to send the listener options here before 2.3 */
memset ( tmpbuf + curoff , 0 , sizeof ( int ) ) ;
curoff + = sizeof ( int ) ;
/* there's a limit to how many FDs may be sent at once */
if ( nb_queued = = MAX_SEND_FD ) {
iov . iov_len = curoff ;
if ( sendmsg ( fd , & msghdr , 0 ) ! = curoff ) {
ha_warning ( " Failed to transfer sockets \n " ) ;
2025-02-06 09:30:30 -05:00
goto out ;
2020-08-19 11:03:55 -04:00
}
/* Wait for an ack */
do {
ret = recv ( fd , & tot_fd_nb , sizeof ( tot_fd_nb ) , 0 ) ;
} while ( ret = = - 1 & & errno = = EINTR ) ;
if ( ret < = 0 ) {
ha_warning ( " Unexpected error while transferring sockets \n " ) ;
2025-02-06 09:30:30 -05:00
goto out ;
2020-08-19 11:03:55 -04:00
}
curoff = 0 ;
nb_queued = 0 ;
}
2020-01-16 09:32:08 -05:00
}
2022-07-28 09:33:41 -04:00
already_sent = 1 ;
2020-08-19 11:03:55 -04:00
/* flush pending stuff */
if ( nb_queued ) {
2017-04-05 16:24:59 -04:00
iov . iov_len = curoff ;
2020-08-19 11:03:55 -04:00
cmsg - > cmsg_len = CMSG_LEN ( nb_queued * sizeof ( int ) ) ;
msghdr . msg_controllen = CMSG_SPACE ( nb_queued * sizeof ( int ) ) ;
2017-04-05 16:24:59 -04:00
if ( sendmsg ( fd , & msghdr , 0 ) ! = curoff ) {
2017-11-24 10:50:31 -05:00
ha_warning ( " Failed to transfer sockets \n " ) ;
2025-02-17 09:16:15 -05:00
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 ) {
ha_warning ( " Unexpected error while transferring sockets \n " ) ;
2017-04-05 16:24:59 -04:00
goto out ;
}
}
out :
2025-02-06 09:37:52 -05:00
if ( fd > = 0 & & old_fcntl > = 0 & & fcntl ( fd , F_SETFL , old_fcntl ) = = - 1 )
2017-11-24 10:50:31 -05:00
ha_warning ( " Cannot make the unix socket non-blocking \n " ) ;
2025-02-06 09:15:27 -05:00
applet_set_eoi ( appctx ) ;
2017-04-05 16:24:59 -04:00
appctx - > st0 = CLI_ST_END ;
free ( cmsgbuf ) ;
free ( tmpbuf ) ;
return 1 ;
}
2018-04-18 07:26:46 -04:00
static int cli_parse_simple ( char * * args , char * payload , struct appctx * appctx , void * private )
{
if ( * args [ 0 ] = = ' h ' )
/* help */
2021-05-09 14:59:23 -04:00
cli_gen_usage_msg ( appctx , args ) ;
2018-04-18 07:26:46 -04:00
else if ( * args [ 0 ] = = ' p ' )
/* prompt */
2023-05-04 08:22:36 -04:00
if ( strcmp ( args [ 1 ] , " timed " ) = = 0 ) {
appctx - > st1 | = APPCTX_CLI_ST1_PROMPT ;
appctx - > st1 ^ = APPCTX_CLI_ST1_TIMED ;
}
else
appctx - > st1 ^ = APPCTX_CLI_ST1_PROMPT ;
2023-04-07 11:58:21 -04:00
else if ( * args [ 0 ] = = ' q ' ) {
2018-04-18 07:26:46 -04:00
/* quit */
2025-02-06 09:15:27 -05:00
applet_set_eoi ( appctx ) ;
2018-04-18 07:26:46 -04:00
appctx - > st0 = CLI_ST_END ;
2023-04-07 11:58:21 -04:00
}
2018-04-18 07:26:46 -04:00
return 1 ;
}
2017-04-05 16:24:59 -04:00
2024-10-24 11:20:57 -04:00
static int cli_parse_echo ( char * * args , char * payload , struct appctx * appctx , void * private )
{
int i = 1 ; /* starts after 'echo' */
chunk_reset ( & trash ) ;
while ( * args [ i ] ) {
/* add a space if there was a word before */
if ( i = = 1 )
chunk_printf ( & trash , " %s " , args [ i ] ) ;
else
chunk_appendf ( & trash , " %s " , args [ i ] ) ;
i + + ;
}
chunk_appendf ( & trash , " \n " ) ;
cli_msg ( appctx , LOG_INFO , trash . area ) ;
return 1 ;
}
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
static int _send_status ( char * * args , char * payload , struct appctx * appctx , void * private )
{
2024-10-22 09:09:29 -04:00
struct listener * mproxy_li ;
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
struct mworker_proc * proc ;
2024-12-09 12:56:01 -05:00
char * msg = " READY \n " ;
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
int pid ;
BUG_ON ( ( strcmp ( args [ 0 ] , " _send_status " ) ! = 0 ) ,
" Triggered in _send_status by unsupported command name. \n " ) ;
pid = atoi ( args [ 2 ] ) ;
list_for_each_entry ( proc , & proc_list , list ) {
/* update status of the new worker */
2024-10-22 09:09:29 -04:00
if ( proc - > pid = = pid ) {
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
proc - > options & = ~ PROC_O_INIT ;
2024-10-22 09:09:29 -04:00
mproxy_li = fdtab [ proc - > ipc_fd [ 0 ] ] . owner ;
stop_listener ( mproxy_li , 0 , 0 , 0 ) ;
}
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
/* send TERM to workers, which have exceeded max_reloads counter */
if ( max_reloads ! = - 1 ) {
if ( ( proc - > options & PROC_O_TYPE_WORKER ) & &
( proc - > options & PROC_O_LEAVING ) & &
( proc - > reloads > max_reloads ) & & ( proc - > pid > 0 ) ) {
kill ( proc - > pid , SIGTERM ) ;
}
}
}
2024-12-02 10:05:16 -05:00
/* At this point we are sure, that newly forked worker is started,
* so we can write our PID in a pidfile , if provided . Master doesn ' t
* perform chroot .
*/
2024-12-03 15:33:55 -05:00
if ( global . pidfile ! = NULL ) {
if ( handle_pidfile ( ) < 0 ) {
ha_alert ( " Fatal error(s) found, exiting. \n " ) ;
exit ( 1 ) ;
}
}
2024-12-02 10:05:16 -05:00
BUG/MINOR: mworker: fix -D -W -sf/-st modes
When a new master process is launched like below:
./haproxy -W -D -p ha.pid -sf $(cat ha.pid)...
The old master process and its workers do not stop. Since the master-worker
refactoring, the code, which sends USR1/TERM to old pids from -sf, is called
only for the standalone mode. In master-worker mode we should receive the READY
message from the newly forked worker at first, in order to be able to terminate
the previous master.
So, to fix this, let's terminate the previous master in _send_status(), where
we parse the READY message from the newly forked worker. And let's continue to
use oldpids array, as it was in 3.0, in order to stop the workers, launched
before the reload.
This patch should be backported only in 3.1.
2024-12-03 15:13:47 -05:00
/* either send USR1/TERM to old master, case when we launched as -W -D ... -sf $(cat pidfile),
* or send USR1 / TERM to old worker processes .
*/
if ( nb_oldpids > 0 ) {
nb_oldpids = tell_old_pids ( oldpids_sig ) ;
}
2024-12-09 12:56:01 -05:00
if ( daemon_fd [ 1 ] ! = - 1 ) {
if ( write ( daemon_fd [ 1 ] , msg , strlen ( msg ) ) < 0 ) {
ha_alert ( " [%s.main()] Failed to write into pipe with parent process: %s \n " , progname , strerror ( errno ) ) ;
exit ( 1 ) ;
}
close ( daemon_fd [ 1 ] ) ;
daemon_fd [ 1 ] = - 1 ;
}
2024-11-12 05:28:46 -05:00
load_status = 1 ;
MEDIUM: startup: split sending oldpids_sig logic for standalone and mworker modes
Before refactoring the master-worker mode, in all runtime modes, when the new
process successfully parsed its configuration and bound to sockets, it sent
either SIGUSR1 or SIGTERM to the previous one in order to terminate it.
Let's keep this logic as is for the standalone mode. In addition, in standalone
mode we need to send the signal to old process before calling set_identity(),
because in set_identity() effective user or group may change. So, the order is
important here.
In case of master-worker mode after refactoring, master terminates the previous
worker by itself up to receiving "READY" status from the new one in
_send_status(). Master also sets at this moment HAPROXY_LOAD_SUCCESS env
variable and checks, if there are some other workers to terminate with
max_reloads exceeded.
So, now in master-worker mode we terminate old workers only, when the new one
has successfully done all initialization steps and has sent "READY" status to
master.
2024-10-03 05:16:35 -04:00
ha_notice ( " Loading success. \n " ) ;
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
MINOR: mworker: reintroduce systemd support
Let's reintroduce systemd support in the refactored master-worker mode.
As for now, the master-worker fork happens during early initialization steps and
then the master process receieves the "READY" status message from the newly
forked worker, that has successfully loaded. Let's propagate this "READY" status
message at this moment to the systemd from the master process context
(_send_status()). We use the master process to send messages to systemd,
because it is only the process, monitored by systemd.
In master recovery mode, we also need to send to the systemd the "READY"
message, but with the status "Reload failed". "READY" will signal to systemd,
that master process is still alive, because it doesn't exit in recovery mode
and it keeps the existed worker. Status "Reload failed" will signal to user,
that something wrong has happened with the configuration. Same message logic
was originally preserved for the case, when the worker fails to read its
configuration, see on_new_child_failure() for more details.
2024-10-11 13:36:32 -04:00
if ( global . tune . options & GTUNE_USE_SYSTEMD )
sd_notifyf ( 0 , " READY=1 \n MAINPID=%lu \n STATUS=Ready. \n " , ( unsigned long ) getpid ( ) ) ;
BUG/MINOR: mworker: fix -D -W -sf/-st modes
When a new master process is launched like below:
./haproxy -W -D -p ha.pid -sf $(cat ha.pid)...
The old master process and its workers do not stop. Since the master-worker
refactoring, the code, which sends USR1/TERM to old pids from -sf, is called
only for the standalone mode. In master-worker mode we should receive the READY
message from the newly forked worker at first, in order to be able to terminate
the previous master.
So, to fix this, let's terminate the previous master in _send_status(), where
we parse the READY message from the newly forked worker. And let's continue to
use oldpids array, as it was in 3.0, in order to stop the workers, launched
before the reload.
This patch should be backported only in 3.1.
2024-12-03 15:13:47 -05:00
2024-12-09 14:20:40 -05:00
/* master and worker have successfully started, now we can set quiet mode
* if MODE_DAEMON
*/
if ( ( ! ( global . mode & MODE_QUIET ) | | ( global . mode & MODE_VERBOSE ) ) & &
( global . mode & MODE_DAEMON ) ) {
/* detach from the tty, this is required to properly daemonize. */
if ( ( getenv ( " HAPROXY_MWORKER_REEXEC " ) = = NULL ) )
stdio_quiet ( - 1 ) ;
global . mode & = ~ MODE_VERBOSE ;
global . mode | = MODE_QUIET ; /* ensure that we won't say anything from now */
}
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
return 1 ;
}
2018-10-26 08:47:47 -04:00
void pcli_write_prompt ( struct stream * s )
{
struct buffer * msg = get_trash_chunk ( ) ;
2022-05-18 09:55:18 -04:00
struct channel * oc = sc_oc ( s - > scf ) ;
2018-10-26 08:47:47 -04:00
2018-12-13 03:05:46 -05:00
if ( ! ( s - > pcli_flags & PCLI_F_PROMPT ) )
2018-12-11 10:10:54 -05:00
return ;
2018-12-13 03:05:46 -05:00
if ( s - > pcli_flags & PCLI_F_PAYLOAD ) {
2018-12-11 10:10:57 -05:00
chunk_appendf ( msg , " + " ) ;
} else {
2023-05-11 10:14:02 -04:00
if ( s - > pcli_next_pid = = 0 ) {
/* master's prompt */
if ( s - > pcli_flags & PCLI_F_TIMED ) {
uint up = ns_to_sec ( now_ns - start_time_ns ) ;
chunk_appendf ( msg , " [%u:%02u:%02u:%02u] " ,
( up / 86400 ) , ( up / 3600 ) % 24 , ( up / 60 ) % 60 , up % 60 ) ;
}
2022-02-02 08:13:54 -05:00
chunk_appendf ( msg , " master%s " ,
2021-11-10 04:57:18 -05:00
( proc_self - > failedreloads > 0 ) ? " [ReloadFailed] " : " " ) ;
2023-05-11 10:14:02 -04:00
}
else {
/* worker's prompt */
if ( s - > pcli_flags & PCLI_F_TIMED ) {
const struct mworker_proc * tmp , * proc ;
uint up ;
/* set proc to the worker corresponding to pcli_next_pid or NULL */
proc = NULL ;
list_for_each_entry ( tmp , & proc_list , list ) {
if ( ! ( tmp - > options & PROC_O_TYPE_WORKER ) )
continue ;
if ( tmp - > pid = = s - > pcli_next_pid ) {
proc = tmp ;
break ;
}
}
if ( ! proc )
chunk_appendf ( msg , " [gone] " ) ;
else {
up = date . tv_sec - proc - > timestamp ;
if ( ( int ) up < 0 ) /* must never be negative because of clock drift */
up = 0 ;
chunk_appendf ( msg , " [%u:%02u:%02u:%02u] " ,
( up / 86400 ) , ( up / 3600 ) % 24 , ( up / 60 ) % 60 , up % 60 ) ;
}
}
2022-02-02 08:13:54 -05:00
chunk_appendf ( msg , " %d " , s - > pcli_next_pid ) ;
2023-05-11 10:14:02 -04:00
}
2022-02-02 08:13:54 -05:00
if ( s - > pcli_flags & ( ACCESS_EXPERIMENTAL | ACCESS_EXPERT | ACCESS_MCLI_DEBUG ) ) {
chunk_appendf ( msg , " ( " ) ;
if ( s - > pcli_flags & ACCESS_EXPERIMENTAL )
chunk_appendf ( msg , " x " ) ;
if ( s - > pcli_flags & ACCESS_EXPERT )
chunk_appendf ( msg , " e " ) ;
if ( s - > pcli_flags & ACCESS_MCLI_DEBUG )
chunk_appendf ( msg , " d " ) ;
chunk_appendf ( msg , " ) " ) ;
}
chunk_appendf ( msg , " > " ) ;
2018-12-11 10:10:57 -05:00
}
2018-10-26 08:47:47 -04:00
co_inject ( oc , msg - > area , msg - > data ) ;
}
2018-10-26 08:47:40 -04:00
/* The pcli_* functions are used for the CLI proxy in the master */
2023-05-14 12:36:00 -04:00
/* flush the input buffer and output an error */
void pcli_error ( struct stream * s , const char * msg )
{
struct buffer * buf = get_trash_chunk ( ) ;
struct channel * oc = & s - > res ;
struct channel * ic = & s - > req ;
chunk_initstr ( buf , msg ) ;
if ( likely ( buf & & buf - > data ) )
co_inject ( oc , buf - > area , buf - > data ) ;
channel_erase ( ic ) ;
}
/* flush the input buffer, output the error and close */
2018-10-26 08:47:48 -04:00
void pcli_reply_and_close ( struct stream * s , const char * msg )
{
struct buffer * buf = get_trash_chunk ( ) ;
chunk_initstr ( buf , msg ) ;
2022-03-31 03:47:24 -04:00
stream_retnclose ( s , buf ) ;
2018-10-26 08:47:48 -04:00
}
2018-10-26 08:47:38 -04:00
static enum obj_type * pcli_pid_to_server ( int proc_pid )
{
struct mworker_proc * child ;
2020-11-05 04:28:53 -05:00
/* return the mCLI applet of the master */
2018-12-11 10:10:53 -05:00
if ( proc_pid = = 0 )
2020-11-05 04:28:53 -05:00
return & mcli_applet . obj_type ;
2018-12-11 10:10:53 -05:00
2018-10-26 08:47:38 -04:00
list_for_each_entry ( child , & proc_list , list ) {
if ( child - > pid = = proc_pid ) {
return & child - > srv - > obj_type ;
}
}
return NULL ;
}
/* Take a CLI prefix in argument (eg: @!1234 @master @1)
* Return :
* 0 : master
* > 0 : pid of a worker
* < 0 : didn ' t find a worker
*/
static int pcli_prefix_to_pid ( const char * prefix )
{
int proc_pid ;
struct mworker_proc * child ;
char * errtol = NULL ;
if ( * prefix ! = ' @ ' ) /* not a prefix, should not happen */
return - 1 ;
prefix + + ;
if ( ! * prefix ) /* sent @ alone, return the master */
return 0 ;
if ( strcmp ( " master " , prefix ) = = 0 ) {
return 0 ;
} else if ( * prefix = = ' ! ' ) {
prefix + + ;
if ( ! * prefix )
return - 1 ;
proc_pid = strtol ( prefix , & errtol , 10 ) ;
if ( * errtol ! = ' \0 ' )
return - 1 ;
list_for_each_entry ( child , & proc_list , list ) {
2019-04-12 10:09:23 -04:00
if ( ! ( child - > options & PROC_O_TYPE_WORKER ) )
2018-11-19 12:46:18 -05:00
continue ;
2018-10-26 08:47:38 -04:00
if ( child - > pid = = proc_pid ) {
return child - > pid ;
}
}
} else {
struct mworker_proc * chosen = NULL ;
/* this is a relative pid */
proc_pid = strtol ( prefix , & errtol , 10 ) ;
if ( * errtol ! = ' \0 ' )
return - 1 ;
if ( proc_pid = = 0 ) /* return the master */
return 0 ;
2022-07-20 08:30:56 -04:00
if ( proc_pid ! = 1 ) /* only the "@1" relative PID is supported */
return - 1 ;
2018-10-26 08:47:38 -04:00
/* chose the right process, the current one is the one with the
least number of reloads */
list_for_each_entry ( child , & proc_list , list ) {
2019-04-12 10:09:23 -04:00
if ( ! ( child - > options & PROC_O_TYPE_WORKER ) )
2018-11-19 12:46:18 -05:00
continue ;
2021-06-15 03:08:18 -04:00
if ( child - > reloads = = 0 )
return child - > pid ;
else if ( chosen = = NULL | | child - > reloads < chosen - > reloads )
chosen = child ;
2018-10-26 08:47:38 -04:00
}
if ( chosen )
return chosen - > pid ;
}
return - 1 ;
}
2023-12-07 12:02:29 -05:00
/*
* pcli_find_and_exec_kw ( ) parses a command for the master CLI . It looks for a
* prefix or a command that is handled directly by the proxy and never sent to
* a worker .
*
* Return :
* > = 0 : number of words that were parsed and need to be skipped
2018-12-11 10:10:53 -05:00
* = - 1 : error
*/
int pcli_find_and_exec_kw ( struct stream * s , char * * args , int argl , char * * errmsg , int * next_pid )
{
if ( argl < 1 )
return 0 ;
/* there is a prefix */
if ( args [ 0 ] [ 0 ] = = ' @ ' ) {
int target_pid = pcli_prefix_to_pid ( args [ 0 ] ) ;
if ( target_pid = = - 1 ) {
memprintf ( errmsg , " Can't find the target PID matching the prefix '%s' \n " , args [ 0 ] ) ;
return - 1 ;
}
/* if the prefix is alone, define a default target */
if ( argl = = 1 )
s - > pcli_next_pid = target_pid ;
else
* next_pid = target_pid ;
return 1 ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( " prompt " , args [ 0 ] ) = = 0 ) {
2023-05-11 10:14:02 -04:00
if ( argl > = 2 & & strcmp ( args [ 1 ] , " timed " ) = = 0 ) {
s - > pcli_flags | = PCLI_F_PROMPT ;
s - > pcli_flags ^ = PCLI_F_TIMED ;
}
else
s - > pcli_flags ^ = PCLI_F_PROMPT ;
2018-12-11 10:10:54 -05:00
return argl ; /* return the number of elements in the array */
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( " quit " , args [ 0 ] ) = = 0 ) {
2023-04-13 09:40:10 -04:00
sc_schedule_abort ( s - > scf ) ;
2023-04-13 09:56:26 -04:00
sc_schedule_shutdown ( s - > scf ) ;
2018-12-11 10:10:55 -05:00
return argl ; /* return the number of elements in the array */
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( args [ 0 ] , " operator " ) = = 0 ) {
2018-12-13 03:05:47 -05:00
if ( ! pcli_has_level ( s , ACCESS_LVL_OPER ) ) {
memprintf ( errmsg , " Permission denied! \n " ) ;
return - 1 ;
}
s - > pcli_flags & = ~ ACCESS_LVL_MASK ;
s - > pcli_flags | = ACCESS_LVL_OPER ;
return argl ;
CLEANUP: Compare the return value of `XXXcmp()` functions with zero
According to coding-style.txt it is recommended to use:
`strcmp(a, b) == 0` instead of `!strcmp(a, b)`
So let's do this.
The change was performed by running the following (very long) coccinelle patch
on src/:
@@
statement S;
expression E;
expression F;
@@
if (
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
(
S
|
{ ... }
)
@@
statement S;
expression E;
expression F;
@@
if (
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
(
S
|
{ ... }
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) != 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
G &&
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
G ||
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
&& G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
|| G
)
@@
expression E;
expression F;
expression G;
@@
(
- !
(
dns_hostname_cmp
|
eb_memcmp
|
memcmp
|
strcasecmp
|
strcmp
|
strncasecmp
|
strncmp
)
- (E, F)
+ (E, F) == 0
)
2021-01-02 16:31:53 -05:00
} else if ( strcmp ( args [ 0 ] , " user " ) = = 0 ) {
2018-12-13 03:05:47 -05:00
if ( ! pcli_has_level ( s , ACCESS_LVL_USER ) ) {
memprintf ( errmsg , " Permission denied! \n " ) ;
return - 1 ;
}
s - > pcli_flags & = ~ ACCESS_LVL_MASK ;
s - > pcli_flags | = ACCESS_LVL_USER ;
return argl ;
2022-02-01 10:08:50 -05:00
} else if ( strcmp ( args [ 0 ] , " expert-mode " ) = = 0 ) {
if ( ! pcli_has_level ( s , ACCESS_LVL_ADMIN ) ) {
memprintf ( errmsg , " Permission denied! \n " ) ;
return - 1 ;
}
s - > pcli_flags & = ~ ACCESS_EXPERT ;
if ( ( argl > 1 ) & & ( strcmp ( args [ 1 ] , " on " ) = = 0 ) )
s - > pcli_flags | = ACCESS_EXPERT ;
return argl ;
} else if ( strcmp ( args [ 0 ] , " experimental-mode " ) = = 0 ) {
if ( ! pcli_has_level ( s , ACCESS_LVL_ADMIN ) ) {
memprintf ( errmsg , " Permission denied! \n " ) ;
return - 1 ;
}
s - > pcli_flags & = ~ ACCESS_EXPERIMENTAL ;
if ( ( argl > 1 ) & & ( strcmp ( args [ 1 ] , " on " ) = = 0 ) )
s - > pcli_flags | = ACCESS_EXPERIMENTAL ;
return argl ;
2022-02-02 05:43:20 -05:00
} else if ( strcmp ( args [ 0 ] , " mcli-debug-mode " ) = = 0 ) {
if ( ! pcli_has_level ( s , ACCESS_LVL_ADMIN ) ) {
memprintf ( errmsg , " Permission denied! \n " ) ;
return - 1 ;
}
s - > pcli_flags & = ~ ACCESS_MCLI_DEBUG ;
if ( ( argl > 1 ) & & ( strcmp ( args [ 1 ] , " on " ) = = 0 ) )
s - > pcli_flags | = ACCESS_MCLI_DEBUG ;
return argl ;
2023-12-06 05:15:01 -05:00
} else if ( strcmp ( args [ 0 ] , " set " ) = = 0 ) {
if ( ( argl > 1 ) & & ( strcmp ( args [ 1 ] , " severity-output " ) = = 0 ) ) {
if ( ( argl > 2 ) & & strcmp ( args [ 2 ] , " none " ) = = 0 ) {
s - > pcli_flags & = ~ ( ACCESS_MCLI_SEVERITY_NB | ACCESS_MCLI_SEVERITY_STR ) ;
} else if ( ( argl > 2 ) & & strcmp ( args [ 2 ] , " string " ) = = 0 ) {
s - > pcli_flags | = ACCESS_MCLI_SEVERITY_STR ;
} else if ( ( argl > 2 ) & & strcmp ( args [ 2 ] , " number " ) = = 0 ) {
s - > pcli_flags | = ACCESS_MCLI_SEVERITY_NB ;
} else {
memprintf ( errmsg , " one of 'none', 'number', 'string' is a required argument \n " ) ;
return - 1 ;
}
/* only skip argl if we have "set severity-output" not only "set" */
return argl ;
}
2018-12-11 10:10:53 -05:00
}
return 0 ;
}
/*
* Parse the CLI request :
* - It does basically the same as the cli_io_handler , but as a proxy
* - It can exec a command and strip non forwardable commands
2018-10-26 08:47:40 -04:00
*
* Return :
2018-12-11 10:10:53 -05:00
* - the number of characters to forward or
* - 1 if there is an error or not enough data
2018-10-26 08:47:40 -04:00
*/
2018-12-11 10:10:53 -05:00
int pcli_parse_request ( struct stream * s , struct channel * req , char * * errmsg , int * next_pid )
2018-10-26 08:47:40 -04:00
{
2022-01-20 02:47:35 -05:00
char * str ;
char * end ;
2021-03-13 04:59:23 -05:00
char * args [ MAX_CLI_ARGS + 1 ] ; /* +1 for storing a NULL */
2018-12-11 10:10:53 -05:00
int argl ; /* number of args */
char * p ;
char * trim = NULL ;
int wtrim = 0 ; /* number of words to trim */
int reql = 0 ;
2018-12-13 03:05:47 -05:00
int ret ;
2018-12-11 10:10:53 -05:00
int i = 0 ;
2018-10-26 08:47:40 -04:00
2022-01-20 02:47:35 -05:00
/* we cannot deal with a wrapping buffer, so let's take care of this
* first .
*/
if ( b_head ( & req - > buf ) + b_data ( & req - > buf ) > b_wrap ( & req - > buf ) )
b_slow_realign ( & req - > buf , trash . area , co_data ( req ) ) ;
str = ( char * ) ci_head ( req ) ;
end = ( char * ) ci_stop ( req ) ;
2018-12-11 10:10:53 -05:00
p = str ;
2018-12-13 03:05:46 -05:00
if ( ! ( s - > pcli_flags & PCLI_F_PAYLOAD ) ) {
2018-12-11 10:10:57 -05:00
/* Looks for the end of one command */
while ( p + reql < end ) {
/* handle escaping */
if ( p [ reql ] = = ' \\ ' ) {
2020-06-18 12:45:04 -04:00
reql + = 2 ;
2018-12-11 10:10:57 -05:00
continue ;
}
if ( p [ reql ] = = ' ; ' | | p [ reql ] = = ' \n ' ) {
/* found the end of the command */
p [ reql ] = ' \n ' ;
reql + + ;
break ;
}
2018-12-11 10:10:53 -05:00
reql + + ;
}
2018-12-11 10:10:57 -05:00
} else {
while ( p + reql < end ) {
if ( p [ reql ] = = ' \n ' ) {
/* found the end of the line */
reql + + ;
break ;
}
2018-12-11 10:10:53 -05:00
reql + + ;
}
}
2018-10-26 08:47:40 -04:00
2018-12-11 10:10:53 -05:00
/* set end to first byte after the end of the command */
end = p + reql ;
2018-10-26 08:47:40 -04:00
2018-12-11 10:10:53 -05:00
/* there is no end to this command, need more to parse ! */
2022-01-20 02:31:50 -05:00
if ( ! reql | | * ( end - 1 ) ! = ' \n ' ) {
2023-11-28 11:28:07 -05:00
ret = - 1 ;
goto end ;
2018-12-11 10:10:53 -05:00
}
2023-11-28 11:57:21 -05:00
/* in payload mode, skip the whole parsing/exec and just look for a pattern */
2018-12-13 03:05:48 -05:00
if ( s - > pcli_flags & PCLI_F_PAYLOAD ) {
2023-11-28 11:57:21 -05:00
if ( reql - 1 = = strlen ( s - > pcli_payload_pat ) ) {
/* the custom pattern len can be 0 (empty line) */
if ( strncmp ( str , s - > pcli_payload_pat , strlen ( s - > pcli_payload_pat ) ) = = 0 ) {
s - > pcli_flags & = ~ PCLI_F_PAYLOAD ;
}
}
2023-11-28 11:28:07 -05:00
ret = reql ;
goto end ;
2018-12-11 10:10:57 -05:00
}
2018-12-11 10:10:53 -05:00
* ( end - 1 ) = ' \0 ' ;
/* splits the command in words */
2021-03-13 04:59:23 -05:00
while ( i < MAX_CLI_ARGS & & p < end ) {
2018-12-11 10:10:53 -05:00
/* skip leading spaces/tabs */
p + = strspn ( p , " \t " ) ;
if ( ! * p )
break ;
args [ i ] = p ;
2020-06-18 12:03:57 -04:00
while ( 1 ) {
p + = strcspn ( p , " \t \\ " ) ;
/* escaped chars using backlashes (\) */
if ( * p = = ' \\ ' ) {
if ( ! * + + p )
break ;
if ( ! * + + p )
break ;
} else {
break ;
2018-12-11 10:10:53 -05:00
}
2018-10-26 08:47:40 -04:00
}
2020-06-18 12:03:57 -04:00
* p + + = 0 ;
2018-12-11 10:10:53 -05:00
i + + ;
2018-10-26 08:47:40 -04:00
}
2018-12-11 10:10:53 -05:00
argl = i ;
2018-10-26 08:47:40 -04:00
2023-11-28 11:57:21 -05:00
/* first look for '<<' at the beginning of the last argument */
2023-11-29 07:20:59 -05:00
if ( argl & & strncmp ( args [ argl - 1 ] , PAYLOAD_PATTERN , strlen ( PAYLOAD_PATTERN ) ) = = 0 ) {
2023-11-28 11:57:21 -05:00
size_t pat_len = strlen ( args [ argl - 1 ] + strlen ( PAYLOAD_PATTERN ) ) ;
/*
* A customized pattern can ' t be more than 7 characters
* if it ' s more , don ' t make it a payload
*/
if ( pat_len < sizeof ( s - > pcli_payload_pat ) ) {
s - > pcli_flags | = PCLI_F_PAYLOAD ;
/* copy the customized pattern, don't store the << */
strncpy ( s - > pcli_payload_pat , args [ argl - 1 ] + strlen ( PAYLOAD_PATTERN ) , sizeof ( s - > pcli_payload_pat ) - 1 ) ;
s - > pcli_payload_pat [ sizeof ( s - > pcli_payload_pat ) - 1 ] = ' \0 ' ;
}
}
2021-03-13 04:59:23 -05:00
for ( ; i < MAX_CLI_ARGS + 1 ; i + + )
2018-12-11 10:10:53 -05:00
args [ i ] = NULL ;
wtrim = pcli_find_and_exec_kw ( s , args , argl , errmsg , next_pid ) ;
/* End of words are ending by \0, we need to replace the \0s by spaces
2022-02-02 08:07:08 -05:00
before forwarding them */
2018-12-11 10:10:53 -05:00
p = str ;
2018-12-13 03:05:48 -05:00
while ( p < end - 1 ) {
2018-12-11 10:10:53 -05:00
if ( * p = = ' \0 ' )
* p = ' ' ;
p + + ;
2018-10-26 08:47:40 -04:00
}
2018-12-11 10:10:53 -05:00
* ( end - 1 ) = ' \n ' ;
if ( wtrim > 0 ) {
trim = & args [ wtrim ] [ 0 ] ;
if ( trim = = NULL ) /* if this was the last word in the table */
trim = end ;
b_del ( & req - > buf , trim - str ) ;
2018-12-13 03:05:47 -05:00
ret = end - trim ;
2018-12-11 10:10:53 -05:00
} else if ( wtrim < 0 ) {
/* parsing error */
2023-11-28 11:28:07 -05:00
ret = - 1 ;
goto end ;
2018-12-13 03:05:47 -05:00
} else {
/* the whole string */
ret = end - str ;
}
if ( ret > 1 ) {
2022-02-02 05:43:20 -05:00
/* the mcli-debug-mode is only sent to the applet of the master */
if ( ( s - > pcli_flags & ACCESS_MCLI_DEBUG ) & & * next_pid < = 0 ) {
2024-08-09 11:41:36 -04:00
const char * cmd = " mcli-debug-mode on -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2022-02-02 05:43:20 -05:00
}
2022-02-01 10:08:50 -05:00
if ( s - > pcli_flags & ACCESS_EXPERIMENTAL ) {
2024-08-09 11:41:36 -04:00
const char * cmd = " experimental-mode on -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2022-02-01 10:08:50 -05:00
}
if ( s - > pcli_flags & ACCESS_EXPERT ) {
2024-08-09 11:41:36 -04:00
const char * cmd = " expert-mode on -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2022-02-01 10:08:50 -05:00
}
2023-12-06 05:15:01 -05:00
if ( s - > pcli_flags & ACCESS_MCLI_SEVERITY_STR ) {
2024-08-08 11:09:15 -04:00
const char * cmd = " set severity-output string -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2023-12-06 05:15:01 -05:00
}
if ( s - > pcli_flags & ACCESS_MCLI_SEVERITY_NB ) {
2024-08-08 11:09:15 -04:00
const char * cmd = " set severity-output number -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2023-12-06 05:15:01 -05:00
}
2022-02-01 10:08:50 -05:00
2018-12-13 03:05:47 -05:00
if ( pcli_has_level ( s , ACCESS_LVL_ADMIN ) ) {
goto end ;
} else if ( pcli_has_level ( s , ACCESS_LVL_OPER ) ) {
2024-08-09 11:41:36 -04:00
const char * cmd = " operator -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2018-12-13 03:05:47 -05:00
} else if ( pcli_has_level ( s , ACCESS_LVL_USER ) ) {
2024-08-09 11:41:36 -04:00
const char * cmd = " user -; " ;
ci_insert ( req , 0 , cmd , strlen ( cmd ) ) ;
ret + = strlen ( cmd ) ;
2018-12-13 03:05:47 -05:00
}
2018-12-11 10:10:53 -05:00
}
2018-12-13 03:05:47 -05:00
end :
2018-10-26 08:47:40 -04:00
2018-12-13 03:05:47 -05:00
return ret ;
2018-10-26 08:47:40 -04:00
}
int pcli_wait_for_request ( struct stream * s , struct channel * req , int an_bit )
{
2018-12-11 10:10:53 -05:00
int next_pid = - 1 ;
2018-10-26 08:47:40 -04:00
int to_forward ;
2018-12-11 10:10:53 -05:00
char * errmsg = NULL ;
2018-10-26 08:47:40 -04:00
2021-12-25 01:45:52 -05:00
/* Don't read the next command if still processing the response of the
2021-10-18 08:52:49 -04:00
* current one . Just wait . At this stage , errors should be handled by
* the response analyzer .
*/
if ( s - > res . analysers & AN_RES_WAIT_CLI )
return 0 ;
2018-12-13 03:05:47 -05:00
if ( ( s - > pcli_flags & ACCESS_LVL_MASK ) = = ACCESS_LVL_NONE )
s - > pcli_flags | = strm_li ( s ) - > bind_conf - > level & ACCESS_LVL_MASK ;
2022-09-24 10:14:50 -04:00
/* stream that comes from the reload listener only responses the reload
* status and quits */
if ( ! ( s - > pcli_flags & PCLI_F_RELOAD )
& & strm_li ( s ) - > bind_conf = = mcli_reload_bind_conf )
goto send_status ;
2018-10-26 08:47:40 -04:00
read_again :
/* if the channel is closed for read, we won't receive any more data
from the client , but we don ' t want to forward this close to the
server */
channel_dont_close ( req ) ;
/* We don't know yet to which server we will connect */
channel_dont_connect ( req ) ;
2023-03-16 09:40:03 -04:00
s - > scf - > flags | = SC_FL_RCV_ONCE ;
2018-10-26 08:47:40 -04:00
/* need more data */
if ( ! ci_data ( req ) )
2022-01-18 02:44:23 -05:00
goto missing_data ;
2018-10-26 08:47:40 -04:00
/* If there is data available for analysis, log the end of the idle time. */
if ( c_data ( req ) & & s - > logs . t_idle = = - 1 )
2023-04-28 03:16:15 -04:00
s - > logs . t_idle = ns_to_ms ( now_ns - s - > logs . accept_ts ) - s - > logs . t_handshake ;
2018-10-26 08:47:40 -04:00
2018-12-11 10:10:53 -05:00
to_forward = pcli_parse_request ( s , req , & errmsg , & next_pid ) ;
2018-10-26 08:47:40 -04:00
if ( to_forward > 0 ) {
2018-12-11 10:10:53 -05:00
int target_pid ;
2018-10-26 08:47:40 -04:00
/* enough data */
/* forward only 1 command */
channel_forward ( req , to_forward ) ;
2018-12-11 10:10:57 -05:00
2018-12-13 03:05:46 -05:00
if ( ! ( s - > pcli_flags & PCLI_F_PAYLOAD ) ) {
2018-12-11 10:10:57 -05:00
/* we send only 1 command per request, and we write close after it */
2023-04-13 09:56:26 -04:00
sc_schedule_shutdown ( s - > scb ) ;
2018-12-11 10:10:57 -05:00
} else {
pcli_write_prompt ( s ) ;
}
s - > res . flags | = CF_WAKE_ONCE ; /* need to be called again */
2018-10-26 08:47:40 -04:00
s - > res . analysers | = AN_RES_WAIT_CLI ;
2018-12-11 10:10:57 -05:00
if ( ! ( s - > flags & SF_ASSIGNED ) ) {
if ( next_pid > - 1 )
target_pid = next_pid ;
else
target_pid = s - > pcli_next_pid ;
/* we can connect now */
s - > target = pcli_pid_to_server ( target_pid ) ;
2018-10-26 08:47:40 -04:00
2021-12-10 08:14:53 -05:00
if ( ! s - > target )
goto server_disconnect ;
2018-12-11 10:10:57 -05:00
s - > flags | = ( SF_DIRECT | SF_ASSIGNED ) ;
channel_auto_connect ( req ) ;
}
2018-10-26 08:47:40 -04:00
} else if ( to_forward = = 0 ) {
/* we trimmed things but we might have other commands to consume */
2018-12-11 10:10:53 -05:00
pcli_write_prompt ( s ) ;
2018-10-26 08:47:40 -04:00
goto read_again ;
2022-01-18 02:44:23 -05:00
} else if ( to_forward = = - 1 ) {
2023-05-14 12:36:00 -04:00
if ( ! errmsg ) /* no error means missing data */
goto missing_data ;
/* there was an error during the parsing */
pcli_error ( s , errmsg ) ;
pcli_write_prompt ( s ) ;
2018-10-26 08:47:40 -04:00
}
return 0 ;
send_help :
b_reset ( & req - > buf ) ;
b_putblk ( & req - > buf , " help \n " , 5 ) ;
goto read_again ;
2021-12-10 08:14:53 -05:00
2022-09-24 10:14:50 -04:00
send_status :
s - > pcli_flags | = PCLI_F_RELOAD ;
2023-05-07 01:07:44 -04:00
/* don't use ci_putblk here because SHUT_DONE could have been sent */
2022-09-24 10:14:50 -04:00
b_reset ( & req - > buf ) ;
b_putblk ( & req - > buf , " _loadstatus;quit \n " , 17 ) ;
goto read_again ;
2022-01-18 02:44:23 -05:00
missing_data :
2023-04-17 10:17:32 -04:00
if ( s - > scf - > flags & ( SC_FL_ABRT_DONE | SC_FL_EOS ) ) {
2022-01-18 02:44:23 -05:00
/* There is no more request or a only a partial one and we
* receive a close from the client , we can leave */
2023-04-13 09:56:26 -04:00
sc_schedule_shutdown ( s - > scf ) ;
2022-01-18 02:44:23 -05:00
s - > req . analysers & = ~ AN_REQ_WAIT_CLI ;
return 1 ;
}
else if ( channel_full ( req , global . tune . maxrewrite ) ) {
/* buffer is full and we didn't catch the end of a command */
goto send_help ;
}
return 0 ;
2021-12-10 08:14:53 -05:00
server_disconnect :
pcli_reply_and_close ( s , " Can't connect to the target CLI! \n " ) ;
return 0 ;
2018-10-26 08:47:40 -04:00
}
int pcli_wait_for_response ( struct stream * s , struct channel * rep , int an_bit )
{
struct proxy * fe = strm_fe ( s ) ;
struct proxy * be = s - > be ;
2023-04-14 06:05:55 -04:00
if ( ( s - > scb - > flags & SC_FL_ERROR ) | | ( rep - > flags & ( CF_READ_TIMEOUT | CF_WRITE_TIMEOUT ) ) | |
2023-04-13 10:37:37 -04:00
( ( s - > scf - > flags & SC_FL_SHUT_DONE ) & & ( rep - > to_forward | | co_data ( rep ) ) ) ) {
2018-11-06 11:37:11 -05:00
pcli_reply_and_close ( s , " Can't connect to the target CLI! \n " ) ;
2021-10-18 08:52:49 -04:00
s - > req . analysers & = ~ AN_REQ_WAIT_CLI ;
2018-11-06 11:37:11 -05:00
s - > res . analysers & = ~ AN_RES_WAIT_CLI ;
return 0 ;
}
2023-03-16 09:40:03 -04:00
s - > scb - > flags | = SC_FL_RCV_ONCE ; /* try to get back here ASAP */
2023-03-17 10:38:18 -04:00
s - > scf - > flags | = SC_FL_SND_NEVERWAIT ;
2018-10-26 08:47:40 -04:00
/* don't forward the close */
channel_dont_close ( & s - > res ) ;
channel_dont_close ( & s - > req ) ;
2018-12-13 03:05:46 -05:00
if ( s - > pcli_flags & PCLI_F_PAYLOAD ) {
2018-12-11 10:10:57 -05:00
s - > res . analysers & = ~ AN_RES_WAIT_CLI ;
s - > req . flags | = CF_WAKE_ONCE ; /* need to be called again if there is some command left in the request */
return 0 ;
}
2018-10-26 08:47:40 -04:00
/* forward the data */
if ( ci_data ( rep ) ) {
c_adv ( rep , ci_data ( rep ) ) ;
return 0 ;
}
2023-04-17 10:17:32 -04:00
if ( s - > scb - > flags & ( SC_FL_ABRT_DONE | SC_FL_EOS ) ) {
MEDIUM: log: consider log-steps proxy setting for existing log origins
During tcp/http transaction processing, haproxy may produce logs at
different steps during the processing (accept, connect, request,
response, close). But the behavior is hardly configurable because
haproxy will only emit a single log per transaction, and by default
it will try to produce the log once all log aliases or fetches used
in the logformat could be satisfied, which means the log is often
emitted during connection teardown, unless "option logasap" is used.
We were often asked to have a way to emit multiple logs for a single
transaction, like for instance emit log during accept, then request,
response and close for instance, see GH #401 for more context.
Thanks to "log-steps" keyword introduced by commit "MINOR: log:
introduce "log-steps" proxy keyword", it is now possible to explictly
configure when logs should be generated by haproxy when processing a
transaction. This commit adds the required checks so that log-steps
proxy option is properly considered for existing logs generated by
haproxy. If "log-steps" is not specified on the proxy, the old behavior
is preserved.
Note: a slight cpu overhead should only be visible when "log-steps"
keyword will be used due to the implementation relying on eb32 lookup
instead of basic bitfield check as described in "MINOR: proxy: add
log_steps struct member". However, the default behavior shouldn't be
affected.
When combining log-steps with log-profiles, user has the ability to
explicitly control how and when haproxy should generate logs during
requests handling.
2024-09-04 09:03:46 -04:00
uint8_t do_log = 0 ;
2018-10-26 08:47:40 -04:00
/* stream cleanup */
2018-10-26 08:47:47 -04:00
pcli_write_prompt ( s ) ;
2022-05-17 13:44:42 -04:00
s - > scb - > flags | = SC_FL_NOLINGER | SC_FL_NOHALF ;
2023-04-13 10:10:23 -04:00
sc_abort ( s - > scb ) ;
2023-04-13 10:23:48 -04:00
sc_shutdown ( s - > scb ) ;
2018-10-26 08:47:40 -04:00
/*
* starting from there this the same code as
* http_end_txn_clean_session ( ) .
*
* It allows to do frontend keepalive while reconnecting to a
* new server for each request .
*/
if ( s - > flags & SF_BE_ASSIGNED ) {
2021-04-06 07:53:36 -04:00
HA_ATOMIC_DEC ( & be - > beconn ) ;
2018-10-26 08:47:40 -04:00
if ( unlikely ( s - > srv_conn ) )
sess_change_server ( s , NULL ) ;
}
2023-04-28 03:16:15 -04:00
s - > logs . t_close = ns_to_ms ( now_ns - s - > logs . accept_ts ) ;
2018-10-26 08:47:40 -04:00
stream_process_counters ( s ) ;
/* don't count other requests' data */
s - > logs . bytes_in - = ci_data ( & s - > req ) ;
s - > logs . bytes_out - = ci_data ( & s - > res ) ;
/* we may need to know the position in the queue */
pendconn_free ( s ) ;
/* let's do a final log if we need it */
MEDIUM: log: consider log-steps proxy setting for existing log origins
During tcp/http transaction processing, haproxy may produce logs at
different steps during the processing (accept, connect, request,
response, close). But the behavior is hardly configurable because
haproxy will only emit a single log per transaction, and by default
it will try to produce the log once all log aliases or fetches used
in the logformat could be satisfied, which means the log is often
emitted during connection teardown, unless "option logasap" is used.
We were often asked to have a way to emit multiple logs for a single
transaction, like for instance emit log during accept, then request,
response and close for instance, see GH #401 for more context.
Thanks to "log-steps" keyword introduced by commit "MINOR: log:
introduce "log-steps" proxy keyword", it is now possible to explictly
configure when logs should be generated by haproxy when processing a
transaction. This commit adds the required checks so that log-steps
proxy option is properly considered for existing logs generated by
haproxy. If "log-steps" is not specified on the proxy, the old behavior
is preserved.
Note: a slight cpu overhead should only be visible when "log-steps"
keyword will be used due to the implementation relying on eb32 lookup
instead of basic bitfield check as described in "MINOR: proxy: add
log_steps struct member". However, the default behavior shouldn't be
affected.
When combining log-steps with log-profiles, user has the ability to
explicitly control how and when haproxy should generate logs during
requests handling.
2024-09-04 09:03:46 -04:00
if ( fe - > to_log = = LW_LOGSTEPS ) {
if ( log_orig_proxy ( LOG_ORIG_TXN_CLOSE , fe ) )
do_log = 1 ;
}
else if ( ! lf_expr_isempty ( & fe - > logformat ) & & s - > logs . logwait )
do_log = 1 ;
if ( do_log & &
2018-10-26 08:47:40 -04:00
! ( s - > flags & SF_MONITOR ) & &
( ! ( fe - > options & PR_O_NULLNOLOG ) | | s - > req . total ) ) {
2024-09-23 11:22:45 -04:00
s - > do_log ( s , log_orig ( LOG_ORIG_TXN_CLOSE , LOG_ORIG_FL_NONE ) ) ;
2018-10-26 08:47:40 -04:00
}
/* stop tracking content-based counters */
stream_stop_content_counters ( s ) ;
stream_update_time_stats ( s ) ;
s - > logs . accept_date = date ; /* user-visible date for logging */
2023-04-28 03:16:15 -04:00
s - > logs . accept_ts = now_ns ; /* corrected date for internal use */
2018-10-26 08:47:40 -04:00
s - > logs . t_handshake = 0 ; /* There are no handshake in keep alive connection. */
s - > logs . t_idle = - 1 ;
2023-04-27 03:46:02 -04:00
s - > logs . request_ts = 0 ;
2018-10-26 08:47:40 -04:00
s - > logs . t_queue = - 1 ;
s - > logs . t_connect = - 1 ;
s - > logs . t_data = - 1 ;
s - > logs . t_close = 0 ;
s - > logs . prx_queue_pos = 0 ; /* we get the number of pending conns before us */
s - > logs . srv_queue_pos = 0 ; /* we will get this number soon */
s - > logs . bytes_in = s - > req . total = ci_data ( & s - > req ) ;
s - > logs . bytes_out = s - > res . total = ci_data ( & s - > res ) ;
stream_del_srv_conn ( s ) ;
if ( objt_server ( s - > target ) ) {
if ( s - > flags & SF_CURR_SESS ) {
s - > flags & = ~ SF_CURR_SESS ;
2021-04-06 07:53:36 -04:00
HA_ATOMIC_DEC ( & __objt_server ( s - > target ) - > cur_sess ) ;
2018-10-26 08:47:40 -04:00
}
2018-12-02 13:28:41 -05:00
if ( may_dequeue_tasks ( __objt_server ( s - > target ) , be ) )
2021-06-22 12:47:51 -04:00
process_srv_queue ( __objt_server ( s - > target ) ) ;
2018-10-26 08:47:40 -04:00
}
s - > target = NULL ;
BUG/MEDIUM: cli: Always release back endpoint between two commands on the mcli
When several commands are chained on the master CLI, the same client
connection is used. Because, it is a TCP connection, the mux PT is used. It
means there is no stream at the mux level. It is not possible to release the
applicative stream between each commands as for the HTTP. So, to work around
this limitation, between two commands, the master CLI is resetting the
stream. It does exactly what it was performed on HTTP to manage keep-alive
connections on old HAProxy versions.
But this part was copied from a code dealing with connection only while the
back endpoint can be an applet or a mux for the master cli. The previous fix
on the mux PT ("BUG/MEDIUM: mux-pt: Never fully close the connection on
shutdown") revealed a bug. Between two commands, the back endpoint was only
released if the connection's XPRT was closed. This works if the back
endpoint is an applet because there is no connection. But for commands sent
to a worker, a connection is used. At this stage, this only works if the
connection's XPRT is closed. Otherwise, the old endpoint is never detached
leading to undefined behavior on the next command execution (most probably a
crash).
Without the commit above, the connection's XPRT is always closed on
shutdown. It is no longer true. At this stage, we must inconditionnally
release the back endpoint by resetting the corresponding sedesc to fix the
bug.
This patch must be backported with the commit above in all stable
versions. On 2.4 and lower, it will need to be adapted.
2024-09-02 12:29:02 -04:00
/* Always release our endpoint */
s - > srv_conn = NULL ;
if ( sc_reset_endp ( s - > scb ) < 0 ) {
if ( ! s - > conn_err_type )
s - > conn_err_type = STRM_ET_CONN_OTHER ;
if ( s - > srv_error )
s - > srv_error ( s , s - > scb ) ;
return 1 ;
2018-10-26 08:47:40 -04:00
}
BUG/MEDIUM: cli: Always release back endpoint between two commands on the mcli
When several commands are chained on the master CLI, the same client
connection is used. Because, it is a TCP connection, the mux PT is used. It
means there is no stream at the mux level. It is not possible to release the
applicative stream between each commands as for the HTTP. So, to work around
this limitation, between two commands, the master CLI is resetting the
stream. It does exactly what it was performed on HTTP to manage keep-alive
connections on old HAProxy versions.
But this part was copied from a code dealing with connection only while the
back endpoint can be an applet or a mux for the master cli. The previous fix
on the mux PT ("BUG/MEDIUM: mux-pt: Never fully close the connection on
shutdown") revealed a bug. Between two commands, the back endpoint was only
released if the connection's XPRT was closed. This works if the back
endpoint is an applet because there is no connection. But for commands sent
to a worker, a connection is used. At this stage, this only works if the
connection's XPRT is closed. Otherwise, the old endpoint is never detached
leading to undefined behavior on the next command execution (most probably a
crash).
Without the commit above, the connection's XPRT is always closed on
shutdown. It is no longer true. At this stage, we must inconditionnally
release the back endpoint by resetting the corresponding sedesc to fix the
bug.
This patch must be backported with the commit above in all stable
versions. On 2.4 and lower, it will need to be adapted.
2024-09-02 12:29:02 -04:00
se_fl_clr ( s - > scb - > sedesc , ~ SE_FL_DETACHED ) ;
2018-10-26 08:47:40 -04:00
2022-05-17 13:40:40 -04:00
sockaddr_free ( & s - > scb - > dst ) ;
2019-07-18 09:47:45 -04:00
2022-05-27 03:03:30 -04:00
sc_set_state ( s - > scb , SC_ST_INI ) ;
2023-04-14 04:42:08 -04:00
s - > scb - > flags & = ~ ( SC_FL_ERROR | SC_FL_SHUT_DONE | SC_FL_SHUT_WANTED ) ;
2022-05-17 13:44:42 -04:00
s - > scb - > flags & = SC_FL_ISBACK | SC_FL_DONT_WAKE ; /* we're in the context of process_stream */
2023-04-03 12:32:50 -04:00
s - > req . flags & = ~ ( CF_AUTO_CONNECT | CF_STREAMER | CF_STREAMER_FAST | CF_WROTE_DATA ) ;
s - > res . flags & = ~ ( CF_STREAMER | CF_STREAMER_FAST | CF_WRITE_EVENT | CF_WROTE_DATA | CF_READ_EVENT ) ;
MEDIUM: stream: remove the confusing SF_ADDR_SET flag
This flag is no longer needed now that it must always match the presence
of a destination address on the backend conn_stream. Worse, before previous
patch, if it were to be accidently removed while the address is present, it
could result in a leak of that address since alloc_dst_address() would first
be called to flush it.
Its usage has a long history where addresses were stored in an area shared
with the connection, but as this is no longer the case, there's no reason
for putting this burden onto application-level code that should not focus
on setting obscure flags.
The only place where that made a small difference is in the dequeuing code
in case of queue redistribution, because previously the code would first
clear the flag, and only later when trying to deal with the queue, would
release the address. It's not even certain whether there would exist a
code path going to connect_server() without calling pendconn_dequeue()
first (e.g. retries on queue timeout maybe?).
Now the pendconn_dequeue() code will rely on SF_ASSIGNED to decide to
clear and release the address, since that flag is always set while in
a server's queue, and its clearance implies that we don't want to keep
the address. At least it remains consistent and there's no more risk of
leaking it.
2022-05-02 10:36:47 -04:00
s - > flags & = ~ ( SF_DIRECT | SF_ASSIGNED | SF_BE_ASSIGNED | SF_FORCE_PRST | SF_IGNORE_PRST ) ;
2018-10-26 08:47:40 -04:00
s - > flags & = ~ ( SF_CURR_SESS | SF_REDIRECTABLE | SF_SRV_REUSED ) ;
s - > flags & = ~ ( SF_ERR_MASK | SF_FINST_MASK | SF_REDISP ) ;
2022-03-29 09:42:09 -04:00
s - > conn_retries = 0 ; /* used for logging too */
2022-03-29 13:02:31 -04:00
s - > conn_exp = TICK_ETERNITY ;
2022-03-30 13:39:30 -04:00
s - > conn_err_type = STRM_ET_NONE ;
2018-10-26 08:47:40 -04:00
/* reinitialise the current rule list pointer to NULL. We are sure that
* any rulelist match the NULL pointer .
*/
s - > current_rule_list = NULL ;
s - > be = strm_fe ( s ) ;
s - > logs . logwait = strm_fe ( s ) - > to_log ;
s - > logs . level = 0 ;
stream_del_srv_conn ( s ) ;
s - > target = NULL ;
/* re-init store persistence */
s - > store_count = 0 ;
2024-07-16 08:42:20 -04:00
s - > uniq_id = _HA_ATOMIC_FETCH_ADD ( & global . req_count , 1 ) ;
2018-10-26 08:47:40 -04:00
2024-09-27 12:14:33 -04:00
s - > scf - > flags & = ~ ( SC_FL_EOI | SC_FL_EOS | SC_FL_ERROR | SC_FL_ABRT_DONE | SC_FL_ABRT_WANTED ) ;
2023-03-17 10:38:18 -04:00
s - > scf - > flags & = ~ SC_FL_SND_NEVERWAIT ;
2023-03-16 09:40:03 -04:00
s - > scf - > flags | = SC_FL_RCV_ONCE ; /* one read is usually enough */
2018-10-26 08:47:40 -04:00
2024-09-27 12:14:33 -04:00
se_have_more_data ( s - > scf - > sedesc ) ;
2018-10-26 08:47:40 -04:00
s - > req . flags | = CF_WAKE_ONCE ; /* need to be called again if there is some command left in the request */
s - > res . analysers & = ~ AN_RES_WAIT_CLI ;
/* We must trim any excess data from the response buffer, because we
* may have blocked an invalid response from a server that we don ' t
2020-04-07 16:07:56 -04:00
* want to accidentally forward once we disable the analysers , nor do
2018-10-26 08:47:40 -04:00
* we want those data to come along with next response . A typical
* example of such data would be from a buggy server responding to
* a HEAD with some data , or sending more than the advertised
* content - length .
*/
if ( unlikely ( ci_data ( & s - > res ) ) )
b_set_data ( & s - > res . buf , co_data ( & s - > res ) ) ;
/* Now we can realign the response buffer */
c_realign_if_empty ( & s - > res ) ;
2023-02-15 02:13:33 -05:00
s - > scf - > ioto = strm_fe ( s ) - > timeout . client ;
s - > scb - > ioto = TICK_ETERNITY ;
2018-10-26 08:47:40 -04:00
s - > req . analyse_exp = TICK_ETERNITY ;
s - > res . analyse_exp = TICK_ETERNITY ;
2023-02-20 02:23:51 -05:00
2018-10-26 08:47:40 -04:00
/* we're removing the analysers, we MUST re-enable events detection.
* We don ' t enable close on the response channel since it ' s either
* already closed , or in keep - alive with an idle connection handler .
*/
channel_auto_read ( & s - > req ) ;
channel_auto_close ( & s - > req ) ;
channel_auto_read ( & s - > res ) ;
return 1 ;
}
return 0 ;
}
2018-10-26 08:47:35 -04:00
/*
* The mworker functions are used to initialize the CLI in the master process
*/
2018-10-26 08:47:45 -04:00
/*
* Stop the mworker proxy
*/
void mworker_cli_proxy_stop ( )
{
2018-11-06 11:37:12 -05:00
if ( mworker_proxy )
stop_proxy ( mworker_proxy ) ;
2018-10-26 08:47:45 -04:00
}
2018-10-26 08:47:35 -04:00
/*
2024-10-23 11:29:10 -04:00
* Create the MASTER proxy
2018-10-26 08:47:35 -04:00
*/
2024-10-23 11:29:10 -04:00
int mworker_cli_create_master_proxy ( char * * errmsg )
2018-10-26 08:47:35 -04:00
{
2024-10-23 11:29:10 -04:00
mworker_proxy = alloc_new_proxy ( " MASTER " , PR_CAP_LISTEN | PR_CAP_INT , errmsg ) ;
if ( ! mworker_proxy ) {
return - 1 ;
}
2018-10-26 08:47:35 -04:00
2018-10-26 08:47:40 -04:00
mworker_proxy - > mode = PR_MODE_CLI ;
2024-10-23 11:29:10 -04:00
/* default to 10 concurrent connections */
mworker_proxy - > maxconn = 10 ;
/* no timeout */
mworker_proxy - > timeout . client = 0 ;
mworker_proxy - > conf . file = strdup ( " MASTER " ) ;
2018-10-26 08:47:35 -04:00
mworker_proxy - > conf . line = 0 ;
mworker_proxy - > accept = frontend_accept ;
2024-10-23 11:29:10 -04:00
mworker_proxy - > lbprm . algo = BE_LB_ALGO_NONE ;
2018-10-26 08:47:35 -04:00
/* Does not init the default target the CLI applet, but must be done in
* the request parsing code */
mworker_proxy - > default_target = NULL ;
2024-10-23 11:29:10 -04:00
mworker_proxy - > next = proxies_list ;
proxies_list = mworker_proxy ;
return 0 ;
}
/*
* Attach servers to ipc_fd [ 0 ] of all presented in proc_list workers . Master and
* worker share MCLI sockpair ( ipc_fd [ 0 ] and ipc_fd [ 1 ] ) . Servers are attached to
* ipc_fd [ 0 ] , which is always opened at master side . ipc_fd [ 0 ] of worker , started
* before the reload , is inherited in master after the reload ( execvp ) .
*/
int mworker_cli_attach_server ( char * * errmsg )
{
char * msg = NULL ;
struct mworker_proc * child ;
BUG_ON ( ( mworker_proxy = = NULL ) , " Triggered in mworker_cli_attach_server(), "
" mworker_proxy must be created before this call. \n " ) ;
2018-10-26 08:47:35 -04:00
/* create all servers using the mworker_proc list */
list_for_each_entry ( child , & proc_list , list ) {
struct server * newsrv = NULL ;
struct sockaddr_storage * sk ;
int port1 , port2 , port ;
struct protocol * proto ;
2020-01-14 09:25:02 -05:00
/* only the workers support the master CLI */
if ( ! ( child - > options & PROC_O_TYPE_WORKER ) )
continue ;
2018-10-26 08:47:35 -04:00
newsrv = new_server ( mworker_proxy ) ;
if ( ! newsrv )
2018-11-22 10:46:51 -05:00
goto error ;
2018-10-26 08:47:35 -04:00
2024-10-23 11:29:10 -04:00
if ( child - > options & PROC_O_INIT )
2021-06-15 03:08:18 -04:00
memprintf ( & msg , " cur-%d " , 1 ) ;
2018-10-26 08:47:35 -04:00
else
memprintf ( & msg , " old-%d " , child - > pid ) ;
newsrv - > next = mworker_proxy - > srv ;
mworker_proxy - > srv = newsrv ;
newsrv - > conf . file = strdup ( msg ) ;
newsrv - > id = strdup ( msg ) ;
newsrv - > conf . line = 0 ;
memprintf ( & msg , " sockpair@%d " , child - > ipc_fd [ 0 ] ) ;
2023-11-09 05:19:24 -05:00
if ( ( sk = str2sa_range ( msg , & port , & port1 , & port2 , NULL , & proto , NULL ,
2024-10-23 11:29:10 -04:00
errmsg , NULL , NULL , NULL , PA_O_STREAM ) ) = = 0 ) {
2018-11-22 10:46:51 -05:00
goto error ;
2018-11-22 10:46:50 -05:00
}
2021-02-20 04:46:51 -05:00
ha_free ( & msg ) ;
2018-10-26 08:47:35 -04:00
2020-09-16 12:25:03 -04:00
if ( ! proto - > connect ) {
2018-11-22 10:46:51 -05:00
goto error ;
2018-10-26 08:47:35 -04:00
}
/* no port specified */
newsrv - > flags | = SRV_F_MAPPORTS ;
newsrv - > addr = * sk ;
2018-10-26 08:47:40 -04:00
/* don't let the server participate to load balancing */
newsrv - > iweight = 0 ;
newsrv - > uweight = 0 ;
2018-10-26 08:47:35 -04:00
srv_lb_commit_status ( newsrv ) ;
2018-10-26 08:47:38 -04:00
child - > srv = newsrv ;
2018-10-26 08:47:35 -04:00
}
2021-07-29 09:13:22 -04:00
2018-10-26 08:47:35 -04:00
return 0 ;
2018-11-22 10:46:51 -05:00
error :
list_for_each_entry ( child , & proc_list , list ) {
free ( ( char * ) child - > srv - > conf . file ) ; /* cast because of const char * */
free ( child - > srv - > id ) ;
2021-02-20 04:46:51 -05:00
ha_free ( & child - > srv ) ;
2018-11-22 10:46:51 -05:00
}
free ( msg ) ;
return - 1 ;
2018-10-26 08:47:35 -04:00
}
2017-04-05 16:24:59 -04:00
2018-10-26 08:47:36 -04:00
/*
* Create a new listener for the master CLI proxy
*/
2024-10-03 05:28:05 -04:00
struct bind_conf * mworker_cli_master_proxy_new_listener ( char * line )
2018-10-26 08:47:36 -04:00
{
struct bind_conf * bind_conf ;
struct listener * l ;
char * err = NULL ;
char * args [ MAX_LINE_ARGS + 1 ] ;
int arg ;
int cur_arg ;
2019-11-25 03:58:37 -05:00
arg = 1 ;
2018-10-26 08:47:36 -04:00
args [ 0 ] = line ;
/* args is a bind configuration with spaces replaced by commas */
while ( * line & & arg < MAX_LINE_ARGS ) {
if ( * line = = ' , ' ) {
* line + + = ' \0 ' ;
while ( * line = = ' , ' )
line + + ;
2019-11-25 03:58:37 -05:00
args [ arg + + ] = line ;
2018-10-26 08:47:36 -04:00
}
line + + ;
}
2019-11-25 03:58:37 -05:00
args [ arg ] = " \0 " ;
2018-10-26 08:47:36 -04:00
bind_conf = bind_conf_alloc ( mworker_proxy , " master-socket " , 0 , " " , xprt_get ( XPRT_RAW ) ) ;
2018-11-22 10:46:51 -05:00
if ( ! bind_conf )
goto err ;
2018-10-26 08:47:36 -04:00
bind_conf - > level & = ~ ACCESS_LVL_MASK ;
bind_conf - > level | = ACCESS_LVL_ADMIN ;
2021-03-12 09:00:57 -05:00
bind_conf - > level | = ACCESS_MASTER | ACCESS_MASTER_ONLY ;
2018-10-26 08:47:36 -04:00
if ( ! str2listener ( args [ 0 ] , mworker_proxy , bind_conf , " master-socket " , 0 , & err ) ) {
ha_alert ( " Cannot create the listener of the master CLI \n " ) ;
2018-11-22 10:46:51 -05:00
goto err ;
2018-10-26 08:47:36 -04:00
}
cur_arg = 1 ;
while ( * args [ cur_arg ] ) {
struct bind_kw * kw ;
2021-03-12 04:14:07 -05:00
const char * best ;
2018-10-26 08:47:36 -04:00
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 ] ) ;
goto err ;
}
2021-03-13 05:00:33 -05:00
if ( kw - > parse ( args , cur_arg , global . cli_fe , bind_conf , & err ) ! = 0 ) {
2018-10-26 08:47:36 -04:00
if ( 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 ] ) ;
goto err ;
}
cur_arg + = 1 + kw - > skip ;
continue ;
}
2021-03-12 04:14:07 -05:00
best = bind_find_best_kw ( args [ cur_arg ] ) ;
if ( best )
memprintf ( & err , " '%s %s' : unknown keyword '%s'. Did you mean '%s' maybe ? " ,
args [ 0 ] , args [ 1 ] , args [ cur_arg ] , best ) ;
else
memprintf ( & err , " '%s %s' : unknown keyword '%s'. " ,
args [ 0 ] , args [ 1 ] , args [ cur_arg ] ) ;
2018-10-26 08:47:36 -04:00
goto err ;
}
2023-01-12 13:10:17 -05:00
bind_conf - > accept = session_accept_fd ;
2023-01-12 13:32:45 -05:00
bind_conf - > nice = - 64 ; /* we want to boost priority for local stats */
2023-01-12 13:58:42 -05:00
bind_conf - > options | = BC_O_UNLIMITED ; /* don't make the peers subject to global limits */
2023-01-12 13:18:34 -05:00
2023-11-28 15:50:21 -05:00
/* Pin master CLI on the first thread of the first group only */
thread_set_pin_grp1 ( & bind_conf - > thread_set , 1 ) ;
2018-10-26 08:47:36 -04:00
list_for_each_entry ( l , & bind_conf - > listeners , by_bind ) {
2020-10-09 10:11:46 -04:00
l - > rx . flags | = RX_F_MWORKER ; /* we are keeping this FD in the master */
2019-02-27 10:25:28 -05:00
global . maxsock + + ; /* for the listening socket */
2018-10-26 08:47:36 -04:00
}
2019-02-27 10:25:28 -05:00
global . maxsock + = mworker_proxy - > maxconn ;
2018-10-26 08:47:36 -04:00
2022-09-24 09:51:27 -04:00
return bind_conf ;
2018-10-26 08:47:36 -04:00
err :
ha_alert ( " %s \n " , err ) ;
2018-11-22 10:46:51 -05:00
free ( err ) ;
free ( bind_conf ) ;
2022-09-24 09:51:27 -04:00
return NULL ;
2018-10-26 08:47:36 -04:00
}
2018-10-26 08:47:30 -04:00
/*
2024-10-03 05:32:16 -04:00
* Creates a " master-socket " bind conf and a listener . Assigns
2024-10-02 08:49:30 -04:00
* this new listener to the one " end " of the given process < proc > sockpair in
* order to have a new master CLI listening socket for this process .
2018-10-26 08:47:30 -04:00
*/
2024-10-02 08:49:30 -04:00
int mworker_cli_global_proxy_new_listener ( struct mworker_proc * proc )
2018-10-26 08:47:30 -04:00
{
struct bind_conf * bind_conf ;
struct listener * l ;
char * path = NULL ;
char * err = NULL ;
/* XXX: we might want to use a separate frontend at some point */
2021-03-13 05:00:33 -05:00
if ( ! global . cli_fe ) {
if ( ( global . cli_fe = cli_alloc_fe ( " GLOBAL " , " master-socket " , 0 ) ) = = NULL ) {
2018-10-26 08:47:30 -04:00
ha_alert ( " out of memory trying to allocate the stats frontend " ) ;
2018-11-22 10:46:51 -05:00
goto error ;
2018-10-26 08:47:30 -04:00
}
}
2021-03-13 05:00:33 -05:00
bind_conf = bind_conf_alloc ( global . cli_fe , " master-socket " , 0 , " " , xprt_get ( XPRT_RAW ) ) ;
2018-11-22 10:46:51 -05:00
if ( ! bind_conf )
goto error ;
2018-10-26 08:47:30 -04:00
bind_conf - > level & = ~ ACCESS_LVL_MASK ;
bind_conf - > level | = ACCESS_LVL_ADMIN ; /* TODO: need to lower the rights with a CLI keyword*/
2021-11-24 12:45:37 -05:00
bind_conf - > level | = ACCESS_FD_LISTENERS ;
2018-10-26 08:47:30 -04:00
2024-10-02 08:49:30 -04:00
if ( ! memprintf ( & path , " sockpair@%d " , proc - > ipc_fd [ 1 ] ) ) {
2018-10-26 08:47:30 -04:00
ha_alert ( " Cannot allocate listener. \n " ) ;
2018-11-22 10:46:51 -05:00
goto error ;
2018-10-26 08:47:30 -04:00
}
2021-03-13 05:00:33 -05:00
if ( ! str2listener ( path , global . cli_fe , bind_conf , " master-socket " , 0 , & err ) ) {
2018-11-22 10:46:50 -05:00
free ( path ) ;
2024-10-02 08:49:30 -04:00
ha_alert ( " Cannot create a CLI sockpair listener. \n " ) ;
2018-11-22 10:46:51 -05:00
goto error ;
2018-10-26 08:47:30 -04:00
}
2021-02-20 04:46:51 -05:00
ha_free ( & path ) ;
2018-10-26 08:47:30 -04:00
2023-01-12 13:10:17 -05:00
bind_conf - > accept = session_accept_fd ;
2023-01-12 13:32:45 -05:00
bind_conf - > nice = - 64 ; /* we want to boost priority for local stats */
2023-01-12 13:58:42 -05:00
bind_conf - > options | = BC_O_UNLIMITED | BC_O_NOSTOP ;
2023-01-12 13:18:34 -05:00
2023-09-13 04:13:30 -04:00
/* Pin master CLI on the first thread of the first group only */
thread_set_pin_grp1 ( & bind_conf - > thread_set , 1 ) ;
2018-10-26 08:47:30 -04:00
list_for_each_entry ( l , & bind_conf - > listeners , by_bind ) {
2021-04-06 07:53:36 -04:00
HA_ATOMIC_INC ( & unstoppable_jobs ) ;
2018-10-26 08:47:30 -04:00
/* it's a sockpair but we don't want to keep the fd in the master */
2020-09-01 09:41:59 -04:00
l - > rx . flags & = ~ RX_F_INHERITED ;
2019-02-27 10:25:28 -05:00
global . maxsock + + ; /* for the listening socket */
2018-10-26 08:47:30 -04:00
}
return 0 ;
2018-11-22 10:46:51 -05:00
error :
2024-10-02 08:49:30 -04:00
close ( proc - > ipc_fd [ 1 ] ) ;
2018-11-22 10:46:51 -05:00
free ( err ) ;
return - 1 ;
2018-10-26 08:47:30 -04:00
}
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 ,
2024-02-15 07:34:05 -05:00
. rcv_buf = appctx_raw_rcv_buf ,
2024-02-20 02:47:38 -05:00
. snd_buf = cli_snd_buf ,
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
2020-11-05 04:28:53 -05:00
/* master CLI */
static struct applet mcli_applet = {
. obj_type = OBJ_TYPE_APPLET ,
. name = " <MCLI> " , /* used for logging */
. fct = cli_io_handler ,
2024-02-15 07:34:05 -05:00
. rcv_buf = appctx_raw_rcv_buf ,
2024-02-20 02:47:38 -05:00
. snd_buf = cli_snd_buf ,
2020-11-05 04:28:53 -05:00
. release = cli_release_handler ,
} ;
2016-11-22 14:21:23 -05:00
/* register cli keywords */
static struct cli_kw_list cli_kws = { { } , {
2021-05-07 05:38:37 -04:00
{ { " help " , NULL } , NULL , cli_parse_simple , NULL , NULL , NULL , ACCESS_MASTER } ,
2024-10-24 11:20:57 -04:00
{ { " echo " , NULL } , " echo <text> : print text to the output " , cli_parse_echo , NULL , NULL , NULL , ACCESS_MASTER } ,
2021-05-07 05:38:37 -04:00
{ { " prompt " , NULL } , NULL , cli_parse_simple , NULL , NULL , NULL , ACCESS_MASTER } ,
{ { " quit " , NULL } , NULL , cli_parse_simple , NULL , NULL , NULL , ACCESS_MASTER } ,
{ { " _getsocks " , NULL } , NULL , _getsocks , NULL } ,
2022-02-02 05:23:58 -05:00
{ { " expert-mode " , NULL } , NULL , cli_parse_expert_experimental_mode , NULL , NULL , NULL , ACCESS_MASTER } , // not listed
{ { " experimental-mode " , NULL } , NULL , cli_parse_expert_experimental_mode , NULL , NULL , NULL , ACCESS_MASTER } , // not listed
2022-02-02 05:43:20 -05:00
{ { " mcli-debug-mode " , NULL } , NULL , cli_parse_expert_experimental_mode , NULL , NULL , NULL , ACCESS_MASTER_ONLY } , // not listed
2023-03-03 11:11:10 -05:00
{ { " set " , " anon " , " on " } , " set anon on [value] : activate the anonymized mode " , cli_parse_set_anon , NULL , NULL } ,
{ { " set " , " anon " , " off " } , " set anon off : deactivate the anonymized mode " , cli_parse_set_anon , NULL , NULL } ,
2022-09-29 04:36:11 -04:00
{ { " set " , " anon " , " global-key " , NULL } , " set anon global-key <value> : change the global anonymizing key " , cli_parse_set_global_key , NULL , NULL } ,
2021-05-07 05:38:37 -04:00
{ { " set " , " maxconn " , " global " , NULL } , " set maxconn global <value> : change the per-process maxconn setting " , cli_parse_set_maxconn_global , NULL } ,
{ { " set " , " rate-limit " , NULL } , " set rate-limit <setting> <value> : change a rate limiting value " , cli_parse_set_ratelimit , NULL } ,
{ { " 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 } ,
{ { " set " , " timeout " , NULL } , " set timeout [cli] <delay> : change a timeout setting " , cli_parse_set_timeout , NULL , NULL } ,
2022-09-14 11:24:22 -04:00
{ { " show " , " anon " , NULL } , " show anon : display the current state of anonymized mode " , cli_parse_show_anon , NULL } ,
2024-11-18 04:46:33 -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 , NULL , ACCESS_MASTER } ,
2021-05-07 05:38:37 -04:00
{ { " show " , " cli " , " sockets " , NULL } , " show cli sockets : dump list of cli sockets " , cli_parse_default , cli_io_handler_show_cli_sock , NULL , NULL , ACCESS_MASTER } ,
{ { " show " , " cli " , " level " , NULL } , " show cli level : display the level of the current CLI session " , cli_parse_show_lvl , NULL , NULL , NULL , ACCESS_MASTER } ,
2023-03-31 10:33:53 -04:00
{ { " show " , " fd " , NULL } , " show fd [-!plcfbsd]* [num] : dump list of file descriptors in use or a specific one " , cli_parse_show_fd , cli_io_handler_show_fd , NULL } ,
2021-12-14 09:22:29 -05:00
{ { " show " , " version " , NULL } , " show version : show version of the current process " , cli_parse_show_version , NULL , NULL , NULL , ACCESS_MASTER } ,
2021-05-07 05:38:37 -04:00
{ { " operator " , NULL } , " operator : lower the level of the current CLI session to operator " , cli_parse_set_lvl , NULL , NULL , NULL , ACCESS_MASTER } ,
{ { " user " , NULL } , " user : lower the level of the current CLI session to user " , cli_parse_set_lvl , NULL , NULL , NULL , ACCESS_MASTER } ,
2024-02-09 14:35:52 -05:00
{ { " wait " , NULL } , " wait {-h|<delay_ms>} cond [args...] : wait the specified delay or condition (-h to see list) " , cli_parse_wait , cli_io_handler_wait , cli_release_wait , NULL } ,
MINOR: mworker/cli: add _send_status to support state transition
In the new master-worker architecture, when a worker process is forked and
successfully initialized it needs somehow to communicate its "READY" state to
the master, in order to terminate the previous worker and workers, that might
exceeded max_reloads counter.
So, let's implement for this a new master CLI _send_status command. A new
worker can send its status string "READY" to the master, when it's about
entering to the run poll loop, thus it can start to receive data.
In _send_status() in the master context we update the status of the new worker:
PROC_O_INIT flag is withdrawn.
When TERM signal is sent to a worker, worker terminates and this triggers the
mworker_catch_sigchld() handler in master. This handler deletes the exiting
process entry from the processes list.
In _send_status() we loop over the processes list twice. At the first time, in
order to stop workers that exceeded the max_reloads counter. At the second time,
in order to stop the worker forked before the last reload. In the corner case,
when max_reloads=1, we avoid to send SIGTERM twice to the same worker by
setting sigterm_sent flag during the first loop.
2024-10-02 08:48:01 -04:00
{ { " _send_status " , NULL } , NULL , _send_status , NULL , NULL , NULL , ACCESS_MASTER_ONLY } ,
2016-11-22 14:21:23 -05:00
{ { } , }
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , cli_register_kw , & cli_kws ) ;
2013-06-21 17:16:39 -04:00
static struct cfg_kw_list cfg_kws = { ILH , {
2021-03-13 05:00:33 -05:00
{ CFG_GLOBAL , " stats " , cli_parse_global } ,
2008-07-09 14:12:41 -04:00
{ 0 , NULL , NULL } ,
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , cfg_register_keywords , & cfg_kws ) ;
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 } ,
} } ;
2018-11-25 13:14:37 -05:00
INITCALL1 ( STG_REGISTER , 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 :
*/