1161 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1161 lines
		
	
	
		
			31 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * This file is distributed as part of the MariaDB Corporation MaxScale.  It 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,
 | |
|  * version 2.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | |
|  * details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program; if not, write to the Free Software Foundation, Inc., 51
 | |
|  * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 | |
|  *
 | |
|  * Copyright MariaDB Corporation Ab 2013-2014
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * @file debugcmd.c  - The debug CLI command line interpreter
 | |
|  *
 | |
|  * The command interpreter for the dbug user interface. The command
 | |
|  * structure is such that there are a numerb of commands, notably
 | |
|  * show and a set of subcommands, the things to show in this case.
 | |
|  *
 | |
|  * Each subcommand has a handler function defined for it that is passeed
 | |
|  * the DCB to use to print the output of the commands and up to 3 arguments
 | |
|  * as numeric values.
 | |
|  *
 | |
|  * There are two "built in" commands, the help command and the quit
 | |
|  * command.
 | |
|  *
 | |
|  * @verbatim
 | |
|  * Revision History
 | |
|  *
 | |
|  * Date		Who			Description
 | |
|  * 20/06/13	Mark Riddoch		Initial implementation
 | |
|  * 17/07/13	Mark Riddoch		Additional commands
 | |
|  * 09/08/2013	Massimiliano Pinto	Added enable/disable commands (now only for log)
 | |
|  * 20/05/14	Mark Riddoch		Added ability to give server and service names rather
 | |
|  *					than simply addresses
 | |
|  * 23/05/14	Mark Riddoch		Added support for developer and user modes
 | |
|  * 29/05/14	Mark Riddoch		Add Filter support
 | |
|  *
 | |
|  * @endverbatim
 | |
|  */
 | |
| #include <my_config.h>
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| #include <service.h>
 | |
| #include <session.h>
 | |
| #include <router.h>
 | |
| #include <filter.h>
 | |
| #include <modules.h>
 | |
| #include <atomic.h>
 | |
| #include <server.h>
 | |
| #include <spinlock.h>
 | |
| #include <dcb.h>
 | |
| #include <poll.h>
 | |
| #include <users.h>
 | |
| #include <dbusers.h>
 | |
| #include <config.h>
 | |
| #include <telnetd.h>
 | |
| #include <adminusers.h>
 | |
| #include <monitor.h>
 | |
| #include <debugcli.h>
 | |
| 
 | |
| #include <skygw_utils.h>
 | |
| #include <log_manager.h>
 | |
| 
 | |
| #define	MAXARGS	5
 | |
| 
 | |
| #define	ARG_TYPE_ADDRESS	1
 | |
| #define	ARG_TYPE_STRING		2
 | |
| #define	ARG_TYPE_SERVICE	3
 | |
| #define	ARG_TYPE_SERVER		4
 | |
| #define	ARG_TYPE_DBUSERS	5
 | |
| #define	ARG_TYPE_SESSION	6
 | |
| #define	ARG_TYPE_DCB		7
 | |
| #define	ARG_TYPE_MONITOR	8
 | |
| #define	ARG_TYPE_FILTER		9
 | |
| 
 | |
| /**
 | |
|  * The subcommand structure
 | |
|  *
 | |
|  * These are the options that may be passed to a command
 | |
|  */
 | |
| struct subcommand {
 | |
| 	char	*arg1;
 | |
| 	int	n_args;
 | |
| 	void	(*fn)();
 | |
| 	char	*help;
 | |
| 	char	*devhelp;
 | |
| 	int	arg_types[3];
 | |
| };
 | |
| 
 | |
| static	void	telnetdShowUsers(DCB *);
 | |
| /**
 | |
|  * The subcommands of the show command
 | |
|  */
 | |
