Merge branch 'MXS-329-develop-20151111' into develop

This commit is contained in:
Markus Makela
2015-11-13 17:26:34 +02:00
57 changed files with 1654 additions and 1053 deletions

View File

@ -1,5 +1,5 @@
if(BUILD_TESTS OR BUILD_TOOLS)
add_library(fullcore STATIC adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c maxscale_pcre2.c)
add_library(fullcore STATIC random_jkiss.c adminusers.c atomic.c config.c buffer.c dbusers.c dcb.c filter.c gwbitmask.c gw_utils.c hashtable.c hint.c housekeeper.c load_utils.c memlog.c modutil.c monitor.c poll.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c gwdirs.c externcmd.c maxscale_pcre2.c)
if(WITH_JEMALLOC)
target_link_libraries(fullcore ${JEMALLOC_LIBRARIES})
elseif(WITH_TCMALLOC)
@ -12,7 +12,7 @@ add_executable(maxscale atomic.c buffer.c spinlock.c gateway.c
gw_utils.c utils.c dcb.c load_utils.c session.c service.c server.c
poll.c config.c users.c hashtable.c dbusers.c thread.c gwbitmask.c
monitor.c adminusers.c secrets.c filter.c modutil.c hint.c
housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c maxscale_pcre2.c)
housekeeper.c memlog.c resultset.c gwdirs.c externcmd.c random_jkiss.c maxscale_pcre2.c)
if(WITH_JEMALLOC)
target_link_libraries(maxscale ${JEMALLOC_LIBRARIES})
@ -23,11 +23,11 @@ endif()
target_link_libraries(maxscale ${EMBEDDED_LIB} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} log_manager utils ssl aio pthread crypt dl crypto inih z rt m stdc++)
install(TARGETS maxscale DESTINATION ${MAXSCALE_BINDIR})
add_executable(maxkeys maxkeys.c secrets.c utils.c gwdirs.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc)
add_executable(maxkeys maxkeys.c spinlock.c secrets.c utils.c gwdirs.c random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc)
target_link_libraries(maxkeys utils pthread crypt crypto)
install(TARGETS maxkeys DESTINATION ${MAXSCALE_BINDIR})
add_executable(maxpasswd maxpasswd.c secrets.c utils.c gwdirs.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc)
add_executable(maxpasswd maxpasswd.c spinlock.c secrets.c utils.c gwdirs.c random_jkiss.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc)
target_link_libraries(maxpasswd utils pthread crypt crypto)
install(TARGETS maxpasswd DESTINATION ${MAXSCALE_BINDIR})

View File

