Addition of developer and user modes to debugcli

In order to be able to protect the interface so that mistyped argument
do not endanger MaxScale yet still allow the developer access a router
option has been added that gives the debugcli two modes.
This commit is contained in:
Mark Riddoch 2014-05-23 13:29:58 +01:00
parent 23a9759fca
commit 7fe50a311f
11 changed files with 280 additions and 49 deletions

View File

@ -1606,3 +1606,31 @@ DCB_CALLBACK *cb, *nextcb;
}
spinlock_release(&dcb->cb_lock);
}
/**
* Check the passed DCB to ensure it is in the list of allDCBS
*
* @param DCB The DCB to check
* @return 1 if the DCB is in the list, otherwise 0
*/
int
dcb_isvalid(DCB *dcb)
{
DCB *ptr;
int rval = 0;
spinlock_acquire(&dcbspin);
ptr = allDCBs;
while (ptr)
{
if (ptr == dcb)
{
rval = 1;
break;
}
ptr = ptr->next;
}
spinlock_release(&dcbspin);
return rval;
}

View File

@ -197,3 +197,26 @@ MONITOR *ptr;
}
spinlock_release(&monLock);
}
/**
* Find a monitor by name
*
* @param name The name of the monitor
* @return Pointer to the monitor or NULL
*/
MONITOR *
monitor_find(char *name)
{
MONITOR *ptr;
spinlock_acquire(&monLock);
ptr = allMonitors;
while (ptr)
{
if (!strcmp(ptr->name, name))
break;
ptr = ptr->next;
}
spinlock_release(&monLock);
return ptr;
}

View File

