merge from branch 'cenh'

merge from branch ‘cenh’
This commit is contained in:
MassimilianoPinto
2014-05-26 12:15:34 +02:00
21 changed files with 778 additions and 124 deletions

View File

@ -49,6 +49,8 @@ MODULES= libdebugcli.so libreadconnroute.so libtestroute.so
all: $(MODULES)
(cd readwritesplit; make )
(cd binlog; make )
libtestroute.so: $(TESTOBJ)
$(CC) $(LDFLAGS) $(TESTOBJ) $(LIBS) -o $@
@ -68,20 +70,24 @@ libreadwritesplit.so:
clean:
rm -f $(OBJ) $(MODULES)
(cd readwritesplit; touch depend.mk; make clean)
(cd binlog; touch depend.mk; make clean)
tags:
ctags $(SRCS) $(HDRS)
(cd readwritesplit; make tags)
(cd binlog; make tags)
depend:
@rm -f depend.mk
cc -M $(CFLAGS) $(SRCS) > depend.mk
(cd readwritesplit; touch depend.mk ; make depend)
(cd binlog; touch depend.mk ; make depend)
install: $(MODULES)
install -D $(MODULES) $(DEST)/MaxScale/modules
(cd readwritesplit; make DEST=$(DEST) install)
cleantests:
$(MAKE) -C readwritesplit/test cleantests
$(MAKE) -C test cleantests

View File