@ -35,7 +35,9 @@
* 15/07/2014 Mark Riddoch Addition of properties
* 28/08/2014 Mark Riddoch Adition of tail pointer to speed
* the gwbuf_append process
*
* 09/11/2015 Martin Brampton Add buffer tracing (conditional compilation),
* accessed by "show buffers" maxadmin command
*
* @endverbatim
*/
#include <stdlib.h>
@ -47,10 +49,23 @@
#include <log_manager.h>
#include <errno.h>
#if defined(BUFFER_TRACE)
#include <hashtable.h>
#include <execinfo.h>
static HASHTABLE *buffer_hashtable = NULL;
#endif
static buffer_object_t* gwbuf_remove_buffer_object(
GWBUF* buf,
buffer_object_t* bufobj);
#if defined(BUFFER_TRACE)
static void gwbuf_add_to_hashtable(GWBUF *buf);
static int bhashfn (void *key);
static int bcmpfn (void *key1, void *key2);
static void gwbuf_remove_from_hashtable(GWBUF *buf);
#endif
/**
* Allocate a new gateway buffer structure of size bytes.
@ -114,9 +129,111 @@ retblock:
"Error : Memory allocation failed due to %s.",
strerror_r(errno, errbuf, sizeof(errbuf)))));
}
#if defined(BUFFER_TRACE)
else
{
gwbuf_add_to_hashtable(rval);
}
#endif
return rval;
}
#if defined(BUFFER_TRACE)
/**
* Store a trace of buffer creation
*
* @param buf The buffer to record
*/
static void
gwbuf_add_to_hashtable(GWBUF *buf)
{
void *array[16];
size_t size, i, total;
char **strings;
char *tracetext;
size = backtrace (array, 16);
strings = backtrace_symbols (array, size);
total = (2 * size) + 1;
for (i = 0; i < size; i++)
{
total += strlen(strings[i]);
}
tracetext = (char *)malloc(total);
if (tracetext)
{
char *ptr = tracetext;
for (i = 0; i < size; i++)
{
sprintf(ptr, "\t%s\n", strings[i]);
ptr += (strlen(strings[i]) + 2);
}
free (strings);
if (NULL == buffer_hashtable)
{
buffer_hashtable = hashtable_alloc(10000, bhashfn, bcmpfn);
hashtable_memory_fns(buffer_hashtable,NULL,NULL,NULL,(HASHMEMORYFN)free);
}
hashtable_add(buffer_hashtable, buf, (void *)tracetext);
}
}
/**
* Hash a buffer (address) to an integer
*
* @param key The pointer to the buffer
*/
static int
bhashfn(void *key)
{
return (int)((uintptr_t) key % INT_MAX);
}
/**
* Compare two buffer keys (pointers)
*
* @param key1 The pointer to the first buffer
* @param key2 The pointer to the second buffer
*/
static int
bcmpfn(void *key1, void *key2)
{
return key1 == key2 ? 0 : 1;
}
/**
* Remove a buffer from the store of buffer traces
*
* @param buf The buffer to be removed
*/
static void
gwbuf_remove_from_hashtable(GWBUF *buf)
{
hashtable_delete(buffer_hashtable, buf);
}
/**
* Print all buffer traces via a given print DCB
*
* @param pdcb Print DCB for output
*/
void
dprintAllBuffers(void *pdcb)
{
void *buf;
char *backtrace;
HASHITERATOR *buffers = hashtable_iterator(buffer_hashtable);
while (NULL != (buf = hashtable_next(buffers)))
{
dcb_printf((DCB *)pdcb, "Buffer: %p\n", (void *)buf);
backtrace = hashtable_fetch(buffer_hashtable, buf);
dcb_printf((DCB *)pdcb, "%s", backtrace);
}
hashtable_iterator_free(buffers);
}
#endif
/**
* Free a gateway buffer
*
@ -157,6 +274,9 @@ BUF_PROPERTY *prop;
buf->hint = buf->hint->next;
hint_free(h);
}
#if defined(BUFFER_TRACE)
gwbuf_remove_from_hashtable(buf);
#endif
free(buf);
}
@ -196,6 +316,9 @@ GWBUF *rval;
rval->tail = rval;
rval->next = NULL;
CHK_GWBUF(rval);
#if defined(BUFFER_TRACE)
gwbuf_add_to_hashtable(rval);
#endif
return rval;
}
@ -263,6 +386,9 @@ GWBUF *gwbuf_clone_portion(
clonebuf->next = NULL;
clonebuf->tail = clonebuf;
CHK_GWBUF(clonebuf);
#if defined(BUFFER_TRACE)
gwbuf_add_to_hashtable(clonebuf);
#endif
return clonebuf;
}

View File

@ -1961,6 +1961,7 @@ static void *uh_keydup(void* key) {
if (current_key->resource)
rval->resource = strdup(current_key->resource);
else rval->resource = NULL;
return (void *) rval;
}

View File

@ -55,6 +55,10 @@
* fixes for various error situations,
* remove dcb_set_state etc, simplifications.
* 10/07/2015 Martin Brampton Simplify, merge dcb_read and dcb_read_n
* 04/09/2015 Martin Brampton Changes to ensure DCB always has session pointer
* 28/09/2015 Martin Brampton Add counters, maxima for DCBs and zombies
* 29/05/2015 Martin Brampton Impose locking in dcb_call_foreach callbacks
* 17/10/2015 Martin Brampton Add hangup for each and bitmask display MaxAdmin
*
* @endverbatim
*/
@ -83,7 +87,11 @@
#define SSL_ERRBUF_LEN 140
static DCB *allDCBs = NULL; /* Diagnostics need a list of DCBs */
static int nDCBs = 0;
static int maxDCBs = 0;
static DCB *zombies = NULL;
static int nzombies = 0;
static int maxzombies = 0;
static SPINLOCK dcbspin = SPINLOCK_INIT;
static SPINLOCK zombiespin = SPINLOCK_INIT;
@ -96,7 +104,7 @@ static int dcb_null_auth(DCB *dcb, SERVER *server, SESSION *session, GWBUF *buf
static inline int dcb_isvalid_nolock(DCB *dcb);
static inline DCB * dcb_find_in_list(DCB *dcb);
static inline void dcb_process_victim_queue(DCB *listofdcb);
static void dcb_close_finish(DCB *);
static void dcb_stop_polling_and_shutdown (DCB *dcb);
static bool dcb_maybe_add_persistent(DCB *);
static inline bool dcb_write_parameter_check(DCB *dcb, GWBUF *queue);
#if defined(FAKE_CODE)
@ -223,29 +231,23 @@ DCB *newdcb;
ptr = ptr->next;
ptr->next = newdcb;
}
nDCBs++;
if (nDCBs > maxDCBs) maxDCBs = nDCBs;
spinlock_release(&dcbspin);
return newdcb;
}
/**
* Free a DCB that has not been associated with a descriptor.
* Provided only for consistency, simply calls dcb_close to guarantee
* safe disposal of a DCB
*
* @param dcb The DCB to free
*/
void
dcb_free(DCB *dcb)
{
if (dcb->fd != DCBFD_CLOSED)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Attempt to free a DCB via dcb_free "
"that has been associated with a descriptor.")));
}
raise(SIGABRT);
/* Another statement to avoid a compiler warning */
dcb_final_free(dcb);
dcb_close(dcb);
}
/*
@ -295,10 +297,10 @@ dcb_final_free(DCB *dcb)
{
DCB_CALLBACK *cb;
CHK_DCB(dcb);
ss_info_dassert(dcb->state == DCB_STATE_DISCONNECTED ||
dcb->state == DCB_STATE_ALLOC,
"dcb not in DCB_STATE_DISCONNECTED not in DCB_STATE_ALLOC state.");
CHK_DCB(dcb);
ss_info_dassert(dcb->state == DCB_STATE_DISCONNECTED ||
dcb->state == DCB_STATE_ALLOC,
"dcb not in DCB_STATE_DISCONNECTED not in DCB_STATE_ALLOC state.");
if (DCB_POLL_BUSY(dcb))
{
@ -332,55 +334,59 @@ dcb_final_free(DCB *dcb)
if (ptr)
ptr->next = dcb->next;
}
spinlock_release(&dcbspin);
nDCBs--;
spinlock_release(&dcbspin);
if (dcb->session) {
/*<
* Terminate client session.
*/
{
SESSION *local_session = dcb->session;
dcb->session = NULL;
CHK_SESSION(local_session);
/**
* Set session's client pointer NULL so that other threads
* won't try to call dcb_close for client DCB
* after this call.
*/
if (local_session->client == dcb)
{
spinlock_acquire(&local_session->ses_lock);
local_session->client = NULL;
spinlock_release(&local_session->ses_lock);
}
session_free(local_session);
}
if (dcb->session) {
/*<
* Terminate client session.
*/
SESSION *local_session = dcb->session;
dcb->session = NULL;
CHK_SESSION(local_session);
/**
* Set session's client pointer NULL so that other threads
* won't try to call dcb_close for client DCB
* after this call.
*/
if (local_session->client == dcb)
{
spinlock_acquire(&local_session->ses_lock);
local_session->client = NULL;
spinlock_release(&local_session->ses_lock);
}
if (SESSION_STATE_DUMMY != local_session->state)
{
session_free(local_session);
}
}
if (dcb->protocol && (!DCB_IS_CLONE(dcb)))
free(dcb->protocol);
if (dcb->protoname)
free(dcb->protoname);
if (dcb->protoname)
free(dcb->protoname);
if (dcb->remote)
free(dcb->remote);
if (dcb->user)
free(dcb->user);
/* Clear write and read buffers */
if (dcb->delayq) {
GWBUF *queue = dcb->delayq;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
}
/* Clear write and read buffers */
if (dcb->delayq) {
GWBUF *queue = dcb->delayq;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
dcb->delayq = NULL;
}
if (dcb->writeq) {
GWBUF *queue = dcb->writeq;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
dcb->writeq = NULL;
}
if (dcb->dcb_readqueue)
{
GWBUF* queue = dcb->dcb_readqueue;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
}
if (dcb->dcb_readqueue)
{
GWBUF* queue = dcb->dcb_readqueue;
while ((queue = gwbuf_consume(queue, GWBUF_LENGTH(queue))) != NULL);
dcb->dcb_readqueue = NULL;
}
spinlock_acquire(&dcb->cb_lock);
while ((cb = dcb->callbacks) != NULL)
@ -410,10 +416,9 @@ dcb_final_free(DCB *dcb)
DCB *
dcb_process_zombies(int threadid)
{
DCB *zombiedcb, *previousdcb;
DCB *zombiedcb;
DCB *previousdcb = NULL, *nextdcb;
DCB *listofdcb = NULL;
DCB *dcb = NULL;
bool succp = false;
/**
* Perform a dirty read to see if there is anything in the queue.
@ -423,7 +428,9 @@ bool succp = false;
* dcb_final_free.
*/
if (!zombies)
{
return NULL;
}
/*
* Process the zombie queue and create a list of DCB's that can be
@ -436,11 +443,10 @@ bool succp = false;
*/
spinlock_acquire(&zombiespin);
zombiedcb = zombies;
previousdcb = NULL;
while (zombiedcb)
{
CHK_DCB(zombiedcb);
nextdcb = zombiedcb->memdata.next;
/*
* Skip processing of DCB's that are
* in the event queue waiting to be processed.
@ -448,7 +454,6 @@ bool succp = false;
if (zombiedcb->evq.next || zombiedcb->evq.prev)
{
previousdcb = zombiedcb;
zombiedcb = zombiedcb->memdata.next;
}
else
{
@ -467,22 +472,25 @@ bool succp = false;
* queue or NULL if the DCB is at the head of the
* queue. Remove zombiedcb from the zombies list.
*/
if (previousdcb == NULL)
if (NULL == previousdcb)
{
zombies = zombiedcb->memdata.next;
else
}
else
{
previousdcb->memdata.next = zombiedcb->memdata.next;
}
LOGIF(LD, (skygw_log_write_flush(
LOGFILE_DEBUG,
"%lu [dcb_process_zombies] Remove dcb "
"%lu [%s] Remove dcb "
"%p fd %d in state %s from the "
"list of zombies.",
pthread_self(),
__func__,
zombiedcb,
zombiedcb->fd,
STRDCBSTATE(zombiedcb->state))));
ss_info_dassert(zombiedcb->state == DCB_STATE_ZOMBIE,
"dcb not in DCB_STATE_ZOMBIE state.");
STRDCBSTATE(zombiedcb->state))));
/*<
* Move zombie dcb to linked list of victim dcbs.
* The variable dcb is used to hold the last DCB
@ -491,33 +499,25 @@ bool succp = false;
* (listofdcb) is not NULL, then it follows that
* dcb will also not be null.
*/
if (listofdcb == NULL)
{
listofdcb = zombiedcb;
}
else
{
dcb->memdata.next = zombiedcb;
}
/* Set dcb for next iteration of loop */
dcb = zombiedcb;
zombiedcb = zombiedcb->memdata.next;
/* After we've moved zombiedcb forward, set
link to null as dcb is last of the new list */
dcb->memdata.next = NULL;
nzombies--;
zombiedcb->memdata.next = listofdcb;
listofdcb = zombiedcb;
}
else
{
/* Since we didn't remove this dcb from the zombies
list, we need to advance the previous pointer */
/* Since we didn't remove this dcb from the zombies
list, we need to advance the previous pointer */
previousdcb = zombiedcb;
zombiedcb = zombiedcb->memdata.next;
}
}
zombiedcb = nextdcb;
}
spinlock_release(&zombiespin);
dcb_process_victim_queue(listofdcb);
if (listofdcb)
{
dcb_process_victim_queue(listofdcb);
}
return zombies;
}
@ -534,12 +534,64 @@ bool succp = false;
static inline void
dcb_process_victim_queue(DCB *listofdcb)
{
DCB *dcb;
DCB *dcb = listofdcb;
dcb = listofdcb;
while (dcb != NULL)
{
DCB *nextdcb = NULL;
DCB *nextdcb;
/*<
* Stop dcb's listening and modify state accordingly.
*/
spinlock_acquire(&dcb->dcb_initlock);
if (dcb->state == DCB_STATE_POLLING || dcb->state == DCB_STATE_LISTENING)
{
if (dcb->state == DCB_STATE_LISTENING)
{
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"%lu [%s] 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(),
__func__,
dcb,
STRDCBSTATE(dcb->state))));
}
else {
/* Must be DCB_STATE_POLLING */
spinlock_release(&dcb->dcb_initlock);
if (0 == dcb->persistentstart && dcb_maybe_add_persistent(dcb))
{
/* Have taken DCB into persistent pool, no further killing */
dcb = dcb->memdata.next;
continue;
}
else
{
DCB *nextdcb;
dcb_stop_polling_and_shutdown(dcb);
spinlock_acquire(&zombiespin);
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
nextdcb = dcb->memdata.next;
dcb->memdata.next = zombies;
zombies = dcb;
nzombies++;
if (nzombies > maxzombies) maxzombies = nzombies;
spinlock_release(&zombiespin);
dcb = nextdcb;
continue;
}
}
}
/*
* Into the final close logic, so if DCB is for backend server, we
* must decrement the number of current connections.
*/
if (dcb->server && 0 == dcb->persistentstart)
{
atomic_add(&dcb->server->stats.n_current, -1);
}
if (dcb->fd > 0)
{
/*<
@ -581,15 +633,36 @@ dcb_process_victim_queue(DCB *listofdcb)
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->state = DCB_STATE_DISCONNECTED;
nextdcb = dcb->memdata.next;
dcb_final_free(dcb);
dcb = nextdcb;
dcb->state = DCB_STATE_DISCONNECTED;
nextdcb = dcb->memdata.next;
spinlock_release(&dcb->dcb_initlock);
dcb_final_free(dcb);
dcb = nextdcb;
}
/** Reset threads session data */
LOGIF(LT, tls_log_info.li_sesid = 0);
}
/**
* Remove a DCB from the poll list and trigger shutdown mechanisms.
*
* @param dcb The DCB to be processed
*/
static void
dcb_stop_polling_and_shutdown (DCB *dcb)
{
poll_remove_dcb(dcb);
/**
* close protocol and router session
*/
if (dcb->func.close != NULL)
{
dcb->func.close(dcb);
}
/** Call possible callback for this DCB in case of close */
dcb_call_callback(dcb, DCB_REASON_CLOSE);
}
/**
* Connect to a server
*
@ -710,7 +783,6 @@ dcb_connect(SERVER *server, SESSION *session, const char *protocol)
session->client,
session->client->fd)));
}
ss_dassert(dcb->fd == DCBFD_CLOSED); /*< must be uninitialized at this point */
/**
* Successfully connected to backend. Assign file descriptor to dcb
*/
@ -1355,29 +1427,28 @@ dcb_write_SSL(DCB *dcb, GWBUF *queue)
#endif /* FAKE_CODE */
do
{
w = gw_write_SSL (dcb->ssl, GWBUF_DATA (queue), GWBUF_LENGTH (queue));
w = gw_write_SSL(dcb->ssl, GWBUF_DATA(queue), GWBUF_LENGTH(queue));
dcb->stats.n_writes++;
if (w <= 0)
{
int ssl_errno = SSL_get_error (dcb->ssl, w);
dcb_write_SSL_error_report (dcb, w, ssl_errno);
int ssl_errno = SSL_get_error(dcb->ssl, w);
dcb_write_SSL_error_report(dcb, w, ssl_errno);
if (ssl_errno != SSL_ERROR_WANT_WRITE)
{
atomic_add (&dcb->writeqlen, gwbuf_length (queue));
atomic_add(&dcb->writeqlen, gwbuf_length(queue));
dcb->stats.n_buffered++;
dcb_write_tidy_up (dcb, below_water);
dcb_write_tidy_up(dcb, below_water);
return 1;
}
#ifdef SS_DEBUG
else
{
skygw_log_write (LD, "SSL error: SSL_ERROR_WANT_WRITE, retrying SSL_write...");
skygw_log_write(LD, "SSL error: SSL_ERROR_WANT_WRITE, retrying SSL_write...");
}
#endif
}
}
while(w <= 0);
} while(w <= 0);
/** Remove written bytes from the queue */
queue = gwbuf_consume(queue, w);
@ -1705,17 +1776,6 @@ dcb_close(DCB *dcb)
{
CHK_DCB(dcb);
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)
{
@ -1726,7 +1786,7 @@ dcb_close(DCB *dcb)
pthread_self(),
dcb,
STRDCBSTATE(dcb->state))));
raise(SIGABRT);
raise(SIGABRT);
}
/**
@ -1738,43 +1798,44 @@ dcb_close(DCB *dcb)
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)
{
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))));
}
if ((dcb->state == DCB_STATE_POLLING && !dcb_maybe_add_persistent(dcb))
|| (dcb->state == DCB_STATE_LISTENING))
{
dcb_close_finish(dcb);
}
}
spinlock_acquire(&zombiespin);
if (dcb->state == DCB_STATE_NOPOLLING || dcb->state == DCB_STATE_ALLOC)
/*
* If DCB is in persistent pool, mark it as an error and exit
*/
if (dcb->persistentstart > 0)
{
dcb->dcb_errhandle_called = true;
return;
}
spinlock_acquire(&zombiespin);
if (!dcb->dcb_is_zombie)
{
if (0 == dcb->persistentstart && dcb->server && DCB_STATE_POLLING == dcb->state)
{
/* May be a candidate for persistence, so save user name */
char *user;
user = session_getUser(dcb->session);
if (user && strlen(user) && !dcb->user)
{
dcb->user = strdup(user);
}
}
/*<
* Add closing dcb to the top of the list.
* Add closing dcb to the top of the list, setting zombie marker
*/
dcb->dcb_is_zombie = true;
dcb->memdata.next = zombies;
zombies = dcb;
/*<
* Set state which indicates that it has been added to zombies
* list.
*/
dcb->state = DCB_STATE_ZOMBIE;
nzombies++;
if (nzombies > maxzombies) maxzombies = nzombies;
/*< Set bit for each maxscale thread. This should be done before
* the state is changed, so as to protect the DCB from premature
* destruction. */
if (dcb->server)
{
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
}
}
spinlock_release(&zombiespin);
}
@ -1789,25 +1850,44 @@ dcb_close(DCB *dcb)
static bool
dcb_maybe_add_persistent(DCB *dcb)
{
char *user;
int poolcount = -1;
user = session_getUser(dcb->session);
if (user
&& strlen(user)
if (dcb->user != NULL
&& strlen(dcb->user)
&& dcb->server
&& dcb->server->persistpoolmax
&& (dcb->server->status & SERVER_RUNNING)
&& !dcb->dcb_errhandle_called
&& !(dcb->flags & DCBF_HUNG)
&& (poolcount = dcb_persistent_clean_count(dcb, false)) < dcb->server->persistpoolmax)
{
DCB_CALLBACK *loopcallback;
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_maybe_add_persistent] Adding DCB to persistent pool, user %s.\n",
pthread_self(),
user)));
dcb->user = strdup(user);
dcb->user)));
dcb->dcb_is_zombie = false;
dcb->persistentstart = time(NULL);
session_unlink_dcb(dcb->session, dcb);
if (dcb->session)
/*<
* Terminate client session.
*/
{
SESSION *local_session = dcb->session;
session_set_dummy(dcb);
CHK_SESSION(local_session);
if (SESSION_STATE_DUMMY != local_session->state)
{
session_free(local_session);
}
}
spinlock_acquire(&dcb->cb_lock);
while ((loopcallback = dcb->callbacks) != NULL)
{
dcb->callbacks = loopcallback->next;
free(loopcallback);
}
spinlock_release(&dcb->cb_lock);
spinlock_acquire(&dcb->server->persistlock);
dcb->nextpersistent = dcb->server->persistent;
dcb->server->persistent = dcb;
@ -1820,58 +1900,21 @@ dcb_maybe_add_persistent(DCB *dcb)
{
LOGIF(LD, (skygw_log_write(
LOGFILE_DEBUG,
"%lu [dcb_maybe_add_persistent] Not adding DCB %p to persistent pool, user %s, "
"max for pool %d, error handle called %s, hung flag %s, pool count %d.\n",
"%lu [dcb_maybe_add_persistent] Not adding DCB %p to persistent pool, "
"user %s, max for pool %d, error handle called %s, hung flag %s, "
"server status %d, pool count %d.\n",
pthread_self(),
dcb,
user ? user : "",
dcb->user ? dcb->user : "",
(dcb->server && dcb->server->persistpoolmax) ? dcb->server->persistpoolmax : 0,
dcb->dcb_errhandle_called ? "true" : "false",
(dcb->flags & DCBF_HUNG) ? "true" : "false",
dcb->server ? dcb->server->status : 0,
poolcount)));
}
return false;
}
/**
* Final calls for DCB close
*
* @param dcb The DCB to print
*
*/
static void
dcb_close_finish(DCB *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))));
/**
* Do a consistency check, then adjust counter if not from persistent pool
*/
if (dcb->server)
{
if (dcb->server->persistent) CHK_DCB(dcb->server->persistent);
if (0 == dcb->persistentstart) atomic_add(&dcb->server->stats.n_current, -1);
}
/**
* close protocol and router session
*/
if (dcb->func.close != NULL)
{
dcb->func.close(dcb);
}
/** Call possible callback for this DCB in case of close */
dcb_call_callback(dcb, DCB_REASON_CLOSE);
}
/**
* Diagnostic to print a DCB
*
@ -1967,6 +2010,15 @@ dprintOneDCB(DCB *pdcb, DCB *dcb)
if (dcb->remote)
dcb_printf(pdcb, "\tConnected to: %s\n",
dcb->remote);
if (dcb->server)
{
if (dcb->server->name)
dcb_printf(pdcb, "\tServer name/IP: %s\n",
dcb->server->name);
if (dcb->server->port)
dcb_printf(pdcb, "\tPort number: %d\n",
dcb->server->port);
}
if (dcb->user)
dcb_printf(pdcb, "\tUsername: %s\n",
dcb->user);
@ -1988,6 +2040,15 @@ dprintOneDCB(DCB *pdcb, DCB *dcb)
dcb_printf(pdcb, "\tRole: %s\n", rolename);
free(rolename);
}
if (!bitmask_isallclear(&dcb->memdata.bitmask))
{
char *bitmasktext = bitmask_render_readable(&dcb->memdata.bitmask);
if (bitmasktext)
{
dcb_printf(pdcb, "\tBitMask: %s\n", bitmasktext);
free(bitmasktext);
}
}
dcb_printf(pdcb, "\tStatistics:\n");
dcb_printf(pdcb, "\t\tNo. of Reads: %d\n", dcb->stats.n_reads);
dcb_printf(pdcb, "\t\tNo. of Writes: %d\n", dcb->stats.n_writes);
@ -2197,13 +2258,11 @@ gw_dcb_state2string (int state)
case DCB_STATE_POLLING:
return "DCB in the polling loop";
case DCB_STATE_NOPOLLING:
return "DCB not in the polling loop";
return "DCB not in polling loop";
case DCB_STATE_LISTENING:
return "DCB for listening socket";
case DCB_STATE_DISCONNECTED:
return "DCB socket closed";
case DCB_STATE_FREED:
return "DCB memory could be freed";
case DCB_STATE_ZOMBIE:
return "DCB Zombie";
case DCB_STATE_UNDEFINED:
@ -2638,17 +2697,21 @@ dcb_call_foreach(struct server* server, DCB_REASON reason)
case DCB_REASON_NOT_RESPONDING:
{
DCB *dcb;
dcb = dcb_get_next(NULL);
spinlock_acquire(&dcbspin);
dcb = allDCBs;
while (dcb != NULL)
{
spinlock_acquire(&dcb->dcb_initlock);
if (dcb->state == DCB_STATE_POLLING && dcb->server &&
strcmp(dcb->server->unique_name,server->unique_name) == 0)
strcmp(dcb->server->unique_name,server->unique_name) == 0)
{
dcb_call_callback(dcb, DCB_REASON_NOT_RESPONDING);
}
dcb = dcb_get_next(dcb);
spinlock_release(&dcb->dcb_initlock);
dcb = dcb->next;
}
spinlock_release(&dcbspin);
break;
}
@ -2658,6 +2721,36 @@ dcb_call_foreach(struct server* server, DCB_REASON reason)
return;
}
/**
* Call all the callbacks on all DCB's that match the server and the reason given
*
* @param reason The DCB_REASON that triggers the callback
*/
void
dcb_hangup_foreach(struct server* server)
{
LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG,
"%lu [dcb_hangup_foreach]",
pthread_self())));
DCB *dcb;
spinlock_acquire(&dcbspin);
dcb = allDCBs;
while (dcb != NULL)
{
spinlock_acquire(&dcb->dcb_initlock);
if (dcb->state == DCB_STATE_POLLING && dcb->server &&
strcmp(dcb->server->unique_name,server->unique_name) == 0)
{
poll_fake_hangup_event(dcb);
}
spinlock_release(&dcb->dcb_initlock);
dcb = dcb->next;
}
spinlock_release(&dcbspin);
}
/**
* Null protocol write routine used for cloned dcb's. It merely consumes
@ -2731,9 +2824,11 @@ dcb_persistent_clean_count(DCB *dcb, bool cleanall)
CHK_DCB(persistentdcb);
nextdcb = persistentdcb->nextpersistent;
if (cleanall
|| persistentdcb-> dcb_errhandle_called
|| count >= server->persistpoolmax
|| (time(NULL) - persistentdcb->persistentstart) > server->persistmaxtime)
|| persistentdcb-> dcb_errhandle_called
|| count >= server->persistpoolmax
|| persistentdcb->server == NULL
|| !(persistentdcb->server->status & SERVER_RUNNING)
|| (time(NULL) - persistentdcb->persistentstart) > server->persistmaxtime)
{
/* Remove from persistent pool */
if (previousdcb) {
@ -2761,7 +2856,11 @@ dcb_persistent_clean_count(DCB *dcb, bool cleanall)
while (disposals)
{
nextdcb = disposals->nextpersistent;
dcb_close_finish(disposals);
disposals->persistentstart = -1;
if (DCB_STATE_POLLING == disposals->state)
{
dcb_stop_polling_and_shutdown(disposals);
}
dcb_close(disposals);
disposals = nextdcb;
}

View File

@ -17,6 +17,7 @@
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <gwbitmask.h>
/**
@ -47,10 +48,14 @@
* Date Who Description
* 28/06/13 Mark Riddoch Initial implementation
* 20/08/15 Martin Brampton Added caveats about limitations (above)
* 17/10/15 Martin Brampton Added display of bitmask
*
* @endverbatim
*/
static int bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit);
static int bitmask_count_bits_set(GWBITMASK *bitmask);
/**
* Initialise a bitmask
*
@ -151,19 +156,42 @@ unsigned char mask;
* Return a non-zero value if the bit at the specified bit
* position in the bitmask is set.
* The bitmask will automatically be extended if the bit is
* beyond the current bitmask length. This could be optimised
* by assuming that a bit beyond the length is unset.
* beyond the current bitmask length. The work is done in the function
* bitmask_isset_without_spinlock, which can be called when a spinlock
* has already been acquired.
*
* @param bitmask Pointer the bitmask
* @param bit Bit to clear
* @param bit Bit to test
*/
int
bitmask_isset(GWBITMASK *bitmask, int bit)
{
int result;
spinlock_acquire(&bitmask->lock);
result = bitmask_isset_without_spinlock(bitmask, bit);
spinlock_release(&bitmask->lock);
return result;
}
/**
* Return a non-zero value if the bit at the specified bit
* position in the bitmask is set. Should be called while holding a
* lock on the bitmask.
*
* The bitmask will automatically be extended if the bit is
* beyond the current bitmask length. This could be optimised
* by assuming that a bit beyond the length is unset.
*
* @param bitmask Pointer the bitmask
* @param bit Bit to test
*/
static int
bitmask_isset_without_spinlock(GWBITMASK *bitmask, int bit)
{
unsigned char *ptr;
unsigned char mask;
spinlock_acquire(&bitmask->lock);
if (bit >= bitmask->length)
{
bitmask->bits = realloc(bitmask->bits,
@ -174,7 +202,6 @@ unsigned char mask;
}
ptr = bitmask->bits + (bit / 8);
mask = 1 << (bit % 8);
spinlock_release(&bitmask->lock);
return *ptr & mask;
}
@ -239,3 +266,90 @@ bitmask_copy(GWBITMASK *dest, GWBITMASK *src)
spinlock_release(&src->lock);
}
/**
* Return a comma separated list of the numbers of the bits that are set in
* a bitmask, numbering starting at zero. Constrained to reject requests that
* could require more than three digit numbers. The returned string must be
* freed by the caller (unless it is null on account of memory allocation
* failure).
*
* @param bitmask Bitmap to make readable
* @return pointer to the newly allocated string, or null if no memory
*/
char *
bitmask_render_readable(GWBITMASK *bitmask)
{
char *toobig = "Bitmask is too large to render readable";
char *empty = "No bits are set";
char onebit[5];
char *result;
int count_set = 0;
spinlock_acquire(&bitmask->lock);
if (999 < bitmask->length)
{
result = malloc(strlen(toobig));
if (result)
{
strcpy(result, toobig);
}
}
else
{
count_set = bitmask_count_bits_set(bitmask);
if (count_set)
{
result = malloc(1 + (4 * count_set));
if (result)
{
result[0] = 0;
for (int i = 0; i<bitmask->length; i++)
{
if (bitmask_isset_without_spinlock(bitmask, i))
{
sprintf(onebit, "%d,", i);
strcat(result, onebit);
}
}
result[strlen(result)-1] = 0;
}
}
else
{
result = malloc(strlen(empty));
if (result)
{
strcpy(result, empty);
}
}
}
spinlock_release(&bitmask->lock);
return result;
}
/**
* Return a count of the number of bits set in a bitmask. Helpful for setting
* the size of string needed to show the set bits in readable form.
*
* @param bitmask Bitmap whose bits are to be counted
* @return int Number of set bits
*/
static int
bitmask_count_bits_set(GWBITMASK *bitmask)
{
const unsigned char oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
unsigned char partresults;
int result = 0;
unsigned char *ptr, *eptr;
ptr = bitmask->bits;
eptr = ptr + (bitmask->length / 8);
while (ptr < eptr)
{
partresults = oneBits[*ptr&0x0f];
partresults += oneBits[*ptr>>4];
result += partresults;
ptr++;
}
return result;
}