| struct subcommand showoptions[] = {
 | |
|         { "dcbs",	0, dprintAllDCBs,
 | |
| 		"Show all descriptor control blocks (network connections)",
 | |
| 		"Show all descriptor control blocks (network connections)",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "dcb",	1, dprintDCB,	
 | |
| 		"Show a single descriptor control block e.g. show dcb 0x493340",
 | |
| 		"Show a single descriptor control block e.g. show dcb 0x493340",
 | |
| 				{ARG_TYPE_DCB, 0, 0} },
 | |
| 	{ "dbusers",	1, dcb_usersPrint,
 | |
| 			"Show statistics and user names for a service's user table.\n\t\tExample : show dbusers <service name>",
 | |
| 			"Show statistics and user names for a service's user table.\n\t\tExample : show dbusers <ptr of 'User's data' from services list>|<service name>",
 | |
| 				{ARG_TYPE_DBUSERS, 0, 0} },
 | |
| 	{ "epoll",	0, dprintPollStats,
 | |
| 			"Show the poll statistics",
 | |
| 			"Show the poll statistics",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "filter",	1, dprintFilter,
 | |
| 			"Show details of a filter, called with a filter name",
 | |
| 			"Show details of a filter, called with the address of a filter",
 | |
| 				{ARG_TYPE_FILTER, 0, 0} },
 | |
| 	{ "filters",	0, dprintAllFilters,
 | |
| 			"Show all filters",
 | |
| 			"Show all filters",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "modules",	0, dprintAllModules,
 | |
| 			"Show all currently loaded modules",
 | |
| 			"Show all currently loaded modules",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "monitor",	1, monitorShow,
 | |
| 			"Show the monitor details",
 | |
| 			"Show the monitor details",
 | |
| 				{ARG_TYPE_MONITOR, 0, 0} },
 | |
| 	{ "monitors",	0, monitorShowAll,
 | |
| 			"Show the monitors that are configured",
 | |
| 			"Show the monitors that are configured",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "server",	1, dprintServer,
 | |
| 			"Show details for a named server, e.g. show server dbnode1",
 | |
| 			"Show details for a server, e.g. show server 0x485390. The address may also be repalced with the server name from the configuration file",
 | |
| 				{ARG_TYPE_SERVER, 0, 0} },
 | |
| 	{ "servers",	0, dprintAllServers,
 | |
| 			"Show all configured servers",
 | |
| 			"Show all configured servers",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "services",	0, dprintAllServices,
 | |
| 			"Show all configured services in MaxScale",
 | |
| 			"Show all configured services in MaxScale",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "service",	1, dprintService,
 | |
| 			"Show a single service in MaxScale, may be passed a service name",
 | |
| 			"Show a single service in MaxScale, may be passed a service name or address of a service object",
 | |
| 				{ARG_TYPE_SERVICE, 0, 0} },
 | |
| 	{ "session",	1, dprintSession,
 | |
| 		 	"Show a single session in MaxScale, e.g. show session 0x284830",
 | |
| 		 	"Show a single session in MaxScale, e.g. show session 0x284830",
 | |
| 				{ARG_TYPE_SESSION, 0, 0} },
 | |
| 	{ "sessions",	0, dprintAllSessions,
 | |
| 		 	"Show all active sessions in MaxScale",
 | |
| 		 	"Show all active sessions in MaxScale",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "threads",	0, dShowThreads,
 | |
| 		 	"Show the status of the polling threads in MaxScale",
 | |
| 		 	"Show the status of the polling threads in MaxScale",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "users",	0, telnetdShowUsers,
 | |
| 			"Show statistics and user names for the debug interface",
 | |
| 			"Show statistics and user names for the debug interface",
 | |
| 				{0, 0, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL,	NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * The subcommands of the list command
 | |
|  */
 | |
| struct subcommand listoptions[] = {
 | |
|         { "clients",	0, dListClients,
 | |
| 		"List all the client connections to MaxScale",
 | |
| 		"List all the client connections to MaxScale",
 | |
| 				{0, 0, 0} },
 | |
|         { "dcbs",	0, dListDCBs,
 | |
| 		"List all the DCBs active within MaxScale",
 | |
| 		"List all the DCBs active within MaxScale",
 | |
| 				{0, 0, 0} },
 | |
|         { "filters",	0, dListFilters,
 | |
| 		"List all the filters defined within MaxScale",
 | |
| 		"List all the filters defined within MaxScale",
 | |
| 				{0, 0, 0} },
 | |
|         { "listeners",	0, dListListeners,
 | |
| 		"List all the listeners defined within MaxScale",
 | |
| 		"List all the listeners defined within MaxScale",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "modules",	0, dprintAllModules,
 | |
| 			"List all currently loaded modules",
 | |
| 			"List all currently loaded modules",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "monitors",	0, monitorList,
 | |
| 			"List all monitors",
 | |
| 			"List all monitors",
 | |
| 				{0, 0, 0} },
 | |
|         { "services",	0, dListServices,
 | |
| 		"List all the services defined within MaxScale",
 | |
| 		"List all the services defined within MaxScale",
 | |
| 				{0, 0, 0} },
 | |
|         { "servers",	0, dListServers,
 | |
| 		"List all the servers defined within MaxScale",
 | |
| 		"List all the servers defined within MaxScale",
 | |
| 				{0, 0, 0} },
 | |
|         { "sessions",	0, dListSessions,
 | |
| 		"List all the active sessions within MaxScale",
 | |
| 		"List all the active sessions within MaxScale",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "threads",	0, dShowThreads,
 | |
| 	 	"List the status of the polling threads in MaxScale",
 | |
| 	 	"List the status of the polling threads in MaxScale",
 | |
| 				{0, 0, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL,	NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| extern void shutdown_server();
 | |
| static void shutdown_service(DCB *dcb, SERVICE *service);
 | |
| static void shutdown_monitor(DCB *dcb, MONITOR *monitor);
 | |
| 
 | |
| /**
 | |
|  * The subcommands of the shutdown command
 | |
|  */
 | |
| struct subcommand shutdownoptions[] = {
 | |
| 	{ "maxscale",
 | |
|           0,
 | |
|           shutdown_server,
 | |
|           "Shutdown MaxScale",
 | |
|           "Shutdown MaxScale",
 | |
|           {0, 0, 0}
 | |
|         },
 | |
| 	{
 | |
|             "monitor",
 | |
|             1,
 | |
|             shutdown_monitor,
 | |
|             "Shutdown a monitor, e.g. shutdown monitor 0x48381e0",
 | |
|             "Shutdown a monitor, e.g. shutdown monitor 0x48381e0",
 | |
|             {ARG_TYPE_MONITOR, 0, 0}
 | |
|         },
 | |
| 	{
 | |
|             "service",
 | |
|             1,
 | |
|             shutdown_service,
 | |
|             "Shutdown a service, e.g. shutdown service \"Sales Database\"",
 | |
|             "Shutdown a service, e.g. shutdown service 0x4838320 or shutdown service \"Sales Database\"",
 | |
|             {ARG_TYPE_SERVICE, 0, 0}
 | |
|         },
 | |
| 	{
 | |
|             NULL,
 | |
|             0,
 | |
|             NULL,
 | |
|             NULL,
 | |
|             NULL,
 | |
|             {0, 0, 0}
 | |
|         }
 | |
| };
 | |
| 
 | |
| 
 | |
| static void restart_service(DCB *dcb, SERVICE *service);
 | |
| static void restart_monitor(DCB *dcb, MONITOR *monitor);
 | |
| /**
 | |
|  * The subcommands of the restart command
 | |
|  */
 | |
| struct subcommand restartoptions[] = {
 | |
| 	{ "monitor",	1, restart_monitor,
 | |
| 		"Restart a monitor, e.g. restart monitor 0x48181e0",
 | |
| 		"Restart a monitor, e.g. restart monitor 0x48181e0",
 | |
| 				{ARG_TYPE_MONITOR, 0, 0} },
 | |
| 	{ "service",	1, restart_service,
 | |
| 		"Restart a service, e.g. restart service \"Test Service\"",
 | |
| 		"Restart a service, e.g. restart service 0x4838320",
 | |
| 				{ARG_TYPE_SERVICE, 0, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL, NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| static void set_server(DCB *dcb, SERVER *server, char *bit);
 | |
| /**
 | |
|  * The subcommands of the set command
 | |
|  */
 | |
| struct subcommand setoptions[] = {
 | |
| 	{ "server",	2, set_server,
 | |
| 		"Set the status of a server. E.g. set server dbnode4 master",
 | |
| 		"Set the status of a server. E.g. set server 0x4838320 master",
 | |
| 				{ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL,	NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| static void clear_server(DCB *dcb, SERVER *server, char *bit);
 | |
| /**
 | |
|  * The subcommands of the clear command
 | |
|  */
 | |
| struct subcommand clearoptions[] = {
 | |
| 	{ "server",	2, clear_server,
 | |
| 		"Clear the status of a server. E.g. clear server dbnode2 master",
 | |
| 		"Clear the status of a server. E.g. clear server 0x4838320 master",
 | |
| 				{ARG_TYPE_SERVER, ARG_TYPE_STRING, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL,	NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| static void reload_dbusers(DCB *dcb, SERVICE *service);
 | |
| static void reload_config(DCB *dcb);
 | |
| 
 | |
| /**
 | |
|  * The subcommands of the reload command
 | |
|  */
 | |
| struct subcommand reloadoptions[] = {
 | |
| 	{ "config",	0, reload_config,
 | |
| 		"Reload the configuration data for MaxScale.",
 | |
| 		"Reload the configuration data for MaxScale.",
 | |
| 				{0, 0, 0} },
 | |
| 	{ "dbusers",	1, reload_dbusers,
 | |
| 		"Reload the dbuser data for a service. E.g. reload dbusers \"splitter service\"",
 | |
| 		"Reload the dbuser data for a service. E.g. reload dbusers 0x849420",
 | |
| 				{ARG_TYPE_SERVICE, 0, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL,	NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| static void enable_log_action(DCB *, char *);
 | |
| static void disable_log_action(DCB *, char *);
 | |
| static void enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor);
 | |
| static void disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor);
 | |
| static void enable_service_root(DCB *dcb, SERVICE *service);
 | |
| static void disable_service_root(DCB *dcb, SERVICE *service);
 | |
| 
 | |
| /**
 | |
|  *  * The subcommands of the enable command
 | |
|  *   */
 | |
| struct subcommand enableoptions[] = {
 | |
|         {
 | |
|                 "heartbeat",
 | |
|                 1,
 | |
|                 enable_monitor_replication_heartbeat,
 | |
|                 "Enable the monitor replication heartbeat, pass a monitor name as argument",
 | |
|                 "Enable the monitor replication heartbeat, pass a monitor name as argument",
 | |
|                 {ARG_TYPE_MONITOR, 0, 0}
 | |
|         },
 | |
|         {
 | |
|                 "log",
 | |
|                 1,
 | |
|                 enable_log_action,
 | |
|                 "Enable Log options for MaxScale, options trace | error | "
 | |
|                 "message E.g. enable log message.",
 | |
|                 "Enable Log options for MaxScale, options trace | error | "
 | |
|                 "message E.g. enable log message.",
 | |
|                 {ARG_TYPE_STRING, 0, 0}
 | |
|         },
 | |
|         {
 | |
|                 "root",
 | |
|                 1,
 | |
|                 enable_service_root,
 | |
|                 "Enable root access to a service, pass a service name to enable root access",
 | |
|                 "Enable root access to a service, pass a service name to enable root access",
 | |
|                 {ARG_TYPE_SERVICE, 0, 0}
 | |
|         },
 | |
|         {
 | |
|                 NULL,
 | |
|                 0,
 | |
|                 NULL,
 | |
|                 NULL,
 | |
|                 NULL,
 | |
|                 {0, 0, 0}
 | |
|         }
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  *  * The subcommands of the disable command
 | |
|  *   */
 | |
| struct subcommand disableoptions[] = {
 | |
|         {
 | |
|                 "heartbeat",
 | |
|                 1,
 | |
|                 disable_monitor_replication_heartbeat,
 | |
|                 "Disable the monitor replication heartbeat",
 | |
|                 "Disable the monitor replication heartbeat",
 | |
|                 {ARG_TYPE_MONITOR, 0, 0}
 | |
|         },
 | |
| 	{
 | |
| 		"log",
 | |
| 		1,
 | |
| 		disable_log_action,
 | |
| 		"Disable Log for MaxScale, Options: debug | trace | error | message "
 | |
| 		"E.g. disable log debug",
 | |
| 		"Disable Log for MaxScale, Options: debug | trace | error | message "
 | |
| 		"E.g. disable log debug",
 | |
| 		{ARG_TYPE_STRING, 0, 0}
 | |
|     	},
 | |
|         {
 | |
|                 "root",
 | |
|                 1,
 | |
|                 disable_service_root,
 | |
|                 "Disable root access to a service",
 | |
|                 "Disable root access to a service",
 | |
|                 {ARG_TYPE_SERVICE, 0, 0}
 | |
|         },
 | |
|     	{
 | |
|             NULL,
 | |
|             0,
 | |
|             NULL,
 | |
|             NULL,
 | |
|             NULL,
 | |
|             {0, 0, 0}
 | |
|     	}
 | |
| };
 | |
| 
 | |
| #if defined(SS_DEBUG)
 | |
| 
 | |
| static void fail_backendfd(void);
 | |
| static void fail_clientfd(void);
 | |
| static void fail_accept(DCB* dcb, char* arg1, char* arg2);
 | |
| /**
 | |
|  *  * The subcommands of the fail command
 | |
|  *   */
 | |
| struct subcommand failoptions[] = {
 | |
|     {
 | |
|         "backendfd",
 | |
|         0,
 | |
|         fail_backendfd,
 | |
|         "Fail backend socket for next operation.",
 | |
|         "Fail backend socket for next operation.",
 | |
|         {ARG_TYPE_STRING, 0, 0}
 | |
|     },
 | |
|     {
 | |
|         "clientfd",
 | |
|         0,
 | |
|         fail_clientfd,
 | |
|         "Fail client socket for next operation.",
 | |
|         "Fail client socket for next operation.",
 | |
|         {ARG_TYPE_STRING, 0, 0}
 | |
|     },
 | |
|     {
 | |
|         "accept",
 | |
|         2,
 | |
|         fail_accept,
 | |
|         "Fail to accept next client connection.",
 | |
|         "Fail to accept next client connection.",
 | |
|         {ARG_TYPE_STRING, ARG_TYPE_STRING, 0}
 | |
|     },
 | |
|     {
 | |
|         NULL,
 | |
|         0,
 | |
|         NULL,
 | |
|         NULL,
 | |
|         NULL,
 | |
|         {0, 0, 0}
 | |
|     }
 | |
| };
 | |
| #endif /* SS_DEBUG */
 | |
| 
 | |
| static void telnetdAddUser(DCB *, char *, char *);
 | |
| /**
 | |
|  * The subcommands of the add command
 | |
|  */
 | |
| struct subcommand addoptions[] = {
 | |
| 	{ "user",	2, telnetdAddUser,
 | |
| 		"Add a new user for the debug interface. E.g. add user john today",
 | |
| 		"Add a new user for the debug interface. E.g. add user john today",
 | |
| 				{ARG_TYPE_STRING, ARG_TYPE_STRING, 0} },
 | |
| 	{ NULL,		0, NULL,		NULL,	NULL,
 | |
| 				{0, 0, 0} }
 | |
| };
 | |
| 
 | |
| 
 | |
| static void telnetdRemoveUser(DCB *, char *, char *);
 | |
| /**
 | |
|  * The subcommands of the remove command
 | |
|  */
 | |
| struct subcommand removeoptions[] = {
 | |
| 	{
 | |
|             "user",
 | |
|             2,
 | |
|             telnetdRemoveUser,
 | |
|             "Remove existing maxscale user. Example : remove user john johnpwd",
 | |
|             "Remove existing maxscale user. Example : remove user john johnpwd",
 | |
|             {ARG_TYPE_STRING, ARG_TYPE_STRING, 0}
 | |
|         },
 | |
| 	{
 | |
|             NULL, 0, NULL, NULL, NULL, {0, 0, 0}
 | |
|         }
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * The debug command table
 | |
|  */
 | |
| static struct {
 | |
| 	char			*cmd;
 | |
| 	struct	subcommand	*options;
 | |
| } cmds[] = {
 | |
| 	{ "add",	addoptions },
 | |
| 	{ "clear",	clearoptions },
 | |
| 	{ "disable",    disableoptions },
 | |
| 	{ "enable",     enableoptions },
 | |
| #if defined(SS_DEBUG)
 | |
|         { "fail",       failoptions },
 | |
| #endif
 | |
| 	{ "list",	listoptions },
 | |
| 	{ "reload",	reloadoptions },
 | |
|         { "remove",     removeoptions },
 | |
| 	{ "restart",	restartoptions },
 | |
| 	{ "set",	setoptions },
 | |
| 	{ "show",	showoptions },
 | |
| 	{ "shutdown",	shutdownoptions },
 | |
| 	{ NULL,		NULL	}
 | |
| };
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Convert a string argument to a numeric, observing prefixes
 | |
|  * for number bases, e.g. 0x for hex, 0 for octal
 | |
|  *
 | |
|  * @param mode		The CLI mode
 | |
|  * @param arg		The string representation of the argument
 | |
|  * @param arg_type	The target type for the argument
 | |
|  * @return The argument as a long integer
 | |
|  */
 | |
| static unsigned long
 | |
| convert_arg(int mode, char *arg, int arg_type)
 | |
| {
 | |
| unsigned long	rval;
 | |
| SERVICE		*service;
 | |
| 
 | |
| 	switch (arg_type)
 | |
| 	{
 | |
| 	case ARG_TYPE_ADDRESS:
 | |
| 		return (unsigned long)strtol(arg, NULL, 0);
 | |
| 	case ARG_TYPE_STRING:
 | |
| 		return (unsigned long)arg;
 | |
| 	case ARG_TYPE_SERVICE:
 | |
| 		if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
 | |
| 			rval = (unsigned long)service_find(arg);
 | |
| 		return rval;
 | |
| 	case ARG_TYPE_SERVER:
 | |
| 		if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
 | |
| 			rval = (unsigned long)server_find_by_unique_name(arg);
 | |
| 		return rval;
 | |
| 	case ARG_TYPE_DBUSERS:
 | |
| 		if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
 | |
| 		{
 | |
| 			service = service_find(arg);
 | |
| 			if (service)
 | |
| 				return (unsigned long)(service->users);
 | |
| 			else
 | |
| 				return 0;
 | |
| 		}
 | |
| 		return rval;
 | |
| 	case ARG_TYPE_DCB:
 | |
| 		rval = (unsigned long)strtol(arg, NULL, 0);
 | |
| 		if (mode == CLIM_USER && dcb_isvalid((DCB *)rval) == 0)
 | |
| 			rval = 0;
 | |
| 		return rval;
 | |
| 	case ARG_TYPE_SESSION:
 | |
| 		rval = (unsigned long)strtol(arg, NULL, 0);
 | |
| 		if (mode == CLIM_USER && session_isvalid((SESSION *)rval) == 0)
 | |
| 			rval = 0;
 | |
| 		return rval;
 | |
| 	case ARG_TYPE_MONITOR:
 | |
| 		if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
 | |
| 			rval = (unsigned long)monitor_find(arg);
 | |
| 		return rval;
 | |
| 	case ARG_TYPE_FILTER:
 | |
| 		if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
 | |
| 			rval = (unsigned long)filter_find(arg);
 | |
| 		return rval;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * We have a complete line from the user, lookup the commands and execute them
 | |
|  *
 | |
|  * Commands are tokenised based on white space and then the first
 | |
|  * word is checked againts the cmds table. If a match is found the
 | |
|  * second word is compared to the different options for that command.
 | |
|  *
 | |
|  * Commands may also take up to 3 additional arguments, these are all
 | |
|  * assumed to the numeric values and will be converted before being passed
 | |
|  * to the handler function for the command.
 | |
|  *
 | |
|  * @param cli		The CLI_SESSION
 | |
|  * @return	Returns 0 if the interpreter should exit
 | |
|  */
 | |
| int
 | |
| execute_cmd(CLI_SESSION *cli)
 | |
| {
 | |
| DCB		*dcb = cli->session->client;
 | |
| int		argc, i, j, found = 0;
 | |
| char		*args[MAXARGS + 1];
 | |
| unsigned long	arg1, arg2, arg3;
 | |
| int		in_quotes = 0, escape_next = 0;
 | |
| char		*ptr, *lptr;
 | |
| 
 | |
| 	args[0] = cli->cmdbuf;
 | |
| 	ptr = args[0];
 | |
| 	lptr = ptr;
 | |
| 	i = 0;
 | |
| 	/*
 | |
| 	 * Break the command line into a number of words. Whitespace is used
 | |
| 	 * to delimit words and may be escaped by use of the \ character or
 | |
| 	 * the use of double quotes.
 | |
| 	 * The array args contains the broken down words, one per index.
 | |
| 	 */
 | |
| 	while (*ptr)
 | |
| 	{
 | |
| 		if (escape_next)
 | |
| 		{
 | |
| 			*lptr++ = *ptr++;
 | |
| 			escape_next = 0;
 | |
| 		}
 | |
| 		else if (*ptr == '\\')
 | |
| 		{
 | |
| 			escape_next = 1;
 | |
| 			ptr++;
 | |
| 		}
 | |
| 		else if (in_quotes == 0 && (*ptr == ' ' || *ptr == '\t' || *ptr == '\r' || *ptr == '\n'))
 | |
| 		{
 | |
| 			*lptr = 0;
 | |
| 			if (args[i] == ptr)
 | |
| 				args[i] = ptr + 1;
 | |
| 			else
 | |
| 			{
 | |
| 				i++;
 | |
| 				if (i >= MAXARGS)
 | |
| 					break;
 | |
| 				args[i] = ptr + 1;
 | |
| 			}
 | |
| 			ptr++;
 | |
| 			lptr++;
 | |
| 		}
 | |
| 		else if (*ptr == '\"' && in_quotes == 0)
 | |
| 		{
 | |
| 			in_quotes = 1;
 | |
| 			ptr++;
 | |
| 		}
 | |
| 		else if (*ptr == '\"' && in_quotes == 1)
 | |
| 		{
 | |
| 			in_quotes = 0;
 | |
| 			ptr++;
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			*lptr++ = *ptr++;
 | |
| 		}
 | |
| 	}
 | |
| 	*lptr = 0;
 | |
| 	args[i+1] = NULL;
 | |
| 
 | |
| 	if (args[0] == NULL || *args[0] == 0)
 | |
| 		return 1;
 | |
| 	for (i = 0; args[i] && *args[i]; i++)
 | |
| 		;
 | |
| 	argc = i - 2;	/* The number of extra arguments to commands */
 | |
| 	
 | |
| 
 | |
| 	if (!strcasecmp(args[0], "help"))
 | |
| 	{
 | |
| 		if (args[1] == NULL || *args[1] == 0)
 | |
| 		{
 | |
| 			found = 1;
 | |
| 			dcb_printf(dcb, "Available commands:\n");
 | |
| 			for (i = 0; cmds[i].cmd; i++)
 | |
| 			{
 | |
| 				if (cmds[i].options[1].arg1 == NULL)
 | |
| 					dcb_printf(dcb, "    %s %s\n", cmds[i].cmd, cmds[i].options[0].arg1);
 | |
| 				else
 | |
| 				{
 | |
| 					dcb_printf(dcb, "    %s [", cmds[i].cmd);
 | |
| 					for (j = 0; cmds[i].options[j].arg1; j++)
 | |
| 					{
 | |
| 						dcb_printf(dcb, "%s%s", cmds[i].options[j].arg1,
 | |
| 							cmds[i].options[j+1].arg1 ? "|" : "");
 | |
| 					}
 | |
| 					dcb_printf(dcb, "]\n");
 | |
| 				}
 | |
| 			}
 | |
| 			dcb_printf(dcb, "\nType help command to see details of each command.\n");
 | |
| 			dcb_printf(dcb, "Where commands require names as arguments and these names contain\n");
 | |
| 			dcb_printf(dcb, "whitespace either the \\ character may be used to escape the whitespace\n");
 | |
| 			dcb_printf(dcb, "or the name may be enclosed in double quotes \".\n\n");
 | |
| 		}
 | |
| 		else
 | |
| 		{
 | |
| 			for (i = 0; cmds[i].cmd; i++)
 | |
| 			{
 | |
| 				if (!strcasecmp(args[1], cmds[i].cmd))
 | |
| 				{
 | |
| 					found = 1;
 | |
| 					dcb_printf(dcb, "Available options to the %s command:\n", args[1]);
 | |
| 					for (j = 0; cmds[i].options[j].arg1; j++)
 | |
| 					{
 | |
| 						dcb_printf(dcb, "    %-10s %s\n", cmds[i].options[j].arg1,
 | |
| 										cmds[i].options[j].help);
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			if (found == 0)
 | |
| 			{
 | |
| 				dcb_printf(dcb, "No command %s to offer help with\n", args[1]);
 | |
| 			}
 | |
| 		}
 | |
| 		found = 1;
 | |
| 	}
 | |
| 	else if (!strcasecmp(args[0], "quit"))
 | |
| 	{
 | |
| 		return 0;
 | |
| 	}
 | |
| 	else if (argc >= 0)
 | |
| 	{
 | |
| 		for (i = 0; cmds[i].cmd; i++)
 | |
| 		{
 | |
| 			if (strcasecmp(args[0], cmds[i].cmd) == 0)
 | |
| 			{
 | |
| 				for (j = 0; cmds[i].options[j].arg1; j++)
 | |
| 				{
 | |
| 					if (strcasecmp(args[1], cmds[i].options[j].arg1) == 0)
 | |
| 					{
 | |
|                                         	found = 1; /**< command and sub-command match */
 | |
| 						if (argc != cmds[i].options[j].n_args)
 | |
| 						{
 | |
| 							dcb_printf(dcb, "Incorrect number of arguments: %s %s expects %d arguments\n",
 | |
| 								cmds[i].cmd, cmds[i].options[j].arg1,
 | |
| 								cmds[i].options[j].n_args);
 | |
| 							
 | |
| 						}
 | |
| 						else
 | |
| 						{
 | |
| 							switch (cmds[i].options[j].n_args)
 | |
| 							{
 | |
| 							case 0:
 | |
| 								cmds[i].options[j].fn(dcb);
 | |
| 								break;
 | |
| 							case 1:
 | |
| 								arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]);
 | |
| 								if (arg1)
 | |
| 									cmds[i].options[j].fn(dcb, arg1);
 | |
| 								else
 | |
| 									dcb_printf(dcb, "Invalid argument: %s\n",
 | |
| 										args[2]);
 | |
| 								break;
 | |
| 							case 2:
 | |
| 								arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]);
 | |
| 								arg2 = convert_arg(cli->mode, args[3],cmds[i].options[j].arg_types[1]);
 | |
| 								if (arg1 && arg2)
 | |
| 									cmds[i].options[j].fn(dcb, arg1, arg2);
 | |
| 								else if (arg1 == 0)
 | |
| 									dcb_printf(dcb, "Invalid argument: %s\n",
 | |
| 										args[2]);
 | |
| 								else
 | |
| 									dcb_printf(dcb, "Invalid argument: %s\n",
 | |
| 										args[3]);
 | |
| 								break;
 | |
| 							case 3:
 | |
| 								arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]);
 | |
| 								arg2 = convert_arg(cli->mode, args[3],cmds[i].options[j].arg_types[1]);
 | |
| 								arg3 = convert_arg(cli->mode, args[4],cmds[i].options[j].arg_types[2]);
 | |
| 								if (arg1 && arg2 && arg3)
 | |
| 									cmds[i].options[j].fn(dcb, arg1, arg2, arg3);
 | |
| 								else if (arg1 == 0)
 | |
| 									dcb_printf(dcb, "Invalid argument: %s\n",
 | |
| 										args[2]);
 | |
| 								else if (arg2 == 0)
 | |
| 									dcb_printf(dcb, "Invalid argument: %s\n",
 | |
| 										args[3]);
 | |
| 								else if (arg3 == 0)
 | |
| 									dcb_printf(dcb, "Invalid argument: %s\n",
 | |
| 										args[4]);
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 				if (!found)
 | |
| 				{
 | |
| 					dcb_printf(dcb,
 | |
| 						"Unknown or missing option for the %s command. Valid sub-commands are:\n",
 | |
| 							cmds[i].cmd);
 | |
| 					for (j = 0; cmds[i].options[j].arg1; j++)
 | |
| 					{
 | |
| 						dcb_printf(dcb, "    %-10s %s\n", cmds[i].options[j].arg1,
 | |
| 										cmds[i].options[j].help);
 | |
| 					}
 | |
| 					found = 1;
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	else if (argc == -1)
 | |
| 	{
 | |
| 		dcb_printf(dcb,
 | |
| 			"Commands must consist of at least two words. Type help for a list of commands\n");
 | |
| 		found = 1;
 | |
| 	}
 | |
| 	if (!found)
 | |
| 		dcb_printf(dcb,
 | |
| 			"Command '%s' not known, type help for a list of available commands\n", args[0]);
 | |
| 	memset(cli->cmdbuf, 0, 80);
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Debug command to stop a service
 | |
|  *
 | |
|  * @param dcb		The DCB to print any output to
 | |
|  * @param service	The service to shutdown
 | |
|  */
 | |
| static void
 | |
| shutdown_service(DCB *dcb, SERVICE *service)
 | |
| {
 | |
| 	serviceStop(service);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Debug command to restart a stopped service
 | |
|  *
 | |
|  * @param dcb		The DCB to print any output to
 | |
|  * @param service	The service to restart
 | |
|  */
 | |
| static void
 | |
| restart_service(DCB *dcb, SERVICE *service)
 | |
| {
 | |
| 	serviceRestart(service);
 | |
| }
 | |
| 
 | |
| static struct {
 | |
| 	char		*str;
 | |
| 	unsigned int	bit;
 | |
| } ServerBits[] = {
 | |
| 	{ "running", 		SERVER_RUNNING },
 | |
| 	{ "master",		SERVER_MASTER },
 | |
| 	{ "slave",		SERVER_SLAVE },
 | |
| 	{ "synced",		SERVER_JOINED },
 | |
| 	{ "ndb",		SERVER_NDB },
 | |
| 	{ "maintenance",	SERVER_MAINT },
 | |
| 	{ "maint",		SERVER_MAINT },
 | |
| 	{ NULL,			0 }
 | |
| };
 | |
| /**
 | |
|  * Map the server status bit
 | |
|  *
 | |
|  * @param str	String representation
 | |
|  * @return bit value or 0 on error
 | |
|  */
 | |
| static unsigned int
 | |
| server_map_status(char *str)
 | |
| {
 | |
| int i;
 | |
| 
 | |
| 	for (i = 0; ServerBits[i].str; i++)
 | |
| 		if (!strcasecmp(str, ServerBits[i].str))
 | |
| 			return ServerBits[i].bit;
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Set the status bit of a server
 | |
|  *
 | |
|  * @param dcb		DCB to send output to
 | |
|  * @param server	The server to set the status of
 | |
|  * @param bit		String representation of the status bit
 | |
|  */
 | |
| static void
 | |
| set_server(DCB *dcb, SERVER *server, char *bit)
 | |
| {
 | |
| unsigned int bitvalue;
 | |
| 
 | |
| 	if ((bitvalue = server_map_status(bit)) != 0)
 | |
| 		server_set_status(server, bitvalue);
 | |
| 	else
 | |
| 		dcb_printf(dcb, "Unknown status bit %s\n", bit);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Clear the status bit of a server
 | |
|  *
 | |
|  * @param dcb		DCB to send output to
 | |
|  * @param server	The server to set the status of
 | |
|  * @param bit		String representation of the status bit
 | |
|  */
 | |
| static void
 | |
| clear_server(DCB *dcb, SERVER *server, char *bit)
 | |
| {
 | |
| unsigned int bitvalue;
 | |
| 
 | |
| 	if ((bitvalue = server_map_status(bit)) != 0)
 | |
| 		server_clear_status(server, bitvalue);
 | |
| 	else
 | |
| 		dcb_printf(dcb, "Unknown status bit %s\n", bit);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Reload the authenticaton data from the backend database of a service.
 | |
|  *
 | |
|  * @param dcb		DCB to send output
 | |
|  * @param service	The service to update
 | |
|  */
 | |
| static void
 | |
| reload_dbusers(DCB *dcb, SERVICE *service)
 | |
| {
 | |
| 	dcb_printf(dcb, "Loaded %d database users for service %s.\n",
 | |
| 			reload_mysql_users(service), service->name);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Relaod the configuration data from the config file
 | |
|  *
 | |
|  * @param dcb		DCB to use to send output
 | |
|  */
 | |
| static void
 | |
| reload_config(DCB *dcb)
 | |
| {
 | |
| 	dcb_printf(dcb, "Reloading configuration from file.\n");
 | |
| 	config_reload();
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Add a new maxscale admin user
 | |
|  *
 | |
|  * @param dcb		The DCB for messages
 | |
|  * @param user		The user name
 | |
|  * @param passwd	The Password of the user
 | |
|  */
 | |
| static void
 | |
| telnetdAddUser(DCB *dcb, char *user, char *passwd)
 | |
| {
 | |
| char	*err;
 | |
| 
 | |
| 	if (admin_search_user(user))
 | |
| 	{
 | |
| 		dcb_printf(dcb, "User %s already exists.\n", user);
 | |
| 		return;
 | |
| 	}
 | |
| 	if ((err = admin_add_user(user, passwd)) == NULL)
 | |
| 		dcb_printf(dcb, "User %s has been successfully added.\n", user);
 | |
| 	else
 | |
| 		dcb_printf(dcb, "Failed to add new user. %s\n", err);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Remove a maxscale admin user
 | |
|  *
 | |
|  * @param dcb		The DCB for messages
 | |
|  * @param user		The user name
 | |
|  * @param passwd	The Password of the user
 | |
|  */
 | |
| static void telnetdRemoveUser(
 | |
|         DCB*  dcb,
 | |
|         char* user,
 | |
|         char* passwd)
 | |
| {
 | |
|         char* err;
 | |
| 
 | |
| 	if (!admin_search_user(user))
 | |
|         {
 | |
|             dcb_printf(dcb, "User %s doesn't exist.\n", user);
 | |
|             return;
 | |
|         }
 | |
|         
 | |
| 	if ((err = admin_remove_user(user, passwd)) == NULL)
 | |
|         {
 | |
|             dcb_printf(dcb, "User %s has been successfully removed.\n", user);
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             dcb_printf(dcb, "Failed to remove user %s. %s\n", user, err);
 | |
|         }
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Print the adminsitration users
 | |
|  *
 | |
|  * @param dcb	The DCB to print the user data to
 | |
|  */
 | |
| static void
 | |
| telnetdShowUsers(DCB *dcb)
 | |
| {
 | |
| 	dcb_printf(dcb, "Administration interface users:\n");
 | |
| 	dcb_PrintAdminUsers(dcb);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Command to shutdown a running monitor
 | |
|  *
 | |
|  * @param dcb	The DCB to use to print messages
 | |
|  * @param monitor	The monitor to shutdown
 | |
|  */
 | |
| static void
 | |
| shutdown_monitor(DCB *dcb, MONITOR *monitor)
 | |
| {
 | |
| 	monitorStop(monitor);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Command to restart a stopped monitor
 | |
|  *
 | |
|  * @param dcb	The DCB to use to print messages
 | |
|  * @param monitor	The monitor to restart
 | |
|  */
 | |
| static void
 | |
| restart_monitor(DCB *dcb, MONITOR *monitor)
 | |
| {
 | |
| 	monitorStart(monitor);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Enable replication heartbeat for a monitor
 | |
|  *
 | |
|  * @param dcb		Connection to user interface
 | |
|  * @param monitor	The monitor
 | |
|  */
 | |
| static void
 | |
| enable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor)
 | |
| {
 | |
| 	monitorSetReplicationHeartbeat(monitor, 1);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Disable replication heartbeat for a monitor
 | |
|  *
 | |
|  * @param dcb		Connection to user interface
 | |
|  * @param monitor	The monitor
 | |
|  */
 | |
| static void
 | |
| disable_monitor_replication_heartbeat(DCB *dcb, MONITOR *monitor)
 | |
| {
 | |
| 	monitorSetReplicationHeartbeat(monitor, 0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Enable root access to a service
 | |
|  *
 | |
|  * @param dcb		Connection to user interface
 | |
|  * @param service	The service
 | |
|  */
 | |
| static void
 | |
| enable_service_root(DCB *dcb, SERVICE *service)
 | |
| {
 | |
| 	serviceEnableRootUser(service, 1);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Disable root access to a service
 | |
|  *
 | |
|  * @param dcb		Connection to user interface
 | |
|  * @param service	The service
 | |
|  */
 | |
| static void
 | |
| disable_service_root(DCB *dcb, SERVICE *service)
 | |
| {
 | |
| 	serviceEnableRootUser(service, 0);
 | |
| }
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * The log enable action
 | |
|  */
 | |
| 
 | |
| static void enable_log_action(DCB *dcb, char *arg1) {
 | |
|         logfile_id_t type;
 | |
|         int max_len = strlen("message");
 | |
| 
 | |
|         if (strncmp(arg1, "debug", max_len) == 0) {
 | |
|                 type = LOGFILE_DEBUG;
 | |
|         } else if (strncmp(arg1, "trace", max_len) == 0) {
 | |
|                 type = LOGFILE_TRACE;
 | |
|         } else if (strncmp(arg1, "error", max_len) == 0) {
 | |
|                 type = LOGFILE_ERROR;
 | |
|         } else if (strncmp(arg1, "message", max_len) == 0) {
 | |
|                 type = LOGFILE_MESSAGE;
 | |
|         } else {
 | |
|                 dcb_printf(dcb, "%s is not supported for enable log\n", arg1);
 | |
|                 return ;
 | |
|         }
 | |
| 
 | |
|         skygw_log_enable(type);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * The log disable action
 | |
|  */
 | |
| 
 | |
| static void disable_log_action(DCB *dcb, char *arg1) {
 | |
|         logfile_id_t type;
 | |
|         int max_len = strlen("message");
 | |
| 
 | |
|         if (strncmp(arg1, "debug", max_len) == 0) {
 | |
|                 type = LOGFILE_DEBUG;
 | |
|         } else if (strncmp(arg1, "trace", max_len) == 0) {
 | |
|                 type = LOGFILE_TRACE;
 | |
|         } else if (strncmp(arg1, "error", max_len) == 0) {
 | |
|                 type = LOGFILE_ERROR;
 | |
|         } else if (strncmp(arg1, "message", max_len) == 0) {
 | |
|                 type = LOGFILE_MESSAGE;
 | |
|         } else {
 | |
|                 dcb_printf(dcb, "%s is not supported for disable log\n", arg1);
 | |
|                 return ;
 | |
|         }
 | |
| 
 | |
|         skygw_log_disable(type);
 | |
| }
 | |
| 
 | |
| #if defined(SS_DEBUG)
 | |
| static void fail_backendfd(void)
 | |
| { 
 | |
|         fail_next_backend_fd = true;
 | |
| }
 | |
| 
 | |
| static void fail_clientfd(void)
 | |
| { 
 | |
|         fail_next_client_fd = true;
 | |
| }
 | |
| 
 | |
| static void fail_accept(
 | |
|         DCB*  dcb,
 | |
|         char* arg1,
 | |
|         char* arg2)
 | |
| {
 | |
|         int failcount = MIN(atoi(arg2), 100);
 | |
|         fail_accept_errno = atoi(arg1);
 | |
| 
 | |
| 
 | |
|         switch(fail_accept_errno) {
 | |
|                 case EAGAIN:
 | |
| //                case EWOULDBLOCK:
 | |
|                 case EBADF:
 | |
|                 case EINTR:
 | |
|                 case EINVAL:
 | |
|                 case EMFILE:
 | |
|                 case ENFILE:
 | |
|                 case ENOTSOCK:
 | |
|                 case EOPNOTSUPP:
 | |
|                 case ENOBUFS:
 | |
|                 case ENOMEM:
 | |
|                 case EPROTO:
 | |
|                         fail_next_accept = failcount;
 | |
|         break;
 | |
| 
 | |
|                 default:
 | |
|                         dcb_printf(dcb,
 | |
|                                    "[%d, %s] is not valid errno for accept.\n",
 | |
|                                    fail_accept_errno,
 | |
|                                    strerror(fail_accept_errno));
 | |
|                 return ;
 | |
|         }
 | |
| }
 | |
| #endif
 | 
