Merge remote-tracking branch 'origin/develop' into MXS-122

Conflicts:
	server/core/dcb.c
This commit is contained in:
counterpoint
2015-07-08 14:54:22 +01:00
45 changed files with 1189 additions and 774 deletions

View File

@ -19,7 +19,6 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define _XOPEN_SOURCE
#include <unistd.h>
#include <crypt.h>
#include <users.h>

View File

@ -311,7 +311,7 @@ if((monitorhash = hashtable_alloc(5,simple_str_hash,strcmp)) == NULL)
return 0;
}
hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
hashtable_memory_fns(monitorhash,(HASHMEMORYFN)strdup,NULL,(HASHMEMORYFN)free,NULL);
/**
* Process the data and create the services and servers defined
* in the data.
@ -429,7 +429,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
/** Add the 5.5.5- string to the start of the version string if
* the version string starts with "10.".
* This mimics MariaDB 10.0 behavior which adds 5.5.5- for backwards compatibility. */
* This mimics MariaDB 10.0 replication which adds 5.5.5- for backwards compatibility. */
if(strncmp(version_string,"10.",3) == 0)
{
((SERVICE *)(obj->element))->version_string = malloc((strlen(version_string) +
@ -940,7 +940,7 @@ hashtable_memory_fns(monitorhash,strdup,NULL,free,NULL);
/* if id is not set, do it now */
if (gateway.id == 0) {
setipaddress(&serv_addr.sin_addr, (address == NULL) ? "0.0.0.0" : address);
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + port != NULL ? atoi(port) : 0 + getpid());
gateway.id = (unsigned long) (serv_addr.sin_addr.s_addr + (port != NULL ? atoi(port) : 0 + getpid()));
}
if (service && socket && protocol) {

View File

@ -974,18 +974,10 @@ getAllUsers(SERVICE *service, USERS *users)
}
}
if(havedb && wildcard_db_grant(dbnm))
if(havedb && wildcard_db_grant(dbnm) && service->optimize_wildcard)
{
if(service->optimize_wildcard)
{
rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources);
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc);
}
else
{
/** Use ANYDB for wildcard grants */
rc = add_mysql_users_with_host_ipv4(users, row[0], row[1], password, "Y", NULL);
}
rc = add_wildcard_users(users, row[0], row[1], password, row[4], dbnm, service->resources);
skygw_log_write(LOGFILE_DEBUG|LOGFILE_TRACE,"%s: Converted '%s' to %d individual database grants.",service->name,dbnm,rc);
}
else
{
@ -1041,8 +1033,8 @@ getAllUsers(SERVICE *service, USERS *users)
} else if(rc == -1) {
/** Duplicate user*/
LOGIF(LE,(skygw_log_write(LT|LE,
"Warning: Duplicate MySQL user found for service [%s]: %s@%s%s%s",
LOGIF(LT,(skygw_log_write(LT,
"Duplicate MySQL user found for service [%s]: %s@%s%s%s",
service->name,
row[0],row[1],havedb?" for database: ":"",
havedb ?dbnm:"")));
@ -1706,6 +1698,41 @@ static int uh_cmpfun( void* v1, void* v2) {
return 0;
}
if(hu2->resource && strlen(hu2->resource) && strchr(hu2->resource,'%') != NULL)
{
regex_t re;
char db[MYSQL_DATABASE_MAXLEN*2 +1];
strcpy(db,hu2->resource);
int len = strlen(db);
char* ptr = strrchr(db,'%');
if(ptr == NULL)
{
return 1;
}
while(ptr)
{
memmove(ptr+1,ptr,(len - (ptr - db)) + 1);
*ptr = '.';
*(ptr + 1) = '*';
len = strlen(db);
ptr = strrchr(db,'%');
}
if((regcomp(&re,db,REG_ICASE|REG_NOSUB)))
{
return 1;
}
if(regexec(&re,hu1->resource,0,NULL,0) == 0)
{
regfree(&re);
return 0;
}
regfree(&re);
}
/* no matches, deny auth */
return 1;
}
@ -1815,18 +1842,6 @@ char *mysql_format_user_entry(void *data)
return mysql_user;
}
/*
* The hash function we use for storing MySQL database names.
*
* @param key The key value
* @return The hash key
*/
int
resource_hash(char *key)
{
return (*key + *(key + 1));
}
/**
* Remove the resources table
*
@ -1850,7 +1865,7 @@ resource_alloc()
{
HASHTABLE *resources;
if ((resources = hashtable_alloc(10, resource_hash, strcmp)) == NULL)
if ((resources = hashtable_alloc(10, simple_str_hash, strcmp)) == NULL)
{
return NULL;
}

View File

@ -49,8 +49,11 @@
* backend
* 07/05/2014 Mark Riddoch Addition of callback mechanism
* 20/06/2014 Mark Riddoch Addition of dcb_clone
* 29/05/2015 Markus Makela Addition of dcb_write_SSL
* 29/05/2015 Markus Makela Addition of dcb_write_SSL
* 11/06/2015 Martin Brampton Persistent connnections and tidy up
* 07/07/2015 Martin Brampton Merged add to zombieslist into dcb_close,
* fixes for various error situations,
* remove dcb_set_state etc, simplifications.
*
* @endverbatim
*/
@ -59,6 +62,7 @@
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <signal.h>
#include <dcb.h>
#include <spinlock.h>
#include <server.h>
@ -86,10 +90,6 @@ static SPINLOCK dcbspin = SPINLOCK_INIT;
static SPINLOCK zombiespin = SPINLOCK_INIT;
static void dcb_final_free(DCB *dcb);
static bool dcb_set_state_nolock(
DCB *dcb,
const dcb_state_t new_state,
dcb_state_t *old_state);
static void dcb_call_callback(DCB *dcb, DCB_REASON reason);
static DCB * dcb_get_next (DCB *dcb);
static int dcb_null_write(DCB *dcb, GWBUF *buf);
@ -237,52 +237,6 @@ dcb_free(DCB *dcb)
dcb_final_free(dcb);
}
/**
* Add the DCB to the end of zombies list.
*
* Adding to list occurs once per DCB. This is ensured by changing the
* state of DCB to DCB_STATE_ZOMBIE after addition. Prior insertion, DCB state
* is checked and operation proceeds only if state differs from DCB_STATE_ZOMBIE.
* @param dcb The DCB to add to the zombie list
* @return none
*/
void
dcb_add_to_zombieslist(DCB *dcb)
{
bool succp = false;
dcb_state_t prev_state = DCB_STATE_UNDEFINED;
CHK_DCB(dcb);
/*<
* Protect zombies list access.
*/
spinlock_acquire(&zombiespin);
/*<
* If dcb is already added to zombies list, return.
*/
if (dcb->state != DCB_STATE_NOPOLLING) {
ss_dassert(dcb->state != DCB_STATE_POLLING &&
dcb->state != DCB_STATE_LISTENING);
spinlock_release(&zombiespin);
return;
}
/*<
* Add closing dcb to the top of the list.
*/
dcb->memdata.next = zombies;
zombies = dcb;
/*<
* Set state which indicates that it has been added to zombies
* list.
*/
succp = dcb_set_state(dcb, DCB_STATE_ZOMBIE, &prev_state);
ss_info_dassert(succp, "Failed to set DCB_STATE_ZOMBIE");
spinlock_release(&zombiespin);
}
/*
* Clone a DCB for internal use, mostly used for specialist filters
* to create dummy clients based on real clients.
@ -599,8 +553,7 @@ bool succp = false;
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
succp = dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
ss_dassert(succp);
dcb->state = DCB_STATE_DISCONNECTED;
nextdcb = dcb->memdata.next;
dcb_final_free(dcb);
dcb = nextdcb;
@ -615,7 +568,7 @@ bool succp = false;
* Connect to a server
*
* This routine will create a server connection
* If succesful the new dcb will be put in
* If successful the new dcb will be put in
* epoll set by dcb->func.connect
*
* @param server The server to connect to
@ -626,11 +579,11 @@ bool succp = false;
DCB *
dcb_connect(SERVER *server, SESSION *session, const char *protocol)
{
DCB *dcb;
GWPROTOCOL *funcs;
int fd;
int rc;
char *user;
DCB *dcb;
GWPROTOCOL *funcs;
int fd;
int rc;
char *user;
user = session_getUser(session);
if (user && strlen(user))
@ -677,7 +630,7 @@ char *user;
if ((funcs = (GWPROTOCOL *)load_module(protocol,
MODULE_PROTOCOL)) == NULL)
{
dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
dcb->state = DCB_STATE_DISCONNECTED;
dcb_final_free(dcb);
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
@ -716,7 +669,7 @@ char *user;
dcb,
session->client,
session->client->fd)));
dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
dcb->state = DCB_STATE_DISCONNECTED;
dcb_final_free(dcb);
return NULL;
} else {
@ -758,10 +711,10 @@ char *user;
*/
rc = poll_add_dcb(dcb);
if (rc == DCBFD_CLOSED) {
dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
dcb_final_free(dcb);
return NULL;
if (rc) {
dcb->state = DCB_STATE_DISCONNECTED;
dcb_final_free(dcb);
return NULL;
}
/**
* The dcb will be addded into poll set by dcb->func.connect
@ -973,10 +926,10 @@ int dcb_read_n(
goto return_n;
}
if (b == 0 && nread == 0)
if (b == 0)
{
/** Handle closed client socket */
if (dcb_isclient(dcb))
if (nread == 0 && dcb_isclient(dcb))
{
char c;
int l_errno = 0;
@ -998,11 +951,6 @@ int dcb_read_n(
n = 0;
goto return_n;
}
else if (b == 0)
{
n = 0;
goto return_n;
}
dcb->last_read = hkheartbeat;
@ -1242,6 +1190,10 @@ int dcb_read_SSL(
}
gwbuf_rtrim(buffer,bufsize - n);
if(buffer == NULL)
{
goto return_n;
}
#ifdef SS_DEBUG
skygw_log_write(LD,"%lu SSL: Truncated buffer from %d to %d bytes. "
"Read %d bytes, %d bytes waiting.\n",pthread_self(),
@ -1268,12 +1220,9 @@ int dcb_read_SSL(
/*< Append read data to the gwbuf */
*head = gwbuf_append(*head, buffer);
rc = ioctl(dcb->fd, FIONREAD, &b);
pending = SSL_pending(dcb->ssl);
} /*< while (true) */
return_n:
return n;
return nread;
}
/**
* General purpose routine to write to a DCB
@ -1628,95 +1577,116 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue)
}
#endif /* FAKE_CODE */
qlen = GWBUF_LENGTH(queue);
w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen);
dcb->stats.n_writes++;
if (w < 0)
do
{
int ssl_errno = SSL_get_error(dcb->ssl,w);
w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), qlen);
dcb->stats.n_writes++;
if (LOG_IS_ENABLED(LOGFILE_DEBUG))
if (w <= 0)
{
switch(ssl_errno)
int ssl_errno = SSL_get_error(dcb->ssl,w);
if (LOG_IS_ENABLED(LOGFILE_DEBUG))
{
case SSL_ERROR_WANT_READ:
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write to dcb "
"%p in state %s fd %d failed "
"due error SSL_ERROR_WANT_READ",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
break;
case SSL_ERROR_WANT_WRITE:
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write to dcb "
"%p in state %s fd %d failed "
"due error SSL_ERROR_WANT_WRITE",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
break;
default:
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write to dcb "
"%p in state %s fd %d failed "
"due error %d",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state),
dcb->fd,ssl_errno)));
if(ssl_errno == SSL_ERROR_SSL ||
ssl_errno == SSL_ERROR_SYSCALL)
switch(ssl_errno)
{
while((ssl_errno = ERR_get_error()) != 0)
case SSL_ERROR_WANT_READ:
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write to dcb "
"%p in state %s fd %d failed "
"due error SSL_ERROR_WANT_READ",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
break;
case SSL_ERROR_WANT_WRITE:
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write to dcb "
"%p in state %s fd %d failed "
"due error SSL_ERROR_WANT_WRITE",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
break;
default:
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Write to dcb "
"%p in state %s fd %d failed "
"due error %d",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state),
dcb->fd,ssl_errno)));
break;
}
}
if (LOG_IS_ENABLED(LOGFILE_ERROR) && ssl_errno != SSL_ERROR_WANT_WRITE)
{
if (ssl_errno == -1)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Write to dcb %p in "
"state %s fd %d failed due to "
"SSL error %d",
dcb,
STRDCBSTATE(dcb->state),
dcb->fd,
ssl_errno)));
if(ssl_errno == SSL_ERROR_SSL || ssl_errno == SSL_ERROR_SYSCALL)
{
char errbuf[140];
ERR_error_string(ssl_errno,errbuf);
skygw_log_write(LE,"%s",errbuf);
if(ssl_errno == SSL_ERROR_SYSCALL)
{
skygw_log_write(LE,"%d:%s",errno,strerror(errno));
}
do
{
char errbuf[140];
ERR_error_string(ssl_errno,errbuf);
skygw_log_write(LE,"%d:%s",ssl_errno,errbuf);
}while((ssl_errno = ERR_get_error()) != 0);
}
}
else if(w == 0)
{
do
{
char errbuf[140];
ERR_error_string(ssl_errno,errbuf);
skygw_log_write(LE,"%d:%s",ssl_errno,errbuf);
}while((ssl_errno = ERR_get_error()) != 0);
}
}
if(ssl_errno != SSL_ERROR_WANT_WRITE)
break;
}
}
if (LOG_IS_ENABLED(LOGFILE_ERROR))
{
if (ssl_errno != 0)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Write to dcb %p in "
"state %s fd %d failed due "
"SSL error %d",
dcb,
STRDCBSTATE(dcb->state),
dcb->fd,
ssl_errno)));
}
}
}while(w <= 0);
if(w <= 0)
{
break;
}
/*
* Pull the number of bytes we have written from
* queue with have.
*/
queue = gwbuf_consume(queue, w);
LOGIF(LD, (skygw_log_write(
else
{
/** Remove written bytes from the queue */
queue = gwbuf_consume(queue, w);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Wrote %d Bytes to dcb %p in "
"state %s fd %d",
"state %s fd %d",
pthread_self(),
w,
dcb,
STRDCBSTATE(dcb->state),
dcb->fd)));
}
} /*< while (queue != NULL) */
/*<
* What wasn't successfully written is stored to write queue
@ -1734,38 +1704,6 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue)
}
} /* if (dcb->writeq) */
if (saved_errno != 0 &&
queue != NULL &&
saved_errno != EAGAIN &&
saved_errno != EWOULDBLOCK)
{
bool dolog = true;
/**
* Do not log if writing COM_QUIT to backend failed.
*/
if (GWBUF_IS_TYPE_MYSQL(queue))
{
uint8_t* data = GWBUF_DATA(queue);
if (data[4] == 0x01)
{
dolog = false;
}
}
if (dolog)
{
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_write] Writing to %s socket failed due %d, %s.",
pthread_self(),
dcb_isclient(dcb) ? "client" : "backend server",
saved_errno,
strerror(saved_errno))));
}
spinlock_release(&dcb->writeqlock);
return 0;
}
spinlock_release(&dcb->writeqlock);
if (dcb->high_water && dcb->writeqlen > dcb->high_water && below_water)
@ -1901,28 +1839,47 @@ dcb_drain_writeq_SSL(DCB *dcb)
while (dcb->writeq != NULL)
{
len = GWBUF_LENGTH(dcb->writeq);
w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len);
w = gw_write_SSL(dcb->ssl, GWBUF_DATA(dcb->writeq), len);
if (w < 0)
{
int ssl_errno = ERR_get_error();
int ssl_errno = SSL_get_error(dcb->ssl,w);
if(ssl_errno == SSL_ERROR_WANT_WRITE ||
ssl_errno == SSL_ERROR_WANT_ACCEPT ||
ssl_errno == SSL_ERROR_WANT_READ)
if(ssl_errno == SSL_ERROR_WANT_WRITE || ssl_errno == SSL_ERROR_WANT_READ)
{
break;
}
skygw_log_write_flush(LOGFILE_ERROR,
"Error : Write to dcb failed due to "
"SSL error %d:",
dcb,
STRDCBSTATE(dcb->state),
dcb->fd,
ssl_errno);
switch(ssl_errno)
{
case SSL_ERROR_SSL:
case SSL_ERROR_SYSCALL:
while((ssl_errno = ERR_get_error()) != 0)
{
char errbuf[140];
ERR_error_string(ssl_errno,errbuf);
skygw_log_write(LE,"%s",errbuf);
}
if(errno != 0)
skygw_log_write(LE,"%d:%s",errno,strerror(errno));
break;
case SSL_ERROR_ZERO_RETURN:
skygw_log_write(LE,"Socket is closed.");
break;
skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Write to dcb %p "
"in state %s fd %d failed: %s",
dcb,
STRDCBSTATE(dcb->state),
dcb->fd,
ERR_error_string(ssl_errno,NULL));
default:
skygw_log_write(LE,"Unexpected error.");
break;
}
break;
}
/*
* Pull the number of bytes we have written from
@ -1962,62 +1919,100 @@ dcb_drain_writeq_SSL(DCB *dcb)
void
dcb_close(DCB *dcb)
{
CHK_DCB(dcb);
CHK_DCB(dcb);
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
"%lu [dcb_close]",
pthread_self())));
/**
* dcb_close may be called for freshly created dcb, in which case
* it only needs to be freed.
*/
if (dcb->state == DCB_STATE_ALLOC)
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
"%lu [dcb_close] DCB %p in state %s",
pthread_self(),
dcb,
dcb ? STRDCBSTATE(dcb->state) : "Invalid DCB")));
if (DCB_STATE_ZOMBIE == dcb->state)
{
return;
}
if (DCB_STATE_UNDEFINED == dcb->state
|| DCB_STATE_DISCONNECTED == dcb->state)
{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"%lu [dcb_close] Error : Removing DCB %p but was in state %s "
"which is not legal for a call to dcb_close. ",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
raise(SIGABRT);
}
/**
* dcb_close may be called for freshly created dcb, in which case
* it only needs to be freed.
*/
if (dcb->state == DCB_STATE_ALLOC && dcb->fd != DCBFD_CLOSED)
{
dcb_final_free(dcb);
return;
}
/*<
* Stop dcb's listening and modify state accordingly.
*/
if (dcb->state == DCB_STATE_POLLING || dcb->state == DCB_STATE_LISTENING)
{
if (dcb->state == DCB_STATE_LISTENING)
{
dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
dcb_final_free(dcb);
return;
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"%lu [dcb_close] Error : Removing DCB %p but was in state %s "
"which is not expected for a call to dcb_close, although it"
"should be processed correctly. ",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
}
ss_dassert(dcb->state == DCB_STATE_POLLING ||
dcb->state == DCB_STATE_NOPOLLING ||
dcb->state == DCB_STATE_ZOMBIE);
/*<
* Stop dcb's listening and modify state accordingly.
*/
if (dcb->state == DCB_STATE_POLLING)
if ((dcb->state == DCB_STATE_POLLING && !dcb_maybe_add_persistent(dcb))
|| (dcb->state == DCB_STATE_LISTENING))
{
if (!dcb_maybe_add_persistent(dcb))
poll_remove_dcb(dcb);
/*
* Return will always be 0 or function will have crashed, so we
* threw away return value.
*/
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))));
/**
* close protocol and router session
*/
if (dcb->func.close != NULL)
{
if (poll_remove_dcb(dcb))
{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"Error : Removing DCB fd == %d in state %s from "
"poll set failed.",
dcb->fd,
STRDCBSTATE(dcb->state))));
}
else
{
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))));
dcb_close_finish(dcb);
ss_dassert(dcb->state == DCB_STATE_NOPOLLING ||
dcb->state == DCB_STATE_ZOMBIE);
}
ss_dassert(dcb->state == DCB_STATE_NOPOLLING ||
dcb->state == DCB_STATE_ZOMBIE ||
dcb->state == DCB_STATE_POLLING);
dcb->func.close(dcb);
}
/** Call possible callback for this DCB in case of close */
dcb_call_callback(dcb, DCB_REASON_CLOSE);
}
}
spinlock_acquire(&zombiespin);
if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ALLOC)
{
/*<
* Add closing dcb to the top of the list.
*/
dcb->memdata.next = zombies;
zombies = dcb;
/*<
* Set state which indicates that it has been added to zombies
* list.
*/
dcb->state = DCB_STATE_ZOMBIE;
}
spinlock_release(&zombiespin);
}
/**
@ -2097,7 +2092,7 @@ dcb_close_finish(DCB *dcb)
/** Call possible callback for this DCB in case of close */
dcb_call_callback(dcb, DCB_REASON_CLOSE);
/** Must be DCB_STATE_NOPOLLING when this function is called */
dcb_add_to_zombieslist(dcb);
dcb_close(dcb);
}
/**
@ -2291,7 +2286,7 @@ DCB *dcb;
}
/**
* Diagnotic routine to print client DCB data in a tabular form.
* Diagnostic routine to print client DCB data in a tabular form.
*
* @param pdcb DCB to print results to
*/
@ -2417,7 +2412,8 @@ dprintDCB(DCB *pdcb, DCB *dcb)
*
*/
const char *
gw_dcb_state2string (int state) {
gw_dcb_state2string (int state)
{
switch(state) {
case DCB_STATE_ALLOC:
return "DCB Allocated";
@ -2515,155 +2511,6 @@ void dcb_hashtable_stats(
dcb_printf(dcb, "\tLongest chain length: %d\n", longest);
}
bool
dcb_set_state(
DCB *dcb,
const dcb_state_t new_state,
dcb_state_t *old_state_ptr)
{
bool succp;
dcb_state_t old_state_buffer;
CHK_DCB(dcb);
spinlock_acquire(&dcb->dcb_initlock);
succp = dcb_set_state_nolock(dcb, new_state, &old_state_buffer);
spinlock_release(&dcb->dcb_initlock);
if (old_state_ptr != NULL)
{
*old_state_ptr = old_state_buffer;
}
return succp;
}
static bool
dcb_set_state_nolock(
DCB *dcb,
const dcb_state_t new_state,
dcb_state_t *old_state_ptr)
{
bool succp = false;
bool old_state_supplied = true;
dcb_state_t state_buffer;
CHK_DCB(dcb);
if (NULL == old_state_ptr)
{
old_state_supplied = false;
old_state_ptr = &state_buffer;
}
*old_state_ptr = dcb->state;
switch (*old_state_ptr) {
case DCB_STATE_UNDEFINED:
dcb->state = new_state;
succp = true;
break;
case DCB_STATE_ALLOC:
switch (new_state) {
/** fall through, for client requests */
case DCB_STATE_POLLING:
/** fall through, for connect listeners */
case DCB_STATE_LISTENING:
/** for failed connections */
case DCB_STATE_DISCONNECTED:
dcb->state = new_state;
succp = true;
default: ;
}
break;
case DCB_STATE_POLLING:
switch(new_state) {
case DCB_STATE_NOPOLLING:
dcb->state = new_state;
succp = true;
default: ;
}
break;
case DCB_STATE_LISTENING:
switch(new_state) {
case DCB_STATE_NOPOLLING:
dcb->state = new_state;
succp = true;
default: ;
}
break;
case DCB_STATE_NOPOLLING:
switch (new_state) {
case DCB_STATE_ZOMBIE: /*< fall through */
dcb->state = new_state;
case DCB_STATE_POLLING: /*< ok to try but state can't change */
succp = true;
default: ;
}
break;
case DCB_STATE_ZOMBIE:
switch (new_state) {
case DCB_STATE_DISCONNECTED: /*< fall through */
dcb->state = new_state;
case DCB_STATE_POLLING: /*< ok to try but state can't change */
succp = true;
default: ;
}
break;
case DCB_STATE_DISCONNECTED:
switch (new_state) {
case DCB_STATE_FREED:
dcb->state = new_state;
succp = true;
default: ;
}
break;
case DCB_STATE_FREED:
break;
default:
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unknown dcb state %s for "
"dcb %p",
STRDCBSTATE(dcb->state),
dcb)));
ss_dassert(false);
break;
} /*< switch (dcb->state) */
if (succp) {
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [dcb_set_state_nolock] dcb %p fd %d %s -> %s",
pthread_self(),
dcb,
dcb->fd,
STRDCBSTATE(*old_state_ptr),
STRDCBSTATE(dcb->state))));
}
else
{
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_set_state_nolock] Failed "
"to change state of DCB %p. "
"Old state %s > new state %s.",
pthread_self(),
dcb,
(old_state_ptr == NULL ? "NULL" : STRDCBSTATE(*old_state_ptr)),
STRDCBSTATE(new_state))));
ss_dassert(old_state_supplied);
}
return succp;
}
/**
* Write data to a socket through an SSL structure. The SSL structure is linked to a DCB's socket
* and all communication is encrypted and done via the SSL structure.
@ -3236,7 +3083,7 @@ int dcb_accept_SSL(DCB* dcb)
do
{
ssl_rval = SSL_accept(dcb->ssl);
errnum = SSL_get_error(dcb->ssl,ssl_rval);
LOGIF(LD,(skygw_log_write_flush(LD,"[dcb_accept_SSL] SSL_accept %d, error %d",
ssl_rval,errnum)));
switch(ssl_rval)

View File

@ -208,6 +208,15 @@ static int set_user();
/** SSL multi-threading functions and structures */
static SPINLOCK* ssl_locks;
static void ssl_locking_function(int mode,int n,const char* file, int line)
{
if(mode & CRYPTO_LOCK)
spinlock_acquire(&ssl_locks[n]);
else
spinlock_release(&ssl_locks[n]);
}
/**
* OpenSSL requires this struct to be defined in order to use dynamic locks
*/
@ -263,6 +272,17 @@ static void ssl_free_dynlock(struct CRYPTO_dynlock_value * n,const char* file, i
free(n);
}
#ifdef OPENSSL_1_0
/**
* The thread ID callback function for OpenSSL dynamic locks.
* @param id Id to modify
*/
static void maxscale_ssl_id(CRYPTO_THREADID* id)
{
CRYPTO_THREADID_set_numeric(id,pthread_self());
}
#endif
/**
* Handler for SIGHUP signal. Reload the configuration for the
* gateway.
@ -1456,10 +1476,6 @@ int main(int argc, char **argv)
SSL_library_init();
SSL_load_error_strings();
OPENSSL_add_all_algorithms_noconf();
CRYPTO_set_dynlock_create_callback(ssl_create_dynlock);
CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock);
CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock);
CRYPTO_set_id_callback(pthread_self);
/* register exit function for embedded MySQL library */
l = atexit(libmysqld_done);
@ -1515,17 +1531,13 @@ int main(int argc, char **argv)
/** Use default log directory /var/log/maxscale/ */
if(logdir == NULL)
{
if(access(default_logdir,F_OK) != 0)
{
if(mkdir(logdir,0555) != 0)
{
fprintf(stderr,
"Error: Cannot create log directory: %s\n",
default_logdir);
goto return_main;
}
}
if(mkdir(default_logdir,0777) != 0 && errno != EEXIST)
{
fprintf(stderr,
"Error: Cannot create log directory: %s\n",
default_logdir);
goto return_main;
}
logdir = strdup(default_logdir);
}
@ -1582,7 +1594,7 @@ int main(int argc, char **argv)
/**
* Set a data directory for the mysqld library, we use
* a unique directory name to avoid clauses if multiple
* instances of the gateway are beign run on the same
* instances of the gateway are being run on the same
* machine.
*/
@ -1793,6 +1805,26 @@ int main(int argc, char **argv)
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
"MaxScale started with %d server threads.",
config_threadcount())));
int numlocks = CRYPTO_num_locks();
if((ssl_locks = malloc(sizeof(SPINLOCK)*(numlocks + 1))) == NULL)
{
char* logerr = "Memory allocation failed";
print_log_n_stderr(true, true, logerr, logerr, eno);
rc = MAXSCALE_INTERNALERROR;
goto return_main;
}
for(i = 0;i<numlocks + 1;i++)
spinlock_init(&ssl_locks[i]);
CRYPTO_set_locking_callback(ssl_locking_function);
CRYPTO_set_dynlock_create_callback(ssl_create_dynlock);
CRYPTO_set_dynlock_destroy_callback(ssl_free_dynlock);
CRYPTO_set_dynlock_lock_callback(ssl_lock_dynlock);
#ifdef OPENSSL_1_0
CRYPTO_THREADID_set_callback(maxscale_ssl_id);
#else
CRYPTO_set_id_callback(pthread_self);
#endif
MaxScaleStarted = time(0);
/*<

View File

@ -192,6 +192,7 @@ monitorAddServer(MONITOR *mon, SERVER *server)
db->con = NULL;
db->next = NULL;
db->mon_err_count = 0;
db->log_version_err = true;
/** Server status is uninitialized */
db->mon_prev_status = -1;
/* pending status is updated by get_replication_tree */