View File

@ -383,7 +383,7 @@ hashtable_fetch(HASHTABLE *table, void *key)
unsigned int hashkey;
HASHENTRIES *entry;
if(table == NULL || key == NULL)
if(table == NULL || key == NULL || 0 == table->hashsize)
return NULL;
hashkey = table->hashfn(key) % table->hashsize;
@ -766,7 +766,9 @@ char buf[40];
key = keyread(fd);
value = valueread(fd);
if (key == NULL || value == NULL)
break;
{
break;
}
hashtable_add(table, key, value);
rval++;
}

View File

@ -69,8 +69,7 @@ int max_poll_sleep;
* thread utilisation and fairer scheduling of the event
* processing.
* 07/07/15 Martin Brampton Simplified add and remove DCB, improve error handling.
* 23/08/15 Martin Brampton Provisionally added test so only DCB with a
* session link can be added to the poll list
* 23/08/15 Martin Brampton Added test so only DCB with a session link can be added to the poll list
*
* @endverbatim
*/
@ -91,7 +90,7 @@ static simple_mutex_t epoll_wait_mutex; /*< serializes calls to epoll_wait */
static int n_waiting = 0; /*< No. of threads in epoll_wait */
static int process_pollq(int thread_id);
static void poll_add_event_to_dcb(DCB* dcb, GWBUF* buf, __uint32_t ev);
static bool poll_dcb_session_check(DCB *dcb, const char *);
DCB *eventq = NULL;
SPINLOCK pollqlock = SPINLOCK_INIT;
@ -294,23 +293,6 @@ poll_add_dcb(DCB *dcb)
STRDCBSTATE(dcb->state))));
raise(SIGABRT);
}
/*
* This test could be wrong. On the face of it, we don't want to add a
* DCB to the poll list if it is not linked to a session because the code
* that handles events will expect to find a session. Test added by
* Martin as an experiment on 23 August 2015
*/
if (false && NULL == dcb->session)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [%s] Error : Attempt to add dcb %p "
"to poll list but it is not linked to a session, crashing.",
__func__,
pthread_self(),
dcb)));
raise(SIGABRT);
}
if (DCB_STATE_POLLING == dcb->state
|| DCB_STATE_LISTENING == dcb->state)
{
@ -380,10 +362,6 @@ poll_remove_dcb(DCB *dcb)
dcb,
STRDCBSTATE(dcb->state))));
}
/*< Set bit for each maxscale thread. This should be done before
* the state is changed, so as to protect the DCB from premature
* destruction. */
bitmask_copy(&dcb->memdata.bitmask, poll_bitmask());
/*<
* Set state to NOPOLLING and remove dcb from poll set.
*/
@ -877,8 +855,12 @@ unsigned long qtime;
#endif /* FAKE_CODE */
ss_debug(spinlock_acquire(&dcb->dcb_initlock);)
ss_dassert(dcb->state != DCB_STATE_ALLOC);
ss_dassert(dcb->state != DCB_STATE_DISCONNECTED);
ss_dassert(dcb->state != DCB_STATE_FREED);
/* It isn't obvious that this is impossible */
/* ss_dassert(dcb->state != DCB_STATE_DISCONNECTED); */
if (DCB_STATE_DISCONNECTED == dcb->state)
{
return 0;
}
ss_debug(spinlock_release(&dcb->dcb_initlock);)
LOGIF(LD, (skygw_log_write(
@ -902,7 +884,10 @@ unsigned long qtime;
dcb,
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->func.write_ready(dcb);
if (poll_dcb_session_check(dcb, "write_ready"))
{
dcb->func.write_ready(dcb);
}
} else {
char errbuf[STRERROR_BUFLEN];
LOGIF(LD, (skygw_log_write(
@ -933,7 +918,10 @@ unsigned long qtime;
dcb,
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->func.accept(dcb);
if (poll_dcb_session_check(dcb, "accept"))
{
dcb->func.accept(dcb);
}
}
else
{
@ -950,7 +938,10 @@ unsigned long qtime;
dcb,
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->func.read(dcb);
if (poll_dcb_session_check(dcb, "read"))
{
dcb->func.read(dcb);
}
}
}
if (ev & EPOLLERR)
@ -987,7 +978,10 @@ unsigned long qtime;
dcb,
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->func.error(dcb);
if (poll_dcb_session_check(dcb, "error"))
{
dcb->func.error(dcb);
}
}
if (ev & EPOLLHUP)
@ -1016,7 +1010,10 @@ unsigned long qtime;
dcb,
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->func.hangup(dcb);
if (poll_dcb_session_check(dcb, "hangup EPOLLHUP"))
{
dcb->func.hangup(dcb);
}
}
else
spinlock_release(&dcb->dcb_initlock);
@ -1049,7 +1046,10 @@ unsigned long qtime;
dcb,
&tls_log_info.li_sesid,
&tls_log_info.li_enabled_logs)));
dcb->func.hangup(dcb);
if (poll_dcb_session_check(dcb, "hangup EPOLLRDHUP"))
{
dcb->func.hangup(dcb);
}
}
else
spinlock_release(&dcb->dcb_initlock);
@ -1118,6 +1118,37 @@ unsigned long qtime;
}
/**
*
* Check that the DCB has a session link before processing.
* If not, log an error. Processing will be bypassed
*
* @param dcb The DCB to check
* @param function The name of the function about to be called
* @return bool Does the DCB have a non-null session link
*/
static bool
poll_dcb_session_check(DCB *dcb, const char *function)
{
if (dcb->session)
{
return true;
}
else
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"%lu [%s] The dcb %p that was about to be processed by %s does not "
"have a non-null session pointer ",
pthread_self(),
__func__,
dcb,
function)));
return false;
}
}
/**
*
* Shutdown the polling loop
*/
void
@ -1543,6 +1574,53 @@ uint32_t ev = EPOLLOUT;
spinlock_release(&pollqlock);
}
/*
* Insert a fake hangup event for a DCB into the polling queue.
*
* This is used when a monitor detects that a server is not responding.
*
* @param dcb DCB to emulate an EPOLLOUT event for
*/
void
poll_fake_hangup_event(DCB *dcb)
{
uint32_t ev = EPOLLRDHUP;
spinlock_acquire(&pollqlock);
if (DCB_POLL_BUSY(dcb))
{
if (dcb->evq.pending_events == 0)
pollStats.evq_pending++;
dcb->evq.pending_events |= ev;
}
else
{
dcb->evq.pending_events = ev;
dcb->evq.inserted = hkheartbeat;
if (eventq)
{
dcb->evq.prev = eventq->evq.prev;
eventq->evq.prev->evq.next = dcb;
eventq->evq.prev = dcb;
dcb->evq.next = eventq;
}
else
{
eventq = dcb;
dcb->evq.prev = dcb;
dcb->evq.next = dcb;
}
pollStats.evq_length++;
pollStats.evq_pending++;
dcb->evq.inserted = hkheartbeat;
if (pollStats.evq_length > pollStats.evq_max)
{
pollStats.evq_max = pollStats.evq_length;
}
}
spinlock_release(&pollqlock);
}
/**
* Print the event queue contents
*

131
server/core/random_jkiss.c Normal file
View File

@ -0,0 +1,131 @@
/*
* This file is distributed as part of the MariaDB Corporation MaxScale. It is free
* software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License as published by the Free Software Foundation,
* version 2.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright MariaDB Corporation Ab 2013-2014
*/
/**
* @file random_jkiss.c - Random number generator for the MariaDB Corporation MaxScale
*
* See http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf for discussion of random
* number generators (RNGs).
*
* @verbatim
* Revision History
*
* Date Who Description
* 26/08/15 Martin Brampton Initial implementation
*
* @endverbatim
*/
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <spinlock.h>
#include <random_jkiss.h>
/* Public domain code for JKISS RNG - Comment header added */
/* If possible, the seed variables will be set from /dev/urandom but
* should that fail, these arbitrary numbers will be used as a last resort.
*/
static unsigned int x = 123456789,y = 987654321,z = 43219876,c = 6543217; /* Seed variables */
static bool init = false;
static SPINLOCK random_jkiss_spinlock = SPINLOCK_INIT;
static unsigned int random_jkiss_devrand(void);
static void random_init_jkiss(void);
/***
*
* Return a pseudo-random number that satisfies major tests for random sequences
*
* @return uint Random number
*
*/
unsigned int
random_jkiss(void)
{
unsigned long long t;
unsigned int result;
spinlock_acquire(&random_jkiss_spinlock);
if (!init)
{
/* Must set init first because initialisation calls this function */
init = true;
spinlock_release(&random_jkiss_spinlock);
random_init_jkiss();
}
x = 314527869 * x + 1234567;
y ^= y << 5;
y ^= y >> 7;
y ^= y << 22;
t = 4294584393ULL * z + c;
c = t >> 32;
z = t;
result = x + y + z;
spinlock_release(&random_jkiss_spinlock);
return result;
}
/* Own code adapted from http://www0.cs.ucl.ac.uk/staff/d.jones/GoodPracticeRNG.pdf */
/***
*
* Obtain a seed random number from /dev/urandom if available.
*
* @return uint Random number
*
*/
static unsigned int
random_jkiss_devrand(void)
{
int fn;
unsigned int r;
if ((fn = open("/dev/urandom", O_RDONLY)) == -1) return 0;
if (read(fn, &r, sizeof(r)) != sizeof(r))
{
r = 0;
}
close(fn);
return r;
}
/***
*
* Initialise the generator using /dev/urandom if available, and warm up
* with 1000 iterations
*
*/
static void
random_init_jkiss(void)
{
int newrand, i;
spinlock_acquire(&random_jkiss_spinlock);
if ((newrand = random_jkiss_devrand()) != 0) x = newrand;
if ((newrand = random_jkiss_devrand()) != 0) y = newrand;
if ((newrand = random_jkiss_devrand()) != 0) z = newrand;
if ((newrand = random_jkiss_devrand()) != 0)
c = newrand % 698769068 + 1; /* Should be less than 698769069 */
spinlock_release(&random_jkiss_spinlock);
/* "Warm up" our random number generator */
for (i = 0; i < 100; i++) random_jkiss();
}