@ -45,7 +45,7 @@
extern int lm_enabled_logfiles_bitmask;
static char *version_str = "V1.0.1";
static char *version_str = "V1.1.0";
/* The router entry points */
static ROUTER *createInstance(SERVICE *service, char **options);
@ -127,6 +127,7 @@ static ROUTER *
createInstance(SERVICE *service, char **options)
{
CLI_INSTANCE *inst;
int i;
if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL)
return NULL;
@ -134,7 +135,29 @@ CLI_INSTANCE *inst;
inst->service = service;
spinlock_init(&inst->lock);
inst->sessions = NULL;
inst->mode = CLIM_USER;
if (options)
{
for (i = 0; options[i]; i++)
{
if (!strcasecmp(options[i], "developer"))
{
inst->mode = CLIM_DEVELOPER;
}
else if (!strcasecmp(options[i], "user"))
{
inst->mode = CLIM_USER;
}
else
{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"Unknown option for CLI '%s'\n",
options[i])));
}
}
}
/*
* We have completed the creation of the instance data, so now
@ -176,11 +199,15 @@ CLI_SESSION *client;
spinlock_release(&inst->lock);
session->state = SESSION_STATE_READY;
client->mode = inst->mode;
dcb_printf(session->client, "Welcome the SkySQL MaxScale Debug Interface (%s).\n",
version_str);
dcb_printf(session->client, "WARNING: This interface is meant for developer usage,\n");
dcb_printf(session->client, "passing incorrect addresses to commands can endanger your MaxScale server.\n\n");
if (client->mode == CLIM_DEVELOPER)
{
dcb_printf(session->client, "WARNING: This interface is meant for developer usage,\n");
dcb_printf(session->client, "passing incorrect addresses to commands can endanger your MaxScale server.\n\n");
}
dcb_printf(session->client, "Type help for a list of available commands.\n\n");
return (void *)client;
@ -281,4 +308,4 @@ static uint8_t getCapabilities(
void* router_session)
{
return 0;
}
}

View File

@ -36,7 +36,10 @@
* Date Who Description
* 20/06/13 Mark Riddoch Initial implementation
* 17/07/13 Mark Riddoch Additional commands
* 09/08/2013 Massimiliano Pinto Addes enable/disable commands (now only for log)
* 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
*
* @endverbatim
*/
@ -68,6 +71,13 @@
#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
/**
* The subcommand structure
*
@ -78,6 +88,7 @@ struct subcommand {
int n_args;
void (*fn)();
char *help;
char *devhelp;
int arg_types[3];
};
@ -86,31 +97,59 @@ static void telnetdShowUsers(DCB *);
* The subcommands of the show command
*/
struct subcommand showoptions[] = {
{ "dcbs", 0, dprintAllDCBs, "Show all descriptor control blocks (network connections)",
{ "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",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "dbusers", 1, dcb_usersPrint, "Show statistics and user names for a service's user table.\n\t\tExample : show dbusers <ptr of 'User's data' from services list>",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "epoll", 0, dprintPollStats, "Show the poll statistics",
{ "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} },
{ "modules", 0, dprintAllModules, "Show all currently loaded modules",
{ "modules", 0, dprintAllModules,
"Show all currently loaded modules",
"Show all currently loaded modules",
{0, 0, 0} },
{ "monitors", 0, monitorShowAll, "Show the monitors that are configured",
{ "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 server, e.g. show server 0x485390",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "servers", 0, dprintAllServers, "Show all configured servers",
{ "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",
{ "services", 0, dprintAllServices,
"Show all configured services in MaxScale",
"Show all configured services in MaxScale",
{0, 0, 0} },
{ "session", 1, dprintSession, "Show a single session in MaxScale, e.g. show session 0x284830",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "sessions", 0, dprintAllSessions, "Show all active sessions in MaxScale",
{ "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} },
{ "users", 0, telnetdShowUsers, "Show statistics and user names for the debug interface",
{ARG_TYPE_ADDRESS, 0, 0} },
{ NULL, 0, NULL, NULL,
{ "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} }
};
@ -126,7 +165,7 @@ struct subcommand shutdownoptions[] = {
0,
shutdown_server,
"Shutdown MaxScale",
"Shutdown MaxScale",
{0, 0, 0}
},
{
@ -134,20 +173,23 @@ struct subcommand shutdownoptions[] = {
1,
shutdown_monitor,
"Shutdown a monitor, e.g. shutdown monitor 0x48381e0",
{ARG_TYPE_ADDRESS, 0, 0}
"Shutdown a monitor, e.g. shutdown monitor 0x48381e0",
{ARG_TYPE_MONITOR, 0, 0}
},
{
"service",
1,
shutdown_service,
"Shutdown a service, e.g. shutdown service 0x4838320",
{ARG_TYPE_ADDRESS, 0, 0}
"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}
}
};
@ -159,11 +201,15 @@ 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",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "service", 1, restart_service, "Restart a service, e.g. restart service 0x4838320",
{ARG_TYPE_ADDRESS, 0, 0} },
{ NULL, 0, NULL, NULL,
{ "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} }
};
@ -172,9 +218,11 @@ 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 0x4838320 master",
{ARG_TYPE_ADDRESS, ARG_TYPE_STRING, 0} },
{ NULL, 0, NULL, NULL,
{ "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} }
};
@ -183,9 +231,11 @@ 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 0x4838320 master",
{ARG_TYPE_ADDRESS, ARG_TYPE_STRING, 0} },
{ NULL, 0, NULL, NULL,
{ "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} }
};
@ -196,11 +246,15 @@ 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.",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "dbusers", 1, reload_dbusers, "Reload the dbuser data for a service. E.g. reload dbusers 0x849420",
{ARG_TYPE_ADDRESS, 0, 0} },
{ NULL, 0, NULL, NULL,
{ "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_DBUSERS, 0, 0} },
{ NULL, 0, NULL, NULL, NULL,
{0, 0, 0} }
};
@ -217,6 +271,8 @@ struct subcommand enableoptions[] = {
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}
},
{
@ -224,6 +280,7 @@ struct subcommand enableoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -239,6 +296,8 @@ struct subcommand disableoptions[] = {
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}
},
{
@ -246,6 +305,7 @@ struct subcommand disableoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -264,6 +324,7 @@ struct subcommand failoptions[] = {
0,
fail_backendfd,
"Fail backend socket for next operation.",
"Fail backend socket for next operation.",
{ARG_TYPE_STRING, 0, 0}
},
{
@ -271,6 +332,7 @@ struct subcommand failoptions[] = {
0,
fail_clientfd,
"Fail client socket for next operation.",
"Fail client socket for next operation.",
{ARG_TYPE_STRING, 0, 0}
},
{
@ -278,6 +340,7 @@ struct subcommand failoptions[] = {
2,
fail_accept,
"Fail to accept next client connection.",
"Fail to accept next client connection.",
{ARG_TYPE_STRING, ARG_TYPE_STRING, 0}
},
{
@ -285,6 +348,7 @@ struct subcommand failoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -295,9 +359,11 @@ 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",
{ "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, NULL, NULL, NULL,
{0, 0, 0} }
};
@ -312,10 +378,11 @@ struct subcommand removeoptions[] = {
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, {0, 0, 0}
NULL, 0, NULL, NULL, NULL, {0, 0, 0}
}
};
@ -348,19 +415,59 @@ static struct {
* 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(char *arg, int arg_type)
convert_arg(int mode, char *arg, int arg_type)
{
unsigned long rval;
SERVICE *service;
switch (arg_type)
{
case ARG_TYPE_SERVICE:
if ((rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
rval = (unsigned long)service_find(arg);
return rval;
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;
}
return 0;
}
@ -387,33 +494,94 @@ int argc, i, j, found = 0;
char *args[MAXARGS];
char *saveptr, *delim = " \t\r\n";
unsigned long arg1, arg2, arg3;
int in_quotes = 0, escape_next = 0;
char *ptr, *lptr;
/* Tokenize the input string */
args[0] = strtok_r(cli->cmdbuf, delim, &saveptr);
args[0] = cli->cmdbuf;
ptr = args[0];
lptr = ptr;
i = 0;
do {
i++;
args[i] = strtok_r(NULL, delim, &saveptr);
} while (args[i] != NULL && i < MAXARGS);
/*
* 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)
if (args[0] == NULL || *args[0] == 0)
return 1;
argc = i - 2; /* The number of extra arguments to commands */
if (!strcasecmp(args[0], "help"))
{
if (args[1] == NULL)
if (args[1] == NULL || *args[1] == 0)
{
found = 1;
dcb_printf(dcb, "Available commands:\n");
for (i = 0; cmds[i].cmd; i++)
{
for (j = 0; cmds[i].options[j].arg1; j++)
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 %s\n", cmds[i].cmd, cmds[i].options[j].arg1);
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
{
@ -449,9 +617,9 @@ unsigned long arg1, arg2, arg3;
{
for (j = 0; cmds[i].options[j].arg1; j++)
{
found = 1; /**< command and sub-command match */
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",
@ -467,7 +635,7 @@ unsigned long arg1, arg2, arg3;
cmds[i].options[j].fn(dcb);
break;
case 1:
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]);
if (arg1)
cmds[i].options[j].fn(dcb, arg1);
else
@ -475,8 +643,8 @@ unsigned long arg1, arg2, arg3;
args[2]);
break;
case 2:
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
arg2 = convert_arg(args[3],cmds[i].options[j].arg_types[1]);
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)
@ -487,9 +655,9 @@ unsigned long arg1, arg2, arg3;
args[3]);
break;
case 3:
arg1 = convert_arg(args[2],cmds[i].options[j].arg_types[0]);
arg2 = convert_arg(args[3],cmds[i].options[j].arg_types[1]);
arg3 = convert_arg(args[4],cmds[i].options[j].arg_types[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]);
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)