View File

@ -15,10 +15,12 @@
*
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/epoll.h>
#include <errno.h>
#include <poll.h>
@ -71,6 +73,7 @@ int max_poll_sleep;
* in the loop after the epoll_wait. This allows for better
* thread utilisaiton and fairer scheduling of the event
* processing.
* 07/07/15 Martin Brampton Simplified add and remove DCB, improve error handling.
*
* @endverbatim
*/
@ -186,6 +189,11 @@ static struct {
*/
static void poll_loadav(void *);
/**
* Function to analyse error return from epoll_ctl
*/
static int poll_resolve_error(DCB *, int, bool);
/**
* Initialise the polling system we are using for the gateway.
*
@ -247,7 +255,7 @@ int
poll_add_dcb(DCB *dcb)
{
int rc = -1;
dcb_state_t old_state = DCB_STATE_UNDEFINED;
dcb_state_t old_state = dcb->state;
dcb_state_t new_state;
struct epoll_event ev;
@ -263,58 +271,67 @@ poll_add_dcb(DCB *dcb)
/*<
* Choose new state according to the role of dcb.
*/
spinlock_acquire(&dcb->dcb_initlock);
if (dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER) {
new_state = DCB_STATE_POLLING;
} else {
ss_dassert(dcb->dcb_role == DCB_ROLE_SERVICE_LISTENER);
new_state = DCB_STATE_LISTENING;
}
/*<
* If dcb is in unexpected state, state change fails indicating that dcb
* is not polling anymore.
/*
* Check DCB current state seems sensible
*/
if (dcb_set_state(dcb, new_state, &old_state)) {
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev);
if (rc != 0) {
int eno = errno;
errno = 0;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Adding dcb %p in state %s "
"to poll set failed. epoll_ctl failed due "
"%d, %s.",
dcb,
STRDCBSTATE(dcb->state),
eno,
strerror(eno))));
} else {
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [poll_add_dcb] Added dcb %p in state %s to "
"poll set.",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
}
ss_info_dassert(rc == 0, "Unable to add poll"); /*< trap in debug */
} else {
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Unable to set new state for dcb %p "
"in state %s. Adding to poll set failed.",
dcb,
STRDCBSTATE(dcb->state))));
if (DCB_STATE_DISCONNECTED == dcb->state
|| DCB_STATE_ZOMBIE == dcb->state
|| DCB_STATE_UNDEFINED == dcb->state)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [poll_add_dcb] Error : existing state of dcb %p "
"is %s, but this should be impossible, crashing.",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
raise(SIGABRT);
}
if (DCB_STATE_POLLING == dcb->state
|| DCB_STATE_LISTENING == dcb->state)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [poll_add_dcb] Error : existing state of dcb %p "
"is %s, but this is probably an error, not crashing.",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
}
dcb->state = new_state;
spinlock_release(&dcb->dcb_initlock);
/*
* The only possible failure that will not cause a crash is
* running out of system resources.
*/
rc = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, dcb->fd, &ev);
if (rc)
{
rc = poll_resolve_error(dcb, errno, true);
}
if (0 == rc)
{
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [poll_add_dcb] Added dcb %p in state %s to poll set.",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
}
else dcb->state = old_state;
return rc;
}
/**
* Remove a descriptor from the set of descriptors within the
* polling environment.
* The state change command may fail because concurrent threads may call
* dcb_set_state simultaneously and the conflict is prevented in dcb_set_state.
*
* @param dcb The descriptor to remove
* @return -1 on error or 0 on success
@ -322,61 +339,121 @@ poll_add_dcb(DCB *dcb)
int
poll_remove_dcb(DCB *dcb)
{
struct epoll_event ev;
int rc = -1;
dcb_state_t old_state = DCB_STATE_UNDEFINED;
dcb_state_t new_state = DCB_STATE_NOPOLLING;
struct epoll_event ev;
CHK_DCB(dcb);
spinlock_acquire(&dcb->dcb_initlock);
/*< It is possible that dcb has already been removed from the set */
if (dcb->state != DCB_STATE_POLLING)
{
if (dcb->state == DCB_STATE_NOPOLLING ||
dcb->state == DCB_STATE_ZOMBIE)
{
rc = 0;
}
goto return_rc;
if (dcb->state == DCB_STATE_NOPOLLING ||
dcb->state == DCB_STATE_ZOMBIE)
{
spinlock_release(&dcb->dcb_initlock);
return 0;
}
if (DCB_STATE_POLLING != dcb->state
&& DCB_STATE_LISTENING != dcb->state)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [poll_remove_dcb] Error : existing state of dcb %p "
"is %s, but this is probably an error, not crashing.",
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
}
/*<
* Set state to NOPOLLING and remove dcb from poll set.
*/
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 */
}
}
/*<
* This call was redundant, but the end result is correct.
dcb->state = DCB_STATE_NOPOLLING;
/**
* Only positive fds can be removed from epoll set.
* But this test was removed by Martin as it is hard to
* see that there should ever be a situation where
* fd isn't positive and the DCB is also in the poll list.
*/
/* if (dcb->fd > 0) { */
spinlock_release(&dcb->dcb_initlock);
rc = epoll_ctl(epoll_fd, EPOLL_CTL_DEL, dcb->fd, &ev);
/**
* The poll_resolve_error function will always
* return 0 or crash. So if it returns non-zero result,
* things have gone wrong and we crash.
*/
else if (old_state == new_state)
{
rc = 0;
goto return_rc;
}
if (rc) rc = poll_resolve_error(dcb, errno, false);
if (rc) raise(SIGABRT);
/*< Set bit for each maxscale thread */
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
rc = 0;
return_rc:
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
return rc;
/* } */
}
/**
* Check error returns from epoll_ctl. Most result in a crash since they
* are "impossible". Adding when already present is assumed non-fatal.
* Likewise, removing when not present is assumed non-fatal.
* It is assumed that callers to poll routines can handle the failure
* that results from hitting system limit, although an error is written
* here to record the problem.
*
* @param errornum The errno set by epoll_ctl
* @param adding True for adding to poll list, false for removing
* @return -1 on error or 0 for possibly revised return code
*/
static int
poll_resolve_error(DCB *dcb, int errornum, bool adding)
{
if (adding)
{
if (EEXIST == errornum)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [poll_resolve_error] Error : epoll_ctl could not add, "
"already exists for DCB %p.",
pthread_self(),
dcb)));
// Assume another thread added and no serious harm done
return 0;
}
if (ENOSPC == errornum)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [poll_resolve_error] The limit imposed by "
"/proc/sys/fs/epoll/max_user_watches was "
"encountered while trying to register (EPOLL_CTL_ADD) a new "
"file descriptor on an epoll instance for dcb %p.",
pthread_self(),
dcb)));
/* Failure - assume handled by callers */
return -1;
}
}
else
{
/* Must be removing */
if (ENOENT == errornum)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [poll_resolve_error] Error : epoll_ctl could not remove, "
"not found, for dcb %p.",
pthread_self(),
dcb)));
// Assume another thread removed and no serious harm done
return 0;
}
}
/* Common checks for add or remove - crash MaxScale */
if (EBADF == errornum) raise(SIGABRT);
if (EINVAL == errornum) raise(SIGABRT);
if (ENOMEM == errornum) raise(SIGABRT);
if (EPERM == errornum) raise(SIGABRT);
/* Undocumented error number */
raise(SIGABRT);
/* The following statement should never be reached, but avoids compiler warning */
return -1;
}
#define BLOCKINGPOLL 0 /*< Set BLOCKING POLL to 1 if using a single thread and to make
@ -1604,7 +1681,7 @@ RESULT_ROW *row;
}
/**
* Return a resultset that has the current set of services in it
* Return a result set that has the current set of services in it
*
* @return A Result set
*/

