Merge branch 'MXS-329-develop-20151111' into develop
This commit is contained in:
@ -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})
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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++;
|
||||
}
|
||||
|
||||
@ -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
131
server/core/random_jkiss.c
Normal 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();
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
Reference in New Issue
Block a user