Addition of new client utility, maxadmin.
Supporting protocol for the admin interface New routing module, cli, which shares source with debugcli Tidyup output of lsit commands
This commit is contained in:
parent
02330093cc
commit
719503e471
4
Makefile
4
Makefile
@ -39,21 +39,25 @@ all:
|
||||
(cd log_manager; make)
|
||||
(cd query_classifier; make)
|
||||
(cd server; make)
|
||||
(cd client; make)
|
||||
|
||||
clean:
|
||||
(cd log_manager; make clean)
|
||||
(cd query_classifier; make clean)
|
||||
(cd server; make clean)
|
||||
(cd client; make clean)
|
||||
|
||||
depend:
|
||||
(cd log_manager; make depend)
|
||||
(cd query_classifier; make depend)
|
||||
(cd server; make depend)
|
||||
(cd client; make depend)
|
||||
|
||||
install:
|
||||
(cd server; make DEST=$(DEST) install)
|
||||
(cd log_manager; make DEST=$(DEST) install)
|
||||
(cd query_classifier; make DEST=$(DEST) install)
|
||||
(cd client; make DEST=$(DEST) install)
|
||||
|
||||
cleantests:
|
||||
$(MAKE) -C test cleantests
|
||||
|
BIN
client/.maxadmin.c.swp
Normal file
BIN
client/.maxadmin.c.swp
Normal file
Binary file not shown.
71
client/Makefile
Normal file
71
client/Makefile
Normal file
@ -0,0 +1,71 @@
|
||||
# This file is distributed as part of 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 SkySQL Ab 2014
|
||||
#
|
||||
# Revision History
|
||||
# Date Who Description
|
||||
# 13/06/14 Mark Riddoch Initial implementation of MaxScale
|
||||
# client program
|
||||
|
||||
CC=cc
|
||||
|
||||
CFLAGS=-c -Wall -g
|
||||
|
||||
SRCS= maxadmin.c
|
||||
|
||||
HDRS=
|
||||
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
|
||||
LIBS=-ledit
|
||||
|
||||
all: maxadmin
|
||||
|
||||
cleantests:
|
||||
$(MAKE) -C test cleantests
|
||||
|
||||
buildtests:
|
||||
$(MAKE) -C test buildtests
|
||||
|
||||
runtests:
|
||||
$(MAKE) -C test runtests
|
||||
|
||||
testall:
|
||||
$(MAKE) -C test testall
|
||||
|
||||
maxadmin: $(OBJ)
|
||||
$(CC) $(LDFLAGS) $(OBJ) $(LIBS) -o $@
|
||||
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) maxadmin
|
||||
- rm *.so
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
|
||||
depend:
|
||||
@rm -f depend.mk
|
||||
cc -M $(CFLAGS) $(SRCS) > depend.mk
|
||||
|
||||
install: maxadmin
|
||||
@mkdir -p $(DEST)/bin
|
||||
install -D maxadmin $(DEST)/bin
|
||||
|
||||
include depend.mk
|
0
client/depend.mk
Normal file
0
client/depend.mk
Normal file
373
client/maxadmin.c
Normal file
373
client/maxadmin.c
Normal file
@ -0,0 +1,373 @@
|
||||
/*
|
||||
* This file is distributed as part of 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 SkySQL Ab 2014
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file maxadmin.c - The MaxScale administration client
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 13/06/14 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include <locale.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <histedit.h>
|
||||
|
||||
static int connectMaxScale(char *hostname, char *port);
|
||||
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 char *
|
||||
prompt(EditLine *el __attribute__((__unused__)))
|
||||
{
|
||||
static char prompt[] = "MaxScale> ";
|
||||
|
||||
return prompt;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
EditLine *el = NULL;
|
||||
int i, num, rv, fatal = 0;
|
||||
char *buf;
|
||||
Tokenizer *tok;
|
||||
History *hist;
|
||||
HistEvent ev;
|
||||
const LineInfo *li;
|
||||
char *hostname = "localhost";
|
||||
char *port = "6603";
|
||||
char *user = "admin";
|
||||
char *passwd = NULL;
|
||||
int so, cmdlen;
|
||||
char *cmd;
|
||||
|
||||
cmd = malloc(1);
|
||||
*cmd = 0;
|
||||
cmdlen = 1;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
if (argv[i][0] == '-')
|
||||
{
|
||||
switch (argv[i][1])
|
||||
{
|
||||
case 'u': /* User */
|
||||
if (argv[i][2])
|
||||
user = &(argv[i][2]);
|
||||
else if (i + 1 < argc)
|
||||
user = argv[++i];
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Missing username"
|
||||
"in -u option.\n");
|
||||
fatal = 1;
|
||||
}
|
||||
break;
|
||||
case 'p': /* Password */
|
||||
if (argv[i][2])
|
||||
passwd = &(argv[i][2]);
|
||||
else if (i + 1 < argc)
|
||||
passwd = argv[++i];
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Missing password "
|
||||
"in -p option.\n");
|
||||
fatal = 1;
|
||||
}
|
||||
break;
|
||||
case 'h': /* hostname */
|
||||
if (argv[i][2])
|
||||
hostname = &(argv[i][2]);
|
||||
else if (i + 1 < argc)
|
||||
hostname = argv[++i];
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Missing hostname value "
|
||||
"in -h option.\n");
|
||||
fatal = 1;
|
||||
}
|
||||
break;
|
||||
case 'P': /* Port */
|
||||
if (argv[i][2])
|
||||
port = &(argv[i][2]);
|
||||
else if (i + 1 < argc)
|
||||
port = argv[++i];
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Missing Port value "
|
||||
"in -P option.\n");
|
||||
fatal = 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cmdlen += strlen(argv[i]) + 1;
|
||||
cmd = realloc(cmd, cmdlen);
|
||||
strcat(cmd, argv[i]);
|
||||
strcat(cmd, " ");
|
||||
}
|
||||
}
|
||||
|
||||
if (fatal)
|
||||
exit(1);
|
||||
|
||||
if (passwd == NULL)
|
||||
{
|
||||
struct termios tty_attr;
|
||||
tcflag_t c_lflag;
|
||||
|
||||
if (tcgetattr(STDIN_FILENO, &tty_attr) < 0)
|
||||
return -1;
|
||||
|
||||
c_lflag = tty_attr.c_lflag;
|
||||
tty_attr.c_lflag &= ~ICANON;
|
||||
tty_attr.c_lflag &= ~ECHO;
|
||||
|
||||
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0)
|
||||
return -1;
|
||||
|
||||
printf("Password: ");
|
||||
passwd = malloc(80);
|
||||
fgets(passwd, 80, stdin);
|
||||
|
||||
tty_attr.c_lflag = c_lflag;
|
||||
if (tcsetattr(STDIN_FILENO, 0, &tty_attr) < 0)
|
||||
return -1;
|
||||
i = strlen(passwd);
|
||||
if (i > 1)
|
||||
passwd[i - 1] = '\0';
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if ((so = connectMaxScale(hostname, port)) == -1)
|
||||
exit(1);
|
||||
if (!authMaxScale(so, user, passwd))
|
||||
{
|
||||
fprintf(stderr, "Failed to connect to MaxScale. "
|
||||
"Incorrect username or password.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (cmdlen > 1)
|
||||
{
|
||||
cmd[cmdlen - 2] = '\0';
|
||||
sendCommand(so, cmd);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
(void) setlocale(LC_CTYPE, "");
|
||||
|
||||
hist = history_init(); /* Init the builtin history */
|
||||
/* Remember 100 events */
|
||||
history(hist, &ev, H_SETSIZE, 100);
|
||||
|
||||
tok = tok_init(NULL); /* Initialize the tokenizer */
|
||||
|
||||
/* Initialize editline */
|
||||
el = el_init(*argv, stdin, stdout, stderr);
|
||||
|
||||
el_set(el, EL_EDITOR, "vi"); /* Default editor is vi */
|
||||
el_set(el, EL_SIGNAL, 1); /* Handle signals gracefully */
|
||||
el_set(el, EL_PROMPT, prompt);/* Set the prompt function */
|
||||
|
||||
/* Tell editline to use this history interface */
|
||||
el_set(el, EL_HIST, history, hist);
|
||||
|
||||
/*
|
||||
* Bind j, k in vi command mode to previous and next line, instead
|
||||
* of previous and next history.
|
||||
*/
|
||||
el_set(el, EL_BIND, "-a", "k", "ed-prev-line", NULL);
|
||||
el_set(el, EL_BIND, "-a", "j", "ed-next-line", NULL);
|
||||
|
||||
/*
|
||||
* Source the user's defaults file.
|
||||
*/
|
||||
el_source(el, NULL);
|
||||
|
||||
while ((buf = el_gets(el, &num)) != NULL && num != 0)
|
||||
{
|
||||
/* Strip trailing \n\r */
|
||||
for (i = num - 1; buf[i] == '\r' || buf[i] == '\n'; i--)
|
||||
buf[i] = 0;
|
||||
|
||||
li = el_line(el);
|
||||
history(hist, &ev, H_ENTER, buf);
|
||||
|
||||
if (!strcasecmp(buf, "quit"))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (!strcasecmp(buf, "history"))
|
||||
{
|
||||
for (rv = history(hist, &ev, H_LAST); rv != -1;
|
||||
rv = history(hist, &ev, H_PREV))
|
||||
fprintf(stdout, "%4d %s\n",
|
||||
ev.num, ev.str);
|
||||
}
|
||||
else if (*buf)
|
||||
{
|
||||
sendCommand(so, buf);
|
||||
}
|
||||
}
|
||||
|
||||
el_end(el);
|
||||
tok_end(tok);
|
||||
history_end(hist);
|
||||
close(so);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
connectMaxScale(char *hostname, char *port)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int so;
|
||||
|
||||
if ((so = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to create socket: %s\n",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
memset(&addr, 0, sizeof addr);
|
||||
addr.sin_family = AF_INET;
|
||||
setipaddress(&addr.sin_addr, hostname);
|
||||
addr.sin_port = htons(atoi(port));
|
||||
if (connect(so, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
fprintf(stderr, "Unable to connect to MaxScale at %s, %s: %s\n",
|
||||
hostname, port, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return so;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set IP address in socket structure in_addr
|
||||
*
|
||||
* @param a Pointer to a struct in_addr into which the address is written
|
||||
* @param p The hostname to lookup
|
||||
* @return 1 on success, 0 on failure
|
||||
*/
|
||||
static int
|
||||
setipaddress(struct in_addr *a, char *p)
|
||||
{
|
||||
#ifdef __USE_POSIX
|
||||
struct addrinfo *ai = NULL, hint;
|
||||
int rc;
|
||||
struct sockaddr_in * res_addr;
|
||||
memset(&hint, 0, sizeof (hint));
|
||||
|
||||
hint.ai_socktype = SOCK_STREAM;
|
||||
hint.ai_flags = AI_CANONNAME;
|
||||
hint.ai_family = AF_INET;
|
||||
|
||||
if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* take the first one */
|
||||
if (ai != NULL) {
|
||||
res_addr = (struct sockaddr_in *)(ai->ai_addr);
|
||||
memcpy(a, &res_addr->sin_addr, sizeof(struct in_addr));
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
struct hostent *h;
|
||||
|
||||
spinlock_acquire(&tmplock);
|
||||
h = gethostbyname(p);
|
||||
spinlock_release(&tmplock);
|
||||
|
||||
if (h == NULL) {
|
||||
if ((a->s_addr = inet_addr(p)) == -1) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* take the first one */
|
||||
memcpy(a, h->h_addr, h->h_length);
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
authMaxScale(int so, char *user, char *password)
|
||||
{
|
||||
char buf[20];
|
||||
|
||||
read(so, buf, 4);
|
||||
write(so, user, strlen(user));
|
||||
read(so, buf, 8);
|
||||
write(so, password, strlen(password));
|
||||
read(so, buf, 6);
|
||||
|
||||
return strncmp(buf, "FAILED", 6);
|
||||
}
|
||||
|
||||
static int
|
||||
sendCommand(int so, char *cmd)
|
||||
{
|
||||
char buf[80];
|
||||
int i;
|
||||
|
||||
write(so, cmd, strlen(cmd));
|
||||
while (1)
|
||||
{
|
||||
if ((i = read(so, buf, 80)) == -1)
|
||||
return 0;
|
||||
if (i > 1 && buf[i-1] == 'K' && buf[i-2] == 'O')
|
||||
{
|
||||
write(1, buf, i - 2);
|
||||
return 1;
|
||||
}
|
||||
write(1, buf, i);
|
||||
}
|
||||
return 1;
|
||||
}
|
@ -1172,15 +1172,17 @@ DCB *dcb;
|
||||
dcb = allDCBs;
|
||||
dcb_printf(pdcb, " %-10s | %-26s | %-20s | %s\n",
|
||||
"DCB", "State", "Service", "Remote");
|
||||
dcb_printf(pdcb, "---------------------------------------------------------------------------\n");
|
||||
dcb_printf(pdcb, "------------+----------------------------+----------------------+----------\n");
|
||||
while (dcb)
|
||||
{
|
||||
dcb_printf(pdcb, " %10p | %-26s | %-20s | %s\n",
|
||||
dcb, gw_dcb_state2string(dcb->state),
|
||||
(dcb->service ? dcb->service->name : ""),
|
||||
(dcb->session->service ?
|
||||
dcb->session->service->name : ""),
|
||||
(dcb->remote ? dcb->remote : ""));
|
||||
dcb = dcb->next;
|
||||
dcb = dcb->next;
|
||||
}
|
||||
dcb_printf(pdcb, "------------+----------------------------+----------------------+----------\n");
|
||||
spinlock_release(&dcbspin);
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ int i;
|
||||
{
|
||||
dcb_printf(dcb, "%-18s | %-15s | Options\n",
|
||||
"Filter", "Module");
|
||||
dcb_printf(dcb, "-------------------------------------------------------------------------------\n");
|
||||
dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
@ -220,6 +220,8 @@ int i;
|
||||
dcb_printf(dcb, "\n");
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allFilters)
|
||||
dcb_printf(dcb, "--------------------+-----------------+----------------------------------------\n");
|
||||
spinlock_release(&filter_spin);
|
||||
}
|
||||
|
||||
|
@ -360,7 +360,7 @@ dprintAllModules(DCB *dcb)
|
||||
MODULES *ptr = registered;
|
||||
|
||||
dcb_printf(dcb, "%-15s | %-11s | Version | API | Status\n", "Module Name", "Module Type");
|
||||
dcb_printf(dcb, "--------------------------------------------------------------------------\n");
|
||||
dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n");
|
||||
while (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "%-15s | %-11s | %-7s ", ptr->module, ptr->type, ptr->version);
|
||||
@ -380,4 +380,5 @@ MODULES *ptr = registered;
|
||||
dcb_printf(dcb, "\n");
|
||||
ptr = ptr->next;
|
||||
}
|
||||
dcb_printf(dcb, "----------------+-------------+---------+-------+-------------------------\n");
|
||||
}
|
||||
|
@ -314,7 +314,7 @@ char *stat;
|
||||
{
|
||||
dcb_printf(dcb, "%-18s | %-15s | Port | %-18s | Connections\n",
|
||||
"Server", "Address", "Status");
|
||||
dcb_printf(dcb, "-------------------------------------------------------------------------------\n");
|
||||
dcb_printf(dcb, "-------------------+-----------------+-------+--------------------+------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
@ -326,6 +326,8 @@ char *stat;
|
||||
free(stat);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allServers)
|
||||
dcb_printf(dcb, "-------------------+-----------------+-------+--------------------+------------\n");
|
||||
spinlock_release(&server_spin);
|
||||
}
|
||||
|
||||
|
@ -828,7 +828,7 @@ SERVICE *ptr;
|
||||
{
|
||||
dcb_printf(dcb, "%-25s | %-20s | #Users | Total Sessions\n",
|
||||
"Service Name", "Router Module");
|
||||
dcb_printf(dcb, "--------------------------------------------------------------------------\n");
|
||||
dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
@ -837,6 +837,8 @@ SERVICE *ptr;
|
||||
ptr->stats.n_current, ptr->stats.n_sessions);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allServices)
|
||||
dcb_printf(dcb, "--------------------------+----------------------+--------+---------------\n");
|
||||
spinlock_release(&service_spin);
|
||||
}
|
||||
|
||||
@ -857,7 +859,7 @@ SERV_PROTOCOL *lptr;
|
||||
{
|
||||
dcb_printf(dcb, "%-20s | %-18s | %-15s | Port | State\n",
|
||||
"Service Name", "Protocol Module", "Address");
|
||||
dcb_printf(dcb, "---------------------------------------------------------------------------\n");
|
||||
dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
@ -875,6 +877,8 @@ SERV_PROTOCOL *lptr;
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allServices)
|
||||
dcb_printf(dcb, "---------------------+--------------------+-----------------+-------+------\n");
|
||||
spinlock_release(&service_spin);
|
||||
}
|
||||
|
||||
|
@ -538,7 +538,7 @@ SESSION *ptr;
|
||||
if (ptr)
|
||||
{
|
||||
dcb_printf(dcb, "Session | Client | State\n");
|
||||
dcb_printf(dcb, "------------------------------------------\n");
|
||||
dcb_printf(dcb, "-----------------+-----------------+----------------\n");
|
||||
}
|
||||
while (ptr)
|
||||
{
|
||||
@ -548,6 +548,8 @@ SESSION *ptr;
|
||||
session_state(ptr->state));
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (allSessions)
|
||||
dcb_printf(dcb, "-----------------+-----------------+----------------\n");
|
||||
spinlock_release(&session_spin);
|
||||
}
|
||||
|
||||
|
46
server/modules/include/maxscaled.h
Normal file
46
server/modules/include/maxscaled.h
Normal file
@ -0,0 +1,46 @@
|
||||
#ifndef _MAXSCALED_H
|
||||
#define _MAXSCALED_H
|
||||
/*
|
||||
* This file is distributed as part of 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 SkySQL Ab 2014
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file maxscaled.h The maxscaled protocol module header file
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 13/06/14 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <dcb.h>
|
||||
|
||||
/**
|
||||
* The telnetd specific protocol structure to put in the DCB.
|
||||
*/
|
||||
typedef struct maxscaled {
|
||||
int state; /**< The connection state */
|
||||
char *username; /**< The login name of the user */
|
||||
} MAXSCALED;
|
||||
|
||||
#define MAXSCALED_STATE_LOGIN 1 /**< Issued login prompt */
|
||||
#define MAXSCALED_STATE_PASSWD 2 /**< Issued password prompt */
|
||||
#define MAXSCALED_STATE_DATA 3 /**< User logged in */
|
||||
|
||||
#endif
|
@ -22,6 +22,7 @@
|
||||
# headers so that liblog_manager.so can
|
||||
# be linked in.
|
||||
# 09/07/2013 Massimiliano Pinto Added the HTTPD protocol module
|
||||
# 13/06/2014 Mark Riddoch Added thr MaxScale protocol module
|
||||
#
|
||||
include ../../../build_gateway.inc
|
||||
|
||||
@ -45,10 +46,12 @@ TELNETDSRCS=telnetd.c
|
||||
TELNETDOBJ=$(TELNETDSRCS:.c=.o)
|
||||
HTTPDSRCS=httpd.c
|
||||
HTTPDOBJ=$(HTTPDSRCS:.c=.o)
|
||||
MAXSCALEDSRCS=maxscaled.c
|
||||
MAXSCALEDOBJ=$(MAXSCALEDSRCS:.c=.o)
|
||||
SRCS=$(MYSQLCLIENTSRCS) $(MYSQLBACKENDSRCS) $(TELNETDSRCS) $(HTTPDSRCS)
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o
|
||||
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so
|
||||
MODULES=libMySQLClient.so libMySQLBackend.so libtelnetd.so libHTTPD.so libmaxscaled.so
|
||||
|
||||
all: $(MODULES)
|
||||
|
||||
@ -64,6 +67,9 @@ libtelnetd.so: $(TELNETDOBJ)
|
||||
libHTTPD.so: $(HTTPDOBJ)
|
||||
$(CC) $(LDFLAGS) $(HTTPDOBJ) $(LIBS) -o $@
|
||||
|
||||
libmaxscaled.so: $(MAXSCALEDOBJ)
|
||||
$(CC) $(LDFLAGS) $(MAXSCALEDOBJ) $(LIBS) -lcrypt -o $@
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
|
383
server/modules/protocol/maxscaled.c
Normal file
383
server/modules/protocol/maxscaled.c
Normal file
@ -0,0 +1,383 @@
|
||||
/*
|
||||
* This file is distributed as part of 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 SkySQL Ab 2014
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <dcb.h>
|
||||
#include <buffer.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <router.h>
|
||||
#include <poll.h>
|
||||
#include <atomic.h>
|
||||
#include <gw.h>
|
||||
#include <adminusers.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <modinfo.h>
|
||||
#include <maxscaled.h>
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_PROTOCOL,
|
||||
MODULE_ALPHA_RELEASE,
|
||||
GWPROTOCOL_VERSION,
|
||||
"A maxscale protocol for the administration interface"
|
||||
};
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
/**
|
||||
* @file maxscaled.c - MaxScale administration protocol
|
||||
*
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
* Date Who Description
|
||||
* 13/06/2014 Mark Riddoch Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
static int maxscaled_read_event(DCB* dcb);
|
||||
static int maxscaled_write_event(DCB *dcb);
|
||||
static int maxscaled_write(DCB *dcb, GWBUF *queue);
|
||||
static int maxscaled_error(DCB *dcb);
|
||||
static int maxscaled_hangup(DCB *dcb);
|
||||
static int maxscaled_accept(DCB *dcb);
|
||||
static int maxscaled_close(DCB *dcb);
|
||||
static int maxscaled_listen(DCB *dcb, char *config);
|
||||
|
||||
/**
|
||||
* The "module object" for the maxscaled protocol module.
|
||||
*/
|
||||
static GWPROTOCOL MyObject = {
|
||||
maxscaled_read_event, /**< Read - EPOLLIN handler */
|
||||
maxscaled_write, /**< Write - data from gateway */
|
||||
maxscaled_write_event, /**< WriteReady - EPOLLOUT handler */
|
||||
maxscaled_error, /**< Error - EPOLLERR handler */
|
||||
maxscaled_hangup, /**< HangUp - EPOLLHUP handler */
|
||||
maxscaled_accept, /**< Accept */
|
||||
NULL, /**< Connect */
|
||||
maxscaled_close, /**< Close */
|
||||
maxscaled_listen, /**< Create a listener */
|
||||
NULL, /**< Authentication */
|
||||
NULL /**< Session */
|
||||
};
|
||||
|
||||
static void maxscaled_command(DCB *, unsigned char *cmd);
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Initialise MaxScaled Protocol module.\n")));
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
GWPROTOCOL *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read event for EPOLLIN on the maxscaled protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
maxscaled_read_event(DCB* dcb)
|
||||
{
|
||||
int n;
|
||||
GWBUF *head = NULL;
|
||||
SESSION *session = dcb->session;
|
||||
MAXSCALED *maxscaled = (MAXSCALED *)dcb->protocol;
|
||||
char *password;
|
||||
|
||||
if ((n = dcb_read(dcb, &head)) != -1)
|
||||
{
|
||||
if (head)
|
||||
{
|
||||
unsigned char *ptr = GWBUF_DATA(head);
|
||||
ptr = GWBUF_DATA(head);
|
||||
if (GWBUF_LENGTH(head))
|
||||
{
|
||||
switch (maxscaled->state)
|
||||
{
|
||||
case MAXSCALED_STATE_LOGIN:
|
||||
maxscaled->username = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
maxscaled->state = MAXSCALED_STATE_PASSWD;
|
||||
dcb_printf(dcb, "PASSWORD");
|
||||
gwbuf_consume(head, GWBUF_LENGTH(head));
|
||||
break;
|
||||
case MAXSCALED_STATE_PASSWD:
|
||||
password = strndup(GWBUF_DATA(head), GWBUF_LENGTH(head));
|
||||
if (admin_verify(maxscaled->username, password))
|
||||
{
|
||||
dcb_printf(dcb, "OK----");
|
||||
maxscaled->state = MAXSCALED_STATE_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_printf(dcb, "FAILED");
|
||||
maxscaled->state = MAXSCALED_STATE_LOGIN;
|
||||
free(maxscaled->username);
|
||||
}
|
||||
gwbuf_consume(head, GWBUF_LENGTH(head));
|
||||
free(password);
|
||||
break;
|
||||
case MAXSCALED_STATE_DATA:
|
||||
SESSION_ROUTE_QUERY(session, head);
|
||||
dcb_printf(dcb, "OK");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Force the free of the buffer header
|
||||
gwbuf_consume(head, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* EPOLLOUT handler for the maxscaled protocol module.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return
|
||||
*/
|
||||
static int
|
||||
maxscaled_write_event(DCB *dcb)
|
||||
{
|
||||
return dcb_drain_writeq(dcb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write routine for the maxscaled protocol module.
|
||||
*
|
||||
* Writes the content of the buffer queue to the socket
|
||||
* observing the non-blocking principles of MaxScale.
|
||||
*
|
||||
* @param dcb Descriptor Control Block for the socket
|
||||
* @param queue Linked list of buffes to write
|
||||
*/
|
||||
static int
|
||||
maxscaled_write(DCB *dcb, GWBUF *queue)
|
||||
{
|
||||
int rc;
|
||||
rc = dcb_write(dcb, queue);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLERR event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
maxscaled_error(DCB *dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLHUP event.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
static int
|
||||
maxscaled_hangup(DCB *dcb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the EPOLLIN event when the DCB refers to the listening
|
||||
* socket for the protocol.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @return The number of new connections created
|
||||
*/
|
||||
static int
|
||||
maxscaled_accept(DCB *dcb)
|
||||
{
|
||||
int n_connect = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int so;
|
||||
struct sockaddr_in addr;
|
||||
socklen_t addrlen = sizeof(struct sockaddr);
|
||||
DCB *client_dcb;
|
||||
MAXSCALED *maxscaled_pr = NULL;
|
||||
|
||||
so = accept(dcb->fd, (struct sockaddr *)&addr, &addrlen);
|
||||
|
||||
if (so == -1)
|
||||
return n_connect;
|
||||
else
|
||||
{
|
||||
atomic_add(&dcb->stats.n_accepts, 1);
|
||||
client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
|
||||
if (client_dcb == NULL)
|
||||
|
||||
{
|
||||
return n_connect;
|
||||
}
|
||||
client_dcb->fd = so;
|
||||
client_dcb->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
client_dcb->session =
|
||||
session_alloc(dcb->session->service, client_dcb);
|
||||
maxscaled_pr = (MAXSCALED *)malloc(sizeof(MAXSCALED));
|
||||
client_dcb->protocol = (void *)maxscaled_pr;
|
||||
|
||||
if (maxscaled_pr == NULL)
|
||||
{
|
||||
dcb_add_to_zombieslist(client_dcb);
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
if (poll_add_dcb(client_dcb) == -1)
|
||||
{
|
||||
dcb_add_to_zombieslist(dcb);
|
||||
return n_connect;
|
||||
}
|
||||
n_connect++;
|
||||
maxscaled_pr->state = MAXSCALED_STATE_LOGIN;
|
||||
maxscaled_pr->username = NULL;
|
||||
dcb_printf(client_dcb, "USER");
|
||||
}
|
||||
}
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
/**
|
||||
* The close handler for the descriptor. Called by the gateway to
|
||||
* explicitly close a connection.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
*/
|
||||
|
||||
static int
|
||||
maxscaled_close(DCB *dcb)
|
||||
{
|
||||
MAXSCALED *maxscaled = dcb->protocol;
|
||||
|
||||
if (maxscaled && maxscaled->username)
|
||||
free(maxscaled->username);
|
||||
|
||||
dcb_close(dcb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maxscale daemon listener entry point
|
||||
*
|
||||
* @param listener The Listener DCB
|
||||
* @param config Configuration (ip:port)
|
||||
*/
|
||||
static int
|
||||
maxscaled_listen(DCB *listener, char *config)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int one = 1;
|
||||
int rc;
|
||||
|
||||
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
if (!parse_bindconfig(config, 6033, &addr))
|
||||
return 0;
|
||||
|
||||
|
||||
if ((listener->fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// socket options
|
||||
setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
// set NONBLOCKING mode
|
||||
setnonblocking(listener->fd);
|
||||
// bind address and port
|
||||
if (bind(listener->fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = listen(listener->fd, SOMAXCONN);
|
||||
|
||||
if (rc == 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"Listening maxscale connections at %s\n",
|
||||
config)));
|
||||
} else {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Failed to start listening for maxscale admin connections "
|
||||
"due error %d, %s\n\n",
|
||||
eno,
|
||||
strerror(eno))));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
if (poll_add_dcb(listener) == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
@ -42,10 +42,12 @@ READCONSRCS=readconnroute.c
|
||||
READCONOBJ=$(READCONSRCS:.c=.o)
|
||||
DEBUGCLISRCS=debugcli.c debugcmd.c
|
||||
DEBUGCLIOBJ=$(DEBUGCLISRCS:.c=.o)
|
||||
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS)
|
||||
CLISRCS=cli.c debugcmd.c
|
||||
CLIOBJ=$(CLISRCS:.c=.o)
|
||||
SRCS=$(TESTSRCS) $(READCONSRCS) $(DEBUGCLISRCS) cli.c
|
||||
OBJ=$(SRCS:.c=.o)
|
||||
LIBS=$(UTILSPATH)/skygw_utils.o -lssl -llog_manager
|
||||
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so
|
||||
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so
|
||||
|
||||
|
||||
all: $(MODULES)
|
||||
@ -59,6 +61,9 @@ libreadconnroute.so: $(READCONOBJ)
|
||||
libdebugcli.so: $(DEBUGCLIOBJ)
|
||||
$(CC) $(LDFLAGS) $(DEBUGCLIOBJ) $(LIBS) -o $@
|
||||
|
||||
libcli.so: $(CLIOBJ)
|
||||
$(CC) $(LDFLAGS) $(CLIOBJ) $(LIBS) -o $@
|
||||
|
||||
libreadwritesplit.so:
|
||||
# (cd readwritesplit; touch depend.mk ; make; cp $@ ..)
|
||||
|
||||
|
297
server/modules/routing/cli.c
Normal file
297
server/modules/routing/cli.c
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* This file is distributed as part of 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 SkySQL Ab 2014
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file cli.c - A "routing module" that in fact merely gives access
|
||||
* to a command line interface
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 18/06/13 Mark Riddoch Initial implementation
|
||||
* 13/06/14 Mark Riddoch Creted from the debugcli
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <service.h>
|
||||
#include <session.h>
|
||||
#include <router.h>
|
||||
#include <modules.h>
|
||||
#include <modinfo.h>
|
||||
#include <atomic.h>
|
||||
#include <spinlock.h>
|
||||
#include <dcb.h>
|
||||
#include <poll.h>
|
||||
#include <debugcli.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_ROUTER,
|
||||
MODULE_ALPHA_RELEASE,
|
||||
ROUTER_VERSION,
|
||||
"The admin user interface"
|
||||
};
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
static char *version_str = "V1.0.0";
|
||||
|
||||
/* The router entry points */
|
||||
static ROUTER *createInstance(SERVICE *service, char **options);
|
||||
static void *newSession(ROUTER *instance, SESSION *session);
|
||||
static void closeSession(ROUTER *instance, void *router_session);
|
||||
static void freeSession(ROUTER *instance, void *router_session);
|
||||
static int execute(ROUTER *instance, void *router_session, GWBUF *queue);
|
||||
static void diagnostics(ROUTER *instance, DCB *dcb);
|
||||
static uint8_t getCapabilities (ROUTER* inst, void* router_session);
|
||||
|
||||
/** The module object definition */
|
||||
static ROUTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
newSession,
|
||||
closeSession,
|
||||
freeSession,
|
||||
execute,
|
||||
diagnostics,
|
||||
NULL,
|
||||
NULL,
|
||||
getCapabilities
|
||||
};
|
||||
|
||||
extern int execute_cmd(CLI_SESSION *cli);
|
||||
|
||||
static SPINLOCK instlock;
|
||||
static CLI_INSTANCE *instances;
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialise CLI router module %s.\n",
|
||||
version_str)));
|
||||
spinlock_init(&instlock);
|
||||
instances = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
ROUTER_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of the router for a particular service
|
||||
* within the gateway.
|
||||
*
|
||||
* @param service The service this router is being create for
|
||||
* @param options Any array of options for the query router
|
||||
*
|
||||
* @return The instance data for this new instance
|
||||
*/
|
||||
static ROUTER *
|
||||
createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
CLI_INSTANCE *inst;
|
||||
int i;
|
||||
|
||||
if ((inst = malloc(sizeof(CLI_INSTANCE))) == NULL)
|
||||
return NULL;
|
||||
|
||||
inst->service = service;
|
||||
spinlock_init(&inst->lock);
|
||||
inst->sessions = NULL;
|
||||
inst->mode = CLIM_USER;
|
||||
|
||||
if (options)
|
||||
{
|
||||
for (i = 0; options[i]; i++)
|
||||
{
|
||||
{
|
||||
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
|
||||
* insert this router instance into the linked list of routers
|
||||
* that have been created with this module.
|
||||
*/
|
||||
spinlock_acquire(&instlock);
|
||||
inst->next = instances;
|
||||
instances = inst;
|
||||
spinlock_release(&instlock);
|
||||
|
||||
return (ROUTER *)inst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate a new session with this instance of the router.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param session The session itself
|
||||
* @return Session specific data for this session
|
||||
*/
|
||||
static void *
|
||||
newSession(ROUTER *instance, SESSION *session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
CLI_SESSION *client;
|
||||
|
||||
if ((client = (CLI_SESSION *)malloc(sizeof(CLI_SESSION))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
client->session = session;
|
||||
|
||||
memset(client->cmdbuf, 0, 80);
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
client->next = inst->sessions;
|
||||
inst->sessions = client;
|
||||
spinlock_release(&inst->lock);
|
||||
|
||||
session->state = SESSION_STATE_READY;
|
||||
client->mode = inst->mode;
|
||||
|
||||
return (void *)client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close a session with the router, this is the mechanism
|
||||
* by which a router may cleanup data structure etc.
|
||||
*
|
||||
* @param instance The router instance data
|
||||
* @param router_session The session being closed
|
||||
*/
|
||||
static void
|
||||
closeSession(ROUTER *instance, void *router_session)
|
||||
{
|
||||
CLI_INSTANCE *inst = (CLI_INSTANCE *)instance;
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
|
||||
|
||||
spinlock_acquire(&inst->lock);
|
||||
if (inst->sessions == session)
|
||||
inst->sessions = session->next;
|
||||
else
|
||||
{
|
||||
CLI_SESSION *ptr = inst->sessions;
|
||||
while (ptr && ptr->next != session)
|
||||
ptr = ptr->next;
|
||||
if (ptr)
|
||||
ptr->next = session->next;
|
||||
}
|
||||
spinlock_release(&inst->lock);
|
||||
/**
|
||||
* Router session is freed in session.c:session_close, when session who
|
||||
* owns it, is freed.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a debugcli session
|
||||
*
|
||||
* @param router_instance The router session
|
||||
* @param router_client_session The router session as returned from newSession
|
||||
*/
|
||||
static void freeSession(
|
||||
ROUTER* router_instance,
|
||||
void* router_client_session)
|
||||
{
|
||||
free(router_client_session);
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have data from the client, we must route it to the backend.
|
||||
* This is simply a case of sending it to the connection that was
|
||||
* chosen when we started the client session.
|
||||
*
|
||||
* @param instance The router instance
|
||||
* @param router_session The router session returned from the newSession call
|
||||
* @param queue The queue of data buffers to route
|
||||
* @return The number of bytes sent
|
||||
*/
|
||||
static int
|
||||
execute(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
{
|
||||
CLI_SESSION *session = (CLI_SESSION *)router_session;
|
||||
|
||||
/* Extract the characters */
|
||||
while (queue)
|
||||
{
|
||||
strncat(session->cmdbuf, GWBUF_DATA(queue), GWBUF_LENGTH(queue));
|
||||
queue = gwbuf_consume(queue, GWBUF_LENGTH(queue));
|
||||
}
|
||||
|
||||
execute_cmd(session);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display router diagnostics
|
||||
*
|
||||
* @param instance Instance of the router
|
||||
* @param dcb DCB to send diagnostics to
|
||||
*/
|
||||
static void
|
||||
diagnostics(ROUTER *instance, DCB *dcb)
|
||||
{
|
||||
return; /* Nothing to do currently */
|
||||
}
|
||||
|
||||
static uint8_t getCapabilities(
|
||||
ROUTER* inst,
|
||||
void* router_session)
|
||||
{
|
||||
return 0;
|
||||
}
|
@ -601,6 +601,8 @@ char *ptr, *lptr;
|
||||
|
||||
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 */
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user