View File

@ -228,6 +228,12 @@ unsigned int randval;
MAXKEYS key;
char secret_file[PATH_MAX + 10];
if(strlen(path) > PATH_MAX)
{
skygw_log_write(LOGFILE_ERROR,"Error: Pathname too long.");
return 1;
}
sprintf(secret_file,"%s/.secrets",path);
/* Open for writing | Create | Truncate the file for writing */

View File

@ -126,7 +126,7 @@ SERVICE *service;
"Error : Unable to load %s module \"%s\".\n\t\t\t"
" Ensure that lib%s.so exists in one of the "
"following directories :\n\t\t\t "
"- %s/modules\n%s%s",
"- %s\n%s%s",
MODULE_ROUTER,
router,
router,
@ -269,7 +269,7 @@ GWPROTOCOL *funcs;
/* Save authentication data to file cache */
char *ptr, path[PATH_MAX + 1];
int mkdir_rval = 0;
strcpy(path, get_cachedir());
strncpy(path, get_cachedir(),PATH_MAX);
strncat(path, "/", 4096);
strncat(path, service->name, PATH_MAX);
if (access(path, R_OK) == -1)
@ -535,11 +535,16 @@ int listeners = 0;
port = service->ports;
while (port)
{
poll_remove_dcb(port->listener);
port->listener->session->state = SESSION_STATE_LISTENER_STOPPED;
listeners++;
port = port->next;
if(port->listener &&
port->listener->session->state == SESSION_STATE_LISTENER)
{
if(poll_remove_dcb(port->listener) == 0)
{
port->listener->session->state = SESSION_STATE_LISTENER_STOPPED;
listeners++;
}
}
port = port->next;
}
service->state = SERVICE_STATE_STOPPED;
@ -563,13 +568,18 @@ int listeners = 0;
port = service->ports;
while (port)
{
if (poll_add_dcb(port->listener) == 0) {
port->listener->session->state = SESSION_STATE_LISTENER;
listeners++;
}
port = port->next;
if(port->listener &&
port->listener->session->state == SESSION_STATE_LISTENER_STOPPED)
{
if(poll_add_dcb(port->listener) == 0)
{
port->listener->session->state = SESSION_STATE_LISTENER;
listeners++;
}
}
port = port->next;
}
service->state = SERVICE_STATE_STARTED;
return listeners;
}
@ -914,10 +924,12 @@ serviceSetSSLVersion(SERVICE *service, char* version)
service->ssl_method_type = SERVICE_SSLV3;
else if(strcasecmp(version,"TLSV10") == 0)
service->ssl_method_type = SERVICE_TLS10;
#ifdef OPENSSL_1_0
else if(strcasecmp(version,"TLSV11") == 0)
service->ssl_method_type = SERVICE_TLS11;
else if(strcasecmp(version,"TLSV12") == 0)
service->ssl_method_type = SERVICE_TLS12;
#endif
else if(strcasecmp(version,"MAX") == 0)
service->ssl_method_type = SERVICE_SSL_TLS_MAX;
else return -1;
@ -1957,13 +1969,14 @@ int serviceInitSSL(SERVICE* service)
case SERVICE_TLS10:
service->method = (SSL_METHOD*)TLSv1_server_method();
break;
#ifdef OPENSSL_1_0
case SERVICE_TLS11:
service->method = (SSL_METHOD*)TLSv1_1_server_method();
break;
case SERVICE_TLS12:
service->method = (SSL_METHOD*)TLSv1_2_server_method();
break;
#endif
/** Rest of these use the maximum available SSL/TLS methods */
case SERVICE_SSL_MAX:
service->method = (SSL_METHOD*)SSLv23_server_method();

