From 792ae454f4073602f6f9733ed25dfacb8ad63a45 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Wed, 25 Jun 2014 10:00:59 +0100 Subject: [PATCH] Add support for #!../maxadmin scripts Add the --help option Add quoting for executing command with arguments that contain whitespace --- client/maxadmin.c | 122 ++++++++++++++++++++++++---- server/core/filter.c | 4 +- server/modules/protocol/maxscaled.c | 2 - server/modules/protocol/telnetd.c | 22 ++--- 4 files changed, 119 insertions(+), 31 deletions(-) diff --git a/client/maxadmin.c b/client/maxadmin.c index 23c3eee04..79161110b 100644 --- a/client/maxadmin.c +++ b/client/maxadmin.c @@ -55,6 +55,7 @@ static int setipaddress(struct in_addr *a, char *p); static int authMaxScale(int so, char *user, char *password); static int sendCommand(int so, char *cmd); static void DoSource(int so, char *cmd); +static void DoUsage(); #ifdef HISTORY static char * @@ -66,6 +67,12 @@ prompt(EditLine *el __attribute__((__unused__))) } #endif +/** + * The main for the maxadmin client + * + * @param argc Number of arguments + * @param argv The command line arguments + */ int main(int argc, char **argv) { @@ -86,6 +93,7 @@ char *user = "admin"; char *passwd = NULL; int so, cmdlen; char *cmd; +int argno = 0; cmd = malloc(1); *cmd = 0; @@ -145,14 +153,41 @@ char *cmd; fatal = 1; } break; + case '-': + { + char *word; + + word = &argv[i][2]; + if (strcmp(word, "help") == 0) + { + DoUsage(); + exit(0); + } + break; + } } } else { - cmdlen += strlen(argv[i]) + 1; - cmd = realloc(cmd, cmdlen); - strcat(cmd, argv[i]); - strcat(cmd, " "); + /* Arguments after the second argument are quoted + * to allow for quoted names on the command line + * to be passed on in quotes. + */ + if (argno++ > 1) + { + cmdlen += strlen(argv[i]) + 3; + cmd = realloc(cmd, cmdlen); + strcat(cmd, "\""); + strcat(cmd, argv[i]); + strcat(cmd, "\" "); + } + else + { + cmdlen += strlen(argv[i]) + 1; + cmd = realloc(cmd, cmdlen); + strcat(cmd, argv[i]); + strcat(cmd, " "); + } } } @@ -198,8 +233,11 @@ char *cmd; if (cmdlen > 1) { - cmd[cmdlen - 2] = '\0'; - sendCommand(so, cmd); + cmd[cmdlen - 2] = '\0'; /* Remove trailing space */ + if (access(cmd, R_OK) == 0) + DoSource(so, cmd); + else + sendCommand(so, cmd); exit(0); } @@ -266,7 +304,14 @@ char *cmd; } else if (!strncasecmp(buf, "source", 6)) { - DoSource(so, buf); + char *ptr; + + /* Find the filename */ + ptr = &buf[strlen("source")]; + while (*ptr && isspace(*ptr)) + ptr++; + + DoSource(so, ptr); } else if (*buf) { @@ -283,6 +328,13 @@ char *cmd; return 0; } +/** + * Connect to the MaxScale server + * + * @param hostname The hostname to connect to + * @param port The port to use for the connection + * @return The connected socket or -1 on error + */ static int connectMaxScale(char *hostname, char *port) { @@ -310,7 +362,7 @@ int so; } -/* +/** * Set IP address in socket structure in_addr * * @param a Pointer to a struct in_addr into which the address is written @@ -364,6 +416,14 @@ setipaddress(struct in_addr *a, char *p) return 0; } +/** + * Perform authentication using the maxscaled protocol conventions + * + * @param so The socket connected to MaxScale + * @param user The username to authenticate + * @param password The password to authenticate with + * @return Non-zero of succesful authentication + */ static int authMaxScale(int so, char *user, char *password) { @@ -378,6 +438,14 @@ char buf[20]; return strncmp(buf, "FAILED", 6); } +/** + * Send a comamnd using the MaxScaled protocol, display the return data + * on standard output + * + * @param so The socket connect to MaxScale + * @param cmd The command to send + * @return 0 if the connection was closed + */ static int sendCommand(int so, char *cmd) { @@ -399,22 +467,23 @@ int i; return 1; } +/** + * Read a file of commands and send them to MaxScale + * + * @param so The socket connected to MaxScale + * @param file The filename + */ static void -DoSource(int so, char *buf) +DoSource(int so, char *file) { char *ptr, *pe; char line[132]; FILE *fp; - /* Find the filename */ - ptr = &buf[strlen("source")]; - while (*ptr && isspace(*ptr)) - ptr++; - - if ((fp = fopen(ptr, "r")) == NULL) + if ((fp = fopen(file, "r")) == NULL) { fprintf(stderr, "Unable to open command file '%s'.\n", - ptr); + file); return; } @@ -439,3 +508,24 @@ FILE *fp; fclose(fp); return; } + +/** + * Display the --help text. + */ +static void +DoUsage() +{ + printf("maxadmin: The MaxScale administrative and monitor client.\n\n"); + printf("Usage: maxadmin [-u user] [-p password] [-h hostname] [-P port] [ | ]\n\n"); + printf(" -u user The user name to use for the connection, default\n"); + printf(" is admin.\n"); + printf(" -p password The user password, if not given the password will\n"); + printf(" be prompted for interactively\n"); + printf(" -h hostname The maxscale host to connecto to. The default is\n"); + printf(" localhost\n"); + printf(" -P port The port to use for the connection, the default\n"); + printf(" port is 6603.\n"); + printf(" --help Print this help text.\n"); + printf("Any remaining arguments are treated as MaxScale commands or a file\n"); + printf("containing commands to execute.\n"); +} diff --git a/server/core/filter.c b/server/core/filter.c index 238298e4b..baeea21d7 100644 --- a/server/core/filter.c +++ b/server/core/filter.c @@ -222,13 +222,13 @@ int i; { dcb_printf(dcb, "Filters\n"); dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n"); - dcb_printf(dcb, "%-18s | %-15s | Options\n", + dcb_printf(dcb, "%-19s | %-15s | Options\n", "Filter", "Module"); dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n"); } while (ptr) { - dcb_printf(dcb, "%-18s | %-15s | ", + dcb_printf(dcb, "%-19s | %-15s | ", ptr->name, ptr->module); for (i = 0; ptr->options && ptr->options[i]; i++) dcb_printf(dcb, "%s ", ptr->options[i]); diff --git a/server/modules/protocol/maxscaled.c b/server/modules/protocol/maxscaled.c index f26a3d17a..0c7b4b744 100644 --- a/server/modules/protocol/maxscaled.c +++ b/server/modules/protocol/maxscaled.c @@ -87,8 +87,6 @@ static GWPROTOCOL MyObject = { NULL /**< Session */ }; -static void maxscaled_command(DCB *, unsigned char *cmd); - /** * Implementation of the mandatory version entry point * diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index e8e8ec7c0..b5ae23c1a 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -83,17 +83,17 @@ static int telnetd_listen(DCB *dcb, char *config); * The "module object" for the telnetd protocol module. */ static GWPROTOCOL MyObject = { - telnetd_read_event, /**< Read - EPOLLIN handler */ - telnetd_write, /**< Write - data from gateway */ - telnetd_write_event, /**< WriteReady - EPOLLOUT handler */ - telnetd_error, /**< Error - EPOLLERR handler */ - telnetd_hangup, /**< HangUp - EPOLLHUP handler */ - telnetd_accept, /**< Accept */ - NULL, /**< Connect */ - telnetd_close, /**< Close */ - telnetd_listen, /**< Create a listener */ - NULL, /**< Authentication */ - NULL /**< Session */ + telnetd_read_event, /**< Read - EPOLLIN handler */ + telnetd_write, /**< Write - data from gateway */ + telnetd_write_event, /**< WriteReady - EPOLLOUT handler */ + telnetd_error, /**< Error - EPOLLERR handler */ + telnetd_hangup, /**< HangUp - EPOLLHUP handler */ + telnetd_accept, /**< Accept */ + NULL, /**< Connect */ + telnetd_close, /**< Close */ + telnetd_listen, /**< Create a listener */ + NULL, /**< Authentication */ + NULL /**< Session */ }; static void telnetd_command(DCB *, unsigned char *cmd);