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);