View File

@ -23,6 +23,7 @@
#include <ctype.h>
#include <mysql_client_server_protocol.h>
#include <gwdirs.h>
#include <random_jkiss.h>
/**
* Generate a random printable character
@ -32,16 +33,13 @@
static unsigned char
secrets_randomchar()
{
return (char)((rand() % ('~' - ' ')) + ' ');
return(char) ((random_jkiss() % ('~' - ' ')) + ' ');
}
static int
secrets_random_str(unsigned char *output, int len)
{
int i;
srand((unsigned long )time(0L) ^ (unsigned long )output);
for (i = 0; i < len; ++i)
for (int i = 0; i < len; ++i)
{
output[i] = secrets_randomchar();
}
@ -53,12 +51,12 @@ secrets_random_str(unsigned char *output, int len)
* and the AES Init Vector.
* If the path parameter is not null the custom path is interpreted as a folder
* containing the .secrets file. Otherwise the default location is used.
* @return The keys structure or NULL on error
* @return The keys structure or NULL on error
*/
static MAXKEYS *
secrets_readKeys(const char* path)
{
char secret_file[PATH_MAX+1];
char secret_file[PATH_MAX + 1];
char *home;
MAXKEYS *keys;
struct stat secret_stats;
@ -74,7 +72,6 @@ secrets_readKeys(const char* path)
{
snprintf(secret_file, PATH_MAX, "%s/.secrets", get_datadir());
}
/* Try to access secrets file */
if (access(secret_file, R_OK) == -1)
{
@ -85,25 +82,23 @@ secrets_readKeys(const char* path)
if (!reported)
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LM, (skygw_log_write(
LOGFILE_MESSAGE,
"Encrypted password file %s can't be accessed "
"(%s). Password encryption is not used.",
secret_file,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LM, (skygw_log_write(LOGFILE_MESSAGE,
"Encrypted password file %s can't be accessed "
"(%s). Password encryption is not used.",
secret_file,
strerror_r(eno, errbuf, sizeof(errbuf)))));
reported = 1;
}
}
else
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : access for secrets file "
"[%s] failed. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : access for secrets file "
"[%s] failed. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
}
return NULL;
}
@ -114,30 +109,29 @@ secrets_readKeys(const char* path)
int eno = errno;
errno = 0;
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed opening secret "
"file [%s]. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : Failed opening secret "
"file [%s]. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
return NULL;
}
/* accessing file details */
if (fstat(fd, &secret_stats) < 0) {
if (fstat(fd, &secret_stats) < 0)
{
int eno = errno;
errno = 0;
close(fd);
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : fstat for secret file %s "
"failed. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : fstat for secret file %s "
"failed. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
return NULL;
}
@ -147,34 +141,30 @@ secrets_readKeys(const char* path)
errno = 0;
close(fd);
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Secrets file %s has "
"incorrect size. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : Secrets file %s has "
"incorrect size. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
return NULL;
}
if (secret_stats.st_mode != (S_IRUSR | S_IFREG))
{
close(fd);
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : Ignoring secrets file "
"%s, invalid permissions.",
secret_file)));
return NULL;
}
if (secret_stats.st_mode != (S_IRUSR|S_IFREG))
if ((keys = (MAXKEYS *) malloc(sizeof(MAXKEYS))) == NULL)
{
close(fd);
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Ignoring secrets file "
"%s, invalid permissions.",
secret_file)));
return NULL;
}
if ((keys = (MAXKEYS *)malloc(sizeof(MAXKEYS))) == NULL)
{
close(fd);
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Memory allocation failed "
"for key structure.")));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : Memory allocation failed "
"for key structure.")));
return NULL;
}
@ -191,31 +181,30 @@ secrets_readKeys(const char* path)
close(fd);
free(keys);
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Read from secrets file "
"%s failed. Read %d, expected %d bytes. Error %d, %s.",
secret_file,
len,
sizeof(MAXKEYS),
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : Read from secrets file "
"%s failed. Read %d, expected %d bytes. Error %d, %s.",
secret_file,
len,
sizeof(MAXKEYS),
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
return NULL;
}
/* Close the file */
if (close(fd) < 0) {
if (close(fd) < 0)
{
int eno = errno;
errno = 0;
free(keys);
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed closing the "
"secrets file %s. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : Failed closing the "
"secrets file %s. Error %d, %s.",
secret_file,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)))));
return NULL;
}
ss_dassert(keys != NULL);
@ -233,31 +222,30 @@ secrets_readKeys(const char* path)
*/
int secrets_writeKeys(const char *path)
{
int fd,randfd;
unsigned int randval;
MAXKEYS key;
char secret_file[PATH_MAX + 10];
int fd, randfd;
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.");
skygw_log_write(LOGFILE_ERROR, "Error: Pathname too long.");
return 1;
}
snprintf(secret_file,PATH_MAX + 9,"%s/.secrets",path);
snprintf(secret_file, PATH_MAX + 9, "%s/.secrets", path);
secret_file[PATH_MAX + 9] = '\0';
/* Open for writing | Create | Truncate the file for writing */
if ((fd = open(secret_file, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR)) < 0)
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed opening secret "
"file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : failed opening secret "
"file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
return 1;
}
@ -265,27 +253,24 @@ int secrets_writeKeys(const char *path)
if ((randfd = open("/dev/random", O_RDONLY)) < 0)
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed opening /dev/random. Error %d, %s.",
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : failed opening /dev/random. Error %d, %s.",
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
close(fd);
return 1;
}
if (read(randfd,(void*)&randval,sizeof(unsigned int)) < 1)
if (read(randfd, (void*) &randval, sizeof(unsigned int)) < 1)
{
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed to read /dev/random.")));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : failed to read /dev/random.")));
close(fd);
close(randfd);
return 1;
}
close(randfd);
srand(randval);
secrets_random_str(key.enckey, MAXSCALE_KEYLEN);
secrets_random_str(key.initvector, MAXSCALE_IV_LEN);
@ -293,13 +278,12 @@ int secrets_writeKeys(const char *path)
if (write(fd, &key, sizeof(key)) < 0)
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed writing into "
"secret file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : failed writing into "
"secret file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
close(fd);
return 1;
}
@ -308,25 +292,23 @@ int secrets_writeKeys(const char *path)
if (close(fd) < 0)
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed closing the "
"secret file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : failed closing the "
"secret file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
}
if (chmod(secret_file, S_IRUSR) < 0)
{
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : failed to change the permissions of the"
"secret file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,
"Error : failed to change the permissions of the"
"secret file [%s]. Error %d, %s.",
secret_file,
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
}
return 0;
@ -341,17 +323,17 @@ int secrets_writeKeys(const char *path)
* Note the return is always a malloc'd string that the caller must free
*
* @param crypt The encrypted password
* @return The decrypted password
* @return The decrypted password
*/
char *
decryptPassword(const char *crypt)
{
MAXKEYS *keys;
AES_KEY aeskey;
unsigned char *plain;
const char *ptr;
unsigned char encrypted[80];
int enlen;
MAXKEYS *keys;
AES_KEY aeskey;
unsigned char *plain;
const char *ptr;
unsigned char encrypted[80];
int enlen;
keys = secrets_readKeys(NULL);
if (!keys)
@ -359,9 +341,9 @@ decryptPassword(const char *crypt)
return strdup(crypt);
}
/*
** If the input is not a HEX string return the input
** it probably was not encrypted
*/
** If the input is not a HEX string return the input
** it probably was not encrypted
*/
for (ptr = crypt; *ptr; ptr++)
{
if (!isxdigit(*ptr))
@ -374,7 +356,7 @@ decryptPassword(const char *crypt)
enlen = strlen(crypt) / 2;
gw_hex2bin(encrypted, crypt, strlen(crypt));
if ((plain = (unsigned char *)malloc(80)) == NULL)
if ((plain = (unsigned char *) malloc(80)) == NULL)
{
free(keys);
return NULL;
@ -385,26 +367,26 @@ decryptPassword(const char *crypt)
AES_cbc_encrypt(encrypted, plain, enlen, &aeskey, keys->initvector, AES_DECRYPT);
free(keys);
return (char *)plain;
return(char *) plain;
}
/**
* Encrypt a password that can be stored in the MaxScale configuration file.
*
* Note the return is always a malloc'd string that the caller must free
* @param path Path the the .secrets file
* @param password The password to encrypt
* @return The encrypted password
* @param path Path the the .secrets file
* @param password The password to encrypt
* @return The encrypted password
*/
char *
encryptPassword(const char* path, const char *password)
{
MAXKEYS *keys;
AES_KEY aeskey;
int padded_len;
char *hex_output;
unsigned char padded_passwd[80];
unsigned char encrypted[80];
MAXKEYS *keys;
AES_KEY aeskey;
int padded_len;
char *hex_output;
unsigned char padded_passwd[80];
unsigned char encrypted[80];
if ((keys = secrets_readKeys(path)) == NULL)
{
@ -412,13 +394,13 @@ encryptPassword(const char* path, const char *password)
}
memset(padded_passwd, 0, 80);
strncpy((char *)padded_passwd, password, 79);
strncpy((char *) padded_passwd, password, 79);
padded_len = ((strlen(password) / AES_BLOCK_SIZE) + 1) * AES_BLOCK_SIZE;
AES_set_encrypt_key(keys->enckey, 8 * MAXSCALE_KEYLEN, &aeskey);
AES_cbc_encrypt(padded_passwd, encrypted, padded_len, &aeskey, keys->initvector, AES_ENCRYPT);
hex_output = (char *)malloc(padded_len * 2);
hex_output = (char *) malloc(padded_len * 2);
gw_bin2hex(hex_output, encrypted, padded_len);
free(keys);

View File

@ -150,7 +150,10 @@ server_get_persistent(SERVER *server, char *user, const char *protocol)
{
DCB *dcb, *previous = NULL;
if (server->persistent && dcb_persistent_clean_count(server->persistent, false) && server->persistent)
if (server->persistent
&& dcb_persistent_clean_count(server->persistent, false)
&& server->persistent
&& (server->status & SERVER_RUNNING))
{
spinlock_acquire(&server->persistlock);
dcb = server->persistent;

View File

@ -258,8 +258,7 @@ GWPROTOCOL *funcs;
}
if (loaded == -1)
{
hashtable_free(service->users->data);
free(service->users);
users_free(service->users);
service->users = NULL;
dcb_close(port->listener);
port->listener = NULL;
@ -347,6 +346,7 @@ GWPROTOCOL *funcs;
== NULL)
{
users_free(service->users);
service->users = NULL;
dcb_close(port->listener);
service->users = NULL;
port->listener = NULL;
@ -359,7 +359,6 @@ GWPROTOCOL *funcs;
goto retblock;
}
memcpy(&(port->listener->func), funcs, sizeof(GWPROTOCOL));
port->listener->session = NULL;
if (port->address)
sprintf(config_bind, "%s:%d", port->address, port->port);
@ -383,6 +382,7 @@ GWPROTOCOL *funcs;
service->name)));
users_free(service->users);
service->users = NULL;
dcb_close(port->listener);
port->listener = NULL;
service->users = NULL;

