Merge branch 'release-1.0GA' into MAX-324
Conflicts: server/MaxScale_template.cnf server/core/service.c
This commit is contained in:
commit
fed828eb29
@ -25,6 +25,7 @@ configure_file(${CMAKE_SOURCE_DIR}/server/include/version.h.in ${CMAKE_BINARY_DI
|
||||
configure_file(${CMAKE_SOURCE_DIR}/maxscale.conf.in ${CMAKE_BINARY_DIR}/maxscale.conf.prep @ONLY)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/etc/init.d/maxscale.in ${CMAKE_BINARY_DIR}/etc/init.d/maxscale.prep @ONLY)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/etc/ubuntu/init.d/maxscale.in ${CMAKE_BINARY_DIR}/etc/ubuntu/init.d/maxscale.prep @ONLY)
|
||||
configure_file(${CMAKE_SOURCE_DIR}/server/test/maxscale_test.h.in ${CMAKE_BINARY_DIR}/server/include/maxscale_test.h)
|
||||
|
||||
|
||||
set(CMAKE_C_FLAGS "-Wall -fPIC")
|
||||
@ -120,43 +121,49 @@ install(FILES server/MaxScale_template.cnf DESTINATION etc)
|
||||
install(FILES ${ERRMSG} DESTINATION mysql)
|
||||
install(FILES ${DOCS} DESTINATION Documentation)
|
||||
install(DIRECTORY DESTINATION log)
|
||||
# See if we are on a RPM-capable or DEB-capable system
|
||||
find_program(RPMBUILD rpmbuild)
|
||||
find_program(DEBBUILD dpkg-buildpackage)
|
||||
set(CPACK_GENERATOR "TGZ")
|
||||
if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) )
|
||||
message(STATUS "Generating RPM packages")
|
||||
set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM")
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
message(WARNING "CMake version is ${CMAKE_VERSION}. Building of packages requires version 2.8.12 or greater.")
|
||||
else()
|
||||
# See if we are on a RPM-capable or DEB-capable system
|
||||
find_program(RPMBUILD rpmbuild)
|
||||
find_program(DEBBUILD dpkg-buildpackage)
|
||||
set(CPACK_GENERATOR "TGZ")
|
||||
if(NOT ( ${RPMBUILD} STREQUAL "RPMBUILD-NOTFOUND" ) )
|
||||
message(STATUS "Generating RPM packages")
|
||||
set(CPACK_GENERATOR "${CPACK_GENERATOR};RPM")
|
||||
endif()
|
||||
|
||||
if(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) )
|
||||
set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB")
|
||||
execute_process(COMMAND dpgk --print-architecture OUTPUT_VARIABLE DEB_ARCHITECTURE)
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEB_ARCHITECTURE})
|
||||
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
message(STATUS "Generating DEB packages for ${DEB_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${MAXSCALE_VERSION_PATCH}")
|
||||
set(CPACK_PACKAGE_CONTACT "MariaDB Corporation Ab")
|
||||
set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}")
|
||||
set(CPACK_PACKAGE_NAME "maxscale")
|
||||
set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README)
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
set(CPACK_RPM_SPEC_INSTALL_POST "/sbin/ldconfig")
|
||||
set(CPACK_RPM_PACKAGE_NAME "maxscale")
|
||||
set(CPACK_RPM_PACKAGE_VENDOR "MariaDB Corporation Ab")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc /etc/ld.so.conf.d /etc/init.d /etc/rc.d/init.d")
|
||||
set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#")
|
||||
set(CPACK_RPM_USER_FILELIST "%ignore /etc/init.d")
|
||||
set(CPACK_RPM_USER_FILELIST "%ignore /etc/ld.so.conf.d")
|
||||
set(CPACK_RPM_USER_FILELIST "%ignore /etc")
|
||||
include(CPack)
|
||||
endif()
|
||||
|
||||
if(NOT ( ${DEBBUILD} STREQUAL "DEBBUILD-NOTFOUND" ) )
|
||||
set(CPACK_GENERATOR "${CPACK_GENERATOR};DEB")
|
||||
execute_process(COMMAND dpgk --print-architecture OUTPUT_VARIABLE DEB_ARCHITECTURE)
|
||||
set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${DEB_ARCHITECTURE})
|
||||
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
message(STATUS "Generating DEB packages for ${DEB_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "MaxScale")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR "${MAXSCALE_VERSION_MAJOR}")
|
||||
set(CPACK_PACKAGE_VERSION_MINOR "${MAXSCALE_VERSION_MINOR}")
|
||||
set(CPACK_PACKAGE_VERSION_PATCH "${MAXSCALE_VERSION_PATCH}")
|
||||
set(CPACK_PACKAGE_CONTACT "MariaDB Corporation Ab")
|
||||
set(CPACK_PACKAGE_FILE_NAME "maxscale-${MAXSCALE_VERSION}")
|
||||
set(CPACK_PACKAGE_NAME "maxscale")
|
||||
set(CPACK_PACKAGE_VENDOR "MariaDB Corporation Ab")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README)
|
||||
set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}")
|
||||
set(CPACK_RPM_SPEC_INSTALL_POST "/sbin/ldconfig")
|
||||
set(CPACK_RPM_PACKAGE_NAME "maxscale")
|
||||
set(CPACK_RPM_PACKAGE_VENDOR "MariaDB Corporation Ab")
|
||||
set(CPACK_RPM_PACKAGE_LICENSE "GPLv2")
|
||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION "/etc /etc/ld.so.conf.d /etc/init.d /etc/rc.d/init.d")
|
||||
set(CPACK_RPM_SPEC_MORE_DEFINE "%define ignore \#")
|
||||
set(CPACK_RPM_USER_FILELIST "%ignore /etc/init.d")
|
||||
set(CPACK_RPM_USER_FILELIST "%ignore /etc/ld.so.conf.d")
|
||||
set(CPACK_RPM_USER_FILELIST "%ignore /etc")
|
||||
include(CPack)
|
||||
|
||||
add_custom_target(buildtests
|
||||
COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DBUILD_TYPE=Debug -DINSTALL_DIR=${CMAKE_BINARY_DIR} -DINSTALL_SYSTEM_FILES=N ${CMAKE_SOURCE_DIR}
|
||||
@ -207,4 +214,4 @@ add_custom_target(testall-valgrind
|
||||
COMMAND killall maxscale
|
||||
COMMENT "Running full test suite with Valgrind..." VERBATIM)
|
||||
|
||||
endif()
|
||||
endif()
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Administration Tutorial.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Administration Tutorial.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale 1.0.3 Release Notes.pdf
Normal file
BIN
Documentation/MariaDB MaxScale 1.0.3 Release Notes.pdf
Normal file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Configuration Guide.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Configuration Guide.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Debug-And-Diagnostic-Support.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Debug-And-Diagnostic-Support.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Filter Tutorial.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Filter Tutorial.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Galera Cluster Connection Routing Tutorial.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Galera Cluster Connection Routing Tutorial.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Galera Cluster Read-Write Splitting Tutorial.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Galera Cluster Read-Write Splitting Tutorial.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale HA with Corosync-Pacemaker.pdf
Executable file
BIN
Documentation/MariaDB MaxScale HA with Corosync-Pacemaker.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale Limitations.pdf
Executable file
BIN
Documentation/MariaDB MaxScale Limitations.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale MaxAdmin.pdf
Executable file
BIN
Documentation/MariaDB MaxScale MaxAdmin.pdf
Executable file
Binary file not shown.
BIN
Documentation/MariaDB MaxScale MySQL Replication Connection Routing Tutorial.pdf
Executable file
BIN
Documentation/MariaDB MaxScale MySQL Replication Connection Routing Tutorial.pdf
Executable file
Binary file not shown.
Binary file not shown.
BIN
Documentation/MariaDB MaxScale_ Getting Started With MaxScale.pdf
Executable file
BIN
Documentation/MariaDB MaxScale_ Getting Started With MaxScale.pdf
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Documentation/filters/MariaDB MaxScale_ Filter_ Regex Filter.pdf
Executable file
BIN
Documentation/filters/MariaDB MaxScale_ Filter_ Regex Filter.pdf
Executable file
Binary file not shown.
BIN
Documentation/filters/MariaDB MaxScale_ Filter_ Tee Filter.pdf
Executable file
BIN
Documentation/filters/MariaDB MaxScale_ Filter_ Tee Filter.pdf
Executable file
Binary file not shown.
BIN
Documentation/filters/MariaDB MaxScale_ Filter_ TopN Filter.pdf
Executable file
BIN
Documentation/filters/MariaDB MaxScale_ Filter_ TopN Filter.pdf
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -2585,6 +2585,7 @@ static bool logfile_init(
|
||||
goto return_with_succp;
|
||||
}
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
if (store_shmem)
|
||||
{
|
||||
fprintf(stderr, "%s\t: %s->%s\n",
|
||||
@ -2598,6 +2599,7 @@ static bool logfile_init(
|
||||
STRLOGNAME(logfile_id),
|
||||
logfile->lf_full_file_name);
|
||||
}
|
||||
#endif
|
||||
succp = true;
|
||||
logfile->lf_state = RUN;
|
||||
CHK_LOGFILE(logfile);
|
||||
|
@ -117,7 +117,7 @@ int skygw_log_rotate(logfile_id_t id);
|
||||
int skygw_log_write_flush(logfile_id_t id, const char* format, ...);
|
||||
int skygw_log_enable(logfile_id_t id);
|
||||
int skygw_log_disable(logfile_id_t id);
|
||||
|
||||
void skygw_log_sync_all(void);
|
||||
|
||||
EXTERN_C_BLOCK_END
|
||||
|
||||
|
@ -9,9 +9,9 @@ macro(set_maxscale_version)
|
||||
#MaxScale version number
|
||||
set(MAXSCALE_VERSION_MAJOR "1")
|
||||
set(MAXSCALE_VERSION_MINOR "0")
|
||||
set(MAXSCALE_VERSION_PATCH "2")
|
||||
set(MAXSCALE_VERSION_PATCH "3")
|
||||
set(MAXSCALE_VERSION_NUMERIC "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}")
|
||||
set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}-beta")
|
||||
set(MAXSCALE_VERSION "${MAXSCALE_VERSION_MAJOR}.${MAXSCALE_VERSION_MINOR}.${MAXSCALE_VERSION_PATCH}-rc")
|
||||
|
||||
endmacro()
|
||||
|
||||
@ -36,7 +36,7 @@ macro(set_variables)
|
||||
set(TEST_PORT_DB "4010" CACHE STRING "port of dbshard router module")
|
||||
|
||||
# port of read/write split router module with hints
|
||||
set(TEST_PORT_RW_HINT "4006" CACHE STRING "port of read/write split router module with hints")
|
||||
set(TEST_PORT_RW_HINT "4009" CACHE STRING "port of read/write split router module with hints")
|
||||
|
||||
# master test server server_id
|
||||
set(TEST_MASTER_ID "3000" CACHE STRING "master test server server_id")
|
||||
|
@ -151,7 +151,7 @@ bool parse_query (
|
||||
THD* thd;
|
||||
uint8_t* data;
|
||||
size_t len;
|
||||
char* query_str;
|
||||
char* query_str = NULL;
|
||||
parsing_info_t* pi;
|
||||
|
||||
CHK_GWBUF(querybuf);
|
||||
@ -173,9 +173,9 @@ bool parse_query (
|
||||
/** Extract query and copy it to different buffer */
|
||||
data = (uint8_t*)GWBUF_DATA(querybuf);
|
||||
len = MYSQL_GET_PACKET_LEN(data)-1; /*< distract 1 for packet type byte */
|
||||
query_str = (char *)malloc(len+1);
|
||||
|
||||
if (query_str == NULL)
|
||||
|
||||
if (len < 1 || len >= ~((size_t)0) - 1 || (query_str = (char *)malloc(len+1)) == NULL)
|
||||
{
|
||||
/** Free parsing info data */
|
||||
parsing_info_done(pi);
|
||||
|
@ -184,7 +184,7 @@ replace=select
|
||||
# max_slave_replication_lag=<allowed lag in seconds for a slave>
|
||||
#
|
||||
# Valid router modules currently are:
|
||||
# readwritesplit, readconnroute, dbshard, debugcli and CLI
|
||||
# readwritesplit, readconnroute, debugcli and CLI
|
||||
#
|
||||
## Examples:
|
||||
|
||||
@ -208,25 +208,13 @@ passwd=mypwd
|
||||
#router_options=slave_selection_criteria=
|
||||
#filters=fetch|qla
|
||||
|
||||
[DBShard Router]
|
||||
type=service
|
||||
router=dbshard
|
||||
servers=server1,server2
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
|
||||
[HTTPD Router]
|
||||
type=service
|
||||
router=testroute
|
||||
servers=server1,server2,server3
|
||||
|
||||
[Debug Interface]
|
||||
type=service
|
||||
router=debugcli
|
||||
|
||||
[CLI]
|
||||
type=service
|
||||
router=CLI
|
||||
router=cli
|
||||
|
||||
## Listener definitions for the services
|
||||
#
|
||||
@ -270,12 +258,6 @@ protocol=MySQLClient
|
||||
port=4006
|
||||
#socket=/tmp/rwsplit.sock
|
||||
|
||||
[DBShard Listener]
|
||||
type=listener
|
||||
service=DBShard Router
|
||||
protocol=MySQLClient
|
||||
port=4010
|
||||
|
||||
[Debug Listener]
|
||||
type=listener
|
||||
service=Debug Interface
|
||||
@ -283,12 +265,6 @@ protocol=telnetd
|
||||
#address=127.0.0.1
|
||||
port=4442
|
||||
|
||||
[HTTPD Listener]
|
||||
type=listener
|
||||
service=HTTPD Router
|
||||
protocol=HTTPD
|
||||
port=6444
|
||||
|
||||
[CLI Listener]
|
||||
type=listener
|
||||
service=CLI
|
||||
|
@ -179,7 +179,7 @@ gwbuf_clone(GWBUF *buf)
|
||||
{
|
||||
GWBUF *rval;
|
||||
|
||||
if ((rval = (GWBUF *)malloc(sizeof(GWBUF))) == NULL)
|
||||
if ((rval = (GWBUF *)calloc(1,sizeof(GWBUF))) == NULL)
|
||||
{
|
||||
ss_dassert(rval != NULL);
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -194,16 +194,43 @@ GWBUF *rval;
|
||||
rval->start = buf->start;
|
||||
rval->end = buf->end;
|
||||
rval->gwbuf_type = buf->gwbuf_type;
|
||||
rval->properties = NULL;
|
||||
rval->hint = NULL;
|
||||
rval->gwbuf_info = buf->gwbuf_info;
|
||||
rval->gwbuf_bufobj = buf->gwbuf_bufobj;
|
||||
rval->next = NULL;
|
||||
rval->tail = rval;
|
||||
CHK_GWBUF(rval);
|
||||
return rval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone whole GWBUF list instead of single buffer.
|
||||
*
|
||||
* @param buf head of the list to be cloned till the tail of it
|
||||
*
|
||||
* @return head of the cloned list or NULL if the list was empty.
|
||||
*/
|
||||
GWBUF* gwbuf_clone_all(
|
||||
GWBUF* buf)
|
||||
{
|
||||
GWBUF* rval;
|
||||
GWBUF* clonebuf;
|
||||
|
||||
if (buf == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
/** Store the head of the list to rval. */
|
||||
clonebuf = gwbuf_clone(buf);
|
||||
rval = clonebuf;
|
||||
|
||||
while (buf->next)
|
||||
{
|
||||
buf = buf->next;
|
||||
clonebuf->next = gwbuf_clone(buf);
|
||||
clonebuf = clonebuf->next;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
GWBUF *gwbuf_clone_portion(
|
||||
GWBUF *buf,
|
||||
|
@ -1891,7 +1891,7 @@ config_truth_value(char *str)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (strcasecmp(str, "flase") == 0 || strcasecmp(str, "off") == 0)
|
||||
if (strcasecmp(str, "false") == 0 || strcasecmp(str, "off") == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -52,8 +52,32 @@
|
||||
#include <mysqld_error.h>
|
||||
|
||||
|
||||
#define DEFAULT_CONNECT_TIMEOUT 3
|
||||
#define DEFAULT_READ_TIMEOUT 1
|
||||
#define DEFAULT_WRITE_TIMEOUT 2
|
||||
|
||||
#define USERS_QUERY_NO_ROOT " AND user NOT IN ('root')"
|
||||
#define LOAD_MYSQL_USERS_QUERY "SELECT user, host, password, concat(user,host,password,Select_priv) AS userdata, Select_priv AS anydb FROM mysql.user WHERE user IS NOT NULL AND user <> ''"
|
||||
|
||||
#if 0
|
||||
# define LOAD_MYSQL_USERS_QUERY \
|
||||
"SELECT DISTINCT \
|
||||
user.user AS user, \
|
||||
user.host AS host, \
|
||||
user.password AS password, \
|
||||
concat(user.user,user.host,user.password, \
|
||||
IF((user.Select_priv+0)||find_in_set('Select',Coalesce(tp.Table_priv,0)),'Y','N') , \
|
||||
COALESCE( db.db,tp.db, '')) AS userdata, \
|
||||
user.Select_priv AS anydb, \
|
||||
COALESCE( db.db,tp.db, NULL) AS db \
|
||||
FROM \
|
||||
mysql.user LEFT JOIN \
|
||||
mysql.db ON user.user=db.user AND user.host=db.host LEFT JOIN \
|
||||
mysql.tables_priv tp ON user.user=tp.user AND user.host=tp.host \
|
||||
WHERE user.user IS NOT NULL AND user.user <> ''"
|
||||
|
||||
#else
|
||||
# define LOAD_MYSQL_USERS_QUERY "SELECT user, host, password, concat(user,host,password,Select_priv) AS userdata, Select_priv AS anydb FROM mysql.user WHERE user IS NOT NULL AND user <> ''"
|
||||
#endif
|
||||
#define MYSQL_USERS_COUNT "SELECT COUNT(1) AS nusers FROM mysql.user"
|
||||
|
||||
#define MYSQL_USERS_WITH_DB_ORDER " ORDER BY host DESC"
|
||||
@ -85,6 +109,11 @@ void *resource_fetch(HASHTABLE *, char *);
|
||||
int resource_add(HASHTABLE *, char *, char *);
|
||||
int resource_hash(char *);
|
||||
static int normalize_hostname(char *input_host, char *output_host);
|
||||
static int gw_mysql_set_timeouts(
|
||||
MYSQL* handle,
|
||||
int read_timeout,
|
||||
int write_timeout,
|
||||
int connect_timeout);
|
||||
|
||||
/**
|
||||
* Load the user/passwd form mysql.user table into the service users' hashtable
|
||||
@ -219,7 +248,7 @@ HASHTABLE *oldresources;
|
||||
int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host, char *passwd, char *anydb, char *db) {
|
||||
struct sockaddr_in serv_addr;
|
||||
MYSQL_USER_HOST key;
|
||||
char ret_ip[INET_ADDRSTRLEN + 1]="";
|
||||
char ret_ip[400]="";
|
||||
int ret = 0;
|
||||
|
||||
if (users == NULL || user == NULL || host == NULL) {
|
||||
@ -409,7 +438,8 @@ getDatabases(SERVICE *service, MYSQL *con)
|
||||
*
|
||||
* @param service The current service
|
||||
* @param users The users table into which to load the users
|
||||
* @return -1 on any error or the number of users inserted (0 means no users at all)
|
||||
* @return -1 on any error or the number of users inserted
|
||||
* (0 means no users at all)
|
||||
*/
|
||||
static int
|
||||
getUsers(SERVICE *service, USERS *users)
|
||||
@ -421,20 +451,24 @@ getUsers(SERVICE *service, USERS *users)
|
||||
char *service_passwd = NULL;
|
||||
char *dpwd;
|
||||
int total_users = 0;
|
||||
SERVER *server;
|
||||
SERVER_REF *server;
|
||||
char *users_query;
|
||||
unsigned char hash[SHA_DIGEST_LENGTH]="";
|
||||
char *users_data = NULL;
|
||||
int nusers = 0;
|
||||
int users_data_row_len = MYSQL_USER_MAXLEN + MYSQL_HOST_MAXLEN + MYSQL_PASSWORD_LEN + sizeof(char) + MYSQL_DATABASE_MAXLEN;
|
||||
int users_data_row_len = MYSQL_USER_MAXLEN +
|
||||
MYSQL_HOST_MAXLEN +
|
||||
MYSQL_PASSWORD_LEN +
|
||||
sizeof(char) +
|
||||
MYSQL_DATABASE_MAXLEN;
|
||||
int dbnames = 0;
|
||||
int db_grants = 0;
|
||||
|
||||
serviceGetUser(service, &service_user, &service_passwd);
|
||||
|
||||
if (service_user == NULL || service_passwd == NULL)
|
||||
return -1;
|
||||
|
||||
if (serviceGetUser(service, &service_user, &service_passwd) == 0)
|
||||
{
|
||||
ss_dassert(service_passwd == NULL || service_user == NULL);
|
||||
return -1;
|
||||
}
|
||||
con = mysql_init(NULL);
|
||||
|
||||
if (con == NULL) {
|
||||
@ -444,13 +478,26 @@ getUsers(SERVICE *service, USERS *users)
|
||||
mysql_error(con))));
|
||||
return -1;
|
||||
}
|
||||
/** Set read, write and connect timeout values */
|
||||
if (gw_mysql_set_timeouts(con,
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
DEFAULT_WRITE_TIMEOUT,
|
||||
DEFAULT_CONNECT_TIMEOUT))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : failed to set timeout values for backend "
|
||||
"connection.")));
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL)) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : failed to set external connection. "
|
||||
"It is needed for backend server connections. "
|
||||
"Exiting.")));
|
||||
"It is needed for backend server connections.")));
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
/**
|
||||
@ -458,46 +505,69 @@ getUsers(SERVICE *service, USERS *users)
|
||||
* out of databases
|
||||
* to try
|
||||
*/
|
||||
server = service->databases;
|
||||
server = service->dbref;
|
||||
dpwd = decryptPassword(service_passwd);
|
||||
|
||||
/* Select a server with Master bit, if available */
|
||||
while (server != NULL && !(server->status & SERVER_MASTER)) {
|
||||
server = server->nextdb;
|
||||
while (server != NULL && !(server->server->status & SERVER_MASTER)) {
|
||||
server = server->next;
|
||||
}
|
||||
|
||||
if (service->svc_do_shutdown)
|
||||
{
|
||||
free(dpwd);
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Try loading data from master server */
|
||||
if (server != NULL && (mysql_real_connect(con, server->name, service_user, dpwd, NULL, server->port, NULL, 0) != NULL)) {
|
||||
if (server != NULL &&
|
||||
(mysql_real_connect(con,
|
||||
server->server->name, service_user,
|
||||
dpwd,
|
||||
NULL,
|
||||
server->server->port,
|
||||
NULL, 0) != NULL))
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"Dbusers : Loading data from backend database with Master role [%s:%i] "
|
||||
"for service [%s]",
|
||||
server->name,
|
||||
server->port,
|
||||
"Dbusers : Loading data from backend database with "
|
||||
"Master role [%s:%i] for service [%s]",
|
||||
server->server->name,
|
||||
server->server->port,
|
||||
service->name)));
|
||||
} else {
|
||||
/* load data from other servers via loop */
|
||||
server = service->databases;
|
||||
server = service->dbref;
|
||||
|
||||
while (server != NULL && (mysql_real_connect(con,
|
||||
server->name,
|
||||
service_user,
|
||||
dpwd,
|
||||
NULL,
|
||||
server->port,
|
||||
NULL,
|
||||
0) == NULL))
|
||||
while (!service->svc_do_shutdown &&
|
||||
server != NULL &&
|
||||
(mysql_real_connect(con,
|
||||
server->server->name,
|
||||
service_user,
|
||||
dpwd,
|
||||
NULL,
|
||||
server->server->port,
|
||||
NULL,
|
||||
0) == NULL))
|
||||
{
|
||||
server = server->nextdb;
|
||||
server = server->next;
|
||||
}
|
||||
|
||||
|
||||
if (service->svc_do_shutdown)
|
||||
{
|
||||
free(dpwd);
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (server != NULL) {
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"Dbusers : Loading data from backend database [%s:%i] "
|
||||
"for service [%s]",
|
||||
server->name,
|
||||
server->port,
|
||||
"Dbusers : Loading data from backend database "
|
||||
"[%s:%i] for service [%s]",
|
||||
server->server->name,
|
||||
server->server->port,
|
||||
service->name)));
|
||||
}
|
||||
}
|
||||
@ -515,9 +585,7 @@ getUsers(SERVICE *service, USERS *users)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* count users */
|
||||
|
||||
/* start with users and db grants for users */
|
||||
/** Count users. Start with users and db grants for users */
|
||||
if (mysql_query(con, MYSQL_USERS_WITH_DB_COUNT)) {
|
||||
if (mysql_errno(con) != ER_TABLEACCESS_DENIED_ERROR) {
|
||||
/* This is an error we cannot handle, return */
|
||||
@ -1193,3 +1261,58 @@ int useorig = 0;
|
||||
|
||||
return netmask;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set read, write and connect timeout values for MySQL database connection.
|
||||
*
|
||||
* @param handle MySQL handle
|
||||
* @param read_timeout Read timeout value in seconds
|
||||
* @param write_timeout Write timeout value in seconds
|
||||
* @param connect_timeout Connect timeout value in seconds
|
||||
*
|
||||
* @return 0 if succeed, 1 if failed
|
||||
*/
|
||||
static int gw_mysql_set_timeouts(
|
||||
MYSQL* handle,
|
||||
int read_timeout,
|
||||
int write_timeout,
|
||||
int connect_timeout)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = mysql_options(handle,
|
||||
MYSQL_OPT_READ_TIMEOUT,
|
||||
(void *)&read_timeout)))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : failed to set read timeout for backend "
|
||||
"connection.")));
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if ((rc = mysql_options(handle,
|
||||
MYSQL_OPT_CONNECT_TIMEOUT,
|
||||
(void *)&connect_timeout)))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : failed to set connect timeout for backend "
|
||||
"connection.")));
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if ((rc = mysql_options(handle,
|
||||
MYSQL_OPT_WRITE_TIMEOUT,
|
||||
(void *)&write_timeout)))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : failed to set write timeout for backend "
|
||||
"connection.")));
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
retblock:
|
||||
return rc;
|
||||
}
|
@ -190,7 +190,7 @@ DCB *rval;
|
||||
rval->readcheck = 0;
|
||||
rval->polloutbusy = 0;
|
||||
rval->writecheck = 0;
|
||||
rval->fd = -1;
|
||||
rval->fd = DCBFD_CLOSED;
|
||||
|
||||
rval->evq.next = NULL;
|
||||
rval->evq.prev = NULL;
|
||||
@ -235,8 +235,10 @@ DCB *rval;
|
||||
void
|
||||
dcb_free(DCB *dcb)
|
||||
{
|
||||
if (dcb->fd == -1)
|
||||
if (dcb->fd == DCBFD_CLOSED)
|
||||
{
|
||||
dcb_final_free(dcb);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -308,7 +310,7 @@ DCB *clone;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
clone->fd = -1;
|
||||
clone->fd = DCBFD_CLOSED;
|
||||
clone->flags |= DCBF_CLONE;
|
||||
clone->state = orig->state;
|
||||
clone->data = orig->data;
|
||||
@ -319,7 +321,10 @@ DCB *clone;
|
||||
clone->protocol = orig->protocol;
|
||||
|
||||
clone->func.write = dcb_null_write;
|
||||
clone->func.close = dcb_null_close;
|
||||
/**
|
||||
* Close triggers closing of router session as well which is needed.
|
||||
*/
|
||||
clone->func.close = orig->func.close;
|
||||
clone->func.auth = dcb_null_auth;
|
||||
|
||||
return clone;
|
||||
@ -383,22 +388,25 @@ DCB_CALLBACK *cb;
|
||||
*/
|
||||
{
|
||||
SESSION *local_session = dcb->session;
|
||||
dcb->session = NULL;
|
||||
CHK_SESSION(local_session);
|
||||
/*<
|
||||
* Remove reference from session if dcb is client.
|
||||
*/
|
||||
if (local_session->client == dcb) {
|
||||
local_session->client = NULL;
|
||||
}
|
||||
dcb->session = NULL;
|
||||
/**
|
||||
* Set session's client pointer NULL so that other threads
|
||||
* won't try to call dcb_close for client DCB
|
||||
* after this call.
|
||||
*/
|
||||
if (local_session->client == dcb)
|
||||
{
|
||||
spinlock_acquire(&local_session->ses_lock);
|
||||
local_session->client = NULL;
|
||||
spinlock_release(&local_session->ses_lock);
|
||||
}
|
||||
session_free(local_session);
|
||||
}
|
||||
}
|
||||
|
||||
if (dcb->protocol && ((dcb->flags & DCBF_CLONE) ==0))
|
||||
free(dcb->protocol);
|
||||
if (dcb->data && ((dcb->flags & DCBF_CLONE) ==0))
|
||||
free(dcb->data);
|
||||
if (dcb->protocol && (!DCB_IS_CLONE(dcb)))
|
||||
free(dcb->protocol);
|
||||
if (dcb->remote)
|
||||
free(dcb->remote);
|
||||
if (dcb->user)
|
||||
@ -423,7 +431,6 @@ DCB_CALLBACK *cb;
|
||||
}
|
||||
spinlock_release(&dcb->cb_lock);
|
||||
|
||||
|
||||
bitmask_free(&dcb->memdata.bitmask);
|
||||
free(dcb);
|
||||
}
|
||||
@ -551,40 +558,42 @@ bool succp = false;
|
||||
DCB* dcb_next = NULL;
|
||||
int rc = 0;
|
||||
|
||||
/*<
|
||||
* Close file descriptor and move to clean-up phase.
|
||||
*/
|
||||
rc = close(dcb->fd);
|
||||
if (dcb->fd > 0)
|
||||
{
|
||||
/*<
|
||||
* Close file descriptor and move to clean-up phase.
|
||||
*/
|
||||
rc = close(dcb->fd);
|
||||
|
||||
if (rc < 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to close "
|
||||
"socket %d on dcb %p due error %d, %s.",
|
||||
dcb->fd,
|
||||
dcb,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
}
|
||||
#if defined(SS_DEBUG)
|
||||
else {
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_process_zombies] Closed socket "
|
||||
"%d on dcb %p.",
|
||||
pthread_self(),
|
||||
dcb->fd,
|
||||
dcb)));
|
||||
#endif /* SS_DEBUG */
|
||||
if (rc < 0)
|
||||
{
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to close "
|
||||
"socket %d on dcb %p due error %d, %s.",
|
||||
dcb->fd,
|
||||
dcb,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb->fd = DCBFD_CLOSED;
|
||||
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_process_zombies] Closed socket "
|
||||
"%d on dcb %p.",
|
||||
pthread_self(),
|
||||
dcb->fd,
|
||||
dcb)));
|
||||
#if defined(FAKE_CODE)
|
||||
conn_open[dcb->fd] = false;
|
||||
conn_open[dcb->fd] = false;
|
||||
#endif /* FAKE_CODE */
|
||||
#if defined(SS_DEBUG)
|
||||
ss_debug(dcb->fd = -1;)
|
||||
}
|
||||
#endif /* SS_DEBUG */
|
||||
}
|
||||
}
|
||||
LOGIF_MAYBE(LT, (dcb_get_ses_log_info(
|
||||
dcb,
|
||||
&tls_log_info.li_sesid,
|
||||
@ -657,7 +666,7 @@ int rc;
|
||||
}
|
||||
fd = dcb->func.connect(dcb, server, session);
|
||||
|
||||
if (fd == -1) {
|
||||
if (fd == DCBFD_CLOSED) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_connect] Failed to connect to server %s:%d, "
|
||||
@ -683,7 +692,7 @@ int rc;
|
||||
session->client,
|
||||
session->client->fd)));
|
||||
}
|
||||
ss_dassert(dcb->fd == -1); /*< must be uninitialized at this point */
|
||||
ss_dassert(dcb->fd == DCBFD_CLOSED); /*< must be uninitialized at this point */
|
||||
/*<
|
||||
* Successfully connected to backend. Assign file descriptor to dcb
|
||||
*/
|
||||
@ -704,7 +713,7 @@ int rc;
|
||||
*/
|
||||
rc = poll_add_dcb(dcb);
|
||||
|
||||
if (rc == -1) {
|
||||
if (rc == DCBFD_CLOSED) {
|
||||
dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
|
||||
dcb_final_free(dcb);
|
||||
return NULL;
|
||||
@ -736,11 +745,22 @@ int dcb_read(
|
||||
GWBUF *buffer = NULL;
|
||||
int b;
|
||||
int rc;
|
||||
int n ;
|
||||
int n;
|
||||
int nread = 0;
|
||||
|
||||
CHK_DCB(dcb);
|
||||
while (true)
|
||||
|
||||
if (dcb->fd <= 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Read failed, dcb is %s.",
|
||||
dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not readable")));
|
||||
n = 0;
|
||||
goto return_n;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
int bufsize;
|
||||
|
||||
@ -864,6 +884,14 @@ int below_water;
|
||||
below_water = (dcb->high_water && dcb->writeqlen < dcb->high_water) ? 1 : 0;
|
||||
ss_dassert(queue != NULL);
|
||||
|
||||
if (dcb->fd <= 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Write failed, dcb is %s.",
|
||||
dcb->fd == DCBFD_CLOSED ? "closed" : "cloned, not writable")));
|
||||
return 0;
|
||||
}
|
||||
/**
|
||||
* SESSION_STATE_STOPPING means that one of the backends is closing
|
||||
* the router session. Some backends may have not completed
|
||||
@ -877,7 +905,8 @@ int below_water;
|
||||
dcb->state != DCB_STATE_POLLING &&
|
||||
dcb->state != DCB_STATE_LISTENING &&
|
||||
dcb->state != DCB_STATE_NOPOLLING &&
|
||||
dcb->session->state != SESSION_STATE_STOPPING))
|
||||
(dcb->session == NULL ||
|
||||
dcb->session->state != SESSION_STATE_STOPPING)))
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
@ -888,7 +917,7 @@ int below_water;
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state),
|
||||
dcb->fd)));
|
||||
ss_dassert(false);
|
||||
//ss_dassert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1209,46 +1238,42 @@ dcb_close(DCB *dcb)
|
||||
*/
|
||||
if (dcb->state == DCB_STATE_POLLING)
|
||||
{
|
||||
if (dcb->fd != -1)
|
||||
{
|
||||
rc = poll_remove_dcb(dcb);
|
||||
rc = poll_remove_dcb(dcb);
|
||||
|
||||
if (rc == 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_close] Removed dcb %p in state %s from "
|
||||
"poll set.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Removing DCB fd == %d in state %s from "
|
||||
"poll set failed.",
|
||||
dcb->fd,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
if (rc == 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [dcb_close] Removed dcb %p in state %s from "
|
||||
"poll set.",
|
||||
pthread_self(),
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
} else {
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Removing DCB fd == %d in state %s from "
|
||||
"poll set failed.",
|
||||
dcb->fd,
|
||||
STRDCBSTATE(dcb->state))));
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
{
|
||||
/**
|
||||
* close protocol and router session
|
||||
*/
|
||||
if (dcb->func.close != NULL)
|
||||
{
|
||||
/**
|
||||
* close protocol and router session
|
||||
*/
|
||||
if (dcb->func.close != NULL)
|
||||
{
|
||||
dcb->func.close(dcb);
|
||||
}
|
||||
dcb_call_callback(dcb, DCB_REASON_CLOSE);
|
||||
|
||||
|
||||
if (dcb->state == DCB_STATE_NOPOLLING)
|
||||
{
|
||||
dcb_add_to_zombieslist(dcb);
|
||||
}
|
||||
dcb->func.close(dcb);
|
||||
}
|
||||
/** Call possible callback for this DCB in case of close */
|
||||
dcb_call_callback(dcb, DCB_REASON_CLOSE);
|
||||
|
||||
if (dcb->state == DCB_STATE_NOPOLLING)
|
||||
{
|
||||
dcb_add_to_zombieslist(dcb);
|
||||
}
|
||||
}
|
||||
|
||||
ss_dassert(dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE);
|
||||
}
|
||||
@ -1585,7 +1610,9 @@ void dcb_hashtable_stats(
|
||||
hashsize);
|
||||
|
||||
dcb_printf(dcb, "\tNo. of entries: %d\n", total);
|
||||
dcb_printf(dcb, "\tAverage chain length: %.1f\n", (float)total / hashsize);
|
||||
dcb_printf(dcb,
|
||||
"\tAverage chain length: %.1f\n",
|
||||
(hashsize == 0 ? (float)hashsize : (float)total / hashsize));
|
||||
dcb_printf(dcb, "\tLongest chain length: %d\n", longest);
|
||||
}
|
||||
|
||||
@ -1757,14 +1784,16 @@ static bool dcb_set_state_nomutex(
|
||||
* @param dcb The DCB to write buffer
|
||||
* @param buf Buffer to write
|
||||
* @param nbytes Number of bytes to write
|
||||
* @return Number of written bytes
|
||||
*/
|
||||
int
|
||||
gw_write(DCB *dcb, const void *buf, size_t nbytes)
|
||||
{
|
||||
int w;
|
||||
int w = 0;
|
||||
int fd = dcb->fd;
|
||||
#if defined(FAKE_CODE)
|
||||
if (dcb_fake_write_errno[fd] != 0) {
|
||||
if (fd > 0 && dcb_fake_write_errno[fd] != 0)
|
||||
{
|
||||
ss_dassert(dcb_fake_write_ev[fd] != 0);
|
||||
w = write(fd, buf, nbytes/2); /*< leave peer to read missing bytes */
|
||||
|
||||
@ -1772,11 +1801,15 @@ gw_write(DCB *dcb, const void *buf, size_t nbytes)
|
||||
w = -1;
|
||||
errno = dcb_fake_write_errno[fd];
|
||||
}
|
||||
} else {
|
||||
} else if (fd > 0)
|
||||
{
|
||||
w = write(fd, buf, nbytes);
|
||||
}
|
||||
#else
|
||||
w = write(fd, buf, nbytes);
|
||||
if (fd > 0)
|
||||
{
|
||||
w = write(fd, buf, nbytes);
|
||||
}
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
#if defined(SS_DEBUG_MYSQL)
|
||||
@ -1958,6 +1991,12 @@ DCB_CALLBACK *cb, *nextcb;
|
||||
{
|
||||
nextcb = cb->next;
|
||||
spinlock_release(&dcb->cb_lock);
|
||||
|
||||
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
|
||||
"%lu [dcb_call_callback] %s",
|
||||
pthread_self(),
|
||||
STRDCBREASON(reason))));
|
||||
|
||||
cb->cb(dcb, reason, cb->userdata);
|
||||
spinlock_acquire(&dcb->cb_lock);
|
||||
cb = nextcb;
|
||||
@ -2043,6 +2082,10 @@ dcb_get_next (DCB* dcb)
|
||||
void
|
||||
dcb_call_foreach(DCB_REASON reason)
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
|
||||
"%lu [dcb_call_foreach]",
|
||||
pthread_self())));
|
||||
|
||||
switch (reason) {
|
||||
case DCB_REASON_CLOSE:
|
||||
case DCB_REASON_DRAINED:
|
||||
@ -2075,7 +2118,7 @@ dcb_call_foreach(DCB_REASON reason)
|
||||
|
||||
/**
|
||||
* Null protocol write routine used for cloned dcb's. It merely consumes
|
||||
* buffers written on the cloned DCB.
|
||||
* buffers written on the cloned DCB and sets the DCB_REPLIED flag.
|
||||
*
|
||||
* @param dcb The descriptor control block
|
||||
* @param buf The buffer being written
|
||||
@ -2088,6 +2131,9 @@ dcb_null_write(DCB *dcb, GWBUF *buf)
|
||||
{
|
||||
buf = gwbuf_consume(buf, GWBUF_LENGTH(buf));
|
||||
}
|
||||
|
||||
dcb->flags |= DCBF_REPLIED;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -91,28 +91,31 @@ filter_free(FILTER_DEF *filter)
|
||||
{
|
||||
FILTER_DEF *ptr;
|
||||
|
||||
/* First of all remove from the linked list */
|
||||
spinlock_acquire(&filter_spin);
|
||||
if (allFilters == filter)
|
||||
if (filter)
|
||||
{
|
||||
allFilters = filter->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = allFilters;
|
||||
while (ptr && ptr->next != filter)
|
||||
/* First of all remove from the linked list */
|
||||
spinlock_acquire(&filter_spin);
|
||||
if (allFilters == filter)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
allFilters = filter->next;
|
||||
}
|
||||
if (ptr)
|
||||
ptr->next = filter->next;
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
else
|
||||
{
|
||||
ptr = allFilters;
|
||||
while (ptr && ptr->next != filter)
|
||||
{
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (ptr)
|
||||
ptr->next = filter->next;
|
||||
}
|
||||
spinlock_release(&filter_spin);
|
||||
|
||||
/* Clean up session and free the memory */
|
||||
free(filter->name);
|
||||
free(filter->module);
|
||||
free(filter);
|
||||
/* Clean up session and free the memory */
|
||||
free(filter->name);
|
||||
free(filter->module);
|
||||
free(filter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include <config.h>
|
||||
#include <poll.h>
|
||||
#include <housekeeper.h>
|
||||
#include <service.h>
|
||||
#include <memlog.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
@ -572,7 +573,7 @@ return_succp:
|
||||
static bool resolve_maxscale_homedir(
|
||||
char** p_home_dir)
|
||||
{
|
||||
bool succp;
|
||||
bool succp = false;
|
||||
char* tmp;
|
||||
char* tmp2;
|
||||
char* log_context = NULL;
|
||||
@ -1837,7 +1838,8 @@ return_main:
|
||||
void
|
||||
shutdown_server()
|
||||
{
|
||||
poll_shutdown();
|
||||
service_shutdown();
|
||||
poll_shutdown();
|
||||
hkshutdown();
|
||||
memlog_flush_all();
|
||||
log_flush_shutdown();
|
||||
|
@ -81,7 +81,7 @@ setipaddress(struct in_addr *a, char *p) {
|
||||
if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : getaddrinfo failed for [%s] due [%s]",
|
||||
"Error: Failed to obtain address for host %s, %s",
|
||||
p,
|
||||
gai_strerror(rc))));
|
||||
|
||||
@ -94,7 +94,7 @@ setipaddress(struct in_addr *a, char *p) {
|
||||
if ((rc = getaddrinfo(p, NULL, &hint, &ai)) != 0) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : getaddrinfo failed for [%s] due [%s]",
|
||||
"Error: Failed to obtain address for host %s, %s",
|
||||
p,
|
||||
gai_strerror(rc))));
|
||||
|
||||
|
@ -449,28 +449,33 @@ void hashtable_get_stats(
|
||||
int i;
|
||||
int j;
|
||||
|
||||
ht = (HASHTABLE *)table;
|
||||
CHK_HASHTABLE(ht);
|
||||
*nelems = 0;
|
||||
*longest = 0;
|
||||
hashtable_read_lock(ht);
|
||||
|
||||
for (i = 0; i < ht->hashsize; i++)
|
||||
*nelems = 0;
|
||||
*longest = 0;
|
||||
*hashsize = 0;
|
||||
|
||||
if (table != NULL)
|
||||
{
|
||||
j = 0;
|
||||
entries = ht->entries[i];
|
||||
while (entries)
|
||||
ht = (HASHTABLE *)table;
|
||||
CHK_HASHTABLE(ht);
|
||||
hashtable_read_lock(ht);
|
||||
|
||||
for (i = 0; i < ht->hashsize; i++)
|
||||
{
|
||||
j++;
|
||||
entries = entries->next;
|
||||
j = 0;
|
||||
entries = ht->entries[i];
|
||||
while (entries)
|
||||
{
|
||||
j++;
|
||||
entries = entries->next;
|
||||
}
|
||||
*nelems += j;
|
||||
if (j > *longest) {
|
||||
*longest = j;
|
||||
}
|
||||
}
|
||||
*nelems += j;
|
||||
if (j > *longest) {
|
||||
*longest = j;
|
||||
}
|
||||
*hashsize = ht->hashsize;
|
||||
hashtable_read_unlock(ht);
|
||||
}
|
||||
*hashsize = ht->hashsize;
|
||||
hashtable_read_unlock(ht);
|
||||
}
|
||||
|
||||
|
||||
@ -503,7 +508,7 @@ hashtable_read_lock(HASHTABLE *table)
|
||||
;
|
||||
spinlock_acquire(&table->spin);
|
||||
}
|
||||
table->n_readers++;
|
||||
atomic_add(&table->n_readers, 1);
|
||||
spinlock_release(&table->spin);
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,40 @@ unsigned char *ptr;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the length of MySQL packet and how much is missing from the GWBUF
|
||||
* passed as parameter.
|
||||
*
|
||||
* This routine assumes that there is only one MySQL packet in the buffer.
|
||||
*
|
||||
* @param buf buffer list including the query, may consist of
|
||||
* multiple buffers
|
||||
* @param nbytes_missing pointer to missing bytecount
|
||||
*
|
||||
* @return the length of MySQL packet and writes missing bytecount to
|
||||
* nbytes_missing.
|
||||
*/
|
||||
int modutil_MySQL_query_len(
|
||||
GWBUF* buf,
|
||||
int* nbytes_missing)
|
||||
{
|
||||
int len;
|
||||
int buflen;
|
||||
|
||||
if (!modutil_is_SQL(buf))
|
||||
{
|
||||
len = 0;
|
||||
goto retblock;
|
||||
}
|
||||
len = MYSQL_GET_PACKET_LEN((uint8_t *)GWBUF_DATA(buf));
|
||||
*nbytes_missing = len-1;
|
||||
buflen = gwbuf_length(buf);
|
||||
|
||||
*nbytes_missing -= buflen-5;
|
||||
|
||||
retblock:
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@ -178,7 +212,7 @@ GWBUF *addition;
|
||||
|
||||
/**
|
||||
* Extract the SQL from a COM_QUERY packet and return in a NULL terminated buffer.
|
||||
* The buffer shoudl be freed by the caller when it is no longer required.
|
||||
* The buffer should be freed by the caller when it is no longer required.
|
||||
*
|
||||
* If the packet is not a COM_QUERY packet then the function will return NULL
|
||||
*
|
||||
@ -234,7 +268,7 @@ modutil_get_query(GWBUF *buf)
|
||||
uint8_t* packet;
|
||||
mysql_server_cmd_t packet_type;
|
||||
size_t len;
|
||||
char* query_str;
|
||||
char* query_str = NULL;
|
||||
|
||||
packet = GWBUF_DATA(buf);
|
||||
packet_type = packet[4];
|
||||
@ -252,7 +286,7 @@ modutil_get_query(GWBUF *buf)
|
||||
|
||||
case MYSQL_COM_QUERY:
|
||||
len = MYSQL_GET_PACKET_LEN(packet)-1; /*< distract 1 for packet type byte */
|
||||
if ((query_str = (char *)malloc(len+1)) == NULL)
|
||||
if (len < 1 || len > ~(size_t)0 - 1 || (query_str = (char *)malloc(len+1)) == NULL)
|
||||
{
|
||||
goto retblock;
|
||||
}
|
||||
@ -262,7 +296,7 @@ modutil_get_query(GWBUF *buf)
|
||||
|
||||
default:
|
||||
len = strlen(STRPACKETTYPE(packet_type))+1;
|
||||
if ((query_str = (char *)malloc(len+1)) == NULL)
|
||||
if (len < 1 || len > ~(size_t)0 - 1 || (query_str = (char *)malloc(len+1)) == NULL)
|
||||
{
|
||||
goto retblock;
|
||||
}
|
||||
@ -390,3 +424,72 @@ int modutil_send_mysql_err_packet (
|
||||
return dcb->func.write(dcb, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Buffer contains at least one of the following:
|
||||
* complete [complete] [partial] mysql packet
|
||||
*
|
||||
* return pointer to gwbuf containing a complete packet or
|
||||
* NULL if no complete packet was found.
|
||||
*/
|
||||
GWBUF* modutil_get_next_MySQL_packet(
|
||||
GWBUF** p_readbuf)
|
||||
{
|
||||
GWBUF* packetbuf;
|
||||
GWBUF* readbuf;
|
||||
size_t buflen;
|
||||
size_t packetlen;
|
||||
size_t totalbuflen;
|
||||
uint8_t* data;
|
||||
size_t nbytes_copied = 0;
|
||||
uint8_t* target;
|
||||
|
||||
readbuf = *p_readbuf;
|
||||
|
||||
if (readbuf == NULL)
|
||||
{
|
||||
packetbuf = NULL;
|
||||
goto return_packetbuf;
|
||||
}
|
||||
CHK_GWBUF(readbuf);
|
||||
|
||||
if (GWBUF_EMPTY(readbuf))
|
||||
{
|
||||
packetbuf = NULL;
|
||||
goto return_packetbuf;
|
||||
}
|
||||
totalbuflen = gwbuf_length(readbuf);
|
||||
data = (uint8_t *)GWBUF_DATA((readbuf));
|
||||
packetlen = MYSQL_GET_PACKET_LEN(data)+4;
|
||||
|
||||
/** packet is incomplete */
|
||||
if (packetlen > totalbuflen)
|
||||
{
|
||||
packetbuf = NULL;
|
||||
goto return_packetbuf;
|
||||
}
|
||||
|
||||
packetbuf = gwbuf_alloc(packetlen);
|
||||
target = GWBUF_DATA(packetbuf);
|
||||
packetbuf->gwbuf_type = readbuf->gwbuf_type; /*< Copy the type too */
|
||||
/**
|
||||
* Copy first MySQL packet to packetbuf and leave posible other
|
||||
* packets to read buffer.
|
||||
*/
|
||||
while (nbytes_copied < packetlen && totalbuflen > 0)
|
||||
{
|
||||
uint8_t* src = GWBUF_DATA((*p_readbuf));
|
||||
size_t bytestocopy;
|
||||
|
||||
buflen = GWBUF_LENGTH((*p_readbuf));
|
||||
bytestocopy = MIN(buflen,packetlen-nbytes_copied);
|
||||
|
||||
memcpy(target+nbytes_copied, src, bytestocopy);
|
||||
*p_readbuf = gwbuf_consume((*p_readbuf), bytestocopy);
|
||||
totalbuflen = gwbuf_length((*p_readbuf));
|
||||
nbytes_copied += bytestocopy;
|
||||
}
|
||||
ss_dassert(buflen == 0 || nbytes_copied == packetlen);
|
||||
|
||||
return_packetbuf:
|
||||
return packetbuf;
|
||||
}
|
||||
|
@ -341,19 +341,26 @@ poll_remove_dcb(DCB *dcb)
|
||||
/*<
|
||||
* Set state to NOPOLLING and remove dcb from poll set.
|
||||
*/
|
||||
if (dcb_set_state(dcb, new_state, &old_state)) {
|
||||
rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev);
|
||||
if (dcb_set_state(dcb, new_state, &old_state))
|
||||
{
|
||||
/**
|
||||
* Only positive fds can be removed from epoll set.
|
||||
*/
|
||||
if (dcb->fd > 0)
|
||||
{
|
||||
rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev);
|
||||
|
||||
if (rc != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : epoll_ctl failed due %d, %s.",
|
||||
eno,
|
||||
strerror(eno))));
|
||||
}
|
||||
ss_dassert(rc == 0); /*< trap in debug */
|
||||
if (rc != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : epoll_ctl failed due %d, %s.",
|
||||
eno,
|
||||
strerror(eno))));
|
||||
}
|
||||
ss_dassert(rc == 0); /*< trap in debug */
|
||||
}
|
||||
}
|
||||
/*<
|
||||
* This call was redundant, but the end result is correct.
|
||||
@ -1323,7 +1330,6 @@ void poll_add_epollin_event_to_dcb(
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void poll_add_event_to_dcb(
|
||||
DCB* dcb,
|
||||
GWBUF* buf,
|
||||
|
@ -252,6 +252,7 @@ MAXKEYS key;
|
||||
"Error : failed opening /dev/random. Error %d, %s.",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -260,6 +261,7 @@ MAXKEYS key;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : failed to read /dev/random.")));
|
||||
close(fd);
|
||||
close(randfd);
|
||||
return 1;
|
||||
}
|
||||
|
@ -68,25 +68,16 @@ server_alloc(char *servname, char *protocol, unsigned short port)
|
||||
{
|
||||
SERVER *server;
|
||||
|
||||
if ((server = (SERVER *)malloc(sizeof(SERVER))) == NULL)
|
||||
if ((server = (SERVER *)calloc(1, sizeof(SERVER))) == NULL)
|
||||
return NULL;
|
||||
server->name = strdup(servname);
|
||||
server->protocol = strdup(protocol);
|
||||
server->port = port;
|
||||
memset(&server->stats, 0, sizeof(SERVER_STATS));
|
||||
server->status = SERVER_RUNNING;
|
||||
server->nextdb = NULL;
|
||||
server->monuser = NULL;
|
||||
server->monpw = NULL;
|
||||
server->unique_name = NULL;
|
||||
server->server_string = NULL;
|
||||
server->node_id = -1;
|
||||
server->rlag = -2;
|
||||
server->node_ts = 0;
|
||||
server->parameters = NULL;
|
||||
server->master_id = -1;
|
||||
server->depth = -1;
|
||||
server->slaves = NULL;
|
||||
|
||||
spinlock_acquire(&server_spin);
|
||||
server->next = allServers;
|
||||
@ -451,6 +442,12 @@ void
|
||||
server_set_status(SERVER *server, int bit)
|
||||
{
|
||||
server->status |= bit;
|
||||
|
||||
/** clear error logged flag before the next failure */
|
||||
if (SERVER_IS_MASTER(server))
|
||||
{
|
||||
server->master_err_is_logged = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +87,6 @@ static void service_add_qualified_param(
|
||||
SERVICE* svc,
|
||||
CONFIG_PARAMETER* param);
|
||||
|
||||
|
||||
/**
|
||||
* Allocate a new service for the gateway to support
|
||||
*
|
||||
@ -102,7 +101,7 @@ service_alloc(const char *servname, const char *router)
|
||||
{
|
||||
SERVICE *service;
|
||||
|
||||
if ((service = (SERVICE *)malloc(sizeof(SERVICE))) == NULL)
|
||||
if ((service = (SERVICE *)calloc(1, sizeof(SERVICE))) == NULL)
|
||||
return NULL;
|
||||
if ((service->router = load_module(router, MODULE_ROUTER)) == NULL)
|
||||
{
|
||||
@ -132,27 +131,10 @@ SERVICE *service;
|
||||
free(service);
|
||||
return NULL;
|
||||
}
|
||||
service->version_string = NULL;
|
||||
memset(&service->stats, 0, sizeof(SERVICE_STATS));
|
||||
service->ports = NULL;
|
||||
service->stats.started = time(0);
|
||||
service->state = SERVICE_STATE_ALLOC;
|
||||
service->credentials.name = NULL;
|
||||
service->credentials.authdata = NULL;
|
||||
service->enable_root = 0;
|
||||
service->localhost_match_wildcard_host = 0;
|
||||
service->routerOptions = NULL;
|
||||
service->databases = NULL;
|
||||
service->svc_config_param = NULL;
|
||||
service->svc_config_version = 0;
|
||||
service->filters = NULL;
|
||||
service->n_filters = 0;
|
||||
service->weightby = 0;
|
||||
service->users = NULL;
|
||||
service->resources = NULL;
|
||||
spinlock_init(&service->spin);
|
||||
spinlock_init(&service->users_table_spin);
|
||||
memset(&service->rate_limit, 0, sizeof(SERVICE_REFRESH_RATE));
|
||||
|
||||
spinlock_acquire(&service_spin);
|
||||
service->next = allServices;
|
||||
@ -233,11 +215,6 @@ GWPROTOCOL *funcs;
|
||||
(port->address == NULL ? "0.0.0.0" : port->address),
|
||||
port->port,
|
||||
service->name)));
|
||||
hashtable_free(service->users->data);
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
|
||||
* This way MaxScale could try reloading users' just after startup
|
||||
@ -349,28 +326,28 @@ serviceStart(SERVICE *service)
|
||||
SERV_PROTOCOL *port;
|
||||
int listeners = 0;
|
||||
|
||||
service->router_instance = service->router->createInstance(service,
|
||||
service->routerOptions);
|
||||
if ((service->router_instance = service->router->createInstance(service,
|
||||
service->routerOptions)) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"%s: Failed to create router instance for service. Service not started.",
|
||||
service->name)));
|
||||
service->state = SERVICE_STATE_FAILED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (service->router_instance == NULL)
|
||||
port = service->ports;
|
||||
while (!service->svc_do_shutdown && port)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : starting the %s service failed.",
|
||||
service->name)));
|
||||
|
||||
listeners += serviceStartPort(service, port);
|
||||
port = port->next;
|
||||
}
|
||||
else
|
||||
if (listeners)
|
||||
{
|
||||
port = service->ports;
|
||||
while (port)
|
||||
{
|
||||
listeners += serviceStartPort(service, port);
|
||||
port = port->next;
|
||||
}
|
||||
if (listeners)
|
||||
service->stats.started = time(0);
|
||||
service->state = SERVICE_STATE_STARTED;
|
||||
service->stats.started = time(0);
|
||||
}
|
||||
|
||||
return listeners;
|
||||
}
|
||||
|
||||
@ -405,12 +382,21 @@ int
|
||||
serviceStartAll()
|
||||
{
|
||||
SERVICE *ptr;
|
||||
int n = 0;
|
||||
int n = 0,i;
|
||||
|
||||
ptr = allServices;
|
||||
while (ptr)
|
||||
while (ptr && !ptr->svc_do_shutdown)
|
||||
{
|
||||
n += serviceStart(ptr);
|
||||
n += (i = serviceStart(ptr));
|
||||
|
||||
if(i == 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to start service '%s'.",
|
||||
ptr->name)));
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
return n;
|
||||
@ -439,6 +425,7 @@ int listeners = 0;
|
||||
|
||||
port = port->next;
|
||||
}
|
||||
service->state = SERVICE_STATE_STOPPED;
|
||||
|
||||
return listeners;
|
||||
}
|
||||
@ -481,7 +468,7 @@ int
|
||||
service_free(SERVICE *service)
|
||||
{
|
||||
SERVICE *ptr;
|
||||
|
||||
SERVER_REF *srv;
|
||||
if (service->stats.n_current)
|
||||
return 0;
|
||||
/* First of all remove from the linked list */
|
||||
@ -503,6 +490,13 @@ SERVICE *ptr;
|
||||
spinlock_release(&service_spin);
|
||||
|
||||
/* Clean up session and free the memory */
|
||||
|
||||
while(service->dbref){
|
||||
srv = service->dbref;
|
||||
service->dbref = service->dbref->next;
|
||||
free(srv);
|
||||
}
|
||||
|
||||
free(service->name);
|
||||
free(service->routerModule);
|
||||
if (service->credentials.name)
|
||||
@ -581,8 +575,13 @@ void
|
||||
serviceAddBackend(SERVICE *service, SERVER *server)
|
||||
{
|
||||
spinlock_acquire(&service->spin);
|
||||
server->nextdb = service->databases;
|
||||
service->databases = server;
|
||||
SERVER_REF *sref;
|
||||
if((sref = calloc(1,sizeof(SERVER_REF))) != NULL)
|
||||
{
|
||||
sref->next = service->dbref;
|
||||
sref->server = server;
|
||||
service->dbref = sref;
|
||||
}
|
||||
spinlock_release(&service->spin);
|
||||
}
|
||||
|
||||
@ -596,12 +595,12 @@ serviceAddBackend(SERVICE *service, SERVER *server)
|
||||
int
|
||||
serviceHasBackend(SERVICE *service, SERVER *server)
|
||||
{
|
||||
SERVER *ptr;
|
||||
SERVER_REF *ptr;
|
||||
|
||||
spinlock_acquire(&service->spin);
|
||||
ptr = service->databases;
|
||||
while (ptr && ptr != server)
|
||||
ptr = ptr->nextdb;
|
||||
ptr = service->dbref;
|
||||
while (ptr && ptr->server != server)
|
||||
ptr = ptr->next;
|
||||
spinlock_release(&service->spin);
|
||||
|
||||
return ptr != NULL;
|
||||
@ -759,7 +758,7 @@ int n = 0;
|
||||
if ((flist = (FILTER_DEF **)malloc(sizeof(FILTER_DEF *))) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Out of memory adding filters to service.\n")));
|
||||
"Error : Out of memory adding filters to service.\n")));
|
||||
return;
|
||||
}
|
||||
ptr = strtok_r(filters, "|", &brkt);
|
||||
@ -770,14 +769,14 @@ int n = 0;
|
||||
(n + 1) * sizeof(FILTER_DEF *))) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Out of memory adding filters to service.\n")));
|
||||
"Error : Out of memory adding filters to service.\n")));
|
||||
return;
|
||||
}
|
||||
if ((flist[n-1] = filter_find(trim(ptr))) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Unable to find filter '%s' for service '%s'\n",
|
||||
"Warning : Unable to find filter '%s' for service '%s'\n",
|
||||
trim(ptr), service->name
|
||||
)));
|
||||
n--;
|
||||
@ -819,7 +818,7 @@ SERVICE *service;
|
||||
void
|
||||
printService(SERVICE *service)
|
||||
{
|
||||
SERVER *ptr = service->databases;
|
||||
SERVER_REF *ptr = service->dbref;
|
||||
struct tm result;
|
||||
char time_buf[30];
|
||||
int i;
|
||||
@ -832,8 +831,8 @@ int i;
|
||||
printf("\tBackend databases\n");
|
||||
while (ptr)
|
||||
{
|
||||
printf("\t\t%s:%d Protocol: %s\n", ptr->name, ptr->port, ptr->protocol);
|
||||
ptr = ptr->nextdb;
|
||||
printf("\t\t%s:%d Protocol: %s\n", ptr->server->name, ptr->server->port, ptr->server->protocol);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
if (service->n_filters)
|
||||
{
|
||||
@ -900,7 +899,7 @@ SERVICE *ptr;
|
||||
*/
|
||||
void dprintService(DCB *dcb, SERVICE *service)
|
||||
{
|
||||
SERVER *server = service->databases;
|
||||
SERVER_REF *server = service->dbref;
|
||||
struct tm result;
|
||||
char timebuf[30];
|
||||
int i;
|
||||
@ -910,7 +909,22 @@ int i;
|
||||
service->name);
|
||||
dcb_printf(dcb, "\tRouter: %s (%p)\n",
|
||||
service->routerModule, service->router);
|
||||
if (service->router)
|
||||
switch (service->state)
|
||||
{
|
||||
case SERVICE_STATE_STARTED:
|
||||
dcb_printf(dcb, "\tState: Started\n");
|
||||
break;
|
||||
case SERVICE_STATE_STOPPED:
|
||||
dcb_printf(dcb, "\tState: Stopped\n");
|
||||
break;
|
||||
case SERVICE_STATE_FAILED:
|
||||
dcb_printf(dcb, "\tState: Failed\n");
|
||||
break;
|
||||
case SERVICE_STATE_ALLOC:
|
||||
dcb_printf(dcb, "\tState: Allocated\n");
|
||||
break;
|
||||
}
|
||||
if (service->router && service->router_instance)
|
||||
service->router->diagnostics(service->router_instance, dcb);
|
||||
dcb_printf(dcb, "\tStarted: %s",
|
||||
asctime_r(localtime_r(&service->stats.started, &result), timebuf));
|
||||
@ -929,9 +943,9 @@ int i;
|
||||
dcb_printf(dcb, "\tBackend databases\n");
|
||||
while (server)
|
||||
{
|
||||
dcb_printf(dcb, "\t\t%s:%d Protocol: %s\n", server->name, server->port,
|
||||
server->protocol);
|
||||
server = server->nextdb;
|
||||
dcb_printf(dcb, "\t\t%s:%d Protocol: %s\n", server->server->name, server->server->port,
|
||||
server->server->protocol);
|
||||
server = server->next;
|
||||
}
|
||||
if (service->weightby)
|
||||
dcb_printf(dcb, "\tRouting weight parameter: %s\n",
|
||||
@ -1397,3 +1411,16 @@ serviceEnableLocalhostMatchWildcardHost(SERVICE *service, int action)
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void service_shutdown()
|
||||
{
|
||||
SERVICE* svc;
|
||||
spinlock_acquire(&service_spin);
|
||||
svc = allServices;
|
||||
while (svc != NULL)
|
||||
{
|
||||
svc->svc_do_shutdown = true;
|
||||
svc = svc->next;
|
||||
}
|
||||
spinlock_release(&service_spin);
|
||||
}
|
@ -85,12 +85,21 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
"session object due error %d, %s.",
|
||||
errno,
|
||||
strerror(errno))));
|
||||
if (client_dcb->data && !DCB_IS_CLONE(client_dcb))
|
||||
{
|
||||
free(client_dcb->data);
|
||||
client_dcb->data = NULL;
|
||||
}
|
||||
goto return_session;
|
||||
}
|
||||
#if defined(SS_DEBUG)
|
||||
session->ses_chk_top = CHK_NUM_SESSION;
|
||||
session->ses_chk_tail = CHK_NUM_SESSION;
|
||||
#endif
|
||||
if (DCB_IS_CLONE(client_dcb))
|
||||
{
|
||||
session->ses_is_child = true;
|
||||
}
|
||||
spinlock_init(&session->ses_lock);
|
||||
/*<
|
||||
* Prevent backend threads from accessing before session is completely
|
||||
@ -149,6 +158,7 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
* Decrease refcount, set dcb's session pointer NULL
|
||||
* and set session pointer to NULL.
|
||||
*/
|
||||
session->client = NULL;
|
||||
session_free(session);
|
||||
client_dcb->session = NULL;
|
||||
session = NULL;
|
||||
@ -189,6 +199,7 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
* Decrease refcount, set dcb's session pointer NULL
|
||||
* and set session pointer to NULL.
|
||||
*/
|
||||
session->client = NULL;
|
||||
session_free(session);
|
||||
client_dcb->session = NULL;
|
||||
session = NULL;
|
||||
@ -207,6 +218,7 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
if (session->state != SESSION_STATE_READY)
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
session->client = NULL;
|
||||
session_free(session);
|
||||
client_dcb->session = NULL;
|
||||
session = NULL;
|
||||
@ -331,12 +343,16 @@ int session_unlink_dcb(
|
||||
|
||||
if (nlink == 0)
|
||||
{
|
||||
session->state = SESSION_STATE_FREE;
|
||||
session->state = SESSION_STATE_TO_BE_FREED;
|
||||
}
|
||||
|
||||
if (dcb != NULL)
|
||||
{
|
||||
dcb->session = NULL;
|
||||
if (session->client == dcb)
|
||||
{
|
||||
session->client = NULL;
|
||||
}
|
||||
dcb->session = NULL;
|
||||
}
|
||||
spinlock_release(&session->ses_lock);
|
||||
|
||||
@ -357,7 +373,6 @@ bool session_free(
|
||||
int i;
|
||||
|
||||
CHK_SESSION(session);
|
||||
|
||||
/*<
|
||||
* Remove one reference. If there are no references left,
|
||||
* free session.
|
||||
@ -388,8 +403,12 @@ bool session_free(
|
||||
spinlock_release(&session_spin);
|
||||
atomic_add(&session->service->stats.n_current, -1);
|
||||
|
||||
/* Free router_session and session */
|
||||
if (session->router_session) {
|
||||
/**
|
||||
* If session is not child of some other session, free router_session.
|
||||
* Otherwise let the parent free it.
|
||||
*/
|
||||
if (!session->ses_is_child && session->router_session)
|
||||
{
|
||||
session->service->router->freeSession(
|
||||
session->service->router_instance,
|
||||
session->router_session);
|
||||
@ -422,7 +441,17 @@ bool session_free(
|
||||
/** Disable trace and decrease trace logger counter */
|
||||
session_disable_log(session, LT);
|
||||
|
||||
free(session);
|
||||
/** If session doesn't have parent referencing to it, it can be freed */
|
||||
if (!session->ses_is_child)
|
||||
{
|
||||
session->state = SESSION_STATE_FREE;
|
||||
|
||||
if (session->data)
|
||||
{
|
||||
free(session->data);
|
||||
}
|
||||
free(session);
|
||||
}
|
||||
succp = true;
|
||||
|
||||
return_succp :
|
||||
@ -687,6 +716,15 @@ session_state(int state)
|
||||
return "Listener Session";
|
||||
case SESSION_STATE_LISTENER_STOPPED:
|
||||
return "Stopped Listener Session";
|
||||
#ifdef SS_DEBUG
|
||||
case SESSION_STATE_STOPPING:
|
||||
return "Stopping session";
|
||||
case SESSION_STATE_TO_BE_FREED:
|
||||
return "Session to be freed";
|
||||
case SESSION_STATE_FREE:
|
||||
return "Freed session";
|
||||
|
||||
#endif
|
||||
default:
|
||||
return "Invalid State";
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ add_test(TestBuffer test_buffer)
|
||||
add_test(TestDCB test_dcb)
|
||||
add_test(TestModutil test_modutil)
|
||||
add_test(TestPoll test_poll)
|
||||
add_test(TestService test_service)
|
||||
add_test(TestService /bin/sh -c " MAXSCALE_HOME=${CMAKE_BINARY_DIR} && ${CMAKE_CURRENT_BINAR_DIR}/test_service")
|
||||
add_test(TestServer test_server)
|
||||
add_test(TestUsers test_users)
|
||||
add_test(TestAdminUsers test_adminusers)
|
||||
|
@ -272,6 +272,8 @@ char *home, buf[1024];
|
||||
if ((home = getenv("MAXSCALE_HOME")) == NULL || strlen(home) >= 1024)
|
||||
home = "/usr/local/skysql";
|
||||
sprintf(buf, "%s/etc/passwd", home);
|
||||
if(!is_valid_posix_path(buf))
|
||||
exit(1);
|
||||
if (strcmp(buf, "/etc/passwd") != 0)
|
||||
unlink(buf);
|
||||
|
||||
|
@ -49,11 +49,13 @@ HINT *hint;
|
||||
char* name = strdup("name");
|
||||
hint = hint_create_parameter(NULL, name, "value");
|
||||
free(name);
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(NULL != hint, "New hint list should not be null");
|
||||
ss_info_dassert(0 == strcmp("value", hint->value), "Hint value should be correct");
|
||||
ss_info_dassert(0 != hint_exists(&hint, HINT_PARAMETER), "Hint of parameter type should exist");
|
||||
ss_dfprintf(stderr, "\t..done\nFree hints.");
|
||||
if (NULL != hint) hint_free(hint);
|
||||
skygw_log_sync_all();
|
||||
ss_dfprintf(stderr, "\t..done\n");
|
||||
|
||||
return 0;
|
||||
|
@ -51,7 +51,7 @@ int result;
|
||||
"testpoll : Initialise the polling system.");
|
||||
poll_init();
|
||||
ss_dfprintf(stderr, "\t..done\nAdd a DCB");
|
||||
dcb = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);
|
||||
dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
|
||||
if(dcb == NULL){
|
||||
ss_dfprintf(stderr, "\nError on function call: dcb_alloc() returned NULL.\n");
|
||||
|
@ -48,7 +48,7 @@ char *status;
|
||||
ss_dfprintf(stderr,
|
||||
"testserver : creating server called MyServer");
|
||||
server = server_alloc("MyServer", "HTTPD", 9876);
|
||||
|
||||
skygw_log_sync_all();
|
||||
|
||||
//ss_info_dassert(NULL != service, "New server with valid protocol and port must not be null");
|
||||
//ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation");
|
||||
@ -56,26 +56,32 @@ char *status;
|
||||
ss_dfprintf(stderr, "\t..done\nTest Parameter for Server.");
|
||||
ss_info_dassert(NULL == serverGetParameter(server, "name"), "Parameter should be null when not set");
|
||||
serverAddParameter(server, "name", "value");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 == strcmp("value", serverGetParameter(server, "name")), "Parameter should be returned correctly");
|
||||
ss_dfprintf(stderr, "\t..done\nTesting Unique Name for Server.");
|
||||
ss_info_dassert(NULL == server_find_by_unique_name("uniquename"), "Should not find non-existent unique name.");
|
||||
server_set_unique_name(server, "uniquename");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(server == server_find_by_unique_name("uniquename"), "Should find by unique name.");
|
||||
ss_dfprintf(stderr, "\t..done\nTesting Status Setting for Server.");
|
||||
status = server_status(server);
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 == strcmp("Running", status), "Status of Server should be Running by default.");
|
||||
if (NULL != status) free(status);
|
||||
server_set_status(server, SERVER_MASTER);
|
||||
status = server_status(server);
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 == strcmp("Master, Running", status), "Should find correct status.");
|
||||
server_clear_status(server, SERVER_MASTER);
|
||||
free(status);
|
||||
status = server_status(server);
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 == strcmp("Running", status), "Status of Server should be Running after master status cleared.");
|
||||
if (NULL != status) free(status);
|
||||
ss_dfprintf(stderr, "\t..done\nRun Prints for Server and all Servers.");
|
||||
printServer(server);
|
||||
printAllServers();
|
||||
skygw_log_sync_all();
|
||||
ss_dfprintf(stderr, "\t..done\nFreeing Server.");
|
||||
ss_info_dassert(0 != server_free(server), "Free should succeed");
|
||||
ss_dfprintf(stderr, "\t..done\n");
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <maxscale_test.h>
|
||||
#include <service.h>
|
||||
|
||||
/**
|
||||
@ -42,15 +42,27 @@ test1()
|
||||
{
|
||||
SERVICE *service;
|
||||
int result;
|
||||
int argc = 3;
|
||||
char buffer[1024];
|
||||
sprintf(buffer,"%s",TEST_LOG_DIR);
|
||||
char* argv[] = {
|
||||
"log_manager",
|
||||
"-j",
|
||||
buffer,
|
||||
NULL
|
||||
};
|
||||
skygw_logmanager_init(argc,argv);
|
||||
|
||||
/* Service tests */
|
||||
ss_dfprintf(stderr,
|
||||
"testservice : creating service called MyService with router nonexistent");
|
||||
service = service_alloc("MyService", "non-existent");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(NULL == service, "New service with invalid router should be null");
|
||||
ss_info_dassert(0 == service_isvalid(service), "Service must not be valid after incorrect creation");
|
||||
ss_dfprintf(stderr, "\t..done\nValid service creation, router testroute.");
|
||||
service = service_alloc("MyService", "testroute");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(NULL != service, "New service with valid router must not be null");
|
||||
ss_info_dassert(0 != service_isvalid(service), "Service must be valid after creation");
|
||||
ss_info_dassert(0 == strcmp("MyService", service_get_name(service)), "Service must have given name");
|
||||
@ -58,12 +70,16 @@ int result;
|
||||
ss_info_dassert(0 != serviceAddProtocol(service, "HTTPD", "localhost", 9876), "Add Protocol should succeed");
|
||||
ss_info_dassert(0 != serviceHasProtocol(service, "HTTPD", 9876), "Service should have new protocol as requested");
|
||||
serviceStartProtocol(service, "HTTPD", 9876);
|
||||
skygw_log_sync_all();
|
||||
ss_dfprintf(stderr, "\t..done\nStarting Service.");
|
||||
result = serviceStart(service);
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 != result, "Start should succeed");
|
||||
result = serviceStop(service);
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 != result, "Stop should succeed");
|
||||
result = serviceStartAll();
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(0 != result, "Start all should succeed");
|
||||
|
||||
ss_dfprintf(stderr, "\t..done\nStopping Service.");
|
||||
|
@ -33,6 +33,8 @@
|
||||
|
||||
#include <users.h>
|
||||
|
||||
#include "log_manager.h"
|
||||
|
||||
/**
|
||||
* test1 Allocate table of users and mess around with it
|
||||
*
|
||||
@ -49,26 +51,34 @@ int result, count;
|
||||
ss_dfprintf(stderr,
|
||||
"testusers : Initialise the user table.");
|
||||
users = users_alloc();
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(NULL != users, "Allocating user table should not return NULL.")
|
||||
ss_dfprintf(stderr, "\t..done\nAdd a user");
|
||||
count = users_add(users, "username", "authorisation");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(1 == count, "Should add one user");
|
||||
authdata = users_fetch(users, "username");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(NULL != authdata, "Fetch valid user must not return NULL");
|
||||
ss_info_dassert(0 == strcmp("authorisation", authdata), "User authorisation should be correct");
|
||||
ss_dfprintf(stderr, "\t..done\nPrint users");
|
||||
usersPrint(users);
|
||||
skygw_log_sync_all();
|
||||
ss_dfprintf(stderr, "\t..done\nUpdate a user");
|
||||
count = users_update(users, "username", "newauth");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(1 == count, "Should update just one user");
|
||||
authdata = users_fetch(users, "username");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(NULL != authdata, "Fetch valid user must not return NULL");
|
||||
ss_info_dassert(0 == strcmp("newauth", authdata), "User authorisation should be correctly updated");
|
||||
ss_dfprintf(stderr, "\t..done\nDelete a user.");
|
||||
count = users_delete(users, "username");
|
||||
skygw_log_sync_all();
|
||||
ss_info_dassert(1 == count, "Should delete just one user");
|
||||
ss_dfprintf(stderr, "\t..done\nFree user table.");
|
||||
users_free(users);
|
||||
skygw_log_sync_all();
|
||||
ss_dfprintf(stderr, "\t..done\n");
|
||||
|
||||
return 0;
|
||||
|
@ -183,32 +183,41 @@ char *sep;
|
||||
void *user;
|
||||
|
||||
dcb_printf(dcb, "Users table data\n");
|
||||
dcb_hashtable_stats(dcb, users->data);
|
||||
if ((iter = hashtable_iterator(users->data)) != NULL)
|
||||
|
||||
if (users == NULL || users->data == NULL)
|
||||
{
|
||||
dcb_printf(dcb, "User names: ");
|
||||
sep = "";
|
||||
dcb_printf(dcb, "Users table is empty\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
dcb_hashtable_stats(dcb, users->data);
|
||||
|
||||
if ((iter = hashtable_iterator(users->data)) != NULL)
|
||||
{
|
||||
dcb_printf(dcb, "User names: ");
|
||||
sep = "";
|
||||
|
||||
if (users->usersCustomUserFormat != NULL) {
|
||||
while ((user = hashtable_next(iter)) != NULL)
|
||||
{
|
||||
char *custom_user;
|
||||
custom_user = users->usersCustomUserFormat(user);
|
||||
if (custom_user) {
|
||||
dcb_printf(dcb, "%s%s", sep, custom_user);
|
||||
free(custom_user);
|
||||
if (users->usersCustomUserFormat != NULL) {
|
||||
while ((user = hashtable_next(iter)) != NULL)
|
||||
{
|
||||
char *custom_user;
|
||||
custom_user = users->usersCustomUserFormat(user);
|
||||
if (custom_user) {
|
||||
dcb_printf(dcb, "%s%s", sep, custom_user);
|
||||
free(custom_user);
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ((user = hashtable_next(iter)) != NULL)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s", sep, (char *)user);
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
while ((user = hashtable_next(iter)) != NULL)
|
||||
{
|
||||
dcb_printf(dcb, "%s%s", sep, (char *)user);
|
||||
sep = ", ";
|
||||
}
|
||||
}
|
||||
|
||||
dcb_printf(dcb, "\n");
|
||||
hashtable_iterator_free(iter);
|
||||
hashtable_iterator_free(iter);
|
||||
}
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
@ -184,6 +184,7 @@ extern GWBUF *gwbuf_rtrim(GWBUF *head, unsigned int length);
|
||||
extern unsigned int gwbuf_length(GWBUF *head);
|
||||
extern GWBUF *gwbuf_clone_portion(GWBUF *head, size_t offset, size_t len);
|
||||
extern GWBUF *gwbuf_clone_transform(GWBUF *head, gwbuf_type_t type);
|
||||
extern GWBUF *gwbuf_clone_all(GWBUF* head);
|
||||
extern void gwbuf_set_type(GWBUF *head, gwbuf_type_t type);
|
||||
extern int gwbuf_add_property(GWBUF *buf, char *name, char *value);
|
||||
extern char *gwbuf_get_property(GWBUF *buf, char *name);
|
||||
@ -195,7 +196,6 @@ void gwbuf_add_buffer_object(GWBUF* buf,
|
||||
void* data,
|
||||
void (*donefun_fp)(void *));
|
||||
void* gwbuf_get_buffer_object_data(GWBUF* buf, bufobj_id_t id);
|
||||
|
||||
EXTERN_C_BLOCK_END
|
||||
|
||||
|
||||
|
@ -129,6 +129,8 @@ typedef struct {
|
||||
*/
|
||||
#define GWPROTOCOL_VERSION {1, 0, 0}
|
||||
|
||||
#define DCBFD_CLOSED -1
|
||||
|
||||
/**
|
||||
* The statitics gathered on a descriptor control block
|
||||
*/
|
||||
@ -329,4 +331,8 @@ bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs);
|
||||
*/
|
||||
#define DCBF_CLONE 0x0001 /*< DCB is a clone */
|
||||
#define DCBF_HUNG 0x0002 /*< Hangup has been dispatched */
|
||||
#define DCBF_REPLIED 0x0004 /*< DCB was written to */
|
||||
|
||||
#define DCB_IS_CLONE(d) ((d)->flags & DCBF_CLONE)
|
||||
#define DCB_REPLIED(d) ((d)->flags & DCBF_REPLIED)
|
||||
#endif /* _DCB_H */
|
||||
|
@ -41,6 +41,9 @@ extern char *modutil_get_SQL(GWBUF *);
|
||||
extern GWBUF *modutil_replace_SQL(GWBUF *, char *);
|
||||
extern char *modutil_get_query(GWBUF* buf);
|
||||
extern int modutil_send_mysql_err_packet(DCB *, int, int, int, const char *, const char *);
|
||||
GWBUF* modutil_get_next_MySQL_packet(GWBUF** p_readbuf);
|
||||
int modutil_MySQL_query_len(GWBUF* buf, int* nbytes_missing);
|
||||
|
||||
|
||||
GWBUF *modutil_create_mysql_err_msg(
|
||||
int packet_number,
|
||||
|
@ -91,6 +91,7 @@ typedef struct server {
|
||||
long master_id; /**< Master server id of this node */
|
||||
int depth; /**< Replication level in the tree */
|
||||
long *slaves; /**< Slaves of this node */
|
||||
bool master_err_is_logged; /*< If node failed, this indicates whether it is logged */
|
||||
} SERVER;
|
||||
|
||||
/**
|
||||
@ -123,8 +124,11 @@ typedef struct server {
|
||||
* Is the server a master? The server must be both running and marked as master
|
||||
* in order for the macro to return true
|
||||
*/
|
||||
#define SERVER_IS_MASTER(server) \
|
||||
(((server)->status & (SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE|SERVER_MAINT)) == (SERVER_RUNNING|SERVER_MASTER))
|
||||
#define SERVER_IS_MASTER(server) SRV_MASTER_STATUS((server)->status)
|
||||
|
||||
#define SRV_MASTER_STATUS(status) ((status & \
|
||||
(SERVER_RUNNING|SERVER_MASTER|SERVER_SLAVE|SERVER_MAINT)) == \
|
||||
(SERVER_RUNNING|SERVER_MASTER))
|
||||
|
||||
/**
|
||||
* Is the server valid candidate for root master. The server must be running,
|
||||
|
@ -99,6 +99,11 @@ typedef struct {
|
||||
time_t last;
|
||||
} SERVICE_REFRESH_RATE;
|
||||
|
||||
typedef struct server_ref_t{
|
||||
struct server_ref_t *next;
|
||||
SERVER* server;
|
||||
}SERVER_REF;
|
||||
|
||||
/**
|
||||
* Defines a service within the gateway.
|
||||
*
|
||||
@ -119,7 +124,7 @@ typedef struct service {
|
||||
void *router_instance;
|
||||
/**< The router instance for this service */
|
||||
char *version_string;/** version string for this service listeners */
|
||||
struct server *databases; /**< The set of servers in the backend */
|
||||
SERVER_REF *dbref; /** server references */
|
||||
SERVICE_USER credentials; /**< The cedentials of the service user */
|
||||
SPINLOCK spin; /**< The service spinlock */
|
||||
SERVICE_STATS stats; /**< The service statistics */
|
||||
@ -130,6 +135,7 @@ typedef struct service {
|
||||
CONFIG_PARAMETER*
|
||||
svc_config_param; /*< list of config params and values */
|
||||
int svc_config_version; /*< Version number of configuration */
|
||||
bool svc_do_shutdown; /*< tells the service to exit loops etc. */
|
||||
SPINLOCK
|
||||
users_table_spin; /**< The spinlock for users data refresh */
|
||||
SERVICE_REFRESH_RATE
|
||||
@ -144,6 +150,8 @@ typedef enum count_spec_t {COUNT_NONE=0, COUNT_ATLEAST, COUNT_EXACT, COUNT_ATMOS
|
||||
|
||||
#define SERVICE_STATE_ALLOC 1 /**< The service has been allocated */
|
||||
#define SERVICE_STATE_STARTED 2 /**< The service has been started */
|
||||
#define SERVICE_STATE_FAILED 3 /**< The service failed to start */
|
||||
#define SERVICE_STATE_STOPPED 4 /**< The service has been stopped */
|
||||
|
||||
extern SERVICE *service_alloc(const char *, const char *);
|
||||
extern int service_free(SERVICE *);
|
||||
@ -184,4 +192,5 @@ extern void dprintService(DCB *, SERVICE *);
|
||||
extern void dListServices(DCB *);
|
||||
extern void dListListeners(DCB *);
|
||||
char* service_get_name(SERVICE* svc);
|
||||
void service_shutdown();
|
||||
#endif
|
||||
|
@ -61,6 +61,7 @@ typedef enum {
|
||||
SESSION_STATE_STOPPING, /*< session and router are being closed */
|
||||
SESSION_STATE_LISTENER, /*< for listener session */
|
||||
SESSION_STATE_LISTENER_STOPPED, /*< for listener session */
|
||||
SESSION_STATE_TO_BE_FREED, /*< ready to be freed as soon as there are no references */
|
||||
SESSION_STATE_FREE /*< for all sessions */
|
||||
} session_state_t;
|
||||
|
||||
@ -124,6 +125,7 @@ typedef struct session {
|
||||
UPSTREAM tail; /*< The tail of the filter chain */
|
||||
struct session *next; /*< Linked list of all sessions */
|
||||
int refcount; /*< Reference count on the session */
|
||||
bool ses_is_child; /*< this is a child session */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t ses_chk_tail;
|
||||
#endif
|
||||
|
@ -213,7 +213,7 @@ HINT_MODE mode = HM_EXECUTE;
|
||||
/*
|
||||
* If we have got here then we have a comment, ptr point to
|
||||
* the comment character if it is a '#' comment or the second
|
||||
* character of the comment if it is a -- or /* comment
|
||||
* character of the comment if it is a -- or \/\* comment
|
||||
*
|
||||
* Move to the next character in the SQL.
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@
|
||||
* Date Who Description
|
||||
* 20/06/2014 Mark Riddoch Initial implementation
|
||||
* 24/06/2014 Mark Riddoch Addition of support for multi-packet queries
|
||||
* 12/12/2014 Mark Riddoch Add support for otehr packet types
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -57,6 +58,33 @@
|
||||
#include <service.h>
|
||||
#include <router.h>
|
||||
#include <dcb.h>
|
||||
#include <sys/time.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define MYSQL_COM_QUIT 0x01
|
||||
#define MYSQL_COM_INITDB 0x02
|
||||
#define MYSQL_COM_FIELD_LIST 0x04
|
||||
#define MYSQL_COM_CHANGE_USER 0x11
|
||||
#define MYSQL_COM_STMT_PREPARE 0x16
|
||||
#define MYSQL_COM_STMT_EXECUTE 0x17
|
||||
#define MYSQL_COM_STMT_SEND_LONG_DATA 0x18
|
||||
#define MYSQL_COM_STMT_CLOSE 0x19
|
||||
#define MYSQL_COM_STMT_RESET 0x1a
|
||||
|
||||
#define REPLY_TIMEOUT_SECOND 5
|
||||
#define REPLY_TIMEOUT_MILLISECOND 1
|
||||
|
||||
static unsigned char required_packets[] = {
|
||||
MYSQL_COM_QUIT,
|
||||
MYSQL_COM_INITDB,
|
||||
MYSQL_COM_FIELD_LIST,
|
||||
MYSQL_COM_CHANGE_USER,
|
||||
MYSQL_COM_STMT_PREPARE,
|
||||
MYSQL_COM_STMT_EXECUTE,
|
||||
MYSQL_COM_STMT_SEND_LONG_DATA,
|
||||
MYSQL_COM_STMT_CLOSE,
|
||||
MYSQL_COM_STMT_RESET,
|
||||
0 };
|
||||
|
||||
/** Defined in log_manager.cc */
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
@ -80,19 +108,20 @@ static void *newSession(FILTER *instance, SESSION *session);
|
||||
static void closeSession(FILTER *instance, void *session);
|
||||
static void freeSession(FILTER *instance, void *session);
|
||||
static void setDownstream(FILTER *instance, void *fsession, DOWNSTREAM *downstream);
|
||||
static void setUpstream(FILTER *instance, void *fsession, UPSTREAM *upstream);
|
||||
static int routeQuery(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
static int clientReply(FILTER *instance, void *fsession, GWBUF *queue);
|
||||
static void diagnostic(FILTER *instance, void *fsession, DCB *dcb);
|
||||
|
||||
|
||||
static FILTER_OBJECT MyObject = {
|
||||
createInstance,
|
||||
newSession,
|
||||
closeSession,
|
||||
freeSession,
|
||||
setDownstream,
|
||||
NULL, // No Upstream requirement
|
||||
setUpstream,
|
||||
routeQuery,
|
||||
NULL, // No client reply
|
||||
clientReply,
|
||||
diagnostic,
|
||||
};
|
||||
|
||||
@ -120,14 +149,59 @@ typedef struct {
|
||||
*/
|
||||
typedef struct {
|
||||
DOWNSTREAM down; /* The downstream filter */
|
||||
UPSTREAM up; /* The upstream filter */
|
||||
|
||||
FILTER_DEF* dummy_filterdef;
|
||||
int active; /* filter is active? */
|
||||
int waiting; /* if the client is waiting for a reply */
|
||||
int replies; /* Number of queries received */
|
||||
int min_replies; /* Minimum number of replies to receive
|
||||
* before forwarding the packet to the client*/
|
||||
DCB *branch_dcb; /* Client DCB for "branch" service */
|
||||
SESSION *branch_session;/* The branch service session */
|
||||
int n_duped; /* Number of duplicated queries */
|
||||
int n_rejected; /* Number of rejected queries */
|
||||
int residual; /* Any outstanding SQL text */
|
||||
GWBUF* tee_replybuf; /* Buffer for reply */
|
||||
SPINLOCK tee_lock;
|
||||
} TEE_SESSION;
|
||||
|
||||
typedef struct orphan_session_tt
|
||||
{
|
||||
SESSION* session;
|
||||
struct orphan_session_tt* next;
|
||||
}orphan_session_t;
|
||||
|
||||
static orphan_session_t* allOrphans = NULL;
|
||||
|
||||
static SPINLOCK orphanLock;
|
||||
static int packet_is_required(GWBUF *queue);
|
||||
static int detect_loops(TEE_INSTANCE *instance, HASHTABLE* ht, SERVICE* session);
|
||||
|
||||
static int hkfn(
|
||||
void* key)
|
||||
{
|
||||
if(key == NULL){
|
||||
return 0;
|
||||
}
|
||||
unsigned int hash = 0,c = 0;
|
||||
char* ptr = (char*)key;
|
||||
while((c = *ptr++)){
|
||||
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||
}
|
||||
return *(int *)key;
|
||||
}
|
||||
|
||||
static int hcfn(
|
||||
void* v1,
|
||||
void* v2)
|
||||
{
|
||||
char* i1 = (char*) v1;
|
||||
char* i2 = (char*) v2;
|
||||
|
||||
return strcmp(i1,i2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
@ -146,6 +220,7 @@ version()
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
spinlock_init(&orphanLock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,7 +307,8 @@ int i;
|
||||
free(my_instance->source);
|
||||
free(my_instance);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (my_instance->match &&
|
||||
regcomp(&my_instance->re, my_instance->match, REG_ICASE))
|
||||
{
|
||||
@ -280,27 +356,148 @@ TEE_INSTANCE *my_instance = (TEE_INSTANCE *)instance;
|
||||
TEE_SESSION *my_session;
|
||||
char *remote, *userName;
|
||||
|
||||
if (strcmp(my_instance->service->name, session->service->name) == 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Error : %s: Recursive use of tee filter in service.",
|
||||
session->service->name)));
|
||||
my_session = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
HASHTABLE* ht = hashtable_alloc(100,hkfn,hcfn);
|
||||
bool is_loop = detect_loops(my_instance,ht,session->service);
|
||||
hashtable_free(ht);
|
||||
|
||||
if(is_loop)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Error : %s: Recursive use of tee filter in service.",
|
||||
session->service->name)));
|
||||
my_session = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if ((my_session = calloc(1, sizeof(TEE_SESSION))) != NULL)
|
||||
{
|
||||
my_session->active = 1;
|
||||
my_session->residual = 0;
|
||||
if (my_instance->source
|
||||
&& (remote = session_get_remote(session)) != NULL)
|
||||
spinlock_init(&my_session->tee_lock);
|
||||
if (my_instance->source &&
|
||||
(remote = session_get_remote(session)) != NULL)
|
||||
{
|
||||
if (strcmp(remote, my_instance->source))
|
||||
{
|
||||
my_session->active = 0;
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Warning : Tee filter is not active.")));
|
||||
}
|
||||
}
|
||||
userName = session_getUser(session);
|
||||
if (my_instance->userName && userName && strcmp(userName,
|
||||
my_instance->userName))
|
||||
|
||||
if (my_instance->userName &&
|
||||
userName &&
|
||||
strcmp(userName, my_instance->userName))
|
||||
{
|
||||
my_session->active = 0;
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Warning : Tee filter is not active.")));
|
||||
}
|
||||
|
||||
if (my_session->active)
|
||||
{
|
||||
my_session->branch_dcb = dcb_clone(session->client);
|
||||
my_session->branch_session = session_alloc(my_instance->service, my_session->branch_dcb);
|
||||
DCB* dcb;
|
||||
SESSION* ses;
|
||||
FILTER_DEF* dummy;
|
||||
UPSTREAM* dummy_upstream;
|
||||
|
||||
if ((dcb = dcb_clone(session->client)) == NULL)
|
||||
{
|
||||
freeSession(instance, (void *)my_session);
|
||||
my_session = NULL;
|
||||
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Creating client DCB for Tee "
|
||||
"filter failed. Terminating session.")));
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
if((dummy = filter_alloc("tee_dummy","tee_dummy")) == NULL)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
freeSession(instance, (void *)my_session);
|
||||
my_session = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : tee: Allocating memory for "
|
||||
"dummy filter definition failed."
|
||||
" Terminating session.")));
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if ((ses = session_alloc(my_instance->service, dcb)) == NULL)
|
||||
{
|
||||
dcb_close(dcb);
|
||||
freeSession(instance, (void *)my_session);
|
||||
my_session = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Creating client session for Tee "
|
||||
"filter failed. Terminating session.")));
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
ss_dassert(ses->ses_is_child);
|
||||
|
||||
dummy->obj = GetModuleObject();
|
||||
dummy->filter = NULL;
|
||||
|
||||
|
||||
if((dummy_upstream = filterUpstream(
|
||||
dummy, my_session, &ses->tail)) == NULL)
|
||||
{
|
||||
spinlock_acquire(&ses->ses_lock);
|
||||
ses->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&ses->ses_lock);
|
||||
|
||||
ses->service->router->closeSession(
|
||||
ses->service->router_instance,
|
||||
ses->router_session);
|
||||
|
||||
ses->client = NULL;
|
||||
dcb->session = NULL;
|
||||
session_free(ses);
|
||||
dcb_close(dcb);
|
||||
freeSession(instance, (void *) my_session);
|
||||
my_session = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : tee: Allocating memory for"
|
||||
"dummy upstream failed."
|
||||
" Terminating session.")));
|
||||
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
ses->tail = *dummy_upstream;
|
||||
my_session->min_replies = 2;
|
||||
my_session->branch_session = ses;
|
||||
my_session->branch_dcb = dcb;
|
||||
my_session->dummy_filterdef = dummy;
|
||||
free(dummy_upstream);
|
||||
}
|
||||
}
|
||||
|
||||
retblock:
|
||||
return my_session;
|
||||
}
|
||||
|
||||
@ -325,17 +522,27 @@ SESSION *bsession;
|
||||
{
|
||||
if ((bsession = my_session->branch_session) != NULL)
|
||||
{
|
||||
CHK_SESSION(bsession);
|
||||
spinlock_acquire(&bsession->ses_lock);
|
||||
|
||||
if (bsession->state != SESSION_STATE_STOPPING)
|
||||
{
|
||||
bsession->state = SESSION_STATE_STOPPING;
|
||||
}
|
||||
router = bsession->service->router;
|
||||
router_instance = bsession->service->router_instance;
|
||||
rsession = bsession->router_session;
|
||||
spinlock_release(&bsession->ses_lock);
|
||||
|
||||
/** Close router session and all its connections */
|
||||
router->closeSession(router_instance, rsession);
|
||||
}
|
||||
dcb_free(my_session->branch_dcb);
|
||||
/* No need to free the session, this is done as
|
||||
* a side effect of closing the client DCB of the
|
||||
* session.
|
||||
*/
|
||||
|
||||
my_session->active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -349,11 +556,129 @@ static void
|
||||
freeSession(FILTER *instance, void *session)
|
||||
{
|
||||
TEE_SESSION *my_session = (TEE_SESSION *)session;
|
||||
SESSION* ses = my_session->branch_session;
|
||||
|
||||
if (ses != NULL)
|
||||
{
|
||||
if (ses->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
session_free(ses);
|
||||
}
|
||||
|
||||
if (ses->state == SESSION_STATE_TO_BE_FREED)
|
||||
{
|
||||
/** Free branch router session */
|
||||
ses->service->router->freeSession(
|
||||
ses->service->router_instance,
|
||||
ses->router_session);
|
||||
/** Free memory of branch client session */
|
||||
ses->state = SESSION_STATE_FREE;
|
||||
free(ses);
|
||||
/** This indicates that branch session is not available anymore */
|
||||
my_session->branch_session = NULL;
|
||||
}
|
||||
else if(ses->state == SESSION_STATE_STOPPING)
|
||||
{
|
||||
orphan_session_t* orphan;
|
||||
if((orphan = malloc(sizeof(orphan_session_t))) == NULL)
|
||||
{
|
||||
skygw_log_write(LOGFILE_ERROR,"Error : Failed to "
|
||||
"allocate memory for orphan session struct, "
|
||||
"child session might leak memory.");
|
||||
}else{
|
||||
orphan->session = ses;
|
||||
spinlock_acquire(&orphanLock);
|
||||
orphan->next = allOrphans;
|
||||
allOrphans = orphan;
|
||||
spinlock_release(&orphanLock);
|
||||
}
|
||||
if(ses->refcount == 0)
|
||||
{
|
||||
ss_dassert(ses->refcount == 0 && ses->client == NULL);
|
||||
ses->state = SESSION_STATE_TO_BE_FREED;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (my_session->dummy_filterdef)
|
||||
{
|
||||
filter_free(my_session->dummy_filterdef);
|
||||
}
|
||||
free(session);
|
||||
|
||||
spinlock_acquire(&orphanLock);
|
||||
orphan_session_t *ptr = allOrphans, *finished = NULL,*tmp = NULL;
|
||||
#ifdef SS_DEBUG
|
||||
int o_stopping = 0, o_ready = 0,o_freed = 0;
|
||||
#endif
|
||||
while(ptr)
|
||||
{
|
||||
if(ptr->session->state == SESSION_STATE_TO_BE_FREED)
|
||||
{
|
||||
if(ptr == allOrphans)
|
||||
{
|
||||
tmp = ptr;
|
||||
allOrphans = ptr->next;
|
||||
}
|
||||
else
|
||||
{
|
||||
tmp = allOrphans;
|
||||
while(tmp && tmp->next != ptr)
|
||||
tmp = tmp->next;
|
||||
if(tmp)
|
||||
{
|
||||
tmp->next = ptr->next;
|
||||
tmp = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef SS_DEBUG
|
||||
else if(ptr->session->state == SESSION_STATE_STOPPING)
|
||||
{
|
||||
o_stopping++;
|
||||
}
|
||||
else if(ptr->session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
o_ready++;
|
||||
}
|
||||
#endif
|
||||
ptr = ptr->next;
|
||||
if(tmp)
|
||||
{
|
||||
tmp->next = finished;
|
||||
finished = tmp;
|
||||
tmp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_release(&orphanLock);
|
||||
|
||||
#ifdef SS_DEBUG
|
||||
if(o_stopping + o_ready > 0)
|
||||
skygw_log_write(LOGFILE_DEBUG,"tee.c: %d orphans in "
|
||||
"SESSION_STATE_STOPPING, %d orphans in "
|
||||
"SESSION_STATE_ROUTER_READY. ",o_stopping,o_ready);
|
||||
#endif
|
||||
|
||||
while(finished)
|
||||
{
|
||||
#ifdef SS_DEBUG
|
||||
skygw_log_write(LOGFILE_DEBUG,"tee.c: %d orphans freed.",++o_freed);
|
||||
#endif
|
||||
tmp = finished;
|
||||
finished = finished->next;
|
||||
|
||||
tmp->session->service->router->freeSession(
|
||||
tmp->session->service->router_instance,
|
||||
tmp->session->router_session);
|
||||
|
||||
tmp->session->state = SESSION_STATE_FREE;
|
||||
free(tmp->session);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the downstream filter or router to which queries will be
|
||||
* passed from this filter.
|
||||
@ -365,9 +690,23 @@ TEE_SESSION *my_session = (TEE_SESSION *)session;
|
||||
static void
|
||||
setDownstream(FILTER *instance, void *session, DOWNSTREAM *downstream)
|
||||
{
|
||||
TEE_SESSION *my_session = (TEE_SESSION *)session;
|
||||
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
||||
my_session->down = *downstream;
|
||||
}
|
||||
|
||||
my_session->down = *downstream;
|
||||
/**
|
||||
* Set the downstream filter or router to which queries will be
|
||||
* passed from this filter.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param downstream The downstream filter or router.
|
||||
*/
|
||||
static void
|
||||
setUpstream(FILTER *instance, void *session, UPSTREAM *upstream)
|
||||
{
|
||||
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
||||
my_session->up = *upstream;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -397,47 +736,133 @@ char *ptr;
|
||||
int length, rval, residual = 0;
|
||||
GWBUF *clone = NULL;
|
||||
|
||||
if (my_session->residual)
|
||||
if (my_session->branch_session &&
|
||||
my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
clone = gwbuf_clone(queue);
|
||||
if (my_session->residual < GWBUF_LENGTH(clone))
|
||||
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
|
||||
my_session->residual -= GWBUF_LENGTH(clone);
|
||||
if (my_session->residual < 0)
|
||||
my_session->residual = 0;
|
||||
}
|
||||
else if ( my_session->active && (ptr = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
if ((my_instance->match == NULL ||
|
||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||
(my_instance->nomatch == NULL ||
|
||||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
|
||||
if (my_session->residual)
|
||||
{
|
||||
char *dummy;
|
||||
|
||||
modutil_MySQL_Query(queue, &dummy, &length, &residual);
|
||||
clone = gwbuf_clone(queue);
|
||||
my_session->residual = residual;
|
||||
|
||||
clone = gwbuf_clone_all(queue);
|
||||
|
||||
if (my_session->residual < GWBUF_LENGTH(clone))
|
||||
{
|
||||
GWBUF_RTRIM(clone, GWBUF_LENGTH(clone) - residual);
|
||||
}
|
||||
my_session->residual -= GWBUF_LENGTH(clone);
|
||||
|
||||
if (my_session->residual < 0)
|
||||
{
|
||||
my_session->residual = 0;
|
||||
}
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
else if (my_session->active && (ptr = modutil_get_SQL(queue)) != NULL)
|
||||
{
|
||||
if ((my_instance->match == NULL ||
|
||||
regexec(&my_instance->re, ptr, 0, NULL, 0) == 0) &&
|
||||
(my_instance->nomatch == NULL ||
|
||||
regexec(&my_instance->nore,ptr,0,NULL, 0) != 0))
|
||||
{
|
||||
char *dummy;
|
||||
|
||||
length = modutil_MySQL_query_len(queue, &residual);
|
||||
clone = gwbuf_clone_all(queue);
|
||||
my_session->residual = residual;
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
else if (packet_is_required(queue))
|
||||
{
|
||||
clone = gwbuf_clone_all(queue);
|
||||
}
|
||||
}
|
||||
/* Pass the query downstream */
|
||||
|
||||
my_session->replies = 0;
|
||||
rval = my_session->down.routeQuery(my_session->down.instance,
|
||||
my_session->down.session, queue);
|
||||
my_session->down.session,
|
||||
queue);
|
||||
if (clone)
|
||||
{
|
||||
my_session->n_duped++;
|
||||
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
|
||||
|
||||
if (my_session->branch_session->state == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
SESSION_ROUTE_QUERY(my_session->branch_session, clone);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Close tee session */
|
||||
my_session->active = 0;
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Closed tee filter session.")));
|
||||
gwbuf_free(clone);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (my_session->active)
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Closed tee filter session.")));
|
||||
my_session->active = 0;
|
||||
}
|
||||
my_session->n_rejected++;
|
||||
}
|
||||
return rval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The clientReply entry point. This is passed the response buffer
|
||||
* to which the filter should be applied. Once processed the
|
||||
* query is passed to the upstream component
|
||||
* (filter or router) in the filter chain.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param reply The response data
|
||||
*/
|
||||
static int
|
||||
clientReply (FILTER* instance, void *session, GWBUF *reply)
|
||||
{
|
||||
int rc;
|
||||
TEE_SESSION *my_session = (TEE_SESSION *) session;
|
||||
|
||||
spinlock_acquire(&my_session->tee_lock);
|
||||
|
||||
ss_dassert(my_session->active);
|
||||
my_session->replies++;
|
||||
|
||||
if (my_session->tee_replybuf == NULL &&
|
||||
instance != NULL)
|
||||
{
|
||||
my_session->tee_replybuf = reply;
|
||||
}
|
||||
else
|
||||
{
|
||||
gwbuf_free(reply);
|
||||
}
|
||||
|
||||
if((my_session->branch_session == NULL ||
|
||||
my_session->replies >= my_session->min_replies) &&
|
||||
my_session->tee_replybuf != NULL)
|
||||
{
|
||||
rc = my_session->up.clientReply (
|
||||
my_session->up.instance,
|
||||
my_session->up.session,
|
||||
my_session->tee_replybuf);
|
||||
my_session->replies = 0;
|
||||
my_session->tee_replybuf = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
spinlock_release(&my_session->tee_lock);
|
||||
return rc;
|
||||
}
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
@ -477,3 +902,74 @@ TEE_SESSION *my_session = (TEE_SESSION *)fsession;
|
||||
my_session->n_rejected);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the packet is a command that must be sent to the branch
|
||||
* to maintain the session consistancy. These are COM_INIT_DB,
|
||||
* COM_CHANGE_USER and COM_QUIT packets.
|
||||
*
|
||||
* @param queue The buffer to check
|
||||
* @return non-zero if the packet should be sent to the branch
|
||||
*/
|
||||
static int
|
||||
packet_is_required(GWBUF *queue)
|
||||
{
|
||||
uint8_t *ptr;
|
||||
int i;
|
||||
|
||||
ptr = GWBUF_DATA(queue);
|
||||
if (GWBUF_LENGTH(queue) > 4)
|
||||
for (i = 0; required_packets[i]; i++)
|
||||
if (ptr[4] == required_packets[i])
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detects possible loops in the query cloning chain.
|
||||
*/
|
||||
int detect_loops(TEE_INSTANCE *instance,HASHTABLE* ht, SERVICE* service)
|
||||
{
|
||||
SERVICE* svc = service;
|
||||
int i;
|
||||
|
||||
if(ht == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(hashtable_add(ht,(void*)service->name,(void*)true) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for(i = 0;i<svc->n_filters;i++)
|
||||
{
|
||||
if(strcmp(svc->filters[i]->module,"tee") == 0)
|
||||
{
|
||||
/*
|
||||
* Found a Tee filter, recurse down its path
|
||||
* if the service name isn't already in the hashtable.
|
||||
*/
|
||||
|
||||
TEE_INSTANCE* ninst = (TEE_INSTANCE*)svc->filters[i]->filter;
|
||||
if(ninst == NULL)
|
||||
{
|
||||
/**
|
||||
* This tee instance hasn't been initialized yet and full
|
||||
* resolution of recursion cannot be done now.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
SERVICE* tgt = ninst->service;
|
||||
|
||||
if(detect_loops((TEE_INSTANCE*)svc->filters[i]->filter,ht,tgt))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -13,4 +13,12 @@ target_link_libraries(harness fullcore)
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_test(TestHintfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.input -o ${CMAKE_CURRENT_BINARY_DIR}/hint_testing.output -c ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.cnf -t 1 -s 1 -e ${CMAKE_CURRENT_SOURCE_DIR}/hint_testing.expected")
|
||||
|
||||
add_test(TestRegexfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/regextest.input -o ${CMAKE_CURRENT_BINARY_DIR}/regextest.output -c ${CMAKE_CURRENT_SOURCE_DIR}/regextest.cnf -t 1 -s 1 -e ${CMAKE_CURRENT_SOURCE_DIR}/regextest.expected")
|
||||
add_test(TestRegexfilter /bin/sh -c "MAXSCALE_HOME=\"${CMAKE_BINARY_DIR}\" ${CMAKE_CURRENT_BINARY_DIR}/harness -i ${CMAKE_CURRENT_SOURCE_DIR}/regextest.input -o ${CMAKE_CURRENT_BINARY_DIR}/regextest.output -c ${CMAKE_CURRENT_SOURCE_DIR}/regextest.cnf -t 1 -s 1 -e ${CMAKE_CURRENT_SOURCE_DIR}/regextest.expected")
|
||||
|
||||
add_test(TestTeeRecursion ${CMAKE_CURRENT_SOURCE_DIR}/tee_recursion.sh
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}
|
||||
${TEST_USER}
|
||||
${TEST_PASSWORD}
|
||||
${TEST_HOST}
|
||||
${TEST_PORT})
|
||||
|
86
server/modules/filter/test/tee_recursion.sh
Executable file
86
server/modules/filter/test/tee_recursion.sh
Executable file
@ -0,0 +1,86 @@
|
||||
#!/bin/bash
|
||||
|
||||
function execute_test()
|
||||
{
|
||||
|
||||
RVAL=$(mysql --connect-timeout=5 -u $USER -p$PWD -h $HOST -P $PORT -e "select 1;"|grep -i error)
|
||||
|
||||
if [[ ! -e $MAXPID ]]
|
||||
then
|
||||
echo "Test failed: $MAXPID was not found."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "$RVAL" != "" ]]
|
||||
then
|
||||
echo "Test failed: Query to backend didn't return an error."
|
||||
return 1
|
||||
fi
|
||||
|
||||
LAST_LOG=$(ls $BINDIR/log -1|grep err|sort|uniq|tail -n 1)
|
||||
TEST_RESULT=$(cat $BINDIR/log/$LAST_LOG | grep -i recursive)
|
||||
if [[ "$TEST_RESULT" != "" ]]
|
||||
then
|
||||
return 0
|
||||
fi
|
||||
echo "Test failed: Log file didn't mention tee recursion."
|
||||
return 1
|
||||
}
|
||||
|
||||
function reload_conf()
|
||||
{
|
||||
$BINDIR/bin/maxadmin --user=admin --password=skysql reload config
|
||||
if [[ $? -ne 0 ]]
|
||||
then
|
||||
echo "Test failed: maxadmin returned a non-zero value."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
if [[ $# -lt 6 ]]
|
||||
then
|
||||
echo "usage: $0 <build dir> <source dir>"
|
||||
exit 1
|
||||
fi
|
||||
BINDIR=$1
|
||||
SRCDIR=$2
|
||||
USER=$3
|
||||
PWD=$4
|
||||
HOST=$5
|
||||
PORT=$6
|
||||
CONF=$BINDIR/etc/MaxScale.cnf
|
||||
OLDCONF=$BINDIR/etc/MaxScale.cnf.old
|
||||
MAXPID=$BINDIR/log/$(ls -1 $BINDIR/log|grep maxscale)
|
||||
TEST1=$SRCDIR/server/modules/filter/test/tee_recursion1.cnf
|
||||
TEST2=$SRCDIR/server/modules/filter/test/tee_recursion2.cnf
|
||||
|
||||
$BINDIR/bin/maxadmin --user=admin --password=skysql flush logs
|
||||
|
||||
mv $CONF $OLDCONF
|
||||
cp $TEST1 $CONF
|
||||
reload_conf
|
||||
execute_test
|
||||
T1RVAL=$?
|
||||
mv $CONF $CONF.test1
|
||||
cp $TEST2 $CONF
|
||||
reload_conf
|
||||
execute_test
|
||||
T2RVAL=$?
|
||||
mv $CONF $CONF.test2
|
||||
mv $OLDCONF $CONF
|
||||
reload_conf
|
||||
|
||||
if [[ $T1RVAL -ne 0 ]]
|
||||
then
|
||||
echo "Test 1 failed."
|
||||
exit 1
|
||||
elif [[ $T2RVAL -ne 0 ]]
|
||||
then
|
||||
echo "Test 2 failed"
|
||||
exit 1
|
||||
else
|
||||
echo "Test successful: log mentions recursive tee usage."
|
||||
fi
|
||||
|
||||
exit 0
|
114
server/modules/filter/test/tee_recursion1.cnf
Normal file
114
server/modules/filter/test/tee_recursion1.cnf
Normal file
@ -0,0 +1,114 @@
|
||||
[maxscale]
|
||||
threads=4
|
||||
|
||||
[MySQL Monitor]
|
||||
type=monitor
|
||||
module=mysqlmon
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
monitor_interval=10000
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=recurse1
|
||||
|
||||
[RW Split Hint Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=recurse2
|
||||
|
||||
|
||||
[Read Connection Router]
|
||||
type=service
|
||||
router=readconnroute
|
||||
router_options=master
|
||||
servers=server1
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=recurse3
|
||||
|
||||
[recurse3]
|
||||
type=filter
|
||||
module=tee
|
||||
service=RW Split Router
|
||||
|
||||
[recurse2]
|
||||
type=filter
|
||||
module=tee
|
||||
service=Read Connection Router
|
||||
|
||||
[recurse1]
|
||||
type=filter
|
||||
module=tee
|
||||
service=RW Split Hint Router
|
||||
|
||||
|
||||
[Debug Interface]
|
||||
type=service
|
||||
router=debugcli
|
||||
|
||||
[CLI]
|
||||
type=service
|
||||
router=cli
|
||||
|
||||
[Read Connection Listener]
|
||||
type=listener
|
||||
service=Read Connection Router
|
||||
protocol=MySQLClient
|
||||
port=4008
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
service=RW Split Router
|
||||
protocol=MySQLClient
|
||||
port=4006
|
||||
|
||||
[RW Split Hint Listener]
|
||||
type=listener
|
||||
service=RW Split Hint Router
|
||||
protocol=MySQLClient
|
||||
port=4009
|
||||
|
||||
[Debug Listener]
|
||||
type=listener
|
||||
service=Debug Interface
|
||||
protocol=telnetd
|
||||
port=4442
|
||||
|
||||
[CLI Listener]
|
||||
type=listener
|
||||
service=CLI
|
||||
protocol=maxscaled
|
||||
port=6603
|
||||
|
||||
[server1]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3000
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server2]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3001
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server3]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3002
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server4]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3003
|
||||
protocol=MySQLBackend
|
112
server/modules/filter/test/tee_recursion2.cnf
Normal file
112
server/modules/filter/test/tee_recursion2.cnf
Normal file
@ -0,0 +1,112 @@
|
||||
[maxscale]
|
||||
threads=4
|
||||
|
||||
[MySQL Monitor]
|
||||
type=monitor
|
||||
module=mysqlmon
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
monitor_interval=10000
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=recurse1|recurse2
|
||||
|
||||
[RW Split Hint Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
|
||||
[Read Connection Router]
|
||||
type=service
|
||||
router=readconnroute
|
||||
router_options=master
|
||||
servers=server1
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=recurse3
|
||||
|
||||
[recurse3]
|
||||
type=filter
|
||||
module=tee
|
||||
service=RW Split Router
|
||||
|
||||
[recurse2]
|
||||
type=filter
|
||||
module=tee
|
||||
service=Read Connection Router
|
||||
|
||||
[recurse1]
|
||||
type=filter
|
||||
module=tee
|
||||
service=RW Split Hint Router
|
||||
|
||||
|
||||
[Debug Interface]
|
||||
type=service
|
||||
router=debugcli
|
||||
|
||||
[CLI]
|
||||
type=service
|
||||
router=cli
|
||||
|
||||
[Read Connection Listener]
|
||||
type=listener
|
||||
service=Read Connection Router
|
||||
protocol=MySQLClient
|
||||
port=4008
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
service=RW Split Router
|
||||
protocol=MySQLClient
|
||||
port=4006
|
||||
|
||||
[RW Split Hint Listener]
|
||||
type=listener
|
||||
service=RW Split Hint Router
|
||||
protocol=MySQLClient
|
||||
port=4009
|
||||
|
||||
[Debug Listener]
|
||||
type=listener
|
||||
service=Debug Interface
|
||||
protocol=telnetd
|
||||
port=4442
|
||||
|
||||
[CLI Listener]
|
||||
type=listener
|
||||
service=CLI
|
||||
protocol=maxscaled
|
||||
port=6603
|
||||
|
||||
[server1]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3000
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server2]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3001
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server3]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3002
|
||||
protocol=MySQLBackend
|
||||
|
||||
[server4]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
port=3003
|
||||
protocol=MySQLBackend
|
@ -161,6 +161,7 @@ typedef struct router_slave {
|
||||
int binlog_pos; /*< Binlog position for this slave */
|
||||
char binlogfile[BINLOG_FNAMELEN+1];
|
||||
/*< Current binlog file for this slave */
|
||||
char *uuid; /*< Slave UUID */
|
||||
BLFILE *file; /*< Currently open binlog file */
|
||||
int serverid; /*< Server-id of the slave */
|
||||
char *hostname; /*< Hostname of the slave, if known */
|
||||
@ -227,6 +228,8 @@ typedef struct {
|
||||
GWBUF *utf8; /*< Set NAMES utf8 */
|
||||
GWBUF *select1; /*< select 1 */
|
||||
GWBUF *selectver; /*< select version() */
|
||||
GWBUF *selectvercom; /*< select @@version_comment */
|
||||
GWBUF *selecthostname;/*< select @@hostname */
|
||||
uint8_t *fde_event; /*< Format Description Event */
|
||||
int fde_len; /*< Length of fde_event */
|
||||
} MASTER_RESPONSES;
|
||||
@ -300,16 +303,19 @@ typedef struct router_instance {
|
||||
#define BLRM_UTF8 0x000C
|
||||
#define BLRM_SELECT1 0x000D
|
||||
#define BLRM_SELECTVER 0x000E
|
||||
#define BLRM_REGISTER 0x000F
|
||||
#define BLRM_BINLOGDUMP 0x0010
|
||||
#define BLRM_SELECTVERCOM 0x000F
|
||||
#define BLRM_SELECTHOSTNAME 0x0010
|
||||
#define BLRM_REGISTER 0x0011
|
||||
#define BLRM_BINLOGDUMP 0x0012
|
||||
|
||||
#define BLRM_MAXSTATE 0x0010
|
||||
#define BLRM_MAXSTATE 0x0012
|
||||
|
||||
static char *blrm_states[] = { "Unconnected", "Connecting", "Authenticated", "Timestamp retrieval",
|
||||
"Server ID retrieval", "HeartBeat Period setup", "binlog checksum config",
|
||||
"binlog checksum rerieval", "GTID Mode retrieval", "Master UUID retrieval",
|
||||
"Set Slave UUID", "Set Names latin1", "Set Names utf8", "select 1",
|
||||
"select version()", "Register slave", "Binlog Dump" };
|
||||
"select version()", "select @@version_comment", "select @@hostname",
|
||||
"Register slave", "Binlog Dump" };
|
||||
|
||||
#define BLRS_CREATED 0x0000
|
||||
#define BLRS_UNREGISTERED 0x0001
|
||||
@ -338,6 +344,8 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered",
|
||||
*/
|
||||
#define COM_QUIT 0x01
|
||||
#define COM_QUERY 0x03
|
||||
#define COM_STATISTICS 0x09
|
||||
#define COM_PING 0x0e
|
||||
#define COM_REGISTER_SLAVE 0x15
|
||||
#define COM_BINLOG_DUMP 0x12
|
||||
|
||||
@ -429,9 +437,9 @@ extern void blr_slave_rotate(ROUTER_SLAVE *slave, uint8_t *ptr);
|
||||
extern int blr_slave_catchup(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, bool large);
|
||||
extern void blr_init_cache(ROUTER_INSTANCE *);
|
||||
|
||||
extern void blr_file_init(ROUTER_INSTANCE *);
|
||||
extern void blr_write_binlog_record(ROUTER_INSTANCE *, REP_HEADER *,uint8_t *);
|
||||
extern void blr_file_rotate(ROUTER_INSTANCE *, char *, uint64_t);
|
||||
extern int blr_file_init(ROUTER_INSTANCE *);
|
||||
extern int blr_write_binlog_record(ROUTER_INSTANCE *, REP_HEADER *,uint8_t *);
|
||||
extern int blr_file_rotate(ROUTER_INSTANCE *, char *, uint64_t);
|
||||
extern void blr_file_flush(ROUTER_INSTANCE *);
|
||||
extern BLFILE *blr_open_binlog(ROUTER_INSTANCE *, char *);
|
||||
extern GWBUF *blr_read_binlog(ROUTER_INSTANCE *, BLFILE *, unsigned int, REP_HEADER *);
|
||||
|
@ -111,9 +111,15 @@ typedef enum {
|
||||
*
|
||||
*/
|
||||
typedef struct mysql_session {
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t myses_chk_top;
|
||||
#endif
|
||||
uint8_t client_sha1[MYSQL_SCRAMBLE_LEN]; /*< SHA1(passowrd) */
|
||||
char user[MYSQL_USER_MAXLEN+1]; /*< username */
|
||||
char db[MYSQL_DATABASE_MAXLEN+1]; /*< database */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t myses_chk_tail;
|
||||
#endif
|
||||
} MYSQL_session;
|
||||
|
||||
|
||||
@ -304,11 +310,9 @@ typedef struct {
|
||||
|
||||
#endif /** _MYSQL_PROTOCOL_H */
|
||||
|
||||
void gw_mysql_close(MySQLProtocol **ptr);
|
||||
MySQLProtocol* mysql_protocol_init(DCB* dcb, int fd);
|
||||
void mysql_protocol_done (DCB* dcb);
|
||||
MySQLProtocol *gw_mysql_init(MySQLProtocol *data);
|
||||
void gw_mysql_close(MySQLProtocol **ptr);
|
||||
int gw_receive_backend_auth(MySQLProtocol *protocol);
|
||||
int gw_decode_mysql_server_handshake(MySQLProtocol *protocol, uint8_t *payload);
|
||||
int gw_read_backend_handshake(MySQLProtocol *protocol);
|
||||
|
@ -323,4 +323,5 @@ typedef struct router_instance {
|
||||
#define BACKEND_TYPE(b) (SERVER_IS_MASTER((b)->backend_server) ? BE_MASTER : \
|
||||
(SERVER_IS_SLAVE((b)->backend_server) ? BE_SLAVE : BE_UNDEFINED));
|
||||
|
||||
|
||||
#endif /*< _RWSPLITROUTER_H */
|
||||
|
@ -671,7 +671,27 @@ int log_no_master = 1;
|
||||
|
||||
if (mon_status_changed(ptr))
|
||||
{
|
||||
dcb_call_foreach(DCB_REASON_NOT_RESPONDING);
|
||||
if (SRV_MASTER_STATUS(ptr->mon_prev_status))
|
||||
{
|
||||
/** Master failed, can't recover */
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Server %s:%d lost the master status.",
|
||||
ptr->server->name,
|
||||
ptr->server->port)));
|
||||
}
|
||||
/**
|
||||
* Here we say: If the server's state changed
|
||||
* so that it isn't running or some other way
|
||||
* lost cluster membership, call call-back function
|
||||
* of every DCB for which such callback was
|
||||
* registered for this kind of issue (DCB_REASON_...)
|
||||
*/
|
||||
if (!(SERVER_IS_RUNNING(ptr->server)) ||
|
||||
!(SERVER_IS_IN_CLUSTER(ptr->server)))
|
||||
{
|
||||
dcb_call_foreach(DCB_REASON_NOT_RESPONDING);
|
||||
}
|
||||
}
|
||||
|
||||
if (mon_status_changed(ptr))
|
||||
@ -734,7 +754,13 @@ int log_no_master = 1;
|
||||
{
|
||||
if (! SERVER_IN_MAINT(ptr->server)) {
|
||||
/* If "detect_stale_master" option is On, let's use the previus master */
|
||||
if (detect_stale_master && root_master && (!strcmp(ptr->server->name, root_master->server->name) && ptr->server->port == root_master->server->port) && (ptr->server->status & SERVER_MASTER) && !(ptr->pending_status & SERVER_MASTER)) {
|
||||
if (detect_stale_master &&
|
||||
root_master &&
|
||||
(!strcmp(ptr->server->name, root_master->server->name) &&
|
||||
ptr->server->port == root_master->server->port) &&
|
||||
(ptr->server->status & SERVER_MASTER) &&
|
||||
!(ptr->pending_status & SERVER_MASTER))
|
||||
{
|
||||
/**
|
||||
* In this case server->status will not be updated from pending_statu
|
||||
* Set the STALE bit for this server in server struct
|
||||
@ -744,24 +770,31 @@ int log_no_master = 1;
|
||||
/* log it once */
|
||||
if (mon_status_changed(ptr)) {
|
||||
LOGIF(LM, (skygw_log_write_flush(
|
||||
LOGFILE_MESSAGE, "[mysql_mon]: root server [%s:%i] is no longer Master,"
|
||||
" let's use it again even if it could be a stale master,"
|
||||
" you have been warned!",
|
||||
ptr->server->name,
|
||||
ptr->server->port)));
|
||||
LOGFILE_MESSAGE,
|
||||
"[mysql_mon]: root server "
|
||||
"[%s:%i] is no longer Master,"
|
||||
" let's use it again even "
|
||||
" if it could be a stale master,"
|
||||
" you have been warned!",
|
||||
ptr->server->name,
|
||||
ptr->server->port)));
|
||||
}
|
||||
} else {
|
||||
ptr->server->status = ptr->pending_status;
|
||||
}
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/* log master detection failure od first master becomes available after failure */
|
||||
if (root_master && mon_status_changed(root_master) && !(root_master->server->status & SERVER_STALE_STATUS)) {
|
||||
if (root_master &&
|
||||
mon_status_changed(root_master) &&
|
||||
!(root_master->server->status & SERVER_STALE_STATUS))
|
||||
{
|
||||
if (root_master->pending_status & (SERVER_MASTER)) {
|
||||
if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) && !(root_master->server->status & SERVER_MAINT)) {
|
||||
if (!(root_master->mon_prev_status & SERVER_STALE_STATUS) &&
|
||||
!(root_master->server->status & SERVER_MAINT))
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Info : A Master Server is now available: %s:%i",
|
||||
@ -777,7 +810,8 @@ int log_no_master = 1;
|
||||
}
|
||||
log_no_master = 1;
|
||||
} else {
|
||||
if (!root_master && log_no_master) {
|
||||
if (!root_master && log_no_master)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : No Master can be determined")));
|
||||
@ -786,13 +820,21 @@ int log_no_master = 1;
|
||||
}
|
||||
|
||||
/* Do now the heartbeat replication set/get for MySQL Replication Consistency */
|
||||
if (replication_heartbeat && root_master && (SERVER_IS_MASTER(root_master->server) || SERVER_IS_RELAY_SERVER(root_master->server))) {
|
||||
if (replication_heartbeat &&
|
||||
root_master &&
|
||||
(SERVER_IS_MASTER(root_master->server) ||
|
||||
SERVER_IS_RELAY_SERVER(root_master->server)))
|
||||
{
|
||||
set_master_heartbeat(handle, root_master);
|
||||
ptr = handle->databases;
|
||||
|
||||
while (ptr) {
|
||||
if( (! SERVER_IN_MAINT(ptr->server)) && SERVER_IS_RUNNING(ptr->server))
|
||||
{
|
||||
if (ptr->server->node_id != root_master->server->node_id && (SERVER_IS_SLAVE(ptr->server) || SERVER_IS_RELAY_SERVER(ptr->server))) {
|
||||
if (ptr->server->node_id != root_master->server->node_id &&
|
||||
(SERVER_IS_SLAVE(ptr->server) ||
|
||||
SERVER_IS_RELAY_SERVER(ptr->server)))
|
||||
{
|
||||
set_slave_heartbeat(handle, ptr);
|
||||
}
|
||||
}
|
||||
|
@ -510,6 +510,16 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
if (nbytes_read < 5) /*< read at least command type */
|
||||
{
|
||||
rc = 0;
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"%p [gw_read_backend_event] Read %d bytes "
|
||||
"from DCB %p, fd %d, session %s. "
|
||||
"Returning to poll wait.\n",
|
||||
pthread_self(),
|
||||
nbytes_read,
|
||||
dcb,
|
||||
dcb->fd,
|
||||
dcb->session)));
|
||||
goto return_rc;
|
||||
}
|
||||
/** There is at least length and command type. */
|
||||
@ -699,16 +709,6 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
|
||||
switch (backend_protocol->protocol_auth_state) {
|
||||
case MYSQL_HANDSHAKE_FAILED:
|
||||
case MYSQL_AUTH_FAILED:
|
||||
{
|
||||
size_t len;
|
||||
char* str;
|
||||
uint8_t* packet = (uint8_t *)queue->start;
|
||||
uint8_t* startpoint;
|
||||
|
||||
len = (size_t)MYSQL_GET_PACKET_LEN(packet);
|
||||
startpoint = &packet[5];
|
||||
str = (char *)malloc(len+1);
|
||||
snprintf(str, len+1, "%s", startpoint);
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to write to backend due to "
|
||||
@ -717,13 +717,11 @@ gw_MySQLWrite_backend(DCB *dcb, GWBUF *queue)
|
||||
while ((queue = gwbuf_consume(
|
||||
queue,
|
||||
GWBUF_LENGTH(queue))) != NULL);
|
||||
free(str);
|
||||
rc = 0;
|
||||
spinlock_release(&dcb->authlock);
|
||||
goto return_rc;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case MYSQL_IDLE:
|
||||
{
|
||||
uint8_t* ptr = GWBUF_DATA(queue);
|
||||
@ -1165,22 +1163,37 @@ gw_backend_close(DCB *dcb)
|
||||
mysql_send_com_quit(dcb, 0, quitbuf);
|
||||
|
||||
mysql_protocol_done(dcb);
|
||||
|
||||
/**
|
||||
* The lock is needed only to protect the read of session->state and
|
||||
* session->client values. Client's state may change by other thread
|
||||
* but client's close and adding client's DCB to zombies list is executed
|
||||
* only if client's DCB's state does _not_ change in parallel.
|
||||
*/
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
/**
|
||||
* If session->state is STOPPING, start closing client session.
|
||||
* Otherwise only this backend connection is closed.
|
||||
*/
|
||||
if (session != NULL && session->state == SESSION_STATE_STOPPING)
|
||||
{
|
||||
client_dcb = session->client;
|
||||
|
||||
if (client_dcb != NULL &&
|
||||
client_dcb->state == DCB_STATE_POLLING)
|
||||
if (session != NULL &&
|
||||
session->state == SESSION_STATE_STOPPING &&
|
||||
session->client != NULL)
|
||||
{
|
||||
if (session->client->state == DCB_STATE_POLLING)
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
|
||||
/** Close client DCB */
|
||||
dcb_close(client_dcb);
|
||||
dcb_close(session->client);
|
||||
}
|
||||
else
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -1495,7 +1508,7 @@ static GWBUF* process_response_data (
|
||||
|
||||
/** Get command which was stored in gw_MySQLWrite_backend */
|
||||
p = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
CHK_PROTOCOL(p);
|
||||
if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(p);
|
||||
|
||||
/** All buffers processed here are sescmd responses */
|
||||
gwbuf_set_type(readbuf, GWBUF_TYPE_SESCMD_RESPONSE);
|
||||
@ -1510,7 +1523,14 @@ static GWBUF* process_response_data (
|
||||
bool succp;
|
||||
|
||||
srvcmd = protocol_get_srv_command(p, false);
|
||||
|
||||
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [process_response_data] Read command %s for DCB %p fd %d.",
|
||||
pthread_self(),
|
||||
STRPACKETTYPE(srvcmd),
|
||||
dcb,
|
||||
dcb->fd)));
|
||||
/**
|
||||
* Read values from protocol structure, fails if values are
|
||||
* uninitialized.
|
||||
@ -1582,7 +1602,7 @@ static GWBUF* process_response_data (
|
||||
if (nbytes_left == 0)
|
||||
{
|
||||
/** No more packets in this response */
|
||||
if (npackets_left == 0)
|
||||
if (npackets_left == 0 && outbuf != NULL)
|
||||
{
|
||||
GWBUF* b = outbuf;
|
||||
|
||||
@ -1622,7 +1642,7 @@ static bool sescmd_response_complete(
|
||||
bool succp;
|
||||
|
||||
p = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
CHK_PROTOCOL(p);
|
||||
if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(p);
|
||||
|
||||
protocol_get_response_status(p, &npackets_left, &nbytes_left);
|
||||
|
||||
|
@ -144,7 +144,7 @@ GetModuleObject()
|
||||
int
|
||||
mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mysql_message) {
|
||||
uint8_t *outbuf = NULL;
|
||||
uint8_t mysql_payload_size = 0;
|
||||
uint32_t mysql_payload_size = 0;
|
||||
uint8_t mysql_packet_header[4];
|
||||
uint8_t *mysql_payload = NULL;
|
||||
uint8_t field_count = 0;
|
||||
@ -223,7 +223,7 @@ int
|
||||
MySQLSendHandshake(DCB* dcb)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
uint8_t mysql_payload_size = 0;
|
||||
uint32_t mysql_payload_size = 0;
|
||||
uint8_t mysql_packet_header[4];
|
||||
uint8_t mysql_packet_id = 0;
|
||||
uint8_t mysql_filler = GW_MYSQL_HANDSHAKE_FILLER;
|
||||
@ -283,7 +283,6 @@ MySQLSendHandshake(DCB* dcb)
|
||||
|
||||
// write packet heder with mysql_payload_size
|
||||
gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size);
|
||||
//mysql_packet_header[0] = mysql_payload_size;
|
||||
|
||||
// write packent number, now is 0
|
||||
mysql_packet_header[3]= mysql_packet_id;
|
||||
@ -378,15 +377,18 @@ MySQLSendHandshake(DCB* dcb)
|
||||
*
|
||||
* Performs the MySQL protocol 4.1 authentication, using data in GWBUF *queue
|
||||
*
|
||||
* The useful data: user, db, client_sha1 are copied into the MYSQL_session * dcb->session->data
|
||||
* (MYSQL_session*)client_data including: user, db, client_sha1 are copied into
|
||||
* the dcb->data and later to dcb->session->data.
|
||||
*
|
||||
* client_capabilitiesa are copied into the dcb->protocol
|
||||
*
|
||||
* @param dcb Descriptor Control Block of the client
|
||||
* @param queue The GWBUF with data from client
|
||||
* @return 0 If succeed, otherwise non-zero value
|
||||
*
|
||||
* @note in case of failure, dcb->data is freed before returning. If succeed,
|
||||
* dcb->data is freed in session.c:session_free.
|
||||
*/
|
||||
|
||||
static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
MySQLProtocol *protocol = NULL;
|
||||
/* int compress = -1; */
|
||||
@ -406,6 +408,13 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
protocol = DCB_PROTOCOL(dcb, MySQLProtocol);
|
||||
CHK_PROTOCOL(protocol);
|
||||
client_data = (MYSQL_session *)calloc(1, sizeof(MYSQL_session));
|
||||
#if defined(SS_DEBUG)
|
||||
client_data->myses_chk_top = CHK_NUM_MYSQLSES;
|
||||
client_data->myses_chk_tail = CHK_NUM_MYSQLSES;
|
||||
#endif
|
||||
/**
|
||||
* Assign authentication structure with client DCB.
|
||||
*/
|
||||
dcb->data = client_data;
|
||||
|
||||
stage1_hash = client_data->client_sha1;
|
||||
@ -426,7 +435,8 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
*/
|
||||
|
||||
/* Detect now if there are enough bytes to continue */
|
||||
if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23)) {
|
||||
if (client_auth_packet_size < (4 + 4 + 4 + 1 + 23))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -619,7 +629,7 @@ int gw_read_client_event(
|
||||
* Now there should be at least one complete mysql packet in read_buffer.
|
||||
*/
|
||||
switch (protocol->protocol_auth_state) {
|
||||
|
||||
|
||||
case MYSQL_AUTH_SENT:
|
||||
{
|
||||
int auth_val;
|
||||
@ -658,8 +668,8 @@ int gw_read_client_event(
|
||||
"%lu [gw_read_client_event] session "
|
||||
"creation failed. fd %d, "
|
||||
"state = MYSQL_AUTH_FAILED.",
|
||||
protocol->owner_dcb->fd,
|
||||
pthread_self())));
|
||||
pthread_self(),
|
||||
protocol->owner_dcb->fd)));
|
||||
|
||||
/** Send ERR 1045 to client */
|
||||
mysql_send_auth_error(
|
||||
@ -671,7 +681,7 @@ int gw_read_client_event(
|
||||
dcb_close(dcb);
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
char* fail_str = NULL;
|
||||
|
||||
@ -704,7 +714,15 @@ int gw_read_client_event(
|
||||
"state = MYSQL_AUTH_FAILED.",
|
||||
protocol->owner_dcb->fd,
|
||||
pthread_self())));
|
||||
|
||||
/**
|
||||
* Release MYSQL_session since it is not used anymore.
|
||||
*/
|
||||
if (!DCB_IS_CLONE(dcb))
|
||||
{
|
||||
free(dcb->data);
|
||||
}
|
||||
dcb->data = NULL;
|
||||
|
||||
dcb_close(dcb);
|
||||
}
|
||||
read_buffer = gwbuf_consume(read_buffer, nbytes_read);
|
||||
@ -1396,15 +1414,13 @@ gw_client_close(DCB *dcb)
|
||||
dcb->state == DCB_STATE_NOPOLLING ||
|
||||
dcb->state == DCB_STATE_ZOMBIE)
|
||||
{
|
||||
CHK_PROTOCOL(protocol);
|
||||
if (!DCB_IS_CLONE(dcb)) CHK_PROTOCOL(protocol);
|
||||
}
|
||||
#endif
|
||||
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
|
||||
"%lu [gw_client_close]",
|
||||
pthread_self())));
|
||||
|
||||
mysql_protocol_done(dcb);
|
||||
|
||||
mysql_protocol_done(dcb);
|
||||
session = dcb->session;
|
||||
/**
|
||||
* session may be NULL if session_alloc failed.
|
||||
|
@ -151,38 +151,6 @@ retblock:
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* gw_mysql_close
|
||||
*
|
||||
* close a connection if opened
|
||||
* free data scructure for MySQLProtocol
|
||||
*
|
||||
* @param ptr The MySQLProtocol ** to close/free
|
||||
*
|
||||
*/
|
||||
void gw_mysql_close(MySQLProtocol **ptr) {
|
||||
MySQLProtocol *conn = *ptr;
|
||||
|
||||
ss_dassert(*ptr != NULL);
|
||||
|
||||
if (*ptr == NULL)
|
||||
return;
|
||||
|
||||
|
||||
if (conn->fd > 0) {
|
||||
/* COM_QUIT will not be sent here, but from the caller of this routine! */
|
||||
close(conn->fd);
|
||||
} else {
|
||||
// no socket here
|
||||
}
|
||||
|
||||
free(*ptr);
|
||||
|
||||
*ptr = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the backend server MySQL handshake
|
||||
*
|
||||
@ -573,6 +541,16 @@ int gw_send_authentication_to_backend(
|
||||
uint8_t *curr_passwd = NULL;
|
||||
unsigned int charset;
|
||||
|
||||
/**
|
||||
* If session is stopping return with error.
|
||||
*/
|
||||
if (conn->owner_dcb->session == NULL ||
|
||||
(conn->owner_dcb->session->state != SESSION_STATE_READY &&
|
||||
conn->owner_dcb->session->state != SESSION_STATE_ROUTER_READY))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (strlen(dbname))
|
||||
curr_db = dbname;
|
||||
|
||||
@ -1661,7 +1639,9 @@ mysql_send_auth_error (
|
||||
* Buffer contains at least one of the following:
|
||||
* complete [complete] [partial] mysql packet
|
||||
*
|
||||
* return pointer to gwbuf containing a complete packet or
|
||||
* @param p_readbuf Address of read buffer pointer
|
||||
*
|
||||
* @return pointer to gwbuf containing a complete packet or
|
||||
* NULL if no complete packet was found.
|
||||
*/
|
||||
GWBUF* gw_MySQL_get_next_packet(
|
||||
|
@ -144,7 +144,7 @@ static ROUTER *
|
||||
GHACreateInstance(SERVICE *service, char **options)
|
||||
{
|
||||
ROUTER_INSTANCE *inst;
|
||||
SERVER *server;
|
||||
SERVER_REF *server;
|
||||
int i, n;
|
||||
|
||||
if ((inst = calloc(1, sizeof(ROUTER_INSTANCE))) == NULL) {
|
||||
@ -159,7 +159,7 @@ int i, n;
|
||||
* that we can maintain a count of the number of connections to each
|
||||
* backend server.
|
||||
*/
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
for (server = service->dbref, n = 0; server; server = server->next)
|
||||
n++;
|
||||
|
||||
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
|
||||
@ -169,7 +169,7 @@ int i, n;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
for (server = service->dbref, n = 0; server; server = server->next)
|
||||
{
|
||||
if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL)
|
||||
{
|
||||
@ -179,7 +179,7 @@ int i, n;
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
inst->servers[n]->server = server;
|
||||
inst->servers[n]->server = server->server;
|
||||
inst->servers[n]->current_connection_count = 0;
|
||||
n++;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ 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 libcli.so
|
||||
MODULES= libdebugcli.so libreadconnroute.so libtestroute.so libcli.so libbinlogrouter.so
|
||||
|
||||
|
||||
all: $(MODULES)
|
||||
@ -68,12 +68,16 @@ libcli.so: $(CLIOBJ)
|
||||
libreadwritesplit.so:
|
||||
(cd readwritesplit; touch depend.mk ; make; cp $@ ..)
|
||||
|
||||
libbinlogrouter.so:
|
||||
(cd binlog; touch depend.mk ; make; cp $@ ..)
|
||||
|
||||
.c.o:
|
||||
$(CC) $(CFLAGS) $< -o $@
|
||||
|
||||
clean:
|
||||
$(DEL) $(OBJ) $(MODULES)
|
||||
(cd readwritesplit; touch depend.mk; make clean)
|
||||
(cd binlog; touch depend.mk; make clean)
|
||||
|
||||
tags:
|
||||
ctags $(SRCS) $(HDRS)
|
||||
@ -83,10 +87,12 @@ depend:
|
||||
@$(DEL) 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)/modules
|
||||
(cd readwritesplit; make DEST=$(DEST) install)
|
||||
(cd binlog; make DEST=$(DEST) install)
|
||||
|
||||
cleantests:
|
||||
$(MAKE) -C test cleantests
|
||||
|
@ -201,7 +201,7 @@ int i;
|
||||
* which of these servers is currently the master and replicate from
|
||||
* that server.
|
||||
*/
|
||||
if (service->databases == NULL || service->databases->nextdb != NULL)
|
||||
if (service->dbref == NULL || service->dbref->next != NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
@ -332,16 +332,6 @@ int i;
|
||||
inst->fileroot = strdup(BINLOG_NAME_ROOT);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
inst->active_logs = 0;
|
||||
inst->reconnect_pending = 0;
|
||||
inst->handling_threads = 0;
|
||||
@ -353,12 +343,31 @@ int i;
|
||||
/*
|
||||
* Initialise the binlog file and position
|
||||
*/
|
||||
blr_file_init(inst);
|
||||
if (blr_file_init(inst) == 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"%s: Service not started due to lack of binlog directory.",
|
||||
service->name)));
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
"Binlog router: current binlog file is: %s, current position %u\n",
|
||||
inst->binlog_name, inst->binlog_position)));
|
||||
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* Initialise the binlog cache for this router instance
|
||||
*/
|
||||
@ -423,6 +432,8 @@ ROUTER_SLAVE *slave;
|
||||
slave->cstate = 0;
|
||||
slave->pthread = 0;
|
||||
slave->overrun = 0;
|
||||
slave->uuid = NULL;
|
||||
slave->hostname = NULL;
|
||||
spinlock_init(&slave->catch_lock);
|
||||
slave->dcb = session->client;
|
||||
slave->router = inst;
|
||||
@ -532,7 +543,7 @@ ROUTER_SLAVE *slave = (ROUTER_SLAVE *)router_session;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Binlog router close session with master server %s",
|
||||
router->service->databases->unique_name)));
|
||||
router->service->dbref->server->unique_name)));
|
||||
blr_master_reconnect(router);
|
||||
return;
|
||||
}
|
||||
@ -777,8 +788,10 @@ struct tm tm;
|
||||
session->serverid);
|
||||
if (session->hostname)
|
||||
dcb_printf(dcb, "\t\tHostname: %s\n", session->hostname);
|
||||
if (session->uuid)
|
||||
dcb_printf(dcb, "\t\tSlave UUID: %s\n", session->uuid);
|
||||
dcb_printf(dcb,
|
||||
"\t\tSlave: %d\n",
|
||||
"\t\tSlave: %s\n",
|
||||
session->dcb->remote);
|
||||
dcb_printf(dcb,
|
||||
"\t\tSlave DCB: %p\n",
|
||||
@ -1034,3 +1047,144 @@ ROUTER_SLAVE *slave;
|
||||
}
|
||||
spinlock_release(&router->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return some basic statistics from the router in response to a COM_STATISTICS
|
||||
* request.
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param slave The "slave" connection that requested the statistics
|
||||
* @param queue The statistics request
|
||||
*
|
||||
* @return non-zero on sucessful send
|
||||
*/
|
||||
int
|
||||
blr_statistics(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
|
||||
{
|
||||
char result[1000], *ptr;
|
||||
GWBUF *ret;
|
||||
int len;
|
||||
|
||||
snprintf(result, 1000,
|
||||
"Uptime: %u Threads: %u Events: %u Slaves: %u Master State: %s",
|
||||
time(0) - router->connect_time,
|
||||
config_threadcount(),
|
||||
router->stats.n_binlogs_ses,
|
||||
router->stats.n_slaves,
|
||||
blrm_states[router->master_state]);
|
||||
if ((ret = gwbuf_alloc(4 + strlen(result))) == NULL)
|
||||
return 0;
|
||||
len = strlen(result);
|
||||
ptr = GWBUF_DATA(ret);
|
||||
*ptr++ = len & 0xff;
|
||||
*ptr++ = (len & 0xff00) >> 8;
|
||||
*ptr++ = (len & 0xff0000) >> 16;
|
||||
*ptr++ = 1;
|
||||
strncpy(ptr, result, len);
|
||||
|
||||
return slave->dcb->func.write(slave->dcb, ret);
|
||||
}
|
||||
|
||||
int
|
||||
blr_ping(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
|
||||
{
|
||||
char *ptr;
|
||||
GWBUF *ret;
|
||||
int len;
|
||||
|
||||
if ((ret = gwbuf_alloc(5)) == NULL)
|
||||
return 0;
|
||||
ptr = GWBUF_DATA(ret);
|
||||
*ptr++ = 0x01;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 0;
|
||||
*ptr++ = 1;
|
||||
*ptr = 0; // OK
|
||||
|
||||
return slave->dcb->func.write(slave->dcb, ret);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* mysql_send_custom_error
|
||||
*
|
||||
* Send a MySQL protocol Generic ERR message, to the dcb
|
||||
* Note the errno and state are still fixed now
|
||||
*
|
||||
* @param dcb Owner_Dcb Control Block for the connection to which the OK is sent
|
||||
* @param packet_number
|
||||
* @param in_affected_rows
|
||||
* @param msg
|
||||
* @return 1 Non-zero if data was sent
|
||||
*
|
||||
*/
|
||||
int
|
||||
blr_send_custom_error(DCB *dcb, int packet_number, int affected_rows, char *msg)
|
||||
{
|
||||
uint8_t *outbuf = NULL;
|
||||
uint32_t mysql_payload_size = 0;
|
||||
uint8_t mysql_packet_header[4];
|
||||
uint8_t *mysql_payload = NULL;
|
||||
uint8_t field_count = 0;
|
||||
uint8_t mysql_err[2];
|
||||
uint8_t mysql_statemsg[6];
|
||||
unsigned int mysql_errno = 0;
|
||||
const char *mysql_error_msg = NULL;
|
||||
const char *mysql_state = NULL;
|
||||
GWBUF *errbuf = NULL;
|
||||
|
||||
mysql_errno = 2003;
|
||||
mysql_error_msg = "An errorr occurred ...";
|
||||
mysql_state = "HY000";
|
||||
|
||||
field_count = 0xff;
|
||||
gw_mysql_set_byte2(mysql_err, mysql_errno);
|
||||
mysql_statemsg[0]='#';
|
||||
memcpy(mysql_statemsg+1, mysql_state, 5);
|
||||
|
||||
if (msg != NULL) {
|
||||
mysql_error_msg = msg;
|
||||
}
|
||||
|
||||
mysql_payload_size = sizeof(field_count) +
|
||||
sizeof(mysql_err) +
|
||||
sizeof(mysql_statemsg) +
|
||||
strlen(mysql_error_msg);
|
||||
|
||||
/** allocate memory for packet header + payload */
|
||||
errbuf = gwbuf_alloc(sizeof(mysql_packet_header) + mysql_payload_size);
|
||||
ss_dassert(errbuf != NULL);
|
||||
|
||||
if (errbuf == NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
outbuf = GWBUF_DATA(errbuf);
|
||||
|
||||
/** write packet header and packet number */
|
||||
gw_mysql_set_byte3(mysql_packet_header, mysql_payload_size);
|
||||
mysql_packet_header[3] = packet_number;
|
||||
|
||||
/** write header */
|
||||
memcpy(outbuf, mysql_packet_header, sizeof(mysql_packet_header));
|
||||
|
||||
mysql_payload = outbuf + sizeof(mysql_packet_header);
|
||||
|
||||
/** write field */
|
||||
memcpy(mysql_payload, &field_count, sizeof(field_count));
|
||||
mysql_payload = mysql_payload + sizeof(field_count);
|
||||
|
||||
/** write errno */
|
||||
memcpy(mysql_payload, mysql_err, sizeof(mysql_err));
|
||||
mysql_payload = mysql_payload + sizeof(mysql_err);
|
||||
|
||||
/** write sqlstate */
|
||||
memcpy(mysql_payload, mysql_statemsg, sizeof(mysql_statemsg));
|
||||
mysql_payload = mysql_payload + sizeof(mysql_statemsg);
|
||||
|
||||
/** write error message */
|
||||
memcpy(mysql_payload, mysql_error_msg, strlen(mysql_error_msg));
|
||||
|
||||
return dcb->func.write(dcb, errbuf);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ extern size_t log_ses_count[];
|
||||
extern __thread log_info_t tls_log_info;
|
||||
|
||||
|
||||
static void blr_file_create(ROUTER_INSTANCE *router, char *file);
|
||||
static int blr_file_create(ROUTER_INSTANCE *router, char *file);
|
||||
static void blr_file_append(ROUTER_INSTANCE *router, char *file);
|
||||
static uint32_t extract_field(uint8_t *src, int bits);
|
||||
static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr);
|
||||
@ -68,7 +68,7 @@ static void blr_log_header(logfile_id_t file, char *msg, uint8_t *ptr);
|
||||
*
|
||||
* @param router The router instance this defines the master for this replication chain
|
||||
*/
|
||||
void
|
||||
int
|
||||
blr_file_init(ROUTER_INSTANCE *router)
|
||||
{
|
||||
char *ptr, path[1024], filename[1050];
|
||||
@ -92,16 +92,28 @@ struct dirent *dp;
|
||||
|
||||
router->binlogdir = strdup(path);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(path, router->binlogdir, 1024);
|
||||
}
|
||||
if (access(router->binlogdir, R_OK) == -1)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
|
||||
"%s: Unable to read the binlog directory %s.",
|
||||
router->service->name, router->binlogdir)));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* First try to find a binlog file number by reading the directory */
|
||||
root_len = strlen(router->fileroot);
|
||||
dirp = opendir(path);
|
||||
if ((dirp = opendir(path)) == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
|
||||
"%s: Unable to read the binlog directory %s, %s.",
|
||||
router->service->name, router->binlogdir,
|
||||
strerror(errno))));
|
||||
return 0;
|
||||
}
|
||||
while ((dp = readdir(dirp)) != NULL)
|
||||
{
|
||||
if (strncmp(dp->d_name, router->fileroot, root_len) == 0)
|
||||
@ -134,20 +146,21 @@ struct dirent *dp;
|
||||
router->initbinlog);
|
||||
else
|
||||
sprintf(filename, BINLOG_NAMEFMT, router->fileroot, 1);
|
||||
blr_file_create(router, filename);
|
||||
if (! blr_file_create(router, filename))
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(filename, BINLOG_NAMEFMT, router->fileroot, n);
|
||||
blr_file_append(router, filename);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
int
|
||||
blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos)
|
||||
{
|
||||
blr_file_create(router, file);
|
||||
return blr_file_create(router, file);
|
||||
}
|
||||
|
||||
|
||||
@ -156,8 +169,9 @@ blr_file_rotate(ROUTER_INSTANCE *router, char *file, uint64_t pos)
|
||||
*
|
||||
* @param router The router instance
|
||||
* @param file The binlog file name
|
||||
* @return Non-zero if the fie creation succeeded
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
blr_file_create(ROUTER_INSTANCE *router, char *file)
|
||||
{
|
||||
char path[1024];
|
||||
@ -175,7 +189,9 @@ unsigned char magic[] = BINLOG_MAGIC;
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
|
||||
"Failed to create binlog file %s", path)));
|
||||
"%s: Failed to create binlog file %s, %s.",
|
||||
router->service->name, path, strerror(errno))));
|
||||
return 0;
|
||||
}
|
||||
fsync(fd);
|
||||
close(router->binlog_fd);
|
||||
@ -184,6 +200,7 @@ unsigned char magic[] = BINLOG_MAGIC;
|
||||
router->binlog_position = 4; /* Initial position after the magic number */
|
||||
spinlock_release(&router->binlog_lock);
|
||||
router->binlog_fd = fd;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
@ -225,15 +242,31 @@ int fd;
|
||||
* @param router The router instance
|
||||
* @param buf The binlog record
|
||||
* @param len The length of the binlog record
|
||||
* @return Return the number of bytes written
|
||||
*/
|
||||
void
|
||||
int
|
||||
blr_write_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *buf)
|
||||
{
|
||||
pwrite(router->binlog_fd, buf, hdr->event_size, hdr->next_pos - hdr->event_size);
|
||||
int n;
|
||||
|
||||
if ((n = pwrite(router->binlog_fd, buf, hdr->event_size,
|
||||
hdr->next_pos - hdr->event_size)) != hdr->event_size)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
|
||||
"%s: Failed to write binlog record at %d of %s, %s. "
|
||||
"Truncating to previous record.",
|
||||
router->service->name, hdr->next_pos - hdr->event_size,
|
||||
router->binlog_name,
|
||||
strerror(errno))));
|
||||
/* Remove any partual event that was written */
|
||||
ftruncate(router->binlog_fd, hdr->next_pos - hdr->event_size);
|
||||
return 0;
|
||||
}
|
||||
spinlock_acquire(&router->binlog_lock);
|
||||
router->binlog_position = hdr->next_pos;
|
||||
router->last_written = hdr->next_pos - hdr->event_size;
|
||||
spinlock_release(&router->binlog_lock);
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,13 +71,13 @@ static GWBUF *blr_make_registration(ROUTER_INSTANCE *router);
|
||||
static GWBUF *blr_make_binlog_dump(ROUTER_INSTANCE *router);
|
||||
void encode_value(unsigned char *data, unsigned int value, int len);
|
||||
void blr_handle_binlog_record(ROUTER_INSTANCE *router, GWBUF *pkt);
|
||||
static void blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *pkt, REP_HEADER *hdr);
|
||||
static int blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *pkt, REP_HEADER *hdr);
|
||||
void blr_distribute_binlog_record(ROUTER_INSTANCE *router, REP_HEADER *hdr, uint8_t *ptr);
|
||||
static void *CreateMySQLAuthData(char *username, char *password, char *database);
|
||||
void blr_extract_header(uint8_t *pkt, REP_HEADER *hdr);
|
||||
inline uint32_t extract_field(uint8_t *src, int bits);
|
||||
static void blr_log_packet(logfile_id_t file, char *msg, uint8_t *ptr, int len);
|
||||
|
||||
static void blr_master_close(ROUTER_INSTANCE *);
|
||||
static int keepalive = 1;
|
||||
|
||||
/**
|
||||
@ -121,7 +121,7 @@ GWBUF *buf;
|
||||
return;
|
||||
}
|
||||
client->session = router->session;
|
||||
if ((router->master = dcb_connect(router->service->databases, router->session, BLR_PROTOCOL)) == NULL)
|
||||
if ((router->master = dcb_connect(router->service->dbref->server, router->session, BLR_PROTOCOL)) == NULL)
|
||||
{
|
||||
char *name;
|
||||
if ((name = malloc(strlen(router->service->name)
|
||||
@ -135,10 +135,10 @@ GWBUF *buf;
|
||||
router->retry_backoff = BLR_MAX_BACKOFF;
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
|
||||
"Binlog router: failed to connect to master server '%s'",
|
||||
router->service->databases->unique_name)));
|
||||
router->service->dbref->server->unique_name)));
|
||||
return;
|
||||
}
|
||||
router->master->remote = strdup(router->service->databases->name);
|
||||
router->master->remote = strdup(router->service->dbref->server->name);
|
||||
LOGIF(LM,(skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"%s: atempting to connect to master server %s.",
|
||||
@ -247,6 +247,37 @@ int do_reconnect = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown a connection to the master
|
||||
*
|
||||
* @param router The router instance
|
||||
*/
|
||||
void
|
||||
blr_master_close(ROUTER_INSTANCE *router)
|
||||
{
|
||||
dcb_close(router->master);
|
||||
router->master_state = BLRM_UNCONNECTED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this master connection for a delayed reconnect, used during
|
||||
* error recovery to cause a reconnect after 60 seconds.
|
||||
*
|
||||
* @param router The router instance
|
||||
*/
|
||||
void
|
||||
blr_master_delayed_connect(ROUTER_INSTANCE *router)
|
||||
{
|
||||
char *name;
|
||||
|
||||
if ((name = malloc(strlen(router->service->name)
|
||||
+ strlen(" Master Recovery")+1)) != NULL);
|
||||
{
|
||||
sprintf(name, "%s Master Recovery", router->service->name);
|
||||
hktask_oneshot(name, blr_start_master, router, 60);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Binlog router master side state machine event handler.
|
||||
*
|
||||
@ -407,6 +438,20 @@ char query[128];
|
||||
case BLRM_SELECTVER:
|
||||
// Response to SELECT VERSION should be stored
|
||||
router->saved_master.selectver = buf;
|
||||
buf = blr_make_query("SELECT @@version_comment limit 1;");
|
||||
router->master_state = BLRM_SELECTVERCOM;
|
||||
router->master->func.write(router->master, buf);
|
||||
break;
|
||||
case BLRM_SELECTVERCOM:
|
||||
// Response to SELECT @@version_comment should be stored
|
||||
router->saved_master.selectvercom = buf;
|
||||
buf = blr_make_query("SELECT @@hostname;");
|
||||
router->master_state = BLRM_SELECTHOSTNAME;
|
||||
router->master->func.write(router->master, buf);
|
||||
break;
|
||||
case BLRM_SELECTHOSTNAME:
|
||||
// Response to SELECT @@hostname should be stored
|
||||
router->saved_master.selecthostname = buf;
|
||||
buf = blr_make_registration(router);
|
||||
router->master_state = BLRM_REGISTER;
|
||||
router->master->func.write(router->master, buf);
|
||||
@ -809,10 +854,36 @@ static REP_HEADER phdr;
|
||||
// into the binlog file
|
||||
if (hdr.event_type == ROTATE_EVENT)
|
||||
router->rotating = 1;
|
||||
blr_write_binlog_record(router, &hdr, ptr);
|
||||
if (blr_write_binlog_record(router, &hdr, ptr) == 0)
|
||||
{
|
||||
/*
|
||||
* Failed to write to the
|
||||
* binlog file, destroy the
|
||||
* buffer chain and close the
|
||||
* connection with the master
|
||||
*/
|
||||
while ((pkt = gwbuf_consume(pkt,
|
||||
GWBUF_LENGTH(pkt))) != NULL);
|
||||
blr_master_close(router);
|
||||
blr_master_delayed_connect(router);
|
||||
return;
|
||||
}
|
||||
if (hdr.event_type == ROTATE_EVENT)
|
||||
{
|
||||
blr_rotate_event(router, ptr, &hdr);
|
||||
if (!blr_rotate_event(router, ptr, &hdr))
|
||||
{
|
||||
/*
|
||||
* Failed to write to the
|
||||
* binlog file, destroy the
|
||||
* buffer chain and close the
|
||||
* connection with the master
|
||||
*/
|
||||
while ((pkt = gwbuf_consume(pkt,
|
||||
GWBUF_LENGTH(pkt))) != NULL);
|
||||
blr_master_close(router);
|
||||
blr_master_delayed_connect(router);
|
||||
return;
|
||||
}
|
||||
}
|
||||
blr_distribute_binlog_record(router, &hdr, ptr);
|
||||
}
|
||||
@ -833,7 +904,20 @@ static REP_HEADER phdr;
|
||||
if (hdr.event_type == ROTATE_EVENT)
|
||||
{
|
||||
router->rotating = 1;
|
||||
blr_rotate_event(router, ptr, &hdr);
|
||||
if (!blr_rotate_event(router, ptr, &hdr))
|
||||
{
|
||||
/*
|
||||
* Failed to write to the
|
||||
* binlog file, destroy the
|
||||
* buffer chain and close the
|
||||
* connection with the master
|
||||
*/
|
||||
while ((pkt = gwbuf_consume(pkt,
|
||||
GWBUF_LENGTH(pkt))) != NULL);
|
||||
blr_master_close(router);
|
||||
blr_master_delayed_connect(router);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -933,7 +1017,7 @@ register uint32_t rval = 0, shift = 0;
|
||||
* @param ptr The packet containing the rotate event
|
||||
* @param hdr The replication message header
|
||||
*/
|
||||
static void
|
||||
static int
|
||||
blr_rotate_event(ROUTER_INSTANCE *router, uint8_t *ptr, REP_HEADER *hdr)
|
||||
{
|
||||
int len, slen;
|
||||
@ -963,9 +1047,14 @@ char file[BINLOG_FNAMELEN+1];
|
||||
if (strncmp(router->binlog_name, file, slen) != 0)
|
||||
{
|
||||
router->stats.n_rotates++;
|
||||
blr_file_rotate(router, file, pos);
|
||||
if (blr_file_rotate(router, file, pos) == 0)
|
||||
{
|
||||
router->rotating = 0;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
router->rotating = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,12 +111,20 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
|
||||
case COM_BINLOG_DUMP:
|
||||
return blr_slave_binlog_dump(router, slave, queue);
|
||||
break;
|
||||
case COM_STATISTICS:
|
||||
return blr_statistics(router, slave, queue);
|
||||
break;
|
||||
case COM_PING:
|
||||
return blr_ping(router, slave, queue);
|
||||
break;
|
||||
case COM_QUIT:
|
||||
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
|
||||
"COM_QUIT received from slave with server_id %d",
|
||||
slave->serverid)));
|
||||
break;
|
||||
default:
|
||||
blr_send_custom_error(slave->dcb, 1, 0,
|
||||
"MySQL command not supported by the binlog router.");
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Unexpected MySQL Command (%d) received from slave",
|
||||
@ -133,12 +141,14 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
|
||||
* when MaxScale registered as a slave. The exception to the rule is the
|
||||
* request to obtain the current timestamp value of the server.
|
||||
*
|
||||
* Five select statements are currently supported:
|
||||
* Seven select statements are currently supported:
|
||||
* SELECT UNIX_TIMESTAMP();
|
||||
* SELECT @master_binlog_checksum
|
||||
* SELECT @@GLOBAL.GTID_MODE
|
||||
* SELECT VERSION()
|
||||
* SELECT 1
|
||||
* SELECT @@version_comment limit 1
|
||||
* SELECT @@hostname
|
||||
*
|
||||
* Two show commands are supported:
|
||||
* SHOW VARIABLES LIKE 'SERVER_ID'
|
||||
@ -208,6 +218,16 @@ int query_len;
|
||||
free(query_text);
|
||||
return blr_slave_replay(router, slave, router->saved_master.selectver);
|
||||
}
|
||||
else if (strcasecmp(word, "@@version_comment") == 0)
|
||||
{
|
||||
free(query_text);
|
||||
return blr_slave_replay(router, slave, router->saved_master.selectvercom);
|
||||
}
|
||||
else if (strcasecmp(word, "@@hostname") == 0)
|
||||
{
|
||||
free(query_text);
|
||||
return blr_slave_replay(router, slave, router->saved_master.selecthostname);
|
||||
}
|
||||
}
|
||||
else if (strcasecmp(word, "SHOW") == 0)
|
||||
{
|
||||
@ -251,6 +271,8 @@ int query_len;
|
||||
}
|
||||
else if (strcasecmp(word, "@slave_uuid") == 0)
|
||||
{
|
||||
if ((word = strtok_r(NULL, sep, &brkb)) != NULL)
|
||||
slave->uuid = strdup(word);
|
||||
free(query_text);
|
||||
return blr_slave_replay(router, slave, router->saved_master.setslaveuuid);
|
||||
}
|
||||
@ -1075,3 +1097,81 @@ uint32_t chksum;
|
||||
encode_value(ptr, chksum, 32);
|
||||
slave->dcb->func.write(slave->dcb, head);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Send the field count packet in a response packet sequence.
|
||||
*
|
||||
* @param router The router
|
||||
* @param slave The slave connection
|
||||
* @param count Number of columns in the result set
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
static int
|
||||
blr_slave_send_fieldcount(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, int count)
|
||||
{
|
||||
GWBUF *pkt;
|
||||
uint8_t *ptr;
|
||||
|
||||
if ((pkt = gwbuf_alloc(5)) == NULL)
|
||||
return 0;
|
||||
ptr = GWBUF_DATA(pkt);
|
||||
encode_value(ptr, 1, 24); // Add length of data packet
|
||||
ptr += 3;
|
||||
*ptr++ = 0x01; // Sequence number in response
|
||||
*ptr++ = count; // Length of result string
|
||||
return slave->dcb->func.write(slave->dcb, pkt);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send the column definition packet in a response packet sequence.
|
||||
*
|
||||
* @param router The router
|
||||
* @param slave The slave connection
|
||||
* @param name Name of the column
|
||||
* @param type Column type
|
||||
* @param len Column length
|
||||
* @param seqno Packet sequence number
|
||||
* @return Non-zero on success
|
||||
*/
|
||||
static int
|
||||
blr_slave_send_columndef(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, char *name, int type, int len, uint8_t seqno)
|
||||
{
|
||||
GWBUF *pkt;
|
||||
uint8_t *ptr;
|
||||
|
||||
if ((pkt = gwbuf_alloc(26 + strlen(name))) == NULL)
|
||||
return 0;
|
||||
ptr = GWBUF_DATA(pkt);
|
||||
encode_value(ptr, 22 + strlen(name), 24); // Add length of data packet
|
||||
ptr += 3;
|
||||
*ptr++ = seqno; // Sequence number in response
|
||||
*ptr++ = 3; // Catalog is always def
|
||||
*ptr++ = 'd';
|
||||
*ptr++ = 'e';
|
||||
*ptr++ = 'f';
|
||||
*ptr++ = 0; // Schema name length
|
||||
*ptr++ = 0; // virtal table name length
|
||||
*ptr++ = 0; // Table name length
|
||||
*ptr++ = strlen(name); // Column name length;
|
||||
while (*name)
|
||||
*ptr++ = *name++; // Copy the column name
|
||||
*ptr++ = 0; // Orginal column name
|
||||
*ptr++ = 0x0c; // Length of next fields always 12
|
||||
*ptr++ = 0x3f; // Character set
|
||||
*ptr++ = 0;
|
||||
encode_value(ptr, len, 32); // Add length of column
|
||||
ptr += 4;
|
||||
*ptr++ = type;
|
||||
*ptr++ = 0x81; // Two bytes of flags
|
||||
if (type == 0xfd)
|
||||
*ptr++ = 0x1f;
|
||||
else
|
||||
*ptr++ = 0x00;
|
||||
*ptr++= 0;
|
||||
*ptr++= 0;
|
||||
*ptr++= 0;
|
||||
return slave->dcb->func.write(slave->dcb, pkt);
|
||||
}
|
||||
|
@ -678,7 +678,7 @@ SERVICE *service;
|
||||
if (service)
|
||||
return (unsigned long)(service->users);
|
||||
else
|
||||
return 0;
|
||||
return 1; /*< invalid argument */
|
||||
}
|
||||
return rval;
|
||||
case ARG_TYPE_DCB:
|
||||
@ -798,7 +798,7 @@ bool in_space = false;
|
||||
}
|
||||
}
|
||||
*lptr = 0;
|
||||
args[i+1] = NULL;
|
||||
args[MIN(MAXARGS-1,i+1)] = NULL;
|
||||
|
||||
if (args[0] == NULL || *args[0] == 0)
|
||||
return 1;
|
||||
@ -886,11 +886,15 @@ bool in_space = false;
|
||||
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
|
||||
if (arg1 == 0x1)
|
||||
{
|
||||
dcb_printf(dcb, "Invalid argument: %s\n",
|
||||
args[2]);
|
||||
args[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
cmds[i].options[j].fn(dcb, arg1);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
arg1 = convert_arg(cli->mode, args[2],cmds[i].options[j].arg_types[0]);
|
||||
|
@ -146,7 +146,8 @@ static void rses_end_locked_router_action(
|
||||
|
||||
static BACKEND *get_root_master(
|
||||
BACKEND **servers);
|
||||
|
||||
static int handle_state_switch(
|
||||
DCB* dcb,DCB_REASON reason, void * routersession);
|
||||
static SPINLOCK instlock;
|
||||
static ROUTER_INSTANCE *instances;
|
||||
|
||||
@ -203,6 +204,7 @@ createInstance(SERVICE *service, char **options)
|
||||
{
|
||||
ROUTER_INSTANCE *inst;
|
||||
SERVER *server;
|
||||
SERVER_REF *sref;
|
||||
int i, n;
|
||||
BACKEND *backend;
|
||||
char *weightby;
|
||||
@ -219,7 +221,7 @@ char *weightby;
|
||||
* that we can maintain a count of the number of connections to each
|
||||
* backend server.
|
||||
*/
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
for (sref = service->dbref, n = 0; sref; sref = sref->next)
|
||||
n++;
|
||||
|
||||
inst->servers = (BACKEND **)calloc(n + 1, sizeof(BACKEND *));
|
||||
@ -229,7 +231,7 @@ char *weightby;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (server = service->databases, n = 0; server; server = server->nextdb)
|
||||
for (sref = service->dbref, n = 0; sref; sref = sref->next)
|
||||
{
|
||||
if ((inst->servers[n] = malloc(sizeof(BACKEND))) == NULL)
|
||||
{
|
||||
@ -239,7 +241,7 @@ char *weightby;
|
||||
free(inst);
|
||||
return NULL;
|
||||
}
|
||||
inst->servers[n]->server = server;
|
||||
inst->servers[n]->server = sref->server;
|
||||
inst->servers[n]->current_connection_count = 0;
|
||||
inst->servers[n]->weight = 1000;
|
||||
n++;
|
||||
@ -409,12 +411,12 @@ BACKEND *master_host = NULL;
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [newSession] Examine server in port %d with "
|
||||
"%d connections. Status is %d, "
|
||||
"%d connections. Status is %s, "
|
||||
"inst->bitvalue is %d",
|
||||
pthread_self(),
|
||||
inst->servers[i]->server->port,
|
||||
inst->servers[i]->current_connection_count,
|
||||
inst->servers[i]->server->status,
|
||||
STRSRVSTATUS(inst->servers[i]->server),
|
||||
inst->bitmask)));
|
||||
}
|
||||
|
||||
@ -536,7 +538,12 @@ BACKEND *master_host = NULL;
|
||||
free(client_rses);
|
||||
return NULL;
|
||||
}
|
||||
inst->stats.n_sessions++;
|
||||
dcb_add_callback(
|
||||
client_rses->backend_dcb,
|
||||
DCB_REASON_NOT_RESPONDING,
|
||||
&handle_state_switch,
|
||||
client_rses);
|
||||
inst->stats.n_sessions++;
|
||||
|
||||
/**
|
||||
* Add this session to the list of active sessions.
|
||||
@ -693,7 +700,8 @@ routeQuery(ROUTER *instance, void *router_session, GWBUF *queue)
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
}
|
||||
|
||||
if (rses_is_closed || backend_dcb == NULL)
|
||||
if (rses_is_closed || backend_dcb == NULL ||
|
||||
SERVER_IS_DOWN(router_cli_ses->backend->server))
|
||||
{
|
||||
LOGIF(LT, (skygw_log_write(
|
||||
LOGFILE_TRACE,
|
||||
@ -797,7 +805,7 @@ clientReply(
|
||||
GWBUF *queue,
|
||||
DCB *backend_dcb)
|
||||
{
|
||||
DCB *client = NULL;
|
||||
DCB *client ;
|
||||
|
||||
client = backend_dcb->session->client;
|
||||
|
||||
@ -841,18 +849,21 @@ static void handleError(
|
||||
else
|
||||
{
|
||||
backend_dcb->dcb_errhandle_called = true;
|
||||
}
|
||||
}
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
sesstate = session->state;
|
||||
client_dcb = session->client;
|
||||
spinlock_release(&session->ses_lock);
|
||||
ss_dassert(client_dcb != NULL);
|
||||
|
||||
if (sesstate == SESSION_STATE_ROUTER_READY)
|
||||
{
|
||||
CHK_DCB(client_dcb);
|
||||
spinlock_release(&session->ses_lock);
|
||||
client_dcb->func.write(client_dcb, gwbuf_clone(errbuf));
|
||||
}
|
||||
else
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
|
||||
/** false because connection is not available anymore */
|
||||
*succp = false;
|
||||
@ -954,3 +965,41 @@ static BACKEND *get_root_master(BACKEND **servers) {
|
||||
}
|
||||
return master_host;
|
||||
}
|
||||
|
||||
static int handle_state_switch(DCB* dcb,DCB_REASON reason, void * routersession)
|
||||
{
|
||||
ss_dassert(dcb != NULL);
|
||||
SESSION* session = dcb->session;
|
||||
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES*)routersession;
|
||||
SERVICE* service = session->service;
|
||||
ROUTER* router = (ROUTER *)service->router;
|
||||
|
||||
switch(reason)
|
||||
{
|
||||
case DCB_REASON_CLOSE:
|
||||
dcb->func.close(dcb);
|
||||
break;
|
||||
case DCB_REASON_DRAINED:
|
||||
/** Do we need to do anything? */
|
||||
break;
|
||||
case DCB_REASON_HIGH_WATER:
|
||||
/** Do we need to do anything? */
|
||||
break;
|
||||
case DCB_REASON_LOW_WATER:
|
||||
/** Do we need to do anything? */
|
||||
break;
|
||||
case DCB_REASON_ERROR:
|
||||
dcb->func.error(dcb);
|
||||
break;
|
||||
case DCB_REASON_HUP:
|
||||
dcb->func.hangup(dcb);
|
||||
break;
|
||||
case DCB_REASON_NOT_RESPONDING:
|
||||
dcb->func.hangup(dcb);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@
|
||||
add_test(NAME SimpleHintTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit_hints.sh hints.log ${TEST_HOST} ${TEST_PORT_RW_HINT} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} simple_tests ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_test(NAME ComplexHintTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit_hints.sh hints.log ${TEST_HOST} ${TEST_PORT_RW_HINT} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} complex_tests ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_test(NAME StackHintTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit_hints.sh hints.log ${TEST_HOST} ${TEST_PORT_RW_HINT} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} stack_tests ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_test(NAME SimpleHintTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit_hints.sh ${CMAKE_CURRENT_BINARY_DIR}/hints.log ${TEST_HOST} ${TEST_PORT_RW_HINT} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR}/simple_tests ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_test(NAME ComplexHintTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit_hints.sh ${CMAKE_CURRENT_BINARY_DIR}/hints.log ${TEST_HOST} ${TEST_PORT_RW_HINT} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR}/complex_tests ${CMAKE_CURRENT_BINARY_DIR})
|
||||
add_test(NAME StackHintTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit_hints.sh ${CMAKE_CURRENT_BINARY_DIR}/hints.log ${TEST_HOST} ${TEST_PORT_RW_HINT} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR}/stack_tests ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
@ -7,16 +7,16 @@ TMASTER_ID=$4
|
||||
TUSER=$5
|
||||
TPWD=$6
|
||||
TESTINPUT=$7
|
||||
|
||||
TESTFILE=$PWD/$(basename -z $TESTINPUT)
|
||||
if [ $# -lt $(( NARGS - 1 )) ] ;
|
||||
then
|
||||
echo""
|
||||
echo "Wrong number of arguments, gave "$#" but "$(( NARGS - 1 ))" is required"
|
||||
echo ""
|
||||
echo "Usage :"
|
||||
echo " rwsplit_hints.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
|
||||
echo ""
|
||||
exit 1
|
||||
echo""
|
||||
echo "Wrong number of arguments, gave "$#" but "$(( NARGS - 1 ))" is required"
|
||||
echo ""
|
||||
echo "Usage :"
|
||||
echo " rwsplit_hints.sh <log filename> <host> <port> <master id> <user> <password> <test file>"
|
||||
echo ""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ $# -eq $NARGS ]
|
||||
@ -26,20 +26,18 @@ else
|
||||
TDIR=.
|
||||
fi
|
||||
|
||||
TESTINPUT=$TDIR/$TESTINPUT
|
||||
|
||||
RUNCMD=mysql\ --host=$THOST\ -P$TPORT\ -u$TUSER\ -p$TPWD\ --unbuffered=true\ --disable-reconnect\ --silent\ --comment
|
||||
i=0
|
||||
|
||||
while read -r LINE
|
||||
do
|
||||
TINPUT[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[1]}'`
|
||||
TRETVAL[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[2]}'`
|
||||
echo "${TINPUT[i]}" >> $TESTINPUT.sql
|
||||
i=$((i+1))
|
||||
TINPUT[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[1]}'`
|
||||
TRETVAL[$i]=`echo "$LINE"|awk '{split($0,a,":");print a[2]}'`
|
||||
echo "${TINPUT[i]}" >> $TESTFILE.sql
|
||||
i=$((i+1))
|
||||
done < $TESTINPUT
|
||||
|
||||
`$RUNCMD < $TESTINPUT.sql > $TESTINPUT.output`
|
||||
`$RUNCMD < $TESTFILE.sql > $TESTFILE.output`
|
||||
|
||||
x=0
|
||||
crash=1
|
||||
@ -47,32 +45,31 @@ all_passed=1
|
||||
|
||||
while read -r TOUTPUT
|
||||
do
|
||||
crash=0
|
||||
if [ "$TOUTPUT" != "${TRETVAL[x]}" -a "${TRETVAL[x]}" != "" ]
|
||||
then
|
||||
all_passed=0
|
||||
echo "$TESTINPUT:$((x + 1)): ${TINPUT[x]} FAILED, return value $TOUTPUT when ${TRETVAL[x]} was expected">>$TLOG;
|
||||
fi
|
||||
x=$((x+1))
|
||||
done < $TESTINPUT.output
|
||||
crash=0
|
||||
if [ "$TOUTPUT" != "${TRETVAL[x]}" -a "${TRETVAL[x]}" != "" ]
|
||||
then
|
||||
all_passed=0
|
||||
echo "$TESTINPUT:$((x + 1)): ${TINPUT[x]} FAILED, return value $TOUTPUT when ${TRETVAL[x]} was expected">>$TLOG;
|
||||
fi
|
||||
x=$((x+1))
|
||||
done < $TESTFILE.output
|
||||
|
||||
if [ $crash -eq 1 ]
|
||||
then
|
||||
all_passed=0
|
||||
for ((v=0;v<$i;v++))
|
||||
do
|
||||
echo "${TINPUT[v]} FAILED, nothing was returned">>$TLOG;
|
||||
echo "${TINPUT[v]} FAILED, nothing was returned">>$TLOG;
|
||||
done
|
||||
fi
|
||||
|
||||
if [ $all_passed -eq 1 ]
|
||||
then
|
||||
echo "Test set: PASSED">>$TLOG;
|
||||
else
|
||||
echo "Test set: FAILED">>$TLOG;
|
||||
fi
|
||||
|
||||
if [ $# -eq $NARGS ]
|
||||
then
|
||||
echo "Test set: PASSED">>$TLOG;
|
||||
cat $TLOG
|
||||
exit 0
|
||||
else
|
||||
echo "Test set: FAILED">>$TLOG;
|
||||
cat $TLOG
|
||||
exit 1
|
||||
fi
|
||||
|
@ -1,27 +1,5 @@
|
||||
#
|
||||
# Example MaxScale.cnf configuration file
|
||||
#
|
||||
#
|
||||
#
|
||||
# Number of server threads
|
||||
# Valid options are:
|
||||
# threads=<number of threads>
|
||||
|
||||
[maxscale]
|
||||
threads=1
|
||||
|
||||
# Define a monitor that can be used to determine the state and role of
|
||||
# the servers.
|
||||
#
|
||||
# Valid options are:
|
||||
#
|
||||
# module=<name of module to load>
|
||||
# servers=<server name>,<server name>,...
|
||||
# user =<user name - must have slave replication and
|
||||
# slave client privileges>
|
||||
# passwd=<password of the above user, plain text currently>
|
||||
# monitor_interval=<sampling interval in milliseconds,
|
||||
# default value is 10000>
|
||||
threads=4
|
||||
|
||||
[MySQL Monitor]
|
||||
type=monitor
|
||||
@ -29,33 +7,14 @@ module=mysqlmon
|
||||
servers=server1,server2,server3,server4
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
|
||||
# A series of service definition
|
||||
#
|
||||
# Valid options are:
|
||||
#
|
||||
# router=<name of router module>
|
||||
# servers=<server name>,<server name>,...
|
||||
# user=<User to fetch password inforamtion with>
|
||||
# passwd=<Password of the user, plain text currently>
|
||||
# enable_root_user=<0 or 1, default is 0>
|
||||
# version_string=<specific string for server handshake,
|
||||
# default is the MariaDB embedded library version>
|
||||
#
|
||||
# Valid router modules currently are:
|
||||
# readwritesplit, readconnroute and debugcli
|
||||
|
||||
monitor_interval=10000
|
||||
|
||||
[RW Split Router]
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
max_slave_connections=90%
|
||||
write_ses_variables_to_all=Yes
|
||||
read_ses_variables_from_slaves=Yes
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=Hint
|
||||
|
||||
[DBShard Router]
|
||||
type=service
|
||||
@ -68,9 +27,6 @@ passwd=maxpwd
|
||||
type=service
|
||||
router=readwritesplit
|
||||
servers=server1,server2,server3,server4
|
||||
max_slave_connections=90%
|
||||
write_ses_variables_to_all=Yes
|
||||
read_ses_variables_from_slaves=Yes
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
filters=Hint
|
||||
@ -84,31 +40,39 @@ servers=server1
|
||||
user=maxuser
|
||||
passwd=maxpwd
|
||||
|
||||
[Hint]
|
||||
type=filter
|
||||
module=hintfilter
|
||||
|
||||
[recurse3]
|
||||
type=filter
|
||||
module=tee
|
||||
service=RW Split Router
|
||||
|
||||
[recurse2]
|
||||
type=filter
|
||||
module=tee
|
||||
service=Read Connection Router
|
||||
|
||||
[recurse1]
|
||||
type=filter
|
||||
module=tee
|
||||
service=RW Split Hint Router
|
||||
|
||||
[HTTPD Router]
|
||||
type=service
|
||||
router=testroute
|
||||
servers=server1,server2,server3
|
||||
|
||||
[Debug Interface]
|
||||
type=service
|
||||
router=debugcli
|
||||
|
||||
[CLI]
|
||||
type=service
|
||||
router=cli
|
||||
|
||||
[Hint]
|
||||
type=filter
|
||||
module=hintfilter
|
||||
|
||||
|
||||
# Listener definitions for the services
|
||||
#
|
||||
# Valid options are:
|
||||
#
|
||||
# service=<name of service defined elsewhere>
|
||||
# protocol=<name of protocol module with which to listen>
|
||||
# port=<Listening port>
|
||||
# address=<Address to bind to>
|
||||
# socket=<Listening socket>
|
||||
[Read Connection Listener]
|
||||
type=listener
|
||||
service=Read Connection Router
|
||||
protocol=MySQLClient
|
||||
port=4008
|
||||
|
||||
[RW Split Listener]
|
||||
type=listener
|
||||
@ -128,28 +92,17 @@ service=RW Split Hint Router
|
||||
protocol=MySQLClient
|
||||
port=4009
|
||||
|
||||
[Read Connection Listener]
|
||||
type=listener
|
||||
service=Read Connection Router
|
||||
protocol=MySQLClient
|
||||
port=4008
|
||||
#socket=/tmp/readconn.sock
|
||||
|
||||
[Debug Listener]
|
||||
type=listener
|
||||
service=Debug Interface
|
||||
protocol=telnetd
|
||||
port=4442
|
||||
#address=127.0.0.1
|
||||
|
||||
[HTTPD Listener]
|
||||
[CLI Listener]
|
||||
type=listener
|
||||
service=HTTPD Router
|
||||
protocol=HTTPD
|
||||
port=6444
|
||||
|
||||
# Definition of the servers
|
||||
|
||||
service=CLI
|
||||
protocol=maxscaled
|
||||
port=6603
|
||||
[server1]
|
||||
type=server
|
||||
address=127.0.0.1
|
||||
|
9
server/test/maxscale_test.h.in
Normal file
9
server/test/maxscale_test.h.in
Normal file
@ -0,0 +1,9 @@
|
||||
#ifndef MAXSCALE_TEST_H
|
||||
#define MAXSCALE_TEST_H
|
||||
#define TEST_DIR "${CMAKE_BINARY_DIR}"
|
||||
#define TEST_LOG_DIR "${CMAKE_BINARY_DIR}/log"
|
||||
#define TEST_BIN_DIR "${CMAKE_BINARY_DIR}/bin"
|
||||
#define TEST_MOD_DIR "${CMAKE_BINARY_DIR}/modules"
|
||||
#define TEST_LIB_DIR "${CMAKE_BINARY_DIR}/lib"
|
||||
#define TEST_ETC_DIR "${CMAKE_BINARY_DIR}/etc"
|
||||
#endif
|
@ -124,7 +124,8 @@ typedef enum skygw_chk_t {
|
||||
CHK_NUM_BACKEND,
|
||||
CHK_NUM_BACKEND_REF,
|
||||
CHK_NUM_PREP_STMT,
|
||||
CHK_NUM_PINFO
|
||||
CHK_NUM_PINFO,
|
||||
CHK_NUM_MYSQLSES
|
||||
} skygw_chk_t;
|
||||
|
||||
# define STRBOOL(b) ((b) ? "true" : "false")
|
||||
@ -279,6 +280,14 @@ typedef enum skygw_chk_t {
|
||||
((t) == HINT_ROUTE_TO_ALL ? "HINT_ROUTE_TO_ALL" : \
|
||||
((t) == HINT_PARAMETER ? "HINT_PARAMETER" : "UNKNOWN HINT TYPE"))))))
|
||||
|
||||
#define STRDCBREASON(r) ((r) == DCB_REASON_CLOSE ? "DCB_REASON_CLOSE" : \
|
||||
((r) == DCB_REASON_DRAINED ? "DCB_REASON_DRAINED" : \
|
||||
((r) == DCB_REASON_HIGH_WATER ? "DCB_REASON_HIGH_WATER" : \
|
||||
((r) == DCB_REASON_LOW_WATER ? "DCB_REASON_LOW_WATER" : \
|
||||
((r) == DCB_REASON_ERROR ? "DCB_REASON_ERROR" : \
|
||||
((r) == DCB_REASON_HUP ? "DCB_REASON_HUP" : \
|
||||
((r) == DCB_REASON_NOT_RESPONDING ? "DCB_REASON_NOT_RESPONDING" : \
|
||||
"Unknown DCB reason")))))))
|
||||
|
||||
#define CHK_MLIST(l) { \
|
||||
ss_info_dassert((l->mlist_chk_top == CHK_NUM_MLIST && \
|
||||
@ -535,6 +544,11 @@ typedef enum skygw_chk_t {
|
||||
"Parsing info struct has invalid check fields"); \
|
||||
}
|
||||
|
||||
#define CHK_MYSQL_SESSION(s) { \
|
||||
ss_info_dassert((s)->myses_chk_top == CHK_NUM_MYSQLSES && \
|
||||
(s)->myses_chk_tail == CHK_NUM_MYSQLSES, \
|
||||
"MYSQL session struct has invalid check fields"); \
|
||||
}
|
||||
|
||||
|
||||
#if defined(FAKE_CODE)
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define SECOND_USEC (1024*1024L)
|
||||
#define MSEC_USEC (1024L)
|
||||
|
@ -2058,11 +2058,29 @@ size_t get_decimal_len(
|
||||
return value > 0 ? (size_t) log10 ((double) value) + 1 : 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Check if the provided pathname is POSIX-compliant. The valid characters
|
||||
* are [a-z A-Z 0-9._-].
|
||||
* @param path A null-terminated string
|
||||
* @return true if it is a POSIX-compliant pathname, otherwise false
|
||||
*/
|
||||
bool is_valid_posix_path(char* path)
|
||||
{
|
||||
char* ptr = path;
|
||||
while (*ptr != '\0')
|
||||
{
|
||||
if (isalnum (*ptr) ||
|
||||
*ptr == '/' ||
|
||||
*ptr == '.' ||
|
||||
*ptr == '-' ||
|
||||
*ptr == '_')
|
||||
{
|
||||
ptr++;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
@ -198,7 +198,7 @@ size_t get_decimal_len(size_t s);
|
||||
char* replace_literal(char* haystack,
|
||||
const char* needle,
|
||||
const char* replacement);
|
||||
|
||||
bool is_valid_posix_path(char* path);
|
||||
EXTERN_C_BLOCK_END
|
||||
|
||||
#endif /* SKYGW_UTILS_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user