View File

@ -64,7 +64,7 @@ int buflen;
ss_info_dassert(!dcb_isvalid(dcb), "Freed DCB must not be valid");
ss_dfprintf(stderr, "\t..done\nMake clone DCB a zombie");
clone->state = DCB_STATE_NOPOLLING;
dcb_add_to_zombieslist(clone);
dcb_close(clone);
ss_info_dassert(dcb_get_zombies() == clone, "Clone DCB must be start of zombie list now");
ss_dfprintf(stderr, "\t..done\nProcess the zombies list");
dcb_process_zombies(0);

View File

@ -331,7 +331,6 @@ const char *gw_dcb_state2string(int); /* DCB state to string */
void dcb_printf(DCB *, const char *, ...); /* DCB version of printf */
int dcb_isclient(DCB *); /* the DCB is the client of the session */
void dcb_hashtable_stats(DCB *, void *); /**< Print statisitics */
void dcb_add_to_zombieslist(DCB* dcb);
int dcb_add_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON, void *),
void *);
int dcb_remove_callback(DCB *, DCB_REASON, int (*)(struct dcb *, DCB_REASON, void *),
@ -340,7 +339,6 @@ int dcb_isvalid(DCB *); /* Check the DCB is in the linked list */
int dcb_count_by_usage(DCB_USAGE); /* Return counts of DCBs */
int dcb_persistent_clean_count(DCB *, bool); /* Clean persistent and return count */
bool dcb_set_state(DCB* dcb, dcb_state_t new_state, dcb_state_t* old_state);
void dcb_call_foreach (struct server* server, DCB_REASON reason);
size_t dcb_get_session_id(DCB* dcb);
bool dcb_get_ses_log_info(DCB* dcb, size_t* sesid, int* enabled_logs);

View File

@ -124,6 +124,7 @@ typedef enum
typedef struct monitor_servers {
SERVER *server; /**< The server being monitored */
MYSQL *con; /**< The MySQL connection */
bool log_version_err;
int mon_err_count;
unsigned int mon_prev_status;
unsigned int pending_status; /**< Pending Status flag bitmap */

View File

@ -117,8 +117,10 @@ typedef enum {
enum{
SERVICE_SSLV3,
SERVICE_TLS10,
#ifdef OPENSSL_1_0
SERVICE_TLS11,
SERVICE_TLS12,
#endif
SERVICE_SSL_MAX,
SERVICE_TLS_MAX,
SERVICE_SSL_TLS_MAX

View File

@ -24,8 +24,9 @@
* @verbatim
* Revision History
*
* Date Who Description
* 02/04/14 Mark Riddoch Initial implementation
* Date Who Description
* 02/04/14 Mark Riddoch Initial implementation
* 11/05/15 Massimilaino Pinto Added mariadb10_compat to master and slave structs
*
* @endverbatim
*/
@ -176,6 +177,7 @@ typedef struct router_slave {
uint32_t lastEventTimestamp;/*< Last event timestamp sent */
SPINLOCK catch_lock; /*< Event catchup lock */
unsigned int cstate; /*< Catch up state */
bool mariadb10_compat;/*< MariaDB 10.0 compatibility */
SPINLOCK rses_lock; /*< Protects rses_deleted */
pthread_t pthread;
struct router_instance
@ -234,6 +236,7 @@ typedef struct {
GWBUF *selectvercom; /*< select @@version_comment */
GWBUF *selecthostname;/*< select @@hostname */
GWBUF *map; /*< select @@max_allowed_packet */
GWBUF *mariadb10; /*< set @mariadb_slave_capability */
uint8_t *fde_event; /*< Format Description Event */
int fde_len; /*< Length of fde_event */
} MASTER_RESPONSES;
@ -242,54 +245,54 @@ typedef struct {
* The per instance data for the router.
*/
typedef struct router_instance {
SERVICE *service; /*< Pointer to the service using this router */
ROUTER_SLAVE *slaves; /*< Link list of all the slave connections */
SPINLOCK lock; /*< Spinlock for the instance data */
char *uuid; /*< UUID for the router to use w/master */
int masterid; /*< Server ID of the master */
int serverid; /*< Server ID to use with master */
int initbinlog; /*< Initial binlog file number */
char *user; /*< User name to use with master */
char *password; /*< Password to use with master */
char *fileroot; /*< Root of binlog filename */
bool master_chksum;/*< Does the master provide checksums */
char *master_uuid; /*< UUID of the master */
DCB *master; /*< DCB for master connection */
DCB *client; /*< DCB for dummy client */
SESSION *session; /*< Fake session for master connection */
unsigned int master_state; /*< State of the master FSM */
uint8_t lastEventReceived;
uint32_t lastEventTimestamp; /*< Timestamp from last event */
GWBUF *residual; /*< Any residual binlog event */
MASTER_RESPONSES saved_master; /*< Saved master responses */
char *binlogdir; /*< The directory with the binlog files */
SPINLOCK binlog_lock; /*< Lock to control update of the binlog position */
char binlog_name[BINLOG_FNAMELEN+1];
SERVICE *service; /*< Pointer to the service using this router */
ROUTER_SLAVE *slaves; /*< Link list of all the slave connections */
SPINLOCK lock; /*< Spinlock for the instance data */
char *uuid; /*< UUID for the router to use w/master */
int masterid; /*< Server ID of the master */
int serverid; /*< Server ID to use with master */
int initbinlog; /*< Initial binlog file number */
char *user; /*< User name to use with master */
char *password; /*< Password to use with master */
char *fileroot; /*< Root of binlog filename */
bool master_chksum; /*< Does the master provide checksums */
bool mariadb10_compat; /*< MariaDB 10.0 compatibility */
char *master_uuid; /*< UUID of the master */
DCB *master; /*< DCB for master connection */
DCB *client; /*< DCB for dummy client */
SESSION *session; /*< Fake session for master connection */
unsigned int master_state; /*< State of the master FSM */
uint8_t lastEventReceived;
uint32_t lastEventTimestamp; /*< Timestamp from last event */
GWBUF *residual; /*< Any residual binlog event */
MASTER_RESPONSES saved_master; /*< Saved master responses */
char *binlogdir; /*< The directory with the binlog files */
SPINLOCK binlog_lock; /*< Lock to control update of the binlog position */
char binlog_name[BINLOG_FNAMELEN+1];
/*< Name of the current binlog file */
uint64_t binlog_position;
uint64_t binlog_position;
/*< Current binlog position */
int binlog_fd; /*< File descriptor of the binlog
int binlog_fd; /*< File descriptor of the binlog
* file being written
*/
uint64_t last_written; /*< Position of last event written */
char prevbinlog[BINLOG_FNAMELEN+1];
int rotating; /*< Rotation in progress flag */
BLFILE *files; /*< Files used by the slaves */
SPINLOCK fileslock; /*< Lock for the files queue above */
unsigned int low_water; /*< Low water mark for client DCB */
unsigned int high_water; /*< High water mark for client DCB */
unsigned int short_burst; /*< Short burst for slave catchup */
unsigned int long_burst; /*< Long burst for slave catchup */
unsigned long burst_size; /*< Maximum size of burst to send */
unsigned long heartbeat; /*< Configured heartbeat value */
ROUTER_STATS stats; /*< Statistics for this router */
int active_logs;
int reconnect_pending;
int retry_backoff;
time_t connect_time;
int handling_threads;
struct router_instance
*next;
uint64_t last_written; /*< Position of last event written */
char prevbinlog[BINLOG_FNAMELEN+1];
int rotating; /*< Rotation in progress flag */
BLFILE *files; /*< Files used by the slaves */
SPINLOCK fileslock; /*< Lock for the files queue above */
unsigned int low_water; /*< Low water mark for client DCB */
unsigned int high_water; /*< High water mark for client DCB */
unsigned int short_burst; /*< Short burst for slave catchup */
unsigned int long_burst; /*< Long burst for slave catchup */
unsigned long burst_size; /*< Maximum size of burst to send */
unsigned long heartbeat; /*< Configured heartbeat value */
ROUTER_STATS stats; /*< Statistics for this router */
int active_logs;
int reconnect_pending;
int retry_backoff;
time_t connect_time;
int handling_threads;
struct router_instance *next;
} ROUTER_INSTANCE;
/**
@ -315,15 +318,16 @@ typedef struct router_instance {
#define BLRM_MAP 0x0011
#define BLRM_REGISTER 0x0012
#define BLRM_BINLOGDUMP 0x0013
#define BLRM_MARIADB10 0x0014
#define BLRM_MAXSTATE 0x0013
#define BLRM_MAXSTATE 0x0014
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()", "select @@version_comment", "select @@hostname",
"select @@mx_allowed_packet", "Register slave", "Binlog Dump" };
"select @@mx_allowed_packet", "Register slave", "Binlog Dump", "Set MariaDB slave capability" };
#define BLRS_CREATED 0x0000
#define BLRS_UNREGISTERED 0x0001
@ -397,6 +401,7 @@ static char *blrs_states[] = { "Created", "Unregistered", "Registered",
#define PREVIOUS_GTIDS_EVENT 0x23
#define MAX_EVENT_TYPE 0x23
#define MAX_EVENT_TYPE_MARIADB10 0xa3
/**
* Binlog event flags

View File

@ -252,6 +252,7 @@ typedef struct rwsplit_config_st {
int rw_max_sescmd_history_size;
bool disable_sescmd_hist;
bool disable_slave_recovery;
bool master_reads; /*< Use master for reads */
} rwsplit_config_t;

View File

@ -245,4 +245,6 @@ 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));
bool subsvc_is_valid(SUBSERVICE*);
#endif /*< _SHARDROUTER_H */

View File

@ -155,7 +155,10 @@ startMonitor(void *arg,void* opt)
else if(!strcmp(params->name,"script"))
{
if(handle->script)
{
free(handle->script);
handle->script = NULL;
}
if(access(params->value,X_OK) == 0)
{
@ -277,7 +280,7 @@ monitorDatabase(MONITOR *mon, MONITOR_SERVERS *database)
{
GALERA_MONITOR* handle = (GALERA_MONITOR*)mon->handle;
MYSQL_ROW row;
MYSQL_RES *result;
MYSQL_RES *result,*result2;
int isjoined = 0;
char *uname = mon->user;
char *passwd = mon->password;
@ -369,6 +372,14 @@ char *server_string;
if (mysql_query(database->con, "SHOW STATUS LIKE 'wsrep_local_state'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_state'\". Expected 2 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
if (strcmp(row[1], "4") == 0)
@ -377,13 +388,22 @@ char *server_string;
/* Check if the node is a donor and is using xtrabackup, in this case it can stay alive */
else if (strcmp(row[1], "2") == 0 && handle->availableWhenDonor == 1) {
if (mysql_query(database->con, "SHOW VARIABLES LIKE 'wsrep_sst_method'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
&& (result2 = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
mysql_free_result(result2);
skygw_log_write(LE,"Error: Unexpected result for \"SHOW VARIABLES LIKE 'wsrep_sst_method'\". Expected 2 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
if (strncmp(row[1], "xtrabackup", 10) == 0)
isjoined = 1;
}
mysql_free_result(result2);
}
}
}
@ -395,6 +415,15 @@ char *server_string;
&& (result = mysql_store_result(database->con)) != NULL)
{
long local_index = -1;
if(mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'wsrep_local_index'\". Expected 2 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
local_index = strtol(row[1], NULL, 10);

View File

@ -366,7 +366,15 @@ char *server_string;
&& (result = mysql_store_result(database->con)) != NULL)
{
long server_id = -1;
if(mysql_field_count(database->con) != 1)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for 'SELECT @@server_id'. Expected 1 column."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
server_id = strtol(row[0], NULL, 10);
@ -392,7 +400,16 @@ char *server_string;
{
int i = 0;
long master_id = -1;
if(mysql_field_count(database->con) < 42)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" "
"returned less than the expected amount of columns. Expected 42 columns"
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
@ -431,7 +448,31 @@ char *server_string;
&& (result = mysql_store_result(database->con)) != NULL)
{
long master_id = -1;
if(mysql_field_count(database->con) < 40)
{
mysql_free_result(result);
if(server_version < 5*10000 + 5*100)
{
if(database->log_version_err)
{
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
" for versions less than 5.5 does not have master_server_id, "
"replication tree cannot be resolved for server %s."
" MySQL Version: %s",database->server->unique_name,version_str);
database->log_version_err = false;
}
}
else
{
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
"returned less than the expected amount of columns. Expected 40 columns."
" MySQL Version: %s",version_str);
}
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
@ -463,6 +504,13 @@ char *server_string;
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SHOW GLOBAL VARIABLES LIKE 'read_only'\". Expected 2 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{

View File

@ -408,6 +408,13 @@ char *server_string;
&& (result = mysql_store_result(database->con)) != NULL)
{
long server_id = -1;
if(mysql_field_count(database->con) != 1)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SELECT @@server_id\". Expected 1 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
server_id = strtol(row[0], NULL, 10);
@ -433,6 +440,16 @@ char *server_string;
{
int i = 0;
long master_id = -1;
if(mysql_field_count(database->con) < 42)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: \"SHOW ALL SLAVES STATUS\" "
"returned less than the expected amount of columns. Expected 42 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/
@ -471,6 +488,29 @@ char *server_string;
&& (result = mysql_store_result(database->con)) != NULL)
{
long master_id = -1;
if(mysql_field_count(database->con) < 40)
{
mysql_free_result(result);
if(server_version < 5*10000 + 5*100)
{
if(database->log_version_err)
{
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
" for versions less than 5.5 does not have master_server_id, "
"replication tree cannot be resolved for server %s."
" MySQL Version: %s",database->server->unique_name,version_str);
database->log_version_err = false;
}
}
else
{
skygw_log_write(LE,"Error: \"SHOW SLAVE STATUS\" "
"returned less than the expected amount of columns. Expected 40 columns."
" MySQL Version: %s",version_str);
}
return;
}
while ((row = mysql_fetch_row(result)))
{
/* get Slave_IO_Running and Slave_SQL_Running values*/

View File

@ -323,6 +323,14 @@ char *server_string;
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_number_of_ready_data_nodes'\". Expected 2 columns."
" MySQL Version: %s",version_str);
return;
}
while ((row = mysql_fetch_row(result)))
{
if (atoi(row[1]) > 0)
@ -335,6 +343,14 @@ char *server_string;
if (mysql_query(database->con, "SHOW STATUS LIKE 'Ndb_cluster_node_id'") == 0
&& (result = mysql_store_result(database->con)) != NULL)
{
if(mysql_field_count(database->con) < 2)
{
mysql_free_result(result);
skygw_log_write(LE,"Error: Unexpected result for \"SHOW STATUS LIKE 'Ndb_cluster_node_id'\". Expected 2 columns."
" MySQL Version: %s",version_str);
return;
}
long cluster_node_id = -1;
while ((row = mysql_fetch_row(result)))
{

View File

@ -58,6 +58,7 @@ extern __thread log_info_t tls_log_info;
* Revision History
* Date Who Description
* 13/06/2014 Mark Riddoch Initial implementation
* 07/07/15 Martin Brampton Correct failure handling
*
* @endverbatim
*/
@ -270,9 +271,7 @@ int n_connect = 0;
{
atomic_add(&dcb->stats.n_accepts, 1);
client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
if (client_dcb == NULL)
{
close(so);
return n_connect;
@ -283,7 +282,8 @@ int n_connect = 0;
if ((maxscaled_pr = (MAXSCALED *)malloc(sizeof(MAXSCALED))) == NULL)
{
client_dcb->protocol = NULL;
dcb_add_to_zombieslist(client_dcb);
close(so);
dcb_close(client_dcb);
return n_connect;
}
maxscaled_pr->username = NULL;
@ -293,9 +293,9 @@ int n_connect = 0;
client_dcb->session =
session_alloc(dcb->session->service, client_dcb);
if (poll_add_dcb(client_dcb) == -1)
if (poll_add_dcb(client_dcb))
{
dcb_add_to_zombieslist(dcb);
dcb_close(dcb);
return n_connect;
}
n_connect++;

View File

@ -1452,8 +1452,8 @@ int gw_MySQLListener(
// add listening socket to poll structure
if (poll_add_dcb(listen_dcb) == -1) {
fprintf(stderr,
"\n* Failed to start polling the socket due error "
"%i, %s.\n\n",
"\n* MaxScale encountered system limit while "
"attempting to register on an epoll instance.\n\n",
errno,
strerror(errno));
return 0;
@ -1688,7 +1688,8 @@ int gw_MySQLAccept(DCB *listener)
client_dcb,
1,
0,
"MaxScale internal error.");
"MaxScale encountered system limit while "
"attempting to register on an epoll instance.");
/** close client_dcb */
dcb_close(client_dcb);

View File

@ -36,6 +36,7 @@
* 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts.
* 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support
* 10/11/2014 Massimiliano Pinto Charset at connect is passed to backend during authentication
* 07/07/15 Martin Brampton Fix problem recognising null password
*
*/
@ -1550,13 +1551,6 @@ int gw_find_mysql_user_password_sha1(char *username, uint8_t *gateway_password,
break;
}
/** See if ANYDB == Y */
if(key.resource)
{
key.resource = NULL;
continue;
}
if (!user_password) {
/*
* user@% not found.

View File

@ -67,6 +67,7 @@ extern __thread log_info_t tls_log_info;
* Date Who Description
* 17/06/2013 Mark Riddoch Initial version
* 17/07/2013 Mark Riddoch Addition of login phase
* 07/07/2015 Martin Brampton Call unified dcb_close on error
*
* @endverbatim
*/
@ -315,13 +316,13 @@ int n_connect = 0;
if (telnetd_pr == NULL)
{
dcb_add_to_zombieslist(client_dcb);
dcb_close(client_dcb);
return n_connect;
}
if (poll_add_dcb(client_dcb) == -1)
if (poll_add_dcb(client_dcb))
{
dcb_add_to_zombieslist(dcb);
dcb_close(dcb);
return n_connect;
}
n_connect++;

View File

@ -35,6 +35,8 @@
* 02/04/2014 Mark Riddoch Initial implementation
* 17/02/2015 Massimiliano Pinto Addition of slave port and username in diagnostics
* 18/02/2015 Massimiliano Pinto Addition of dcb_close in closeSession
* 07/05/2015 Massimiliano Pinto Addition of MariaDB 10 compatibility support
*
* @endverbatim
*/
@ -195,6 +197,7 @@ unsigned char *defuuid;
inst->retry_backoff = 1;
inst->binlogdir = NULL;
inst->heartbeat = 300; // Default is every 5 minutes
inst->mariadb10_compat = false;
inst->user = strdup(service->credentials.name);
inst->password = strdup(service->credentials.authdata);
@ -282,6 +285,10 @@ unsigned char *defuuid;
{
inst->masterid = atoi(value);
}
else if (strcmp(options[i], "mariadb10-compatibility") == 0)
{
inst->mariadb10_compat = config_truth_value(value);
}
else if (strcmp(options[i], "filestem") == 0)
{
inst->fileroot = strdup(value);
@ -388,6 +395,7 @@ unsigned char *defuuid;
inst->saved_master.selectvercom = blr_cache_read_response(inst, "selectvercom");
inst->saved_master.selecthostname = blr_cache_read_response(inst, "selecthostname");
inst->saved_master.map = blr_cache_read_response(inst, "map");
inst->saved_master.mariadb10 = blr_cache_read_response(inst, "mariadb10");
/*
* Initialise the binlog file and position
@ -490,6 +498,7 @@ ROUTER_SLAVE *slave;
strcpy(slave->binlogfile, "unassigned");
slave->connect_time = time(0);
slave->lastEventTimestamp = 0;
slave->mariadb10_compat = false;
/**
* Add this session to the list of active sessions.

View File

@ -23,8 +23,9 @@
* @verbatim
* Revision History
*
* Date Who Description
* Date Who Description
* 14/04/2014 Mark Riddoch Initial implementation
* 07/05/2015 Massimiliano Pinto Added MAX_EVENT_TYPE_MARIADB10
*
* @endverbatim
*/
@ -439,15 +440,26 @@ struct stat statb;
hdr->next_pos = EXTRACT32(&hdbuf[13]);
hdr->flags = EXTRACT16(&hdbuf[17]);
if (hdr->event_type > MAX_EVENT_TYPE)
{
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
"Invalid event type 0x%x. "
if (router->mariadb10_compat) {
if (hdr->event_type > MAX_EVENT_TYPE_MARIADB10) {
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
"Invalid MariaDB 10 event type 0x%x. "
"Binlog file is %s, position %d",
hdr->event_type,
file->binlogname, pos)));
return NULL;
}
return NULL;
}
} else {
if (hdr->event_type > MAX_EVENT_TYPE) {
LOGIF(LE, (skygw_log_write(LOGFILE_ERROR,
"Invalid event type 0x%x. "
"Binlog file is %s, position %d",
hdr->event_type,
file->binlogname, pos)));
return NULL;
}
}
if (hdr->next_pos < pos && hdr->event_type != ROTATE_EVENT)
{

View File

@ -31,8 +31,9 @@
* @verbatim
* Revision History
*
* Date Who Description
* Date Who Description
* 02/04/2014 Mark Riddoch Initial implementation
* 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility
*
* @endverbatim
*/
@ -448,11 +449,27 @@ char query[128];
GWBUF_CONSUME_ALL(router->saved_master.chksum2);
router->saved_master.chksum2 = buf;
blr_cache_response(router, "chksum2", buf);
buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE");
router->master_state = BLRM_GTIDMODE;
if (router->mariadb10_compat) {
buf = blr_make_query("SET @mariadb_slave_capability=4");
router->master_state = BLRM_MARIADB10;
} else {
buf = blr_make_query("SELECT @@GLOBAL.GTID_MODE");
router->master_state = BLRM_GTIDMODE;
}
router->master->func.write(router->master, buf);
break;
}
case BLRM_MARIADB10:
// Response to the SET @mariadb_slave_capability=4, should be stored
if (router->saved_master.mariadb10)
GWBUF_CONSUME_ALL(router->saved_master.mariadb10);
router->saved_master.mariadb10 = buf;
blr_cache_response(router, "mariadb10", buf);
buf = blr_make_query("SHOW VARIABLES LIKE 'SERVER_UUID'");
router->master_state = BLRM_MUUID;
router->master->func.write(router->master, buf);
break;
case BLRM_GTIDMODE:
// Response to the GTID_MODE, should be stored
if (router->saved_master.gtid_mode)

View File

@ -36,9 +36,12 @@
* 18/02/2015 Massimiliano Pinto Addition of DISCONNECT ALL and DISCONNECT SERVER server_id
* 18/03/2015 Markus Makela Better detection of CRC32 | NONE checksum
* 19/03/2015 Massimiliano Pinto Addition of basic MariaDB 10 compatibility support
* 07/05/2015 Massimiliano Pinto Added MariaDB 10 Compatibility
* 11/05/2015 Massimiliano Pinto Only MariaDB 10 Slaves can register to binlog router with a MariaDB 10 Master
*
* @endverbatim
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -123,7 +126,28 @@ blr_slave_request(ROUTER_INSTANCE *router, ROUTER_SLAVE *slave, GWBUF *queue)
return blr_slave_query(router, slave, queue);
break;
case COM_REGISTER_SLAVE:
return blr_slave_register(router, slave, queue);
/*
* If Master is MariaDB10 don't allow registration from
* MariaDB/Mysql 5 Slaves
*/
if (router->mariadb10_compat && !slave->mariadb10_compat) {
slave->state = BLRS_ERRORED;
blr_send_custom_error(slave->dcb, 1, 0,
"MariaDB 10 Slave is required for Slave registration");
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"%s: Slave %s: a MariaDB 10 Slave is required for Slave registration",
router->service->name,
slave->dcb->remote)));
dcb_close(slave->dcb);
return 1;
} else {
/* Master and Slave version OK: continue with slave registration */
return blr_slave_register(router, slave, queue);
}
break;
case COM_BINLOG_DUMP:
return blr_slave_binlog_dump(router, slave, queue);
@ -366,10 +390,17 @@ int query_len;
free(query_text);
return blr_slave_replay(router, slave, router->saved_master.heartbeat);
}
else if (strcasecmp(word, "@mariadb_slave_capability") == 0)
else if (strcasecmp(word, "@mariadb_slave_capability") == 0)
{
free(query_text);
return blr_slave_send_ok(router, slave);
/* mariadb10 compatibility is set for the slave */
slave->mariadb10_compat=true;
free(query_text);
if (router->mariadb10_compat) {
return blr_slave_replay(router, slave, router->saved_master.mariadb10);
} else {
return blr_slave_send_ok(router, slave);
}
}
else if (strcasecmp(word, "@master_binlog_checksum") == 0)
{
@ -442,7 +473,7 @@ int query_len;
query_text = strndup(qtext, query_len);
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR, "Unexpected query from slave server %s", query_text)));
LOGFILE_ERROR, "Unexpected query from slave %s: %s", slave->dcb->remote, query_text)));
free(query_text);
blr_slave_send_error(router, slave, "Unexpected SQL query received from slave.");
return 1;

View File

@ -113,14 +113,10 @@ MAXINFO_TREE *col, *table;
#endif
default:
*parse_error = PARSE_SYNTAX_ERROR;
if (tree)
free_tree(tree);
return NULL;
}
}
*parse_error = PARSE_SYNTAX_ERROR;
if (tree)
free_tree(tree);
return NULL;
}

View File

@ -226,7 +226,7 @@ static rses_property_t* mysql_sescmd_get_property(
static rses_property_t* rses_property_init(
rses_property_type_t prop_type);
static void rses_property_add(
static int rses_property_add(
ROUTER_CLIENT_SES* rses,
rses_property_t* prop);
@ -287,7 +287,7 @@ static sescmd_cursor_t* backend_ref_get_sescmd_cursor (backend_ref_t* bref);
static int router_handle_state_switch(DCB* dcb, DCB_REASON reason, void* data);
static bool handle_error_new_connection(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
ROUTER_CLIENT_SES** rses,
DCB* backend_dcb,
GWBUF* errmsg);
static void handle_error_reply_client(
@ -1243,7 +1243,8 @@ static bool get_dcb(
SERVER_IS_SLAVE(b->backend_server) &&
(max_rlag == MAX_RLAG_UNDEFINED ||
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
b->backend_server->rlag <= max_rlag)))
b->backend_server->rlag <= max_rlag)) &&
!rses->rses_config.master_reads)
{
/** found slave */
candidate_bref = &backend_ref[i];
@ -2941,6 +2942,11 @@ static void bref_clear_state(
backend_ref_t* bref,
bref_state_t state)
{
if(bref == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to bref_clear_state. (%s:%d)",__FILE__,__LINE__);
return;
}
if (state != BREF_WAITING_RESULT)
{
bref->bref_state &= ~state;
@ -2970,6 +2976,11 @@ static void bref_set_state(
backend_ref_t* bref,
bref_state_t state)
{
if(bref == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to bref_set_state. (%s:%d)",__FILE__,__LINE__);
return;
}
if (state != BREF_WAITING_RESULT)
{
bref->bref_state |= state;
@ -3533,7 +3544,8 @@ static rses_property_t* rses_property_init(
prop = (rses_property_t*)calloc(1, sizeof(rses_property_t));
if (prop == NULL)
{
goto return_prop;
skygw_log_write(LE,"Error: Malloc returned NULL. (%s:%d)",__FILE__,__LINE__);
return NULL;
}
prop->rses_prop_type = prop_type;
#if defined(SS_DEBUG)
@ -3541,7 +3553,6 @@ static rses_property_t* rses_property_init(
prop->rses_prop_chk_tail = CHK_NUM_ROUTER_PROPERTY;
#endif
return_prop:
CHK_RSES_PROP(prop);
return prop;
}
@ -3552,6 +3563,11 @@ return_prop:
static void rses_property_done(
rses_property_t* prop)
{
if(prop == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to rses_property_done. (%s:%d)",__FILE__,__LINE__);
return;
}
CHK_RSES_PROP(prop);
switch (prop->rses_prop_type) {
@ -3585,10 +3601,20 @@ static void rses_property_done(
*
* Router client session must be locked.
*/
static void rses_property_add(
static int rses_property_add(
ROUTER_CLIENT_SES* rses,
rses_property_t* prop)
{
if(rses == NULL)
{
skygw_log_write(LE,"Error: Router client session is NULL. (%s:%d)",__FILE__,__LINE__);
return -1;
}
if(prop == NULL)
{
skygw_log_write(LE,"Error: Router client session property is NULL. (%s:%d)",__FILE__,__LINE__);
return -1;
}
rses_property_t* p;
CHK_CLIENT_RSES(rses);
@ -3610,6 +3636,7 @@ static void rses_property_add(
}
p->rses_prop_next = prop;
}
return 0;
}
/**
@ -3620,7 +3647,13 @@ static mysql_sescmd_t* rses_property_get_sescmd(
rses_property_t* prop)
{
mysql_sescmd_t* sescmd;
if(prop == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to rses_property_get_sescmd. (%s:%d)",__FILE__,__LINE__);
return NULL;
}
CHK_RSES_PROP(prop);
ss_dassert(prop->rses_prop_rsession == NULL ||
SPINLOCK_IS_LOCKED(&prop->rses_prop_rsession->rses_lock));
@ -3633,22 +3666,6 @@ static mysql_sescmd_t* rses_property_get_sescmd(
}
return sescmd;
}
/**
static void rses_begin_locked_property_action(
rses_property_t* prop)
{
CHK_RSES_PROP(prop);
spinlock_acquire(&prop->rses_prop_lock);
}
static void rses_end_locked_property_action(
rses_property_t* prop)
{
CHK_RSES_PROP(prop);
spinlock_release(&prop->rses_prop_lock);
}
*/
/**
* Create session command property.
@ -3681,6 +3698,11 @@ static mysql_sescmd_t* mysql_sescmd_init (
static void mysql_sescmd_done(
mysql_sescmd_t* sescmd)
{
if(sescmd == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to mysql_sescmd_done. (%s:%d)",__FILE__,__LINE__);
return;
}
CHK_RSES_PROP(sescmd->my_sescmd_prop);
gwbuf_free(sescmd->my_sescmd_buf);
memset(sescmd, 0, sizeof(mysql_sescmd_t));
@ -3763,7 +3785,7 @@ static GWBUF* sescmd_cursor_process_replies(
dcb_close(bref->bref_dcb);
*reconnect = true;
if(replybuf)
gwbuf_consume(replybuf,gwbuf_length(replybuf));
while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf))));
}
}
/** This is a response from the master and it is the "right" one.
@ -3806,7 +3828,7 @@ static GWBUF* sescmd_cursor_process_replies(
skygw_log_write(LOGFILE_DEBUG,"Slave '%s' responded faster to a session command.",
bref->bref_backend->backend_server->unique_name);
if(replybuf)
gwbuf_free(replybuf);
while((replybuf = gwbuf_consume(replybuf,gwbuf_length(replybuf))));
return NULL;
}
@ -3853,6 +3875,12 @@ static bool sescmd_cursor_is_active(
sescmd_cursor_t* sescmd_cursor)
{
bool succp;
if(sescmd_cursor == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_is_active. (%s:%d)",__FILE__,__LINE__);
return false;
}
ss_dassert(SPINLOCK_IS_LOCKED(&sescmd_cursor->scmd_cur_rses->rses_lock));
succp = sescmd_cursor->scmd_cur_active;
@ -3878,6 +3906,11 @@ static GWBUF* sescmd_cursor_clone_querybuf(
sescmd_cursor_t* scur)
{
GWBUF* buf;
if(scur == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_clone_querybuf. (%s:%d)",__FILE__,__LINE__);
return NULL;
}
ss_dassert(scur->scmd_cur_cmd != NULL);
buf = gwbuf_clone(scur->scmd_cur_cmd->my_sescmd_buf);
@ -3890,7 +3923,12 @@ static bool sescmd_cursor_history_empty(
sescmd_cursor_t* scur)
{
bool succp;
if(scur == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_history_empty. (%s:%d)",__FILE__,__LINE__);
return true;
}
CHK_SESCMD_CUR(scur);
if (scur->scmd_cur_rses->rses_properties[RSES_PROP_TYPE_SESCMD] == NULL)
@ -3910,6 +3948,11 @@ static void sescmd_cursor_reset(
sescmd_cursor_t* scur)
{
ROUTER_CLIENT_SES* rses;
if(scur == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_reset. (%s:%d)",__FILE__,__LINE__);
return;
}
CHK_SESCMD_CUR(scur);
CHK_CLIENT_RSES(scur->scmd_cur_rses);
rses = scur->scmd_cur_rses;
@ -3926,6 +3969,11 @@ static bool execute_sescmd_history(
{
bool succp;
sescmd_cursor_t* scur;
if(bref == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_history. (%s:%d)",__FILE__,__LINE__);
return false;
}
CHK_BACKEND_REF(bref);
scur = &bref->bref_sescmd_cur;
@ -3961,7 +4009,12 @@ static bool execute_sescmd_in_backend(
bool succp;
int rc = 0;
sescmd_cursor_t* scur;
GWBUF* buf;
if(backend_ref == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to execute_sescmd_in_backend. (%s:%d)",__FILE__,__LINE__);
return false;
}
if (BREF_IS_CLOSED(backend_ref))
{
succp = false;
@ -3993,27 +4046,9 @@ static bool execute_sescmd_in_backend(
/** Cursor is left active when function returns. */
sescmd_cursor_set_active(scur, true);
}
#if defined(SS_DEBUG)
LOGIF(LT, tracelog_routed_query(scur->scmd_cur_rses,
"execute_sescmd_in_backend",
backend_ref,
sescmd_cursor_clone_querybuf(scur)));
{
GWBUF* tmpbuf = sescmd_cursor_clone_querybuf(scur);
uint8_t* ptr = GWBUF_DATA(tmpbuf);
unsigned char cmd = MYSQL_GET_COMMAND(ptr);
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [execute_sescmd_in_backend] Just before write, fd "
"%d : cmd %s.",
pthread_self(),
dcb->fd,
STRPACKETTYPE(cmd))));
gwbuf_free(tmpbuf);
}
#endif /*< SS_DEBUG */
buf = sescmd_cursor_clone_querybuf(scur);
switch (scur->scmd_cur_cmd->my_sescmd_packet_type) {
case MYSQL_COM_CHANGE_USER:
/** This makes it possible to handle replies correctly */
@ -4022,7 +4057,7 @@ static bool execute_sescmd_in_backend(
dcb,
NULL,
dcb->session,
sescmd_cursor_clone_querybuf(scur));
buf);
break;
case MYSQL_COM_INIT_DB:
@ -4048,10 +4083,11 @@ static bool execute_sescmd_in_backend(
* Mark session command buffer, it triggers writing
* MySQL command to protocol
*/
gwbuf_set_type(scur->scmd_cur_cmd->my_sescmd_buf, GWBUF_TYPE_SESCMD);
rc = dcb->func.write(
dcb,
sescmd_cursor_clone_querybuf(scur));
buf);
break;
}
@ -4061,6 +4097,7 @@ static bool execute_sescmd_in_backend(
}
else
{
while((buf = GWBUF_CONSUME_ALL(buf)) != NULL);
succp = false;
}
return_succp:
@ -4082,6 +4119,12 @@ static bool sescmd_cursor_next(
rses_property_t* prop_curr;
rses_property_t* prop_next;
if(scur == NULL)
{
skygw_log_write(LE,"Error: NULL parameter passed to sescmd_cursor_next. (%s:%d)",__FILE__,__LINE__);
return false;
}
ss_dassert(scur != NULL);
ss_dassert(*(scur->scmd_cur_ptr_property) != NULL);
ss_dassert(SPINLOCK_IS_LOCKED(
@ -4408,11 +4451,21 @@ static bool route_session_write(
* prevent it from being released before properties
* are cleaned up as a part of router sessionclean-up.
*/
prop = rses_property_init(RSES_PROP_TYPE_SESCMD);
if((prop = rses_property_init(RSES_PROP_TYPE_SESCMD)) == NULL)
{
skygw_log_write(LE,"Error: Router session property initialization failed");
rses_end_locked_router_action(router_cli_ses);
return false;
}
mysql_sescmd_init(prop, querybuf, packet_type, router_cli_ses);
/** Add sescmd property to router client session */
rses_property_add(router_cli_ses, prop);
if(rses_property_add(router_cli_ses, prop) != 0)
{
skygw_log_write(LE,"Error: Session property addition failed.");
rses_end_locked_router_action(router_cli_ses);
return false;
}
for (i=0; i<router_cli_ses->rses_nbackends; i++)
{
@ -4535,7 +4588,10 @@ static void rwsplit_process_router_options(
int i;
char* value;
select_criteria_t c;
if(options == NULL)
return;
for (i = 0; options[i]; i++)
{
if ((value = strchr(options[i], '=')) == NULL)
@ -4588,6 +4644,10 @@ static void rwsplit_process_router_options(
{
router->rwsplit_config.disable_slave_recovery = config_truth_value(value);
}
else if(strcmp(options[i],"master_accept_reads") == 0)
{
router->rwsplit_config.master_reads = config_truth_value(value);
}
}
} /*< for */
}
@ -4619,7 +4679,7 @@ static void handleError (
SESSION* session;
ROUTER_INSTANCE* inst = (ROUTER_INSTANCE *)instance;
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session;
CHK_DCB(backend_dcb);
/** Reset error handle flag from a given DCB */
@ -4686,14 +4746,15 @@ static void handleError (
{
/**
* This is called in hope of getting replacement for
* failed slave(s).
* failed slave(s). This call may free rses.
*/
*succp = handle_error_new_connection(inst,
rses,
&rses,
backend_dcb,
errmsgbuf);
}
rses_end_locked_router_action(rses);
/* Free the lock if rses still exists */
if (rses) rses_end_locked_router_action(rses);
break;
}
@ -4762,10 +4823,11 @@ static void handle_error_reply_client(
*/
static bool handle_error_new_connection(
ROUTER_INSTANCE* inst,
ROUTER_CLIENT_SES* rses,
ROUTER_CLIENT_SES** rses,
DCB* backend_dcb,
GWBUF* errmsg)
{
ROUTER_CLIENT_SES* myrses;
SESSION* ses;
int router_nservers;
int max_nslaves;
@ -4773,7 +4835,8 @@ static bool handle_error_new_connection(
backend_ref_t* bref;
bool succp;
ss_dassert(SPINLOCK_IS_LOCKED(&rses->rses_lock));
myrses = *rses;
ss_dassert(SPINLOCK_IS_LOCKED(&myrses->rses_lock));
ses = backend_dcb->session;
CHK_SESSION(ses);
@ -4781,7 +4844,7 @@ static bool handle_error_new_connection(
/**
* If bref == NULL it has been replaced already with another one.
*/
if ((bref = get_bref_from_dcb(rses, backend_dcb)) == NULL)
if ((bref = get_bref_from_dcb(myrses, backend_dcb)) == NULL)
{
succp = true;
goto return_succp;
@ -4824,25 +4887,25 @@ static bool handle_error_new_connection(
(void *)bref);
router_nservers = router_get_servercount(inst);
max_nslaves = rses_get_max_slavecount(rses, router_nservers);
max_slave_rlag = rses_get_max_replication_lag(rses);
max_nslaves = rses_get_max_slavecount(myrses, router_nservers);
max_slave_rlag = rses_get_max_replication_lag(myrses);
/**
* Try to get replacement slave or at least the minimum
* number of slave connections for router session.
*/
if(inst->rwsplit_config.disable_slave_recovery)
{
succp = have_enough_servers(&rses,1,router_nservers,inst) ? true : false;
succp = have_enough_servers(&myrses,1,router_nservers,inst) ? true : false;
}
else
{
succp = select_connect_backend_servers(
&rses->rses_master_ref,
rses->rses_backend_ref,
&myrses->rses_master_ref,
myrses->rses_backend_ref,
router_nservers,
max_nslaves,
max_slave_rlag,
rses->rses_config.rw_slave_select_criteria,
myrses->rses_config.rw_slave_select_criteria,
ses,
inst);
}
@ -5079,10 +5142,9 @@ static int router_handle_state_switch(
{
backend_ref_t* bref;
int rc = 1;
ROUTER_CLIENT_SES* rses;
SESSION* ses;
SERVER* srv;
ROUTER_CLIENT_SES* rses;
SESSION* ses;
CHK_DCB(dcb);
bref = (backend_ref_t *)data;
CHK_BACKEND_REF(bref);
@ -5103,8 +5165,7 @@ static int router_handle_state_switch(
STRSRVSTATUS(srv))));
ses = dcb->session;
CHK_SESSION(ses);
rses = (ROUTER_CLIENT_SES *)dcb->session->router_session;
rses = (ROUTER_CLIENT_SES *)dcb->session->router_session;
CHK_CLIENT_RSES(rses);
switch (reason) {

View File

@ -46,7 +46,7 @@ bool extract_database(GWBUF* buf, char* str)
tok = strtok_r(query," ;",&saved);
if(tok == NULL || strcasecmp(tok,"use") != 0)
{
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet.");
succp = false;
goto retblock;
}
@ -54,7 +54,7 @@ bool extract_database(GWBUF* buf, char* str)
tok = strtok_r(NULL," ;",&saved);
if(tok == NULL)
{
skygw_log_write(LOGFILE_ERROR,"Schemarouter: Malformed chage database packet.");
skygw_log_write(LOGFILE_ERROR,"extract_database: Malformed chage database packet.");
succp = false;
goto retblock;
}

View File

@ -319,7 +319,7 @@ parse_mapping_response(ROUTER_CLIENT_SES* rses, char* target, GWBUF* buf)
if(PTR_IS_RESULTSET(((unsigned char*)buf->start)) &&
modutil_count_signal_packets(buf,0,0,&more) == 2)
{
ptr = (char*)buf->start;
ptr = (unsigned char*)buf->start;
if(ptr[5] != 1)
{

View File

@ -1,9 +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"
#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