View File

@ -27,6 +27,7 @@
* 02/09/13 Massimiliano Pinto Added session refcounter
* 29/05/14 Mark Riddoch Addition of filter mechanism
* 23/08/15 Martin Brampton Tidying; slight improvement in safety
* 17/09/15 Martin Brampton Keep failed session in existence - leave DCBs to close
*
* @endverbatim
*/
@ -51,8 +52,10 @@ static size_t session_id;
static SPINLOCK session_spin = SPINLOCK_INIT;
static SESSION *allSessions = NULL;
static struct session session_dummy_struct;
static int session_setup_filters(SESSION *session);
static void session_simple_free(SESSION *session, DCB *dcb);
/**
* Allocate a new session for a new client of the specified service.
@ -71,55 +74,56 @@ session_alloc(SERVICE *service, DCB *client_dcb)
SESSION *session;
session = (SESSION *)calloc(1, sizeof(SESSION));
ss_info_dassert(session != NULL,
"Allocating memory for session failed.");
ss_info_dassert(session != NULL, "Allocating memory for session failed.");
if (session == NULL)
{
char errbuf[STRERROR_BUFLEN];
char errbuf[STRERROR_BUFLEN];
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed to allocate memory for "
"session object due error %d, %s.",
errno,
strerror_r(errno, errbuf, sizeof(errbuf)))));
/* Does this possibly need a lock? */
/*
* This is really not the right way to do this. The data in a DCB is
* router specific and should be freed by a function in the relevant
* router. This would be better achieved by placing a function reference
* in the DCB and having dcb_final_free call it to dispose of the data
* at the final destruction of the DCB. However, this piece of code is
* only run following a calloc failure, so the system is probably on
* the point of crashing anyway.
*
*/
if (client_dcb->data && !DCB_IS_CLONE(client_dcb))
{
void *clientdata = client_dcb->data;
void * clientdata = client_dcb->data;
client_dcb->data = NULL;
free(clientdata);
}
goto return_session;
return NULL;
}
#if defined(SS_DEBUG)
session->ses_chk_top = CHK_NUM_SESSION;
session->ses_chk_tail = CHK_NUM_SESSION;
#endif
if (DCB_IS_CLONE(client_dcb))
{
session->ses_is_child = true;
}
session->ses_is_child = (bool) DCB_IS_CLONE(client_dcb);
spinlock_init(&session->ses_lock);
/*<
* Prevent backend threads from accessing before session is completely
* initialized.
*/
spinlock_acquire(&session->ses_lock);
session->service = service;
session->client = client_dcb;
session->n_filters = 0;
memset(&session->stats, 0, sizeof(SESSION_STATS));
session->stats.connect = time(0);
session->state = SESSION_STATE_ALLOC;
session->client = client_dcb;
session->n_filters = 0;
memset(&session->stats, 0, sizeof(SESSION_STATS));
session->stats.connect = time(0);
session->state = SESSION_STATE_ALLOC;
/*<
* Associate the session to the client DCB and set the reference count on
* the session to indicate that there is a single reference to the
* Associate the session to the client DCB and set the reference count on
* the session to indicate that there is a single reference to the
* session. There is no need to protect this or use atomic add as the
* session has not been made available to the other threads at this
* point.
*/
session->data = client_dcb->data;
client_dcb->session = session;
session->refcount = 1;
/*<
* This indicates that session is ready to be shared with backend
@ -127,139 +131,145 @@ session_alloc(SERVICE *service, DCB *client_dcb)
*/
session->state = SESSION_STATE_READY;
/*< Release session lock */
spinlock_release(&session->ses_lock);
/*
* Only create a router session if we are not the listening
* DCB or an internal DCB. Creating a router session may create a connection to a
* backend server, depending upon the router module implementation
* and should be avoided for the listener session
*
* Router session creation may create other DCBs that link to the
* session, therefore it is important that the session lock is
/*
* Only create a router session if we are not the listening
* DCB or an internal DCB. Creating a router session may create a connection to a
* backend server, depending upon the router module implementation
* and should be avoided for the listener session
*
* Router session creation may create other DCBs that link to the
* session, therefore it is important that the session lock is
* relinquished before the router call.
*/
if (client_dcb->state != DCB_STATE_LISTENING &&
*/
if (client_dcb->state != DCB_STATE_LISTENING &&
client_dcb->dcb_role != DCB_ROLE_INTERNAL)
{
session->router_session =
service->router->newSession(service->router_instance, session);
{
session->router_session = service->router->newSession(service->router_instance, session);
if (session->router_session == NULL)
{
/**
* Inform other threads that session is closing.
*/
session->state = SESSION_STATE_STOPPING;
/*<
* Decrease refcount, set dcb's session pointer NULL
* and set session pointer to NULL.
*/
session->client = NULL;
session_free(session);
client_dcb->session = NULL;
session = NULL;
{
session->state = SESSION_STATE_TO_BE_FREED;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed to create %s session.",
"%lu [%s] Error : Failed to create %s session because router"
"could not establish a new router session, see earlier error.",
pthread_self(),
__func__,
service->name)));
goto return_session;
}
/*
* Pending filter chain being setup set the head of the chain to
* be the router. As filters are inserted the current head will
* be pushed to the filter and the head updated.
*
* NB This dictates that filters are created starting at the end
* of the chain nearest the router working back to the client
* protocol end of the chain.
*/
session->head.instance = service->router_instance;
session->head.session = session->router_session;
/*
* Pending filter chain being setup set the head of the chain to
* be the router. As filters are inserted the current head will
* be pushed to the filter and the head updated.
*
* NB This dictates that filters are created starting at the end
* of the chain nearest the router working back to the client
* protocol end of the chain.
*/
session->head.instance = service->router_instance;
session->head.session = session->router_session;
session->head.routeQuery = (void *)(service->router->routeQuery);
session->head.routeQuery = (void *)(service->router->routeQuery);
session->tail.instance = session;
session->tail.session = session;
session->tail.clientReply = session_reply;
session->tail.instance = session;
session->tail.session = session;
session->tail.clientReply = session_reply;
if (service->n_filters > 0)
{
if (!session_setup_filters(session))
{
/**
* Inform other threads that session is closing.
*/
session->state = SESSION_STATE_STOPPING;
/*<
* Decrease refcount, set dcb's session pointer NULL
* and set session pointer to NULL.
*/
session->client = NULL;
session_free(session);
client_dcb->session = NULL;
session = NULL;
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"Error : Setting up filters failed. "
"Terminating session %s.",
service->name)));
goto return_session;
}
}
}
spinlock_acquire(&session->ses_lock);
if (session->state != SESSION_STATE_READY)
if (SESSION_STATE_TO_BE_FREED != session->state
&& service->n_filters > 0
&& !session_setup_filters(session))
{
spinlock_release(&session->ses_lock);
session->client = NULL;
session_free(session);
client_dcb->session = NULL;
session = NULL;
LOGIF(LE, (skygw_log_write_flush(
LOGFILE_ERROR,
"Error : Failed to create %s session.",
service->name)));
spinlock_release(&session_spin);
session->state = SESSION_STATE_TO_BE_FREED;
LOGIF(LE, (skygw_log_write(
LOGFILE_ERROR,
"Error : Setting up filters failed. "
"Terminating session %s.",
service->name)));
}
}
if (SESSION_STATE_TO_BE_FREED != session->state)
{
session->state = SESSION_STATE_ROUTER_READY;
if (session->client->user == NULL)
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Started session [%lu] for %s service ",
session->ses_id,
service->name)));
}
else
{
session->state = SESSION_STATE_ROUTER_READY;
spinlock_release(&session->ses_lock);
spinlock_acquire(&session_spin);
/** Assign a session id and increase */
session->ses_id = ++session_id;
session->next = allSessions;
allSessions = session;
spinlock_release(&session_spin);
if (session->client->user == NULL)
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Started session [%lu] for %s service ",
session->ses_id,
service->name)));
}
else
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Started %s client session [%lu] for '%s' from %s",
service->name,
session->ses_id,
session->client->user,
session->client->remote)));
}
atomic_add(&service->stats.n_sessions, 1);
atomic_add(&service->stats.n_current, 1);
CHK_SESSION(session);
}
return_session:
return session;
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Started %s client session [%lu] for '%s' from %s",
service->name,
session->ses_id,
session->client->user,
session->client->remote)));
}
}
else
{
LOGIF(LT, (skygw_log_write(
LOGFILE_TRACE,
"Start %s client session [%lu] for '%s' from %s failed, will be "
"closed as soon as all related DCBs have been closed.",
service->name,
session->ses_id,
session->client->user,
session->client->remote)));
}
spinlock_acquire(&session_spin);
/** Assign a session id and increase, insert session into list */
session->ses_id = ++session_id;
session->next = allSessions;
allSessions = session;
spinlock_release(&session_spin);
atomic_add(&service->stats.n_sessions, 1);
atomic_add(&service->stats.n_current, 1);
CHK_SESSION(session);
client_dcb->session = session;
return SESSION_STATE_TO_BE_FREED == session->state ? NULL : session;
}
/**
* Allocate a dummy session so that DCBs can always have sessions.
*
* Only one dummy session exists, it is statically declared
*
* @param client_dcb The client side DCB
* @return The dummy created session
*/
SESSION *
session_set_dummy(DCB *client_dcb)
{
SESSION *session;
session = &session_dummy_struct;
#if defined(SS_DEBUG)
session->ses_chk_top = CHK_NUM_SESSION;
session->ses_chk_tail = CHK_NUM_SESSION;
#endif
session->ses_is_child = false;
spinlock_init(&session->ses_lock);
session->service = NULL;
session->client = NULL;
session->n_filters = 0;
memset(&session->stats, 0, sizeof(SESSION_STATS));
session->stats.connect = 0;
session->state = SESSION_STATE_DUMMY;
session->data = NULL;
session->refcount = 1;
session->ses_id = 0;
session->next = NULL;
client_dcb->session = session;
return session;
}
/**
@ -356,30 +366,67 @@ int session_unlink_dcb(
return nlink;
}
/**
* Deallocate the specified session, minimal actions during session_alloc
* Since changes to keep new session in existence until all related DCBs
* have been destroyed, this function is redundant. Just left until we are
* sure of the direction taken.
*
* @param session The session to deallocate
*/
static void
session_simple_free(SESSION *session, DCB *dcb)
{
/* Does this possibly need a lock? */
if (dcb->data && !DCB_IS_CLONE(dcb))
{
void * clientdata = dcb->data;
dcb->data = NULL;
free(clientdata);
}
if (session)
{
if (SESSION_STATE_DUMMY == session->state)
{
return;
}
if (session && session->router_session)
{
session->service->router->freeSession(
session->service->router_instance,
session->router_session);
}
session->state = SESSION_STATE_STOPPING;
}
free(session);
}
/**
* Deallocate the specified session
*
* @param session The session to deallocate
*/
bool session_free(
SESSION *session)
bool
session_free(SESSION *session)
{
bool succp = false;
SESSION *ptr;
int nlink;
int i;
if (session && SESSION_STATE_DUMMY == session->state)
{
return true;
}
CHK_SESSION(session);
/*<
/*
* Remove one reference. If there are no references left,
* free session.
*/
nlink = session_unlink_dcb(session, NULL);
if (nlink != 0) {
ss_dassert(nlink > 0);
goto return_succp;
if (atomic_add(&session->refcount, -1) > 1)
{
/* Must be one or more references left */
return false;
}
session->state = SESSION_STATE_TO_BE_FREED;
/* First of all remove from the linked list */
spinlock_acquire(&session_spin);
@ -389,13 +436,16 @@ bool session_free(
}
else
{
ptr = allSessions;
while (ptr && ptr->next != session)
SESSION *chksession;
chksession = allSessions;
while (chksession && chksession->next != session)
{
ptr = ptr->next;
chksession = chksession->next;
}
if (ptr)
ptr->next = session->next;
if (chksession)
{
chksession->next = session->next;
}
}
spinlock_release(&session_spin);
atomic_add(&session->service->stats.n_current, -1);
@ -412,6 +462,7 @@ bool session_free(
}
if (session->n_filters)
{
int i;
for (i = 0; i < session->n_filters; i++)
{
if (session->filters[i].filter)
@ -449,10 +500,7 @@ bool session_free(
}
free(session);
}
succp = true;
return_succp :
return succp;
return true;
}
/**
@ -662,11 +710,11 @@ int i;
ptr->client->user?ptr->client->user:"",
ptr->client->user?"@":"",
ptr->client->remote);
dcb_printf(dcb, "\tConnected: %s",
dcb_printf(dcb, "\tConnected: %s\n",
asctime_r(localtime_r(&ptr->stats.connect, &result), buf));
if(ptr->client->state == DCB_STATE_POLLING)
{
dcb_printf(dcb, "\tIdle: %.0f seconds",idle);
dcb_printf(dcb, "\tIdle: %.0f seconds\n",idle);
}
}
@ -734,6 +782,8 @@ session_state(int state)
{
case SESSION_STATE_ALLOC:
return "Session Allocated";
case SESSION_STATE_DUMMY:
return "Dummy Session";
case SESSION_STATE_READY:
return "Session Ready";
case SESSION_STATE_ROUTER_READY:

View File

@ -1,10 +1,10 @@
execute_process(COMMAND ${CMAKE_COMMAND} -E copy ${ERRMSG} ${CMAKE_CURRENT_BINARY_DIR})
add_executable(test_mysql_users test_mysql_users.c)
add_executable(test_hash testhash.c)
add_executable(test_hint testhint.c)
add_executable(test_spinlock testspinlock.c)
add_executable(test_hash testhash.c ../random_jkiss.c)
add_executable(test_hint testhint.c ../random_jkiss.c)
add_executable(test_spinlock testspinlock.c ../random_jkiss.c)
add_executable(test_filter testfilter.c)
add_executable(test_buffer testbuffer.c)
add_executable(test_buffer testbuffer.c ../random_jkiss.c)
add_executable(test_dcb testdcb.c)
add_executable(test_modutil testmodutil.c)
add_executable(test_poll testpoll.c)
@ -12,9 +12,9 @@ add_executable(test_service testservice.c)
add_executable(test_server testserver.c)
add_executable(test_users testusers.c)
add_executable(test_adminusers testadminusers.c)
add_executable(testmemlog testmemlog.c)
add_executable(testmemlog testmemlog.c ../random_jkiss.c)
add_executable(testfeedback testfeedback.c)
add_executable(testmaxscalepcre2 testmaxscalepcre2.c)
add_executable(testmaxscalepcre2 testmaxscalepcre2.c ../random_jkiss.c)
target_link_libraries(test_mysql_users MySQLClient fullcore)
target_link_libraries(test_hash fullcore log_manager)
target_link_libraries(test_hint fullcore log_manager)

View File

@ -84,28 +84,6 @@ init_test_env(NULL);
result = serviceStartAll();
mxs_log_flush_sync();
ss_info_dassert(0 != result, "Start all should succeed");
ss_dfprintf(stderr, "\t..done\nTiming out a session.");
service->conn_timeout = 1;
result = serviceStart(service);
mxs_log_flush_sync();
ss_info_dassert(0 != result, "Start should succeed");
serviceStop(service);
mxs_log_flush_sync();
ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed");
if((dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER)) == NULL)
return 1;
ss_info_dassert(dcb != NULL, "DCB allocation failed");
session = session_alloc(service,dcb);
ss_info_dassert(session != NULL, "Session allocation failed");
dcb->state = DCB_STATE_POLLING;
sleep(15);
ss_info_dassert(dcb->state != DCB_STATE_POLLING, "Session timeout failed");
ss_dfprintf(stderr, "\t..done\nStopping Service.");
serviceStop(service);
ss_info_dassert(service->state == SERVICE_STATE_STOPPED, "Stop should succeed");

View File

@ -66,7 +66,7 @@ int result;
sleep(10);
poll_shutdown();
ss_dfprintf(stderr, "\t..done\nTidy up.");
dcb_free(dcb);
dcb_close(dcb);
ss_dfprintf(stderr, "\t..done\n");
return 0;

View File

@ -43,6 +43,7 @@
#include <skygw_utils.h>
#include <log_manager.h>
#include <secrets.h>
#include <random_jkiss.h>
/* used in the hex2bin function */
#define char_val(X) (X >= '0' && X <= '9' ? X-'0' :\
@ -97,7 +98,7 @@ char *gw_strend(register const char *s) {
* generate a random char
*****************************************/
static char gw_randomchar() {
return (char)((rand() % 78) + 30);
return (char)((random_jkiss() % 78) + 30);
}
/*****************************************
@ -107,7 +108,6 @@ static char gw_randomchar() {
int gw_generate_random_str(char *output, int len) {
int i;
srand(time(0L));
for ( i = 0; i < len; ++i ) {
output[i] = gw_randomchar();