@ -29,6 +29,7 @@
* 25/02/14 Massimiliano Pinto Added: service refresh limit feature
* 28/02/14 Massimiliano Pinto users_alloc moved from service_alloc to serviceStartPort (generic hashable for services)
* 07/05/14 Massimiliano Pinto Added: version_string initialized to NULL
* 23/05/14 Mark Riddoch Addition of service validation call
*
* @endverbatim
*/
@ -114,6 +115,33 @@ SERVICE *service;
return service;
}
/**
* Check to see if a service pointer is valid
*
* @param service The poitner to check
* @return 1 if the service is in the list of all services
*/
int
service_isvalid(SERVICE *service)
{
SERVICE *ptr;
int rval = 0;
spinlock_acquire(&service_spin);
ptr = allServices;
while (ptr)
{
if (ptr == service)
{
rval = 1;
break;
}
ptr = ptr->next;
}
spinlock_release(&service_spin);
return rval;
}
/**
* Start an individual port/protocol pair
*

View File

@ -269,6 +269,34 @@ return_succp :
return succp;
}
/**
* Check to see if a session is valid, i.e. in the list of all sessions
*
* @param session Session to check
* @return 1 if the session is valid otherwise 0
*/
int
session_isvalid(SESSION *session)
{
SESSION *ptr;
int rval = 0;
spinlock_acquire(&session_spin);
ptr = allSessions;
while (ptr)
{
if (ptr == session)
{
rval = 1;
break;
}
ptr = ptr->next;
}
spinlock_release(&session_spin);
return rval;
}
/**
* Print details of an individual session
*

View File

@ -273,6 +273,7 @@ int dcb_add_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON, void
void *);
int dcb_remove_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON),
void *);
int dcb_isvalid(DCB *); /* Check the DCB is in the linked list */
bool dcb_set_state(
DCB* dcb,

View File

@ -29,6 +29,7 @@
* Date Who Description
* 07/07/13 Mark Riddoch Initial implementation
* 25/07/13 Mark Riddoch Addition of diagnotics
* 23/05/14 Mark Riddoch Addition of routine to find monitors by name
*
* @endverbatim
*/
@ -79,6 +80,7 @@ typedef struct monitor {
extern MONITOR *monitor_alloc(char *, char *);
extern void monitor_free(MONITOR *);
extern MONITOR *monitor_find(char *);
extern void monitorAddServer(MONITOR *, SERVER *);
extern void monitorAddUser(MONITOR *, char *, char *);
extern void monitorStop(MONITOR *);

View File

@ -129,6 +129,7 @@ typedef struct service {
extern SERVICE *service_alloc(char *, char *);
extern int service_free(SERVICE *);
extern SERVICE *service_find(char *);
extern int service_isvalid(SERVICE *);
extern int serviceAddProtocol(SERVICE *, char *, char *, unsigned short);
extern int serviceHasProtocol(SERVICE *, char *, unsigned short);
extern void serviceAddBackend(SERVICE *, SERVER *);

View File

@ -87,6 +87,7 @@ typedef struct session {
SESSION *session_alloc(struct service *, struct dcb *);
bool session_free(SESSION *);
int session_isvalid(SESSION *);
void printAllSessions();
void printSession(SESSION *);
void dprintAllSessions(struct dcb *);

View File

@ -41,6 +41,7 @@ struct cli_session;
typedef struct cli_instance {
SPINLOCK lock; /*< The instance spinlock */
SERVICE *service; /*< The debug cli service */
int mode; /*< CLI interface mode */
struct cli_session
*sessions; /*< Linked list of sessions within this instance */
struct cli_instance
@ -53,8 +54,13 @@ typedef struct cli_instance {
*/
typedef struct cli_session {
char cmdbuf[80]; /*< The command buffer used to build up user commands */
int mode; /*< The CLI Mode for this session */
SESSION *session; /*< The gateway session */
struct cli_session
*next; /*< The next pointer for the list of sessions */
} CLI_SESSION;
/* Command line interface modes */
#define CLIM_USER 1
#define CLIM_DEVELOPER 2
#endif

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

@ -39,6 +39,7 @@
* 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
*/
@ -73,6 +74,9 @@
#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
*
@ -83,6 +87,7 @@ struct subcommand {
int n_args;
void (*fn)();
char *help;
char *devhelp;
int arg_types[3];
};
@ -91,33 +96,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>|<service name>",
{ "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",
{ "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. The address may also be repalced with the server name form the configuration file",
{ "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",
{ "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} },
{ "service", 1, dprintService, "Show a single service in MaxScale, may be passed a service name or address of a service object",
{ "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",
{ARG_TYPE_ADDRESS, 0, 0} },
{ "sessions", 0, dprintAllSessions, "Show all active sessions in MaxScale",
{ "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} }
};
@ -133,7 +164,7 @@ struct subcommand shutdownoptions[] = {
0,
shutdown_server,
"Shutdown MaxScale",
"Shutdown MaxScale",
{0, 0, 0}
},
{
@ -141,12 +172,14 @@ 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 \"Sales Database\"",
"Shutdown a service, e.g. shutdown service 0x4838320 or shutdown service \"Sales Database\"",
{ARG_TYPE_SERVICE, 0, 0}
},
@ -155,6 +188,7 @@ struct subcommand shutdownoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -166,11 +200,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",
{ "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, NULL, NULL, NULL,
{0, 0, 0} }
};
@ -179,9 +217,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",
{ "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, NULL, NULL, NULL,
{0, 0, 0} }
};
@ -190,9 +230,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",
{ "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, NULL, NULL, NULL,
{0, 0, 0} }
};
@ -203,11 +245,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.",
{ "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 0x849420",
{ "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, NULL, NULL, NULL,
{0, 0, 0} }
};
@ -224,6 +270,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}
},
{
@ -231,6 +279,7 @@ struct subcommand enableoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -246,6 +295,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}
},
{
@ -253,6 +304,7 @@ struct subcommand disableoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -271,6 +323,7 @@ struct subcommand failoptions[] = {
0,
fail_backendfd,
"Fail backend socket for next operation.",
"Fail backend socket for next operation.",
{ARG_TYPE_STRING, 0, 0}
},
{
@ -278,6 +331,7 @@ struct subcommand failoptions[] = {
0,
fail_clientfd,
"Fail client socket for next operation.",
"Fail client socket for next operation.",
{ARG_TYPE_STRING, 0, 0}
},
{
@ -285,6 +339,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}
},
{
@ -292,6 +347,7 @@ struct subcommand failoptions[] = {
0,
NULL,
NULL,
NULL,
{0, 0, 0}
}
};
@ -302,9 +358,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} }
};
@ -319,10 +377,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}
}
};
@ -355,12 +414,13 @@ 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;
@ -372,15 +432,15 @@ SERVICE *service;
case ARG_TYPE_STRING:
return (unsigned long)arg;
case ARG_TYPE_SERVICE:
if ((rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
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 ((rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
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 ((rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
if (mode == CLIM_USER || (rval = (unsigned long)strtol(arg, NULL, 0)) == 0)
{
service = service_find(arg);
if (service)
@ -389,6 +449,20 @@ SERVICE *service;
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;
}
@ -486,11 +560,23 @@ char *ptr, *lptr;
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
{
@ -544,7 +630,7 @@ char *ptr, *lptr;
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
@ -552,8 +638,8 @@ char *ptr, *lptr;
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)
@ -564,9 +650,9 @@ char *ptr, *lptr;
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)