Merge branch 'develop' of github.com:skysql/MaxScale into develop
This commit is contained in:
commit
e967aca2f0
@ -66,6 +66,11 @@ if(GCOV)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lgcov")
|
||||
endif()
|
||||
|
||||
if(FAKE_CODE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFAKE_CODE")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFAKE_CODE")
|
||||
endif()
|
||||
|
||||
subdirs(MYSQL_DIR_ALL ${MYSQL_DIR})
|
||||
foreach(DIR ${MYSQL_DIR_ALL})
|
||||
include_directories(${DIR})
|
||||
@ -152,7 +157,7 @@ add_custom_target(buildtests
|
||||
add_custom_target(testall
|
||||
COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DBUILD_TYPE=Debug -DINSTALL_DIR=${CMAKE_BINARY_DIR} -DINSTALL_SYSTEM_FILES=N ${CMAKE_SOURCE_DIR}
|
||||
COMMAND make install
|
||||
COMMAND cp ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf
|
||||
COMMAND /bin/sh -c "${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null"
|
||||
COMMAND /bin/sh -c "make test || echo \"Test results written to: ${CMAKE_BINARY_DIR}/Testing/Temporary/\""
|
||||
COMMAND killall maxscale
|
||||
|
@ -162,7 +162,8 @@ struct logfile_st {
|
||||
mlist_t lf_blockbuf_list;
|
||||
int lf_buf_size;
|
||||
bool lf_flushflag;
|
||||
int lf_spinlock; /**< lf_flushflag */
|
||||
bool lf_rotateflag;
|
||||
int lf_spinlock; /**< lf_flushflag & lf_rotateflag */
|
||||
int lf_npending_writes;
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t lf_chk_tail;
|
||||
@ -232,6 +233,11 @@ static bool logfile_init(
|
||||
static void logfile_done(logfile_t* logfile);
|
||||
static void logfile_free_memory(logfile_t* lf);
|
||||
static void logfile_flush(logfile_t* lf);
|
||||
static void logfile_rotate(logfile_t* lf);
|
||||
static bool logfile_create(logfile_t* lf);
|
||||
static bool logfile_open_file(filewriter_t* fw, logfile_t* lf);
|
||||
static char* form_full_file_name(strpart_t* parts, int seqno, int seqnoidx);
|
||||
|
||||
static bool filewriter_init(
|
||||
logmanager_t* logmanager,
|
||||
filewriter_t* fw,
|
||||
@ -249,14 +255,16 @@ static bool logmanager_register(bool writep);
|
||||
static void logmanager_unregister(void);
|
||||
static bool logmanager_init_nomutex(int argc, char* argv[]);
|
||||
static void logmanager_done_nomutex(void);
|
||||
|
||||
static int logmanager_write_log(
|
||||
logfile_id_t id,
|
||||
bool flush,
|
||||
bool use_valist,
|
||||
bool spread_down,
|
||||
size_t len,
|
||||
const char* str,
|
||||
va_list valist);
|
||||
bool flush,
|
||||
bool use_valist,
|
||||
bool spread_down,
|
||||
bool rotate,
|
||||
size_t len,
|
||||
const char* str,
|
||||
va_list valist);
|
||||
|
||||
static blockbuf_t* blockbuf_init(logfile_id_t id);
|
||||
static void blockbuf_node_done(void* bb_data);
|
||||
@ -276,6 +284,8 @@ static bool check_file_and_path(
|
||||
bool* writable);
|
||||
|
||||
static bool file_is_symlink(char* filename);
|
||||
static int skygw_log_disable_raw(logfile_id_t id, bool emergency); /*< no locking */
|
||||
|
||||
|
||||
const char* get_suffix_default(void)
|
||||
{
|
||||
@ -391,8 +401,10 @@ static bool logmanager_init_nomutex(
|
||||
*/
|
||||
lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles;
|
||||
|
||||
/** Initialize filewriter data and open the (first) log file(s)
|
||||
* for each log file type. */
|
||||
/**
|
||||
* Initialize filewriter data and open the log file
|
||||
* for each log file type.
|
||||
*/
|
||||
if (!filewriter_init(lm, fw, lm->lm_clientmes, lm->lm_logmes))
|
||||
{
|
||||
err = 1;
|
||||
@ -627,6 +639,8 @@ static logfile_t* logmanager_get_logfile(
|
||||
*
|
||||
* @param spread_down - in, use
|
||||
* if true, log string is spread to all logs having larger id.
|
||||
*
|
||||
* @param rotate if set, closes currently open log file and opens a new one
|
||||
*
|
||||
* @param str_len - in, use
|
||||
* length of formatted string
|
||||
@ -648,6 +662,7 @@ static int logmanager_write_log(
|
||||
bool flush,
|
||||
bool use_valist,
|
||||
bool spread_down,
|
||||
bool rotate,
|
||||
size_t str_len,
|
||||
const char* str,
|
||||
va_list valist)
|
||||
@ -671,6 +686,7 @@ static int logmanager_write_log(
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
strlen(errstr)+1,
|
||||
errstr,
|
||||
valist);
|
||||
@ -687,14 +703,21 @@ static int logmanager_write_log(
|
||||
CHK_LOGFILE(lf);
|
||||
|
||||
/**
|
||||
* When string pointer is NULL, case is skygw_log_flush and no
|
||||
* writing is involved. With flush && str != NULL case is
|
||||
* skygw_log_write_flush.
|
||||
* When string pointer is NULL, operation is either flush or rotate.
|
||||
*/
|
||||
if (str == NULL) {
|
||||
ss_dassert(flush);
|
||||
logfile_flush(lf); /**< here we wake up file writer */
|
||||
} else {
|
||||
if (str == NULL)
|
||||
{
|
||||
if (flush)
|
||||
{
|
||||
logfile_flush(lf); /*< wakes up file writer */
|
||||
}
|
||||
else if (rotate)
|
||||
{
|
||||
logfile_rotate(lf); /*< wakes up file writer */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Length of string that will be written, limited by bufsize */
|
||||
int safe_str_len;
|
||||
|
||||
@ -702,14 +725,13 @@ static int logmanager_write_log(
|
||||
|
||||
/** Findout how much can be safely written with current block size */
|
||||
if (timestamp_len-1+str_len > lf->lf_buf_size)
|
||||
{
|
||||
safe_str_len = lf->lf_buf_size;
|
||||
}
|
||||
{
|
||||
safe_str_len = lf->lf_buf_size;
|
||||
}
|
||||
else
|
||||
{
|
||||
safe_str_len = timestamp_len-1+str_len;
|
||||
}
|
||||
|
||||
{
|
||||
safe_str_len = timestamp_len-1+str_len;
|
||||
}
|
||||
/**
|
||||
* Seek write position and register to block buffer.
|
||||
* Then print formatted string to write position.
|
||||
@ -717,32 +739,26 @@ static int logmanager_write_log(
|
||||
|
||||
#if defined (SS_LOG_DEBUG)
|
||||
{
|
||||
|
||||
char *copy,*tok;
|
||||
int tokval;
|
||||
char *copy,*tok;
|
||||
int tokval;
|
||||
|
||||
simple_mutex_lock(&msg_mutex,true);
|
||||
simple_mutex_lock(&msg_mutex,true);
|
||||
copy = strdup(str);
|
||||
tok = strtok(copy,"|");
|
||||
tok = strtok(NULL,"|");
|
||||
|
||||
copy = strdup(str);
|
||||
|
||||
tok = strtok(copy,"|");
|
||||
|
||||
tok = strtok(NULL,"|");
|
||||
|
||||
if(strstr(str,"message|") && tok){
|
||||
|
||||
tokval = atoi(tok);
|
||||
|
||||
if(prevval > 0){
|
||||
ss_dassert(tokval == (prevval + 1));
|
||||
}
|
||||
|
||||
prevval = tokval;
|
||||
}
|
||||
|
||||
free(copy);
|
||||
simple_mutex_unlock(&msg_mutex);
|
||||
if(strstr(str,"message|") && tok)
|
||||
{
|
||||
tokval = atoi(tok);
|
||||
|
||||
if(prevval > 0)
|
||||
{
|
||||
ss_dassert(tokval == (prevval + 1));
|
||||
}
|
||||
prevval = tokval;
|
||||
}
|
||||
free(copy);
|
||||
simple_mutex_unlock(&msg_mutex);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -799,7 +815,7 @@ static int logmanager_write_log(
|
||||
if (wp[safe_str_len-2] == '\n')
|
||||
{
|
||||
wp[safe_str_len-2]=' ';
|
||||
}
|
||||
}
|
||||
wp[safe_str_len-1] = '\n';
|
||||
blockbuf_unregister(bb);
|
||||
|
||||
@ -858,7 +874,7 @@ static int logmanager_write_log(
|
||||
blockbuf_unregister(bb_c);
|
||||
}
|
||||
} /* if (spread_down) */
|
||||
}
|
||||
} /* if (str == NULL) */
|
||||
|
||||
return_err:
|
||||
return err;
|
||||
@ -1185,25 +1201,36 @@ return_err:
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
int skygw_log_disable(
|
||||
logfile_id_t id)
|
||||
logfile_id_t id) /*< no locking */
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = skygw_log_disable_raw(id, false);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int skygw_log_disable_raw(
|
||||
logfile_id_t id,
|
||||
bool emergency) /*< no locking */
|
||||
{
|
||||
bool err = 0;
|
||||
|
||||
if (!logmanager_register(true)) {
|
||||
//fprintf(stderr, "ERROR: Can't register to logmanager\n");
|
||||
if (!logmanager_register(true))
|
||||
{
|
||||
err = -1;
|
||||
goto return_err;
|
||||
}
|
||||
CHK_LOGMANAGER(lm);
|
||||
|
||||
if (logfile_set_enabled(id, false)) {
|
||||
lm->lm_enabled_logfiles &= ~id;
|
||||
/**
|
||||
* Set global variable
|
||||
*/
|
||||
lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles;
|
||||
if (emergency || logfile_set_enabled(id, false))
|
||||
{
|
||||
lm->lm_enabled_logfiles &= ~id;
|
||||
/**
|
||||
* Set global variable
|
||||
*/
|
||||
lm_enabled_logfiles_bitmask = lm->lm_enabled_logfiles;
|
||||
}
|
||||
|
||||
logmanager_unregister();
|
||||
@ -1234,6 +1261,7 @@ static bool logfile_set_enabled(
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
strlen(errstr)+1,
|
||||
errstr,
|
||||
notused);
|
||||
@ -1259,6 +1287,7 @@ static bool logfile_set_enabled(
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
strlen(logstr)+1,
|
||||
logstr,
|
||||
notused);
|
||||
@ -1304,9 +1333,6 @@ int skygw_log_write_flush(
|
||||
/**
|
||||
* Find out the length of log string (to be formatted str).
|
||||
*/
|
||||
|
||||
|
||||
|
||||
va_start(valist, str);
|
||||
len = vsnprintf(NULL, 0, str, valist);
|
||||
va_end(valist);
|
||||
@ -1318,7 +1344,7 @@ int skygw_log_write_flush(
|
||||
* Write log string to buffer and add to file write list.
|
||||
*/
|
||||
va_start(valist, str);
|
||||
err = logmanager_write_log(id, true, true, true, len, str, valist);
|
||||
err = logmanager_write_log(id, true, true, true, false, len, str, valist);
|
||||
va_end(valist);
|
||||
|
||||
if (err != 0) {
|
||||
@ -1357,9 +1383,6 @@ int skygw_log_write(
|
||||
err = 1;
|
||||
goto return_unregister;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find out the length of log string (to be formatted str).
|
||||
*/
|
||||
@ -1375,7 +1398,7 @@ int skygw_log_write(
|
||||
*/
|
||||
|
||||
va_start(valist, str);
|
||||
err = logmanager_write_log(id, false, true, true, len, str, valist);
|
||||
err = logmanager_write_log(id, false, true, true, false, len, str, valist);
|
||||
va_end(valist);
|
||||
|
||||
if (err != 0) {
|
||||
@ -1402,7 +1425,7 @@ int skygw_log_flush(
|
||||
goto return_err;
|
||||
}
|
||||
CHK_LOGMANAGER(lm);
|
||||
err = logmanager_write_log(id, true, false, false, 0, NULL, valist);
|
||||
err = logmanager_write_log(id, true, false, false, false, 0, NULL, valist);
|
||||
|
||||
if (err != 0) {
|
||||
fprintf(stderr, "skygw_log_flush failed.\n");
|
||||
@ -1415,6 +1438,60 @@ return_err:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace current logfile with new file with increased sequence number on
|
||||
* its name.
|
||||
*/
|
||||
int skygw_log_rotate(
|
||||
logfile_id_t id)
|
||||
{
|
||||
int err = 0;
|
||||
logfile_t* lf;
|
||||
va_list valist; /**< Dummy, must be present but it is not processed */
|
||||
|
||||
if (!logmanager_register(false))
|
||||
{
|
||||
ss_dfprintf(stderr,
|
||||
"Can't register to logmanager, rotating failed\n");
|
||||
goto return_err;
|
||||
}
|
||||
CHK_LOGMANAGER(lm);
|
||||
lf = &lm->lm_logfile[id];
|
||||
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Log rotation is called for %s.",
|
||||
lf->lf_full_file_name)));
|
||||
|
||||
err = logmanager_write_log(id, false, false, false, true, 0, NULL, valist);
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Log file rotation failed for file %s.",
|
||||
lf->lf_full_file_name)));
|
||||
|
||||
fprintf(stderr, "skygw_log_rotate failed.\n");
|
||||
goto return_unregister;
|
||||
}
|
||||
|
||||
return_unregister:
|
||||
LOGIF(LM, (skygw_log_write_flush(
|
||||
LOGFILE_MESSAGE,
|
||||
"File %s use for log writing..",
|
||||
lf->lf_full_file_name)));
|
||||
|
||||
logmanager_unregister();
|
||||
|
||||
return_err:
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @node Register as a logging client to logmanager.
|
||||
*
|
||||
@ -1828,6 +1905,237 @@ static void logfile_flush(
|
||||
skygw_message_send(lf->lf_logmes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set rotate flag for a log file and wake up the writer thread which then
|
||||
* performs the actual rotation task.
|
||||
*
|
||||
* @param lf logfile pointer
|
||||
*/
|
||||
static void logfile_rotate(
|
||||
logfile_t* lf)
|
||||
{
|
||||
CHK_LOGFILE(lf);
|
||||
acquire_lock(&lf->lf_spinlock);
|
||||
lf->lf_rotateflag = true;
|
||||
release_lock(&lf->lf_spinlock);
|
||||
skygw_message_send(lf->lf_logmes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Forms complete path name for logfile and tests that the file doesn't conflict
|
||||
* with any existing file and it is writable.
|
||||
*
|
||||
* @param lf logfile pointer
|
||||
*
|
||||
* @return true if succeed, false if failed
|
||||
*/
|
||||
static bool logfile_create(
|
||||
logfile_t* lf)
|
||||
{
|
||||
bool namecreatefail;
|
||||
bool nameconflicts;
|
||||
bool store_shmem;
|
||||
bool writable;
|
||||
bool succp;
|
||||
strpart_t spart[3]; /*< string parts of which the file is composed of */
|
||||
|
||||
/**
|
||||
* sparts is an array but next pointers are used to walk through
|
||||
* the list of string parts.
|
||||
*/
|
||||
spart[0].sp_next = &spart[1];
|
||||
spart[1].sp_next = &spart[2];
|
||||
spart[2].sp_next = NULL;
|
||||
|
||||
spart[1].sp_string = lf->lf_name_prefix;
|
||||
spart[2].sp_string = lf->lf_name_suffix;
|
||||
|
||||
store_shmem = lf->lf_store_shmem;
|
||||
|
||||
do {
|
||||
namecreatefail = false;
|
||||
nameconflicts = false;
|
||||
|
||||
spart[0].sp_string = lf->lf_filepath;
|
||||
/**
|
||||
* Create name for log file. Seqno is added between prefix &
|
||||
* suffix (index == 2)
|
||||
*/
|
||||
lf->lf_full_file_name =
|
||||
form_full_file_name(spart, lf->lf_name_seqno, 2);
|
||||
|
||||
if (store_shmem)
|
||||
{
|
||||
spart[0].sp_string = lf->lf_linkpath;
|
||||
/**
|
||||
* Create name for link file
|
||||
*/
|
||||
lf->lf_full_link_name = form_full_file_name(
|
||||
spart,
|
||||
lf->lf_name_seqno,
|
||||
2);
|
||||
}
|
||||
/**
|
||||
* At least one of the files couldn't be created. Increase
|
||||
* sequence number and retry until succeeds.
|
||||
*/
|
||||
if (lf->lf_full_file_name == NULL ||
|
||||
(store_shmem && lf->lf_full_link_name == NULL))
|
||||
{
|
||||
namecreatefail = true;
|
||||
goto file_create_fail;
|
||||
}
|
||||
|
||||
/**
|
||||
* If file exists but is different type, create fails and
|
||||
* new, increased sequence number is added to file name.
|
||||
*/
|
||||
if (check_file_and_path(lf->lf_full_file_name, &writable))
|
||||
{
|
||||
/** Found similarly named file which isn't writable */
|
||||
if (!writable || file_is_symlink(lf->lf_full_file_name))
|
||||
{
|
||||
nameconflicts = true;
|
||||
goto file_create_fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Opening the file failed for some other reason than
|
||||
* existing non-writable file. Shut down.
|
||||
*/
|
||||
if (!writable)
|
||||
{
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
}
|
||||
|
||||
if (store_shmem)
|
||||
{
|
||||
if (check_file_and_path(lf->lf_full_file_name, &writable))
|
||||
{
|
||||
/** Found similarly named file which isn't writable */
|
||||
if (!writable ||
|
||||
file_is_symlink(lf->lf_full_file_name))
|
||||
{
|
||||
unlink(lf->lf_full_file_name);
|
||||
nameconflicts = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Opening the file failed for some other reason than
|
||||
* existing non-writable file. Shut down.
|
||||
*/
|
||||
if (!writable)
|
||||
{
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
}
|
||||
}
|
||||
file_create_fail:
|
||||
if (namecreatefail || nameconflicts)
|
||||
{
|
||||
lf->lf_name_seqno += 1;
|
||||
|
||||
if (lf->lf_full_file_name != NULL)
|
||||
{
|
||||
free(lf->lf_full_file_name);
|
||||
lf->lf_full_file_name = NULL;
|
||||
}
|
||||
if (lf->lf_full_link_name != NULL)
|
||||
{
|
||||
free(lf->lf_full_link_name);
|
||||
lf->lf_full_link_name = NULL;
|
||||
}
|
||||
}
|
||||
} while (namecreatefail || nameconflicts);
|
||||
|
||||
succp = true;
|
||||
|
||||
return_succp:
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a log file and writes header to the beginning of it. File name, FILE*,
|
||||
* and file descriptor are stored to skygw_file_t struct which is stored in
|
||||
* filewriter strcuture passed as parameter.
|
||||
*
|
||||
* @param fw filewriter pointer
|
||||
* @param lf logfile pointer
|
||||
*
|
||||
* @return true if succeed; the resulting skygw_file_t is written in filewriter,
|
||||
* false if failed.
|
||||
*
|
||||
*/
|
||||
static bool logfile_open_file(
|
||||
filewriter_t* fw,
|
||||
logfile_t* lf)
|
||||
{
|
||||
bool succp;
|
||||
char* start_msg_str;
|
||||
int err;
|
||||
|
||||
if (lf->lf_store_shmem)
|
||||
{
|
||||
/** Create symlink pointing to log file */
|
||||
fw->fwr_file[lf->lf_id] = skygw_file_init(
|
||||
lf->lf_full_file_name,
|
||||
lf->lf_full_link_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Create normal disk-resident log file */
|
||||
fw->fwr_file[lf->lf_id] = skygw_file_init(
|
||||
lf->lf_full_file_name,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (fw->fwr_file[lf->lf_id] == NULL)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Error : opening logfile %s failed.\n",
|
||||
lf->lf_full_file_name);
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
if (lf->lf_enabled)
|
||||
{
|
||||
start_msg_str = strdup("---\tLogging is enabled.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
start_msg_str = strdup("---\tLogging is disabled.\n");
|
||||
}
|
||||
err = skygw_file_write(fw->fwr_file[lf->lf_id],
|
||||
(void *)start_msg_str,
|
||||
strlen(start_msg_str),
|
||||
true);
|
||||
|
||||
if (err != 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Error : writing to file %s failed due to %d, %s. "
|
||||
"Exiting MaxScale.\n",
|
||||
lf->lf_full_file_name,
|
||||
err,
|
||||
strerror(err));
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
free(start_msg_str);
|
||||
succp = true;
|
||||
|
||||
return_succp:
|
||||
return succp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @node Combine all name parts from left to right.
|
||||
@ -2109,12 +2417,6 @@ static bool logfile_init(
|
||||
{
|
||||
bool succp = false;
|
||||
fnames_conf_t* fn = &logmanager->lm_fnames_conf;
|
||||
/** string parts of which the file is composed of */
|
||||
strpart_t strparts[3];
|
||||
bool namecreatefail = false;
|
||||
bool nameconflicts = false;
|
||||
bool writable;
|
||||
|
||||
logfile->lf_state = INIT;
|
||||
#if defined(SS_DEBUG)
|
||||
logfile->lf_chk_top = CHK_NUM_LOGFILE;
|
||||
@ -2128,21 +2430,12 @@ static bool logfile_init(
|
||||
logfile->lf_name_seqno = 1;
|
||||
logfile->lf_lmgr = logmanager;
|
||||
logfile->lf_flushflag = false;
|
||||
logfile->lf_rotateflag= false;
|
||||
logfile->lf_spinlock = 0;
|
||||
logfile->lf_store_shmem = store_shmem;
|
||||
logfile->lf_write_syslog = write_syslog;
|
||||
logfile->lf_buf_size = MAX_LOGSTRLEN;
|
||||
logfile->lf_enabled = logmanager->lm_enabled_logfiles & logfile_id;
|
||||
/**
|
||||
* strparts is an array but next pointers are used to walk through
|
||||
* the list of string parts.
|
||||
*/
|
||||
strparts[0].sp_next = &strparts[1];
|
||||
strparts[1].sp_next = &strparts[2];
|
||||
strparts[2].sp_next = NULL;
|
||||
|
||||
strparts[1].sp_string = logfile->lf_name_prefix;
|
||||
strparts[2].sp_string = logfile->lf_name_suffix;
|
||||
/**
|
||||
* If file is stored in shared memory in /dev/shm, a link
|
||||
* pointing to shm file is created and located to the file
|
||||
@ -2160,7 +2453,7 @@ static bool logfile_init(
|
||||
if (c == NULL)
|
||||
{
|
||||
succp = false;
|
||||
goto file_create_fail;
|
||||
goto return_with_succp;
|
||||
}
|
||||
sprintf(c, "%s%d", shm_pathname_prefix, pid);
|
||||
logfile->lf_filepath = c;
|
||||
@ -2169,7 +2462,7 @@ static bool logfile_init(
|
||||
errno != EEXIST)
|
||||
{
|
||||
succp = false;
|
||||
goto file_create_fail;
|
||||
goto return_with_succp;
|
||||
}
|
||||
logfile->lf_linkpath = strdup(fn->fn_logpath);
|
||||
logfile->lf_linkpath = add_slash(logfile->lf_linkpath);
|
||||
@ -2179,114 +2472,11 @@ static bool logfile_init(
|
||||
logfile->lf_filepath = strdup(fn->fn_logpath);
|
||||
}
|
||||
logfile->lf_filepath = add_slash(logfile->lf_filepath);
|
||||
|
||||
do {
|
||||
namecreatefail = false;
|
||||
nameconflicts = false;
|
||||
|
||||
strparts[0].sp_string = logfile->lf_filepath;
|
||||
/**
|
||||
* Create name for log file. Seqno is added between prefix &
|
||||
* suffix (index == 2)
|
||||
*/
|
||||
logfile->lf_full_file_name =
|
||||
form_full_file_name(strparts, logfile->lf_name_seqno, 2);
|
||||
|
||||
if (store_shmem)
|
||||
{
|
||||
strparts[0].sp_string = logfile->lf_linkpath;
|
||||
/**
|
||||
* Create name for link file
|
||||
*/
|
||||
logfile->lf_full_link_name =
|
||||
form_full_file_name(strparts,
|
||||
logfile->lf_name_seqno,
|
||||
2);
|
||||
}
|
||||
/**
|
||||
* At least one of the files couldn't be created. Increase
|
||||
* sequence number and retry until succeeds.
|
||||
*/
|
||||
if (logfile->lf_full_file_name == NULL ||
|
||||
(store_shmem && logfile->lf_full_link_name == NULL))
|
||||
{
|
||||
namecreatefail = true;
|
||||
goto file_create_fail;
|
||||
}
|
||||
|
||||
/**
|
||||
* If file exists but is different type, create fails and
|
||||
* new, increased sequence number is added to file name.
|
||||
*/
|
||||
if (check_file_and_path(
|
||||
logfile->lf_full_file_name,
|
||||
&writable))
|
||||
{
|
||||
/** Found similarly named file which isn't writable */
|
||||
if (!writable ||
|
||||
file_is_symlink(logfile->lf_full_file_name))
|
||||
{
|
||||
nameconflicts = true;
|
||||
goto file_create_fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Opening the file failed for some other reason than
|
||||
* existing non-writable file. Shut down.
|
||||
*/
|
||||
if (!writable)
|
||||
{
|
||||
succp = false;
|
||||
goto return_with_succp;
|
||||
}
|
||||
}
|
||||
|
||||
if (store_shmem)
|
||||
{
|
||||
if (check_file_and_path(
|
||||
logfile->lf_full_file_name,
|
||||
&writable))
|
||||
{
|
||||
/** Found similarly named file which isn't writable */
|
||||
if (!writable ||
|
||||
file_is_symlink(logfile->lf_full_file_name))
|
||||
{
|
||||
unlink(logfile->lf_full_file_name);
|
||||
nameconflicts = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/**
|
||||
* Opening the file failed for some other reason than
|
||||
* existing non-writable file. Shut down.
|
||||
*/
|
||||
if (!writable)
|
||||
{
|
||||
succp = false;
|
||||
goto return_with_succp;
|
||||
}
|
||||
}
|
||||
}
|
||||
file_create_fail:
|
||||
if (namecreatefail || nameconflicts)
|
||||
{
|
||||
logfile->lf_name_seqno += 1;
|
||||
|
||||
if (logfile->lf_full_file_name != NULL)
|
||||
{
|
||||
free(logfile->lf_full_file_name);
|
||||
logfile->lf_full_file_name = NULL;
|
||||
}
|
||||
if (logfile->lf_full_link_name != NULL)
|
||||
{
|
||||
free(logfile->lf_full_link_name);
|
||||
logfile->lf_full_link_name = NULL;
|
||||
}
|
||||
}
|
||||
} while (namecreatefail || nameconflicts);
|
||||
if (!(succp = logfile_create(logfile)))
|
||||
{
|
||||
goto return_with_succp;
|
||||
}
|
||||
/**
|
||||
* Create a block buffer list for log file. Clients' writes go to buffers
|
||||
* from where separate log flusher thread writes them to disk.
|
||||
@ -2321,11 +2511,11 @@ file_create_fail:
|
||||
CHK_LOGFILE(logfile);
|
||||
|
||||
return_with_succp:
|
||||
if (!succp) {
|
||||
if (!succp)
|
||||
{
|
||||
logfile_done(logfile);
|
||||
}
|
||||
ss_dassert(logfile->lf_state == RUN ||
|
||||
logfile->lf_state == DONE);
|
||||
ss_dassert(logfile->lf_state == RUN || logfile->lf_state == DONE);
|
||||
return succp;
|
||||
}
|
||||
|
||||
@ -2384,16 +2574,14 @@ static void logfile_free_memory(
|
||||
}
|
||||
|
||||
/**
|
||||
* @node Initialize filewriter struct to a given address
|
||||
* @node Initialize filewriter data and open the log file for each log file type.
|
||||
*
|
||||
* Parameters:
|
||||
* @param fw - <usage>
|
||||
* <description>
|
||||
* @param logmanager Log manager struct
|
||||
* @param fw File writer struct
|
||||
* @param clientmes Messaging from file writer to log manager
|
||||
* @param logmes Messaging from loggers to file writer thread
|
||||
*
|
||||
* @return
|
||||
*
|
||||
*
|
||||
* @details (write detailed description here)
|
||||
* @return true if succeed, false if failed
|
||||
*
|
||||
*/
|
||||
static bool filewriter_init(
|
||||
@ -2406,7 +2594,6 @@ static bool filewriter_init(
|
||||
logfile_t* lf;
|
||||
logfile_id_t id;
|
||||
int i;
|
||||
char* start_msg_str;
|
||||
|
||||
CHK_LOGMANAGER(logmanager);
|
||||
|
||||
@ -2424,47 +2611,25 @@ static bool filewriter_init(
|
||||
if (fw->fwr_logmes == NULL || fw->fwr_clientmes == NULL) {
|
||||
goto return_succp;
|
||||
}
|
||||
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1) {
|
||||
|
||||
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1)
|
||||
{
|
||||
id = (logfile_id_t)i;
|
||||
lf = logmanager_get_logfile(logmanager, id);
|
||||
|
||||
if (lf->lf_store_shmem)
|
||||
{
|
||||
/** Create symlink pointing to log file */
|
||||
fw->fwr_file[id] = skygw_file_init(lf->lf_full_file_name,
|
||||
lf->lf_full_link_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Create normal disk-resident log file */
|
||||
fw->fwr_file[id] = skygw_file_init(lf->lf_full_file_name,
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (fw->fwr_file[id] == NULL)
|
||||
if (!(succp = logfile_open_file(fw, lf)))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Error : opening %s failed, %s. Exiting "
|
||||
"Error : opening log file %s failed. Exiting "
|
||||
"MaxScale\n",
|
||||
lf->lf_full_file_name,
|
||||
strerror(errno));
|
||||
goto return_succp;
|
||||
}
|
||||
|
||||
if (lf->lf_enabled) {
|
||||
start_msg_str = strdup("---\tLogging is enabled.\n");
|
||||
} else {
|
||||
start_msg_str = strdup("---\tLogging is disabled.\n");
|
||||
}
|
||||
skygw_file_write(fw->fwr_file[id],
|
||||
(void *)start_msg_str,
|
||||
strlen(start_msg_str),
|
||||
true);
|
||||
free(start_msg_str);
|
||||
}
|
||||
lf->lf_full_file_name);
|
||||
goto return_succp;
|
||||
}
|
||||
} /*< for */
|
||||
fw->fwr_state = RUN;
|
||||
CHK_FILEWRITER(fw);
|
||||
succp = true;
|
||||
|
||||
return_succp:
|
||||
if (!succp) {
|
||||
filewriter_done(fw);
|
||||
@ -2485,9 +2650,10 @@ static void filewriter_done(
|
||||
case INIT:
|
||||
fw->fwr_logmes = NULL;
|
||||
fw->fwr_clientmes = NULL;
|
||||
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++) {
|
||||
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i++)
|
||||
{
|
||||
id = (logfile_id_t)i;
|
||||
skygw_file_done(fw->fwr_file[id]);
|
||||
skygw_file_close(fw->fwr_file[id], true);
|
||||
}
|
||||
fw->fwr_state = DONE;
|
||||
case DONE:
|
||||
@ -2555,6 +2721,7 @@ static void* thr_filewriter_fun(
|
||||
blockbuf_state_t flush_blockbuf; /**< flush single block buffer. */
|
||||
bool flush_logfile; /**< flush logfile */
|
||||
bool flushall_logfiles;/**< flush all logfiles */
|
||||
bool rotate_logfile; /*< close current and open new file */
|
||||
size_t vn1;
|
||||
size_t vn2;
|
||||
|
||||
@ -2576,7 +2743,8 @@ static void* thr_filewriter_fun(
|
||||
flushall_logfiles = skygw_thread_must_exit(thr);
|
||||
|
||||
/** Process all logfiles which have buffered writes. */
|
||||
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1) {
|
||||
for (i=LOGFILE_FIRST; i<=LOGFILE_LAST; i <<= 1)
|
||||
{
|
||||
retry_flush_on_exit:
|
||||
/**
|
||||
* Get file pointer of current logfile.
|
||||
@ -2585,13 +2753,45 @@ static void* thr_filewriter_fun(
|
||||
lf = &lm->lm_logfile[(logfile_id_t)i];
|
||||
|
||||
/**
|
||||
* read and reset logfile's flushflag
|
||||
* read and reset logfile's flush- and rotateflag
|
||||
*/
|
||||
acquire_lock(&lf->lf_spinlock);
|
||||
flush_logfile = lf->lf_flushflag;
|
||||
lf->lf_flushflag = false;
|
||||
flush_logfile = lf->lf_flushflag;
|
||||
rotate_logfile = lf->lf_rotateflag;
|
||||
lf->lf_flushflag = false;
|
||||
lf->lf_rotateflag = false;
|
||||
release_lock(&lf->lf_spinlock);
|
||||
|
||||
/**
|
||||
* Log rotation :
|
||||
* Close current, and open a new file for the log.
|
||||
*/
|
||||
if (rotate_logfile)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
lf->lf_name_seqno += 1; /*< new sequence number */
|
||||
|
||||
if (!(succp = logfile_create(lf)))
|
||||
{
|
||||
lf->lf_name_seqno -= 1; /*< restore */
|
||||
}
|
||||
else if ((succp = logfile_open_file(fwr, lf)))
|
||||
{
|
||||
skygw_file_close(file, false); /*< close old file */
|
||||
}
|
||||
|
||||
if (!succp)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Log rotation failed. "
|
||||
"Creating replacement file %s "
|
||||
"failed. Continuing "
|
||||
"logging to existing file.",
|
||||
lf->lf_full_file_name)));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/**
|
||||
* get logfile's block buffer list
|
||||
*/
|
||||
@ -2603,7 +2803,10 @@ static void* thr_filewriter_fun(
|
||||
#endif
|
||||
node = bb_list->mlist_first;
|
||||
|
||||
while (node != NULL) {
|
||||
while (node != NULL)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
CHK_MLIST_NODE(node);
|
||||
bb = (blockbuf_t *)node->mlnode_data;
|
||||
CHK_BLOCKBUF(bb);
|
||||
@ -2622,19 +2825,33 @@ static void* thr_filewriter_fun(
|
||||
* buffer is at least half-full
|
||||
* -> write to disk
|
||||
*/
|
||||
while(bb->bb_refcount > 0) {
|
||||
while(bb->bb_refcount > 0)
|
||||
{
|
||||
simple_mutex_unlock(
|
||||
&bb->bb_mutex);
|
||||
simple_mutex_lock(
|
||||
&bb->bb_mutex,
|
||||
true);
|
||||
}
|
||||
|
||||
skygw_file_write(file,
|
||||
(void *)bb->bb_buf,
|
||||
bb->bb_buf_used,
|
||||
(flush_logfile ||
|
||||
flushall_logfiles));
|
||||
err = skygw_file_write(
|
||||
file,
|
||||
(void *)bb->bb_buf,
|
||||
bb->bb_buf_used,
|
||||
(flush_logfile ||
|
||||
flushall_logfiles));
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Error : Write to %s log "
|
||||
": %s failed due to %d, "
|
||||
"%s. Disabling the log.",
|
||||
STRLOGNAME((logfile_id_t)i),
|
||||
lf->lf_full_file_name,
|
||||
err,
|
||||
strerror(err));
|
||||
/** Force log off */
|
||||
skygw_log_disable_raw((logfile_id_t)i, true);
|
||||
}
|
||||
/**
|
||||
* Reset buffer's counters and mark
|
||||
* not full.
|
||||
|
@ -75,6 +75,7 @@ void skygw_logmanager_exit(void);
|
||||
void skygw_log_done(void);
|
||||
int skygw_log_write(logfile_id_t id, const char* format, ...);
|
||||
int skygw_log_flush(logfile_id_t id);
|
||||
int skygw_log_rotate(logfile_id_t id);
|
||||
int skygw_log_write_flush(logfile_id_t id, const char* format, ...);
|
||||
int skygw_log_enable(logfile_id_t id);
|
||||
int skygw_log_disable(logfile_id_t id);
|
||||
|
15
macros.cmake
15
macros.cmake
@ -27,7 +27,7 @@ macro(set_variables)
|
||||
set(TEST_HOST "127.0.0.1" CACHE STRING "hostname or IP address of MaxScale's host")
|
||||
|
||||
# port of read connection router module
|
||||
set(TEST_PORT_RW "4008" CACHE STRING "port of read connection router module")
|
||||
set(TEST_PORT "4008" CACHE STRING "port of read connection router module")
|
||||
|
||||
# port of read/write split router module
|
||||
set(TEST_PORT_RW "4006" CACHE STRING "port of read/write split router module")
|
||||
@ -38,6 +38,9 @@ macro(set_variables)
|
||||
# master test server server_id
|
||||
set(TEST_MASTER_ID "3000" CACHE STRING "master test server server_id")
|
||||
|
||||
# master test server port
|
||||
set(MASTER_PORT "3000" CACHE STRING "master test server port")
|
||||
|
||||
# username of MaxScale user
|
||||
set(TEST_USER "maxuser" CACHE STRING "username of MaxScale user")
|
||||
|
||||
@ -202,6 +205,16 @@ debugmsg("Search returned: ${MYSQL_DIR_LOC}")
|
||||
unset(DEB_FNC)
|
||||
unset(RPM_FNC)
|
||||
|
||||
#Find the MySQL client library
|
||||
find_library(MYSQLCLIENT_LIBRARIES NAMES mysqlclient PATH_SUFFIXES mysql mariadb)
|
||||
if(${MYSQLCLIENT_LIBRARIES} MATCHES "NOTFOUND")
|
||||
set(MYSQLCLIENT_FOUND FALSE CACHE INTERNAL "")
|
||||
message(STATUS "Cannot find MySQL client library: Login tests disabled.")
|
||||
else()
|
||||
set(MYSQLCLIENT_FOUND TRUE CACHE INTERNAL "")
|
||||
message(STATUS "Found MySQL client library: ${MYSQLCLIENT_LIBRARIES}")
|
||||
endif()
|
||||
|
||||
#Check RabbitMQ headers and libraries
|
||||
if(BUILD_RABBITMQ)
|
||||
include(CheckCSourceCompiles)
|
||||
|
@ -57,21 +57,19 @@ int main(int argc, char** argv)
|
||||
{
|
||||
fgets(readbuff,4092,infile);
|
||||
psize = strlen(readbuff);
|
||||
if(psize < 0 || psize > 4092){
|
||||
continue;
|
||||
}
|
||||
qbuff = gwbuf_alloc(psize + 7);
|
||||
*(qbuff->sbuf->data + 0) = (unsigned char)psize;
|
||||
*(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8);
|
||||
*(qbuff->sbuf->data + 2) = (unsigned char)(psize>>16);
|
||||
*(qbuff->sbuf->data + 4) = 0x03;
|
||||
memcpy(qbuff->start + 5,readbuff,psize + 1);
|
||||
parse_query(qbuff);
|
||||
tok = skygw_get_canonical(qbuff);
|
||||
fprintf(outfile,"%s\n",tok);
|
||||
free(tok);
|
||||
gwbuf_free(qbuff);
|
||||
|
||||
if(psize < 4092){
|
||||
qbuff = gwbuf_alloc(psize + 7);
|
||||
*(qbuff->sbuf->data + 0) = (unsigned char)psize;
|
||||
*(qbuff->sbuf->data + 1) = (unsigned char)(psize>>8);
|
||||
*(qbuff->sbuf->data + 2) = (unsigned char)(psize>>16);
|
||||
*(qbuff->sbuf->data + 4) = 0x03;
|
||||
memcpy(qbuff->start + 5,readbuff,psize + 1);
|
||||
parse_query(qbuff);
|
||||
tok = skygw_get_canonical(qbuff);
|
||||
fprintf(outfile,"%s\n",tok);
|
||||
free(tok);
|
||||
gwbuf_free(qbuff);
|
||||
}
|
||||
}
|
||||
fclose(infile);
|
||||
fclose(outfile);
|
||||
|
@ -38,6 +38,8 @@
|
||||
* 09/09/14 Massimiliano Pinto Added localhost_match_wildcard_host parameter
|
||||
* 12/09/14 Mark Riddoch Addition of checks on servers list and
|
||||
* internal router suppression of messages
|
||||
* 30/10/14 Massimiliano Pinto Added disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor timeouts for connect/read/write
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -58,6 +60,7 @@
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
extern int setipaddress(struct in_addr *, char *);
|
||||
static int process_config_context(CONFIG_CONTEXT *);
|
||||
static int process_config_update(CONFIG_CONTEXT *);
|
||||
static void free_config_context(CONFIG_CONTEXT *);
|
||||
@ -772,6 +775,10 @@ int error_count = 0;
|
||||
unsigned long interval = 0;
|
||||
int replication_heartbeat = 0;
|
||||
int detect_stale_master = 0;
|
||||
int disable_master_failback = 0;
|
||||
int connect_timeout = 0;
|
||||
int read_timeout = 0;
|
||||
int write_timeout = 0;
|
||||
|
||||
module = config_get_value(obj->parameters, "module");
|
||||
servers = config_get_value(obj->parameters, "servers");
|
||||
@ -789,6 +796,20 @@ int error_count = 0;
|
||||
detect_stale_master = atoi(config_get_value(obj->parameters, "detect_stale_master"));
|
||||
}
|
||||
|
||||
if (config_get_value(obj->parameters, "disable_master_failback")) {
|
||||
disable_master_failback = atoi(config_get_value(obj->parameters, "disable_master_failback"));
|
||||
}
|
||||
|
||||
if (config_get_value(obj->parameters, "backend_connect_timeout")) {
|
||||
connect_timeout = atoi(config_get_value(obj->parameters, "backend_connect_timeout"));
|
||||
}
|
||||
if (config_get_value(obj->parameters, "backend_read_timeout")) {
|
||||
read_timeout = atoi(config_get_value(obj->parameters, "backend_read_timeout"));
|
||||
}
|
||||
if (config_get_value(obj->parameters, "backend_write_timeout")) {
|
||||
write_timeout = atoi(config_get_value(obj->parameters, "backend_write_timeout"));
|
||||
}
|
||||
|
||||
if (module)
|
||||
{
|
||||
obj->element = monitor_alloc(obj->object, module);
|
||||
@ -816,6 +837,18 @@ int error_count = 0;
|
||||
if(detect_stale_master == 1)
|
||||
monitorDetectStaleMaster(obj->element, detect_stale_master);
|
||||
|
||||
/* disable master failback */
|
||||
if(disable_master_failback == 1)
|
||||
monitorDisableMasterFailback(obj->element, disable_master_failback);
|
||||
|
||||
/* set timeouts */
|
||||
if (connect_timeout > 0)
|
||||
monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, connect_timeout);
|
||||
if (read_timeout > 0)
|
||||
monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, read_timeout);
|
||||
if (write_timeout > 0)
|
||||
monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, write_timeout);
|
||||
|
||||
/* get the servers to monitor */
|
||||
s = strtok(servers, ",");
|
||||
while (s)
|
||||
@ -1360,11 +1393,11 @@ SERVER *server;
|
||||
user,
|
||||
auth);
|
||||
if (enable_root_user)
|
||||
serviceEnableRootUser(service, atoi(enable_root_user));
|
||||
serviceEnableRootUser(obj->element, atoi(enable_root_user));
|
||||
|
||||
if (allow_localhost_match_wildcard_host && service)
|
||||
if (allow_localhost_match_wildcard_host)
|
||||
serviceEnableLocalhostMatchWildcardHost(
|
||||
service,
|
||||
obj->element,
|
||||
atoi(allow_localhost_match_wildcard_host));
|
||||
}
|
||||
}
|
||||
@ -1625,6 +1658,10 @@ static char *monitor_params[] =
|
||||
"monitor_interval",
|
||||
"detect_replication_lag",
|
||||
"detect_stale_master",
|
||||
"disable_master_failback",
|
||||
"backend_connect_timeout",
|
||||
"backend_read_timeout",
|
||||
"backend_write_timeout",
|
||||
NULL
|
||||
};
|
||||
/**
|
||||
|
@ -468,26 +468,56 @@ getUsers(SERVICE *service, USERS *users)
|
||||
server = service->databases;
|
||||
dpwd = decryptPassword(service_passwd);
|
||||
|
||||
while (server != NULL && (mysql_real_connect(con,
|
||||
server->name,
|
||||
service_user,
|
||||
dpwd,
|
||||
NULL,
|
||||
server->port,
|
||||
NULL,
|
||||
0) == NULL))
|
||||
{
|
||||
server = server->nextdb;
|
||||
/* Select a server with Master bit, if available */
|
||||
while (server != NULL && !(server->status & SERVER_MASTER)) {
|
||||
server = server->nextdb;
|
||||
}
|
||||
|
||||
/* Try loading data from master server */
|
||||
if (server != NULL && (mysql_real_connect(con, server->name, service_user, dpwd, NULL, server->port, NULL, 0) != NULL)) {
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"Dbusers : Loading data from backend database with Master role [%s:%i] "
|
||||
"for service [%s]",
|
||||
server->name,
|
||||
server->port,
|
||||
service->name)));
|
||||
} else {
|
||||
/* load data from other servers via loop */
|
||||
server = service->databases;
|
||||
|
||||
while (server != NULL && (mysql_real_connect(con,
|
||||
server->name,
|
||||
service_user,
|
||||
dpwd,
|
||||
NULL,
|
||||
server->port,
|
||||
NULL,
|
||||
0) == NULL))
|
||||
{
|
||||
server = server->nextdb;
|
||||
}
|
||||
|
||||
if (server != NULL) {
|
||||
LOGIF(LD, (skygw_log_write_flush(
|
||||
LOGFILE_DEBUG,
|
||||
"Dbusers : Loading data from backend database [%s:%i] "
|
||||
"for service [%s]",
|
||||
server->name,
|
||||
server->port,
|
||||
service->name)));
|
||||
}
|
||||
}
|
||||
|
||||
free(dpwd);
|
||||
|
||||
if (server == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to get user data from backend database "
|
||||
"for service [%s]. Missing server information.",
|
||||
service->name)));
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to get user data from backend database "
|
||||
"for service [%s]. Missing server information.",
|
||||
service->name)));
|
||||
mysql_close(con);
|
||||
return -1;
|
||||
}
|
||||
@ -663,8 +693,8 @@ getUsers(SERVICE *service, USERS *users)
|
||||
/* load all mysql database names */
|
||||
dbnames = getDatabases(service, con);
|
||||
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"Loaded %d MySQL Database Names for service [%s]",
|
||||
dbnames,
|
||||
service->name)));
|
||||
|
@ -121,8 +121,8 @@ DCB *rval;
|
||||
#if defined(SS_DEBUG)
|
||||
rval->dcb_chk_top = CHK_NUM_DCB;
|
||||
rval->dcb_chk_tail = CHK_NUM_DCB;
|
||||
rval->dcb_errhandle_called = false;
|
||||
#endif
|
||||
rval->dcb_errhandle_called = false;
|
||||
rval->dcb_role = role;
|
||||
spinlock_init(&rval->dcb_initlock);
|
||||
spinlock_init(&rval->writeqlock);
|
||||
@ -509,10 +509,14 @@ bool succp = false;
|
||||
pthread_self(),
|
||||
dcb->fd,
|
||||
dcb)));
|
||||
#endif /* SS_DEBUG */
|
||||
#if defined(FAKE_CODE)
|
||||
conn_open[dcb->fd] = false;
|
||||
#endif /* FAKE_CODE */
|
||||
#if defined(SS_DEBUG)
|
||||
ss_debug(dcb->fd = -1;)
|
||||
}
|
||||
#endif
|
||||
#endif /* SS_DEBUG */
|
||||
succp = dcb_set_state(dcb, DCB_STATE_DISCONNECTED, NULL);
|
||||
ss_dassert(succp);
|
||||
dcb_next = dcb->memdata.next;
|
||||
@ -658,7 +662,6 @@ int dcb_read(
|
||||
int rc;
|
||||
int n ;
|
||||
int nread = 0;
|
||||
int eno = 0;
|
||||
|
||||
CHK_DCB(dcb);
|
||||
while (true)
|
||||
@ -669,8 +672,6 @@ int dcb_read(
|
||||
|
||||
if (rc == -1)
|
||||
{
|
||||
eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : ioctl FIONREAD for dcb %p in "
|
||||
@ -678,8 +679,8 @@ int dcb_read(
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state),
|
||||
dcb->fd,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
errno,
|
||||
strerror(errno))));
|
||||
n = -1;
|
||||
goto return_n;
|
||||
}
|
||||
@ -727,22 +728,18 @@ int dcb_read(
|
||||
"for dcb %p fd %d, due %d, %s.",
|
||||
dcb,
|
||||
dcb->fd,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
errno,
|
||||
strerror(errno))));
|
||||
|
||||
n = -1;
|
||||
ss_dassert(buffer != NULL);
|
||||
goto return_n;
|
||||
}
|
||||
GW_NOINTR_CALL(n = read(dcb->fd, GWBUF_DATA(buffer), bufsize);
|
||||
dcb->stats.n_reads++);
|
||||
|
||||
if (n <= 0)
|
||||
{
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
|
||||
if (eno != 0 && eno != EAGAIN && eno != EWOULDBLOCK)
|
||||
{
|
||||
if (errno != 0 && errno != EAGAIN && errno != EWOULDBLOCK)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
@ -751,10 +748,10 @@ int dcb_read(
|
||||
dcb,
|
||||
STRDCBSTATE(dcb->state),
|
||||
dcb->fd,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
errno,
|
||||
strerror(errno))));
|
||||
}
|
||||
gwbuf_free(buffer);
|
||||
gwbuf_free(buffer);
|
||||
goto return_n;
|
||||
}
|
||||
nread += n;
|
||||
@ -862,7 +859,7 @@ int below_water;
|
||||
while (queue != NULL)
|
||||
{
|
||||
int qlen;
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
if (dcb->dcb_role == DCB_ROLE_REQUEST_HANDLER &&
|
||||
dcb->session != NULL)
|
||||
{
|
||||
@ -878,7 +875,7 @@ int below_water;
|
||||
fail_next_backend_fd = false;
|
||||
}
|
||||
}
|
||||
#endif /* SS_DEBUG */
|
||||
#endif /* FAKE_CODE */
|
||||
qlen = GWBUF_LENGTH(queue);
|
||||
GW_NOINTR_CALL(
|
||||
w = gw_write(
|
||||
@ -1684,7 +1681,7 @@ int gw_write(
|
||||
size_t nbytes)
|
||||
{
|
||||
int w;
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
if (dcb_fake_write_errno[fd] != 0) {
|
||||
ss_dassert(dcb_fake_write_ev[fd] != 0);
|
||||
w = write(fd, buf, nbytes/2); /*< leave peer to read missing bytes */
|
||||
@ -1698,7 +1695,7 @@ int gw_write(
|
||||
}
|
||||
#else
|
||||
w = write(fd, buf, nbytes);
|
||||
#endif /* SS_DEBUG && SS_TEST */
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
#if defined(SS_DEBUG_MYSQL)
|
||||
{
|
||||
|
@ -1025,7 +1025,7 @@ int main(int argc, char **argv)
|
||||
|
||||
progname = *argv;
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
memset(conn_open, 0, sizeof(bool)*10240);
|
||||
memset(dcb_fake_write_errno, 0, sizeof(unsigned char)*10240);
|
||||
memset(dcb_fake_write_ev, 0, sizeof(__int32_t)*10240);
|
||||
@ -1033,7 +1033,7 @@ int main(int argc, char **argv)
|
||||
fail_next_client_fd = false;
|
||||
fail_next_accept = 0;
|
||||
fail_accept_errno = 0;
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
file_write_header(stderr);
|
||||
/*<
|
||||
* Register functions which are called at exit except libmysqld-related,
|
||||
@ -1851,8 +1851,6 @@ static int write_pid_file(char *home_dir) {
|
||||
|
||||
/* close file */
|
||||
close(fd);
|
||||
|
||||
fprintf(stderr, "MaxScale PID %s in pidfile %s\n", pidstr, pidfile);
|
||||
}
|
||||
|
||||
/* success */
|
||||
|
@ -26,6 +26,8 @@
|
||||
* 08/07/13 Mark Riddoch Initial implementation
|
||||
* 23/05/14 Massimiliano Pinto Addition of monitor_interval parameter
|
||||
* and monitor id
|
||||
* 30/10/14 Massimiliano Pinto Addition of disable_master_failback parameter
|
||||
* 07/11/14 Massimiliano Pinto Addition of monitor network timeouts
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -329,3 +331,31 @@ monitorDetectStaleMaster(MONITOR *mon, int enable)
|
||||
mon->module->detectStaleMaster(mon->handle, enable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable Master Failback
|
||||
*
|
||||
* @param mon The monitor instance
|
||||
* @param disable The value 1 disable the failback, 0 keeps it
|
||||
*/
|
||||
void
|
||||
monitorDisableMasterFailback(MONITOR *mon, int disable)
|
||||
{
|
||||
if (mon->module->disableMasterFailback != NULL) {
|
||||
mon->module->disableMasterFailback(mon->handle, disable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Monitor timeouts for connect/read/write
|
||||
*
|
||||
* @param mon The monitor instance
|
||||
* @param type The timeout handling type
|
||||
* @param value The timeout to set
|
||||
*/
|
||||
void
|
||||
monitorSetNetworkTimeout(MONITOR *mon, int type, int value) {
|
||||
if (mon->module->setNetworkTimeout != NULL) {
|
||||
mon->module->setNetworkTimeout(mon->handle, type, value);
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +625,7 @@ uint32_t ev;
|
||||
thread_data[thread_id].event = ev;
|
||||
}
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
if (dcb_fake_write_ev[dcb->fd] != 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
@ -637,7 +637,7 @@ uint32_t ev;
|
||||
ev |= dcb_fake_write_ev[dcb->fd];
|
||||
dcb_fake_write_ev[dcb->fd] = 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
ss_debug(spinlock_acquire(&dcb->dcb_initlock);)
|
||||
ss_dassert(dcb->state != DCB_STATE_ALLOC);
|
||||
ss_dassert(dcb->state != DCB_STATE_DISCONNECTED);
|
||||
@ -735,7 +735,7 @@ uint32_t ev;
|
||||
if (ev & EPOLLERR)
|
||||
{
|
||||
int eno = gw_getsockerrno(dcb->fd);
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
if (eno == 0) {
|
||||
eno = dcb_fake_write_errno[dcb->fd];
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
@ -748,7 +748,7 @@ uint32_t ev;
|
||||
strerror(eno))));
|
||||
}
|
||||
dcb_fake_write_errno[dcb->fd] = 0;
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
if (eno != 0) {
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
@ -1073,13 +1073,26 @@ double avg1 = 0.0, avg5 = 0.0, avg15 = 0.0;
|
||||
{
|
||||
char *event_string
|
||||
= event_to_string(thread_data[i].event);
|
||||
bool from_heap;
|
||||
|
||||
if (event_string == NULL)
|
||||
{
|
||||
from_heap = false;
|
||||
event_string = "??";
|
||||
}
|
||||
else
|
||||
{
|
||||
from_heap = true;
|
||||
}
|
||||
dcb_printf(dcb,
|
||||
" %2d | %-10s | %6d | %-16p | %s\n",
|
||||
i, state, thread_data[i].n_fds,
|
||||
thread_data[i].cur_dcb, event_string);
|
||||
free(event_string);
|
||||
|
||||
if (from_heap)
|
||||
{
|
||||
free(event_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -269,7 +269,17 @@ MAXKEYS key;
|
||||
errno,
|
||||
strerror(errno))));
|
||||
}
|
||||
chmod(secret_file, S_IRUSR);
|
||||
|
||||
if( chmod(secret_file, S_IRUSR) < 0)
|
||||
{
|
||||
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(errno))));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -30,7 +30,8 @@
|
||||
* 28/05/14 Massimiliano Pinto Addition of rlagd and node_ts fields
|
||||
* 20/06/14 Massimiliano Pinto Addition of master_id, depth, slaves fields
|
||||
* 26/06/14 Mark Riddoch Addition of server parameters
|
||||
* 30/08/14 Massimiliano Pinto Addition of new service status description
|
||||
* 30/08/14 Massimiliano Pinto Addition of new service status description
|
||||
* 30/10/14 Massimiliano Pinto Addition of SERVER_MASTER_STICKINESS description
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -424,6 +425,8 @@ char *status = NULL;
|
||||
strcat(status, "Slave of External Server, ");
|
||||
if (server->status & SERVER_STALE_STATUS)
|
||||
strcat(status, "Stale Status, ");
|
||||
if (server->status & SERVER_MASTER_STICKINESS)
|
||||
strcat(status, "Master Stickiness, ");
|
||||
if (server->status & SERVER_AUTH_ERROR)
|
||||
strcat(status, "Auth Error, ");
|
||||
if (server->status & SERVER_RUNNING)
|
||||
|
@ -145,6 +145,7 @@ SERVICE *service;
|
||||
service->filters = NULL;
|
||||
service->n_filters = 0;
|
||||
service->weightby = 0;
|
||||
service->users = NULL;
|
||||
service->resources = NULL;
|
||||
spinlock_init(&service->spin);
|
||||
spinlock_init(&service->users_table_spin);
|
||||
@ -213,42 +214,46 @@ GWPROTOCOL *funcs;
|
||||
if (strcmp(port->protocol, "MySQLClient") == 0) {
|
||||
int loaded;
|
||||
|
||||
/*
|
||||
* Allocate specific data for MySQL users
|
||||
* including hosts and db names
|
||||
*/
|
||||
service->users = mysql_users_alloc();
|
||||
|
||||
if ((loaded = load_mysql_users(service)) < 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to load users from %s:%d for "
|
||||
"service %s.",
|
||||
port->address,
|
||||
port->port,
|
||||
service->name)));
|
||||
hashtable_free(service->users->data);
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
|
||||
* This way MaxScale could try reloading users' just after startup
|
||||
*/
|
||||
service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME;
|
||||
service->rate_limit.nloads=1;
|
||||
if (service->users == NULL) {
|
||||
/*
|
||||
* Allocate specific data for MySQL users
|
||||
* including hosts and db names
|
||||
*/
|
||||
service->users = mysql_users_alloc();
|
||||
|
||||
if ((loaded = load_mysql_users(service)) < 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Unable to load users from %s:%d for "
|
||||
"service %s.",
|
||||
(port->address == NULL ? "0.0.0.0" : port->address),
|
||||
port->port,
|
||||
service->name)));
|
||||
hashtable_free(service->users->data);
|
||||
free(service->users);
|
||||
dcb_free(port->listener);
|
||||
port->listener = NULL;
|
||||
goto retblock;
|
||||
}
|
||||
/* At service start last update is set to USERS_REFRESH_TIME seconds earlier.
|
||||
* This way MaxScale could try reloading users' just after startup
|
||||
*/
|
||||
service->rate_limit.last=time(NULL) - USERS_REFRESH_TIME;
|
||||
service->rate_limit.nloads=1;
|
||||
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Loaded %d MySQL Users for service [%s].",
|
||||
loaded, service->name)));
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Loaded %d MySQL Users for service [%s].",
|
||||
loaded, service->name)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Generic users table */
|
||||
service->users = users_alloc();
|
||||
if (service->users == NULL) {
|
||||
/* Generic users table */
|
||||
service->users = users_alloc();
|
||||
}
|
||||
}
|
||||
|
||||
if ((funcs=(GWPROTOCOL *)load_module(port->protocol, MODULE_PROTOCOL))
|
||||
|
@ -133,8 +133,9 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
session->router_session =
|
||||
service->router->newSession(service->router_instance,
|
||||
session);
|
||||
|
||||
if (session->router_session == NULL) {
|
||||
|
||||
if (session->router_session == NULL)
|
||||
{
|
||||
/**
|
||||
* Inform other threads that session is closing.
|
||||
*/
|
||||
@ -153,7 +154,6 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
|
||||
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
|
||||
@ -187,20 +187,22 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
session_free(session);
|
||||
client_dcb->session = NULL;
|
||||
session = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGIF(LE, (skygw_log_write(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Failed to create %s session.",
|
||||
"Error : Setting up filters failed. "
|
||||
"Terminating session %s.",
|
||||
service->name)));
|
||||
goto return_session;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spinlock_acquire(&session_spin);
|
||||
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
|
||||
if (session->state != SESSION_STATE_READY)
|
||||
{
|
||||
session_free(session);
|
||||
spinlock_release(&session->ses_lock);
|
||||
session_free(session);
|
||||
client_dcb->session = NULL;
|
||||
session = NULL;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -212,10 +214,13 @@ session_alloc(SERVICE *service, DCB *client_dcb)
|
||||
else
|
||||
{
|
||||
session->state = SESSION_STATE_ROUTER_READY;
|
||||
session->next = allSessions;
|
||||
spinlock_release(&session->ses_lock);
|
||||
spinlock_acquire(&session_spin);
|
||||
session->next = allSessions;
|
||||
allSessions = session;
|
||||
spinlock_release(&session_spin);
|
||||
atomic_add(&service->stats.n_sessions, 1);
|
||||
|
||||
atomic_add(&service->stats.n_sessions, 1);
|
||||
atomic_add(&service->stats.n_current, 1);
|
||||
CHK_SESSION(session);
|
||||
}
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <dcb.h>
|
||||
|
||||
@ -44,6 +44,7 @@ test1()
|
||||
{
|
||||
DCB *dcb;
|
||||
int result;
|
||||
int eno = 0;
|
||||
|
||||
/* Poll tests */
|
||||
ss_dfprintf(stderr,
|
||||
@ -51,10 +52,35 @@ int result;
|
||||
poll_init();
|
||||
ss_dfprintf(stderr, "\t..done\nAdd a DCB");
|
||||
dcb = dcb_alloc(DCB_ROLE_SERVICE_LISTENER);
|
||||
|
||||
if(dcb == NULL){
|
||||
ss_dfprintf(stderr, "\nError on function call: dcb_alloc() returned NULL.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
dcb->fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
poll_add_dcb(dcb);
|
||||
poll_remove_dcb(dcb);
|
||||
poll_add_dcb(dcb);
|
||||
|
||||
if(dcb->fd < 0){
|
||||
ss_dfprintf(stderr, "\nError on function call: socket() returned %d: %s\n",errno,strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if((eno = poll_add_dcb(dcb)) != 0){
|
||||
ss_dfprintf(stderr, "\nError on function call: poll_add_dcb() returned %d.\n",eno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((eno = poll_remove_dcb(dcb)) != 0){
|
||||
ss_dfprintf(stderr, "\nError on function call: poll_remove_dcb() returned %d.\n",eno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((eno = poll_add_dcb(dcb)) != 0){
|
||||
ss_dfprintf(stderr, "\nError on function call: poll_add_dcb() returned %d.\n",eno);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ss_dfprintf(stderr, "\t..done\nStart wait for events.");
|
||||
sleep(10);
|
||||
poll_shutdown();
|
||||
|
@ -225,7 +225,9 @@ int gw_getsockerrno(
|
||||
goto return_eno;
|
||||
}
|
||||
|
||||
getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&eno, &elen);
|
||||
if(getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&eno, &elen) != 0){
|
||||
eno = 0;
|
||||
}
|
||||
|
||||
return_eno:
|
||||
return eno;
|
||||
|
@ -205,9 +205,9 @@ typedef struct dcb_callback {
|
||||
typedef struct dcb {
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t dcb_chk_top;
|
||||
bool dcb_errhandle_called;
|
||||
#endif
|
||||
dcb_role_t dcb_role;
|
||||
bool dcb_errhandle_called; /*< this can be called only once */
|
||||
dcb_role_t dcb_role;
|
||||
SPINLOCK dcb_initlock;
|
||||
DCBEVENTQ evq; /**< The event queue for this DCB */
|
||||
int fd; /**< The descriptor */
|
||||
@ -252,14 +252,14 @@ typedef struct dcb {
|
||||
#endif
|
||||
} DCB;
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
unsigned char dcb_fake_write_errno[10240];
|
||||
__int32_t dcb_fake_write_ev[10240];
|
||||
bool fail_next_backend_fd;
|
||||
bool fail_next_client_fd;
|
||||
int fail_next_accept;
|
||||
int fail_accept_errno;
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
/* A few useful macros */
|
||||
#define DCB_SESSION(x) (x)->session
|
||||
|
@ -33,6 +33,8 @@
|
||||
* 23/05/14 Massimiliano Pinto Addition of defaultId and setInterval
|
||||
* 23/06/14 Massimiliano Pinto Addition of replicationHeartbeat
|
||||
* 28/08/14 Massimiliano Pinto Addition of detectStaleMaster
|
||||
* 30/10/14 Massimiliano Pinto Addition of disableMasterFailback
|
||||
* 07/11/14 Massimiliano Pinto Addition of setNetworkTimeout
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -70,9 +72,11 @@ typedef struct {
|
||||
void (*defaultUser)(void *, char *, char *);
|
||||
void (*diagnostics)(DCB *, void *);
|
||||
void (*setInterval)(void *, size_t);
|
||||
void (*setNetworkTimeout)(void *, int, int);
|
||||
void (*defaultId)(void *, unsigned long);
|
||||
void (*replicationHeartbeat)(void *, int);
|
||||
void (*detectStaleMaster)(void *, int);
|
||||
void (*disableMasterFailback)(void *, int);
|
||||
} MONITOR_OBJECT;
|
||||
|
||||
/**
|
||||
@ -96,6 +100,20 @@ typedef enum
|
||||
MONITOR_STATE_FREED = 0x08
|
||||
} monitor_state_t;
|
||||
|
||||
/**
|
||||
* Monitor network timeout types
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
MONITOR_CONNECT_TIMEOUT = 0,
|
||||
MONITOR_READ_TIMEOUT = 1,
|
||||
MONITOR_WRITE_TIMEOUT = 2
|
||||
} monitor_timeouts_t;
|
||||
|
||||
#define DEFAULT_CONNECT_TIMEOUT 3
|
||||
#define DEFAULT_READ_TIMEOUT 1
|
||||
#define DEFAULT_WRITE_TIMEOUT 2
|
||||
|
||||
/**
|
||||
* Representation of the running monitor.
|
||||
*/
|
||||
@ -123,4 +141,6 @@ extern void monitorSetId(MONITOR *, unsigned long);
|
||||
extern void monitorSetInterval (MONITOR *, unsigned long);
|
||||
extern void monitorSetReplicationHeartbeat(MONITOR *, int);
|
||||
extern void monitorDetectStaleMaster(MONITOR *, int);
|
||||
extern void monitorDisableMasterFailback(MONITOR *, int);
|
||||
extern void monitorSetNetworkTimeout(MONITOR *, int, int);
|
||||
#endif
|
||||
|
@ -40,6 +40,7 @@
|
||||
* 26/06/14 Mark Riddoch Adidtion of server parameters
|
||||
* 30/07/14 Massimiliano Pinto Addition of NDB status for MySQL Cluster
|
||||
* 30/08/14 Massimiliano Pinto Addition of SERVER_STALE_STATUS
|
||||
* 27/10/14 Massimiliano Pinto Addition of SERVER_MASTER_STICKINESS
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -105,6 +106,7 @@ typedef struct server {
|
||||
#define SERVER_MAINT 0x0020 /**<< Server is in maintenance mode */
|
||||
#define SERVER_SLAVE_OF_EXTERNAL_MASTER 0x0040 /**<< Server is slave of a Master outside the provided replication topology */
|
||||
#define SERVER_STALE_STATUS 0x0080 /**<< Server stale status, monitor didn't update it */
|
||||
#define SERVER_MASTER_STICKINESS 0x0100 /**<< Server Master stickiness */
|
||||
#define SERVER_AUTH_ERROR 0x1000 /**<< Authentication erorr from monitor */
|
||||
|
||||
/**
|
||||
|
@ -435,7 +435,7 @@ HINT_MODE mode = HM_EXECUTE;
|
||||
token_free(tok);
|
||||
} /*< while */
|
||||
|
||||
if (tok->token == TOK_EOL)
|
||||
if ( tok && tok->token == TOK_EOL)
|
||||
{
|
||||
token_free(tok);
|
||||
}
|
||||
@ -550,7 +550,7 @@ HINT_TOKEN *tok;
|
||||
else if (!inword && inquote == '\0' && **ptr == '=')
|
||||
{
|
||||
*dest = **ptr;
|
||||
*dest++;
|
||||
dest++;
|
||||
(*ptr)++;
|
||||
break;
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ routeQuery(FILTER *instance, void *session, GWBUF *queue)
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *)instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION *)session;
|
||||
char *ptr;
|
||||
int length;
|
||||
int length = 0;
|
||||
struct tm t;
|
||||
struct timeval tv;
|
||||
|
||||
|
@ -415,8 +415,9 @@ GWBUF *clone = NULL;
|
||||
modutil_MySQL_Query(queue, &dummy, &length, &residual);
|
||||
clone = gwbuf_clone(queue);
|
||||
my_session->residual = residual;
|
||||
free(ptr);
|
||||
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/* Pass the query downstream */
|
||||
|
@ -748,9 +748,12 @@ int load_filter(FILTERCHAIN* fc, CONFIG* cnf)
|
||||
}
|
||||
|
||||
int x;
|
||||
for(x = 0;x<paramc;x++){
|
||||
free(fparams[x]->name);
|
||||
free(fparams[x]->value);
|
||||
|
||||
if(fparams){
|
||||
for(x = 0;x<paramc;x++){
|
||||
free(fparams[x]->name);
|
||||
free(fparams[x]->value);
|
||||
}
|
||||
}
|
||||
|
||||
free(fparams);
|
||||
@ -769,15 +772,18 @@ FILTERCHAIN* load_filter_module(char* str)
|
||||
flt_ptr->next = instance.head;
|
||||
}
|
||||
|
||||
if((flt_ptr->instance = (FILTER_OBJECT*)load_module(str, MODULE_FILTER)) == NULL)
|
||||
{
|
||||
printf("Error: Module loading failed: %s\n",str);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Module loading failed: %s\n",str);
|
||||
free(flt_ptr->down);
|
||||
free(flt_ptr);
|
||||
return NULL;
|
||||
}
|
||||
flt_ptr->name = strdup(str);
|
||||
if(flt_ptr){
|
||||
if( (flt_ptr->instance = (FILTER_OBJECT*)load_module(str, MODULE_FILTER)) == NULL)
|
||||
{
|
||||
printf("Error: Module loading failed: %s\n",str);
|
||||
skygw_log_write(LOGFILE_ERROR,"Error: Module loading failed: %s\n",str);
|
||||
free(flt_ptr->down);
|
||||
free(flt_ptr);
|
||||
return NULL;
|
||||
}
|
||||
flt_ptr->name = strdup(str);
|
||||
}
|
||||
|
||||
return flt_ptr;
|
||||
}
|
||||
|
||||
@ -925,14 +931,35 @@ GWBUF* gen_packet(PACKET pkt)
|
||||
|
||||
int process_opts(int argc, char** argv)
|
||||
{
|
||||
unsigned int fd = open_file("harness.cnf",1), buffsize = 1024;
|
||||
int rd,rdsz;
|
||||
unsigned int fsize;
|
||||
int fd, buffsize = 1024;
|
||||
int rd,rdsz, rval;
|
||||
size_t fsize;
|
||||
char *buff = calloc(buffsize,sizeof(char)), *tok = NULL;
|
||||
|
||||
/**Parse 'harness.cnf' file*/
|
||||
fsize = lseek(fd,0,SEEK_END);
|
||||
lseek(fd,0,SEEK_SET);
|
||||
|
||||
if(buff == NULL){
|
||||
printf("Error: Call to malloc() failed.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((fd = open_file("harness.cnf",1)) < 0){
|
||||
printf("Failed to open configuration file.\n");
|
||||
free(buff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
if( (rval = lseek(fd,0,SEEK_END)) < 0 ||
|
||||
lseek(fd,0,SEEK_SET) < 0){
|
||||
printf("Error: Cannot seek file.\n");
|
||||
close(fd);
|
||||
free(buff);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fsize = (size_t)rval;
|
||||
|
||||
instance.thrcount = 1;
|
||||
instance.session_count = 1;
|
||||
rdsz = read(fd,buff,fsize);
|
||||
|
@ -314,10 +314,10 @@ char *remote, *user;
|
||||
else
|
||||
my_session->userName = NULL;
|
||||
my_session->active = 1;
|
||||
if (my_instance->source && strcmp(my_session->clientHost,
|
||||
if (my_instance->source && my_session->clientHost && strcmp(my_session->clientHost,
|
||||
my_instance->source))
|
||||
my_session->active = 0;
|
||||
if (my_instance->user && strcmp(my_session->userName,
|
||||
if (my_instance->user && my_session->userName && strcmp(my_session->userName,
|
||||
my_instance->user))
|
||||
my_session->active = 0;
|
||||
|
||||
|
@ -281,6 +281,7 @@ typedef struct {
|
||||
* created or received */
|
||||
unsigned long tid; /*< MySQL Thread ID, in
|
||||
* handshake */
|
||||
unsigned int charset; /*< MySQL character set at connect time */
|
||||
#if defined(SS_DEBUG)
|
||||
skygw_chk_t protocol_chk_tail;
|
||||
#endif
|
||||
@ -296,6 +297,7 @@ typedef struct {
|
||||
#define MYSQL_GET_STMTOK_NATTR(payload) (gw_mysql_get_byte2(&payload[11]))
|
||||
#define MYSQL_IS_ERROR_PACKET(payload) (MYSQL_GET_COMMAND(payload)==0xff)
|
||||
#define MYSQL_IS_COM_QUIT(payload) (MYSQL_GET_COMMAND(payload)==0x01)
|
||||
#define MYSQL_IS_CHANGE_USER(payload) (MYSQL_GET_COMMAND(payload)==0x11)
|
||||
#define MYSQL_GET_NATTR(payload) ((int)payload[4])
|
||||
|
||||
#endif /** _MYSQL_PROTOCOL_H */
|
||||
@ -313,6 +315,7 @@ int gw_send_authentication_to_backend(
|
||||
char *user,
|
||||
uint8_t *passwd,
|
||||
MySQLProtocol *protocol);
|
||||
|
||||
const char *gw_mysql_protocol_state2string(int state);
|
||||
int gw_do_connect_to_backend(char *host, int port, int* fd);
|
||||
int mysql_send_com_quit(DCB* dcb, int packet_number, GWBUF* buf);
|
||||
@ -334,6 +337,11 @@ int gw_send_change_user_to_backend(
|
||||
char *user,
|
||||
uint8_t *passwd,
|
||||
MySQLProtocol *protocol);
|
||||
|
||||
GWBUF* gw_create_change_user_packet(
|
||||
MYSQL_session* mses,
|
||||
MySQLProtocol* protocol);
|
||||
|
||||
int gw_find_mysql_user_password_sha1(
|
||||
char *username,
|
||||
uint8_t *gateway_password,
|
||||
|
@ -30,6 +30,8 @@
|
||||
* Interval is printed in diagnostics.
|
||||
* 03/06/14 Mark Riddoch Add support for maintenance mode
|
||||
* 24/06/14 Massimiliano Pinto Added depth level 0 for each node
|
||||
* 30/10/14 Massimiliano Pinto Added disableMasterFailback feature
|
||||
* 10/11/14 Massimiliano Pinto Added setNetworkTimeout for connect,read,write
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -52,7 +54,7 @@ extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.2.0";
|
||||
static char *version_str = "V1.4.0";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
@ -68,6 +70,10 @@ static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUsers(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
static MONITOR_SERVERS *get_candidate_master(MONITOR_SERVERS *);
|
||||
static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *, MONITOR_SERVERS *, int);
|
||||
static void disableMasterFailback(void *, int);
|
||||
static void setNetworkTimeout(void *arg, int type, int value);
|
||||
|
||||
static MONITOR_OBJECT MyObject = {
|
||||
startMonitor,
|
||||
@ -76,10 +82,12 @@ static MONITOR_OBJECT MyObject = {
|
||||
unregisterServer,
|
||||
defaultUsers,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
setInterval,
|
||||
setNetworkTimeout,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
disableMasterFailback
|
||||
};
|
||||
|
||||
/**
|
||||
@ -147,6 +155,11 @@ MYSQL_MONITOR *handle;
|
||||
handle->defaultPasswd = NULL;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->disableMasterFailback = 0;
|
||||
handle->master = NULL;
|
||||
handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT;
|
||||
handle->read_timeout=DEFAULT_READ_TIMEOUT;
|
||||
handle->write_timeout=DEFAULT_WRITE_TIMEOUT;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
@ -264,6 +277,10 @@ char *sep;
|
||||
}
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tMaster Failback:\t%s\n", (handle->disableMasterFailback == 1) ? "off" : "on");
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
@ -301,16 +318,18 @@ MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
* @param database The database to probe
|
||||
* @param handle The MySQL Monitor object
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MONITOR_SERVERS *database, char *defaultUser, char *defaultPasswd)
|
||||
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isjoined = 0;
|
||||
char *uname = defaultUser, *passwd = defaultPasswd;
|
||||
char *uname = handle->defaultUser;
|
||||
char *passwd = handle->defaultPasswd;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
@ -330,10 +349,15 @@ char *server_string;
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int read_timeout = 1;
|
||||
int connect_timeout = handle->connect_timeout;
|
||||
int read_timeout = handle->read_timeout;
|
||||
int write_timeout = handle->write_timeout;;
|
||||
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con, database->server->name,
|
||||
uname, dpwd, NULL, database->server->port, NULL, 0) == NULL)
|
||||
@ -345,7 +369,15 @@ char *server_string;
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con))));
|
||||
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
|
||||
/* Also clear Joined, M/S and Stickiness bits */
|
||||
server_clear_status(database->server, SERVER_JOINED);
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
server_clear_status(database->server, SERVER_MASTER_STICKINESS);
|
||||
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
@ -421,10 +453,11 @@ char *server_string;
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr;
|
||||
long master_id;
|
||||
size_t nrounds = 0;
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr;
|
||||
size_t nrounds = 0;
|
||||
MONITOR_SERVERS *candidate_master = NULL;
|
||||
int master_stickiness = handle->disableMasterFailback;
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
@ -453,39 +486,35 @@ size_t nrounds = 0;
|
||||
* interval, then skip monitoring checks. Excluding the first
|
||||
* round.
|
||||
*/
|
||||
if (nrounds != 0 &&
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >=
|
||||
MON_BASE_INTERVAL_MS)
|
||||
|
||||
if (nrounds != 0 && ((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >= MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
nrounds += 1;
|
||||
master_id = -1;
|
||||
ptr = handle->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
unsigned int prev_status = ptr->server->status;
|
||||
monitorDatabase(ptr, handle->defaultUser, handle->defaultPasswd);
|
||||
|
||||
/* set master_id to the lowest value of ptr->server->node_id */
|
||||
monitorDatabase(handle, ptr);
|
||||
|
||||
/* clear bits for non member nodes */
|
||||
if ( ! SERVER_IN_MAINT(ptr->server) && (ptr->server->node_id < 0 || ! SERVER_IS_JOINED(ptr->server))) {
|
||||
ptr->server->depth = -1;
|
||||
|
||||
if ((! SERVER_IN_MAINT(ptr->server)) && ptr->server->node_id >= 0 && SERVER_IS_JOINED(ptr->server)) {
|
||||
ptr->server->depth = 0;
|
||||
if (ptr->server->node_id < master_id && master_id >= 0) {
|
||||
master_id = ptr->server->node_id;
|
||||
} else {
|
||||
if (master_id < 0) {
|
||||
master_id = ptr->server->node_id;
|
||||
}
|
||||
}
|
||||
} else if (!SERVER_IN_MAINT(ptr->server)) {
|
||||
/* clear M/S status */
|
||||
server_clear_status(ptr->server, SERVER_SLAVE);
|
||||
server_clear_status(ptr->server, SERVER_MASTER);
|
||||
ptr->server->depth = -1;
|
||||
|
||||
/* clear master sticky status */
|
||||
server_clear_status(ptr->server, SERVER_MASTER_STICKINESS);
|
||||
}
|
||||
|
||||
/* Log server status change */
|
||||
if (ptr->server->status != prev_status ||
|
||||
SERVER_IS_DOWN(ptr->server))
|
||||
{
|
||||
@ -496,24 +525,50 @@ size_t nrounds = 0;
|
||||
ptr->server->port,
|
||||
STRSRVSTATUS(ptr->server))));
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's select a master server:
|
||||
* it could be the candidate master following MIN(node_id) rule or
|
||||
* the server that was master in the previous monitor polling cycle
|
||||
* Decision depends on master_stickiness value set in configuration
|
||||
*/
|
||||
|
||||
/* get the candidate master, followinf MIN(node_id) rule */
|
||||
candidate_master = get_candidate_master(handle->databases);
|
||||
|
||||
/* Select the master, based on master_stickiness */
|
||||
handle->master = set_cluster_master(handle->master, candidate_master, master_stickiness);
|
||||
|
||||
ptr = handle->databases;
|
||||
|
||||
/* this server loop sets Master and Slave roles */
|
||||
while (ptr)
|
||||
{
|
||||
if ((! SERVER_IN_MAINT(ptr->server)) && ptr->server->node_id >= 0 && master_id >= 0) {
|
||||
/* set the Master role */
|
||||
if (SERVER_IS_JOINED(ptr->server) && (ptr->server->node_id == master_id)) {
|
||||
server_set_status(ptr->server, SERVER_MASTER);
|
||||
server_clear_status(ptr->server, SERVER_SLAVE);
|
||||
} else if (SERVER_IS_JOINED(ptr->server) && (ptr->server->node_id > master_id)) {
|
||||
while (ptr && handle->master) {
|
||||
if (!SERVER_IS_JOINED(ptr->server) || SERVER_IN_MAINT(ptr->server)) {
|
||||
ptr = ptr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptr != handle->master) {
|
||||
/* set the Slave role */
|
||||
server_set_status(ptr->server, SERVER_SLAVE);
|
||||
server_clear_status(ptr->server, SERVER_MASTER);
|
||||
}
|
||||
server_set_status(ptr->server, SERVER_SLAVE);
|
||||
server_clear_status(ptr->server, SERVER_MASTER);
|
||||
|
||||
/* clear master stickyness */
|
||||
server_clear_status(ptr->server, SERVER_MASTER_STICKINESS);
|
||||
} else {
|
||||
/* set the Master role */
|
||||
server_set_status(handle->master->server, SERVER_MASTER);
|
||||
server_clear_status(handle->master->server, SERVER_SLAVE);
|
||||
|
||||
if (candidate_master && handle->master->server->node_id != candidate_master->server->node_id) {
|
||||
/* set master stickyness */
|
||||
server_set_status(handle->master->server, SERVER_MASTER_STICKINESS);
|
||||
} else {
|
||||
/* clear master stickyness */
|
||||
server_clear_status(ptr->server, SERVER_MASTER_STICKINESS);
|
||||
}
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
@ -533,3 +588,143 @@ setInterval(void *arg, size_t interval)
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
/**
|
||||
* get candidate master from all nodes
|
||||
*
|
||||
* The current available rule: get the server with MIN(node_id)
|
||||
* node_id comes from 'wsrep_local_index' variable
|
||||
*
|
||||
* @param servers The monitored servers list
|
||||
* @return The candidate master on success, NULL on failure
|
||||
*/
|
||||
static MONITOR_SERVERS *get_candidate_master(MONITOR_SERVERS *servers) {
|
||||
MONITOR_SERVERS *ptr = servers;
|
||||
MONITOR_SERVERS *candidate_master = NULL;
|
||||
long min_id = -1;
|
||||
|
||||
/* set min_id to the lowest value of ptr->server->node_id */
|
||||
while(ptr) {
|
||||
if ((! SERVER_IN_MAINT(ptr->server)) && ptr->server->node_id >= 0 && SERVER_IS_JOINED(ptr->server)) {
|
||||
ptr->server->depth = 0;
|
||||
if ((ptr->server->node_id < min_id) && min_id >= 0) {
|
||||
min_id = ptr->server->node_id;
|
||||
candidate_master = ptr;
|
||||
} else {
|
||||
if (min_id < 0) {
|
||||
min_id = ptr->server->node_id;
|
||||
candidate_master = ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return candidate_master;
|
||||
}
|
||||
|
||||
/**
|
||||
* set the master server in the cluster
|
||||
*
|
||||
* master could be the last one from previous monitor cycle Iis running) or
|
||||
* the candidate master.
|
||||
* The selection is based on the configuration option mapped to master_stickiness
|
||||
* The candidate master may change over time due to
|
||||
* 'wsrep_local_index' value change in the Galera Cluster
|
||||
* Enabling master_stickiness will avoid master change unless a failure is spotted
|
||||
*
|
||||
* @param current_master Previous master server
|
||||
* @param candidate_master The candidate master server accordingly to the selection rule
|
||||
* @return The master node pointer (could be NULL)
|
||||
*/
|
||||
static MONITOR_SERVERS *set_cluster_master(MONITOR_SERVERS *current_master, MONITOR_SERVERS *candidate_master, int master_stickiness) {
|
||||
/*
|
||||
* if current master is not set or master_stickiness is not enable
|
||||
* just return candidate_master.
|
||||
*/
|
||||
if (current_master == NULL || master_stickiness == 0) {
|
||||
return candidate_master;
|
||||
} else {
|
||||
/*
|
||||
* if current_master is still a cluster member use it
|
||||
*
|
||||
*/
|
||||
if (SERVER_IS_JOINED(current_master->server) && (! SERVER_IN_MAINT(current_master->server))) {
|
||||
return current_master;
|
||||
} else
|
||||
return candidate_master;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable/Enable the Master failback in a Galera Cluster.
|
||||
*
|
||||
* A restarted / rejoined node may get back the previous 'wsrep_local_index'
|
||||
* from Cluster: if the value is the lowest in the cluster it will be selected as Master
|
||||
* This will cause a Master change even if there is no failure.
|
||||
* The option if set to 1 will avoid this situation, keeping the current Master (if running) available
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param disable To disable it use 1, 0 keeps failback
|
||||
*/
|
||||
static void
|
||||
disableMasterFailback(void *arg, int disable)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->disableMasterFailback, &disable, sizeof(int));
|
||||
}
|
||||
|
||||
static void
|
||||
setNetworkTimeout(void *arg, int type, int value)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
int max_timeout = (int)(handle->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
824
server/modules/monitor/mm_mon.c
Normal file
824
server/modules/monitor/mm_mon.c
Normal file
@ -0,0 +1,824 @@
|
||||
/*
|
||||
* 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 mysql_mon.c - A MySQL Multi Muster cluster monitor
|
||||
*
|
||||
* @verbatim
|
||||
* Revision History
|
||||
*
|
||||
* Date Who Description
|
||||
* 08/09/14 Massimiliano Pinto Initial implementation
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <monitor.h>
|
||||
#include <mysqlmon.h>
|
||||
#include <thread.h>
|
||||
#include <mysql.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <skygw_utils.h>
|
||||
#include <log_manager.h>
|
||||
#include <secrets.h>
|
||||
#include <dcb.h>
|
||||
#include <modinfo.h>
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.0.1";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
MODULE_BETA_RELEASE,
|
||||
MONITOR_VERSION,
|
||||
"A MySQL Multi Master monitor"
|
||||
};
|
||||
|
||||
static void *startMonitor(void *);
|
||||
static void stopMonitor(void *);
|
||||
static void registerServer(void *, SERVER *);
|
||||
static void unregisterServer(void *, SERVER *);
|
||||
static void defaultUser(void *, char *, char *);
|
||||
static void diagnostics(DCB *, void *);
|
||||
static void setInterval(void *, size_t);
|
||||
static void detectStaleMaster(void *, int);
|
||||
static bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *);
|
||||
static void monitor_set_pending_status(MONITOR_SERVERS *, int);
|
||||
static void monitor_clear_pending_status(MONITOR_SERVERS *, int);
|
||||
|
||||
static MONITOR_OBJECT MyObject = {
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
registerServer,
|
||||
unregisterServer,
|
||||
defaultUser,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
detectStaleMaster,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* Implementation of the mandatory version entry point
|
||||
*
|
||||
* @return version string of the module
|
||||
*/
|
||||
char *
|
||||
version()
|
||||
{
|
||||
return version_str;
|
||||
}
|
||||
|
||||
/**
|
||||
* The module initialisation routine, called when the module
|
||||
* is first loaded.
|
||||
*/
|
||||
void
|
||||
ModuleInit()
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write(
|
||||
LOGFILE_MESSAGE,
|
||||
"Initialise the MySQL Monitor module %s.",
|
||||
version_str)));
|
||||
}
|
||||
|
||||
/**
|
||||
* The module entry point routine. It is this routine that
|
||||
* must populate the structure that is referred to as the
|
||||
* "module object", this is a structure with the set of
|
||||
* external entry points for this module.
|
||||
*
|
||||
* @return The module object
|
||||
*/
|
||||
MONITOR_OBJECT *
|
||||
GetModuleObject()
|
||||
{
|
||||
return &MyObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the instance of the monitor, returning a handle on the monitor.
|
||||
*
|
||||
* This function creates a thread to execute the actual monitoring.
|
||||
*
|
||||
* @param arg The current handle - NULL if first start
|
||||
* @return A handle to use when interacting with the monitor
|
||||
*/
|
||||
static void *
|
||||
startMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle;
|
||||
|
||||
if (arg)
|
||||
{
|
||||
handle = arg; /* Must be a restart */
|
||||
handle->shutdown = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((handle = (MYSQL_MONITOR *)malloc(sizeof(MYSQL_MONITOR))) == NULL)
|
||||
return NULL;
|
||||
handle->databases = NULL;
|
||||
handle->shutdown = 0;
|
||||
handle->defaultUser = NULL;
|
||||
handle->defaultPasswd = NULL;
|
||||
handle->id = MONITOR_DEFAULT_ID;
|
||||
handle->interval = MONITOR_INTERVAL;
|
||||
handle->replicationHeartbeat = 0;
|
||||
handle->detectStaleMaster = 0;
|
||||
handle->master = NULL;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop a running monitor
|
||||
*
|
||||
* @param arg Handle on thr running monior
|
||||
*/
|
||||
static void
|
||||
stopMonitor(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
handle->shutdown = 1;
|
||||
thread_wait((void *)handle->tid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a server that must be added to the monitored servers for
|
||||
* a monitoring module.
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to add
|
||||
*/
|
||||
static void
|
||||
registerServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *db;
|
||||
|
||||
if ((db = (MONITOR_SERVERS *)malloc(sizeof(MONITOR_SERVERS))) == NULL)
|
||||
return;
|
||||
db->server = server;
|
||||
db->con = NULL;
|
||||
db->next = NULL;
|
||||
db->mon_err_count = 0;
|
||||
db->mon_prev_status = 0;
|
||||
/* pending status is updated by monitorMain */
|
||||
db->pending_status = 0;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
|
||||
if (handle->databases == NULL)
|
||||
handle->databases = db;
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = db;
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a server from those being monitored by a monitoring module
|
||||
*
|
||||
* @param arg A handle on the running monitor module
|
||||
* @param server The server to remove
|
||||
*/
|
||||
static void
|
||||
unregisterServer(void *arg, SERVER *server)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr, *lptr;
|
||||
|
||||
spinlock_acquire(&handle->lock);
|
||||
if (handle->databases == NULL)
|
||||
{
|
||||
spinlock_release(&handle->lock);
|
||||
return;
|
||||
}
|
||||
if (handle->databases->server == server)
|
||||
{
|
||||
ptr = handle->databases;
|
||||
handle->databases = handle->databases->next;
|
||||
free(ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = handle->databases;
|
||||
while (ptr->next != NULL && ptr->next->server != server)
|
||||
ptr = ptr->next;
|
||||
if (ptr->next)
|
||||
{
|
||||
lptr = ptr->next;
|
||||
ptr->next = ptr->next->next;
|
||||
free(lptr);
|
||||
}
|
||||
}
|
||||
spinlock_release(&handle->lock);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default username and password to use to monitor if the server does not
|
||||
* override this.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param uname The default user name
|
||||
* @param passwd The default password
|
||||
*/
|
||||
static void
|
||||
defaultUser(void *arg, char *uname, char *passwd)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
|
||||
if (handle->defaultUser)
|
||||
free(handle->defaultUser);
|
||||
if (handle->defaultPasswd)
|
||||
free(handle->defaultPasswd);
|
||||
handle->defaultUser = strdup(uname);
|
||||
handle->defaultPasswd = strdup(passwd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Daignostic interface
|
||||
*
|
||||
* @param dcb DCB to print diagnostics
|
||||
* @param arg The monitor handle
|
||||
*/
|
||||
static void diagnostics(DCB *dcb, void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *db;
|
||||
char *sep;
|
||||
|
||||
switch (handle->status)
|
||||
{
|
||||
case MONITOR_RUNNING:
|
||||
dcb_printf(dcb, "\tMonitor running\n");
|
||||
break;
|
||||
case MONITOR_STOPPING:
|
||||
dcb_printf(dcb, "\tMonitor stopping\n");
|
||||
break;
|
||||
case MONITOR_STOPPED:
|
||||
dcb_printf(dcb, "\tMonitor stopped\n");
|
||||
break;
|
||||
}
|
||||
|
||||
dcb_printf(dcb,"\tSampling interval:\t%lu milliseconds\n", handle->interval);
|
||||
dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
sep = "";
|
||||
while (db)
|
||||
{
|
||||
dcb_printf(dcb,
|
||||
"%s%s:%d",
|
||||
sep,
|
||||
db->server->name,
|
||||
db->server->port);
|
||||
sep = ", ";
|
||||
db = db->next;
|
||||
}
|
||||
dcb_printf(dcb, "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitor an individual server
|
||||
*
|
||||
* @param handle The MySQL Monitor object
|
||||
* @param database The database to probe
|
||||
*/
|
||||
static void
|
||||
monitorDatabase(MYSQL_MONITOR *handle, MONITOR_SERVERS *database)
|
||||
{
|
||||
MYSQL_ROW row;
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
int isslave = 0;
|
||||
int ismaster = 0;
|
||||
char *uname = handle->defaultUser;
|
||||
char *passwd = handle->defaultPasswd;
|
||||
unsigned long int server_version = 0;
|
||||
char *server_string;
|
||||
|
||||
if (database->server->monuser != NULL)
|
||||
{
|
||||
uname = database->server->monuser;
|
||||
passwd = database->server->monpw;
|
||||
}
|
||||
|
||||
if (uname == NULL)
|
||||
return;
|
||||
|
||||
/* Don't probe servers in maintenance mode */
|
||||
if (SERVER_IN_MAINT(database->server))
|
||||
return;
|
||||
|
||||
/** Store prevous status */
|
||||
database->mon_prev_status = database->server->status;
|
||||
|
||||
if (database->con == NULL || mysql_ping(database->con) != 0)
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int read_timeout = 1;
|
||||
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
uname,
|
||||
dpwd,
|
||||
NULL,
|
||||
database->server->port,
|
||||
NULL,
|
||||
0) == NULL)
|
||||
{
|
||||
free(dpwd);
|
||||
|
||||
if (mon_print_fail_status(database))
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor was unable to connect to "
|
||||
"server %s:%d : \"%s\"",
|
||||
database->server->name,
|
||||
database->server->port,
|
||||
mysql_error(database->con))));
|
||||
}
|
||||
|
||||
/* The current server is not running
|
||||
*
|
||||
* Store server NOT running in server and monitor server pending struct
|
||||
*
|
||||
*/
|
||||
if (mysql_errno(database->con) == ER_ACCESS_DENIED_ERROR)
|
||||
{
|
||||
server_set_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_set_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
server_clear_status(database->server, SERVER_RUNNING);
|
||||
monitor_clear_pending_status(database, SERVER_RUNNING);
|
||||
|
||||
/* Also clear M/S state in both server and monitor server pending struct */
|
||||
server_clear_status(database->server, SERVER_SLAVE);
|
||||
server_clear_status(database->server, SERVER_MASTER);
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Clean addition status too */
|
||||
server_clear_status(database->server, SERVER_STALE_STATUS);
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
|
||||
return;
|
||||
} else {
|
||||
server_clear_status(database->server, SERVER_AUTH_ERROR);
|
||||
monitor_clear_pending_status(database, SERVER_AUTH_ERROR);
|
||||
}
|
||||
free(dpwd);
|
||||
}
|
||||
/* Store current status in both server and monitor server pending struct */
|
||||
server_set_status(database->server, SERVER_RUNNING);
|
||||
monitor_set_pending_status(database, SERVER_RUNNING);
|
||||
|
||||
/* get server version from current server */
|
||||
server_version = mysql_get_server_version(database->con);
|
||||
|
||||
/* get server version string */
|
||||
server_string = (char *)mysql_get_server_info(database->con);
|
||||
if (server_string) {
|
||||
database->server->server_string = realloc(database->server->server_string, strlen(server_string)+1);
|
||||
if (database->server->server_string)
|
||||
strcpy(database->server->server_string, server_string);
|
||||
}
|
||||
|
||||
/* get server_id form current node */
|
||||
if (mysql_query(database->con, "SELECT @@server_id") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long server_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
server_id = strtol(row[0], NULL, 10);
|
||||
if ((errno == ERANGE && (server_id == LONG_MAX
|
||||
|| server_id == LONG_MIN)) || (errno != 0 && server_id == 0))
|
||||
{
|
||||
server_id = -1;
|
||||
}
|
||||
database->server->node_id = server_id;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
/* Check if the Slave_SQL_Running and Slave_IO_Running status is
|
||||
* set to Yes
|
||||
*/
|
||||
|
||||
/* Check first for MariaDB 10.x.x and get status for multimaster replication */
|
||||
if (server_version >= 100000) {
|
||||
|
||||
if (mysql_query(database->con, "SHOW ALL SLAVES STATUS") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
int i = 0;
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
if (strncmp(row[12], "Yes", 3) == 0
|
||||
&& strncmp(row[13], "Yes", 3) == 0) {
|
||||
isslave += 1;
|
||||
}
|
||||
|
||||
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
|
||||
* the replication tree, slaves ids will be added to master(s) and we will have at least the
|
||||
* root master server.
|
||||
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
|
||||
*/
|
||||
if (strncmp(row[12], "Yes", 3) == 0) {
|
||||
/* get Master_Server_Id values */
|
||||
master_id = atol(row[41]);
|
||||
if (master_id == 0)
|
||||
master_id = -1;
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
/* store master_id of current node */
|
||||
memcpy(&database->server->master_id, &master_id, sizeof(long));
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
/* If all configured slaves are running set this node as slave */
|
||||
if (isslave > 0 && isslave == i)
|
||||
isslave = 1;
|
||||
else
|
||||
isslave = 0;
|
||||
}
|
||||
} else {
|
||||
if (mysql_query(database->con, "SHOW SLAVE STATUS") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
long master_id = -1;
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
/* get Slave_IO_Running and Slave_SQL_Running values*/
|
||||
if (strncmp(row[10], "Yes", 3) == 0
|
||||
&& strncmp(row[11], "Yes", 3) == 0) {
|
||||
isslave = 1;
|
||||
}
|
||||
|
||||
/* If Slave_IO_Running = Yes, assign the master_id to current server: this allows building
|
||||
* the replication tree, slaves ids will be added to master(s) and we will have at least the
|
||||
* root master server.
|
||||
* Please note, there could be no slaves at all if Slave_SQL_Running == 'No'
|
||||
*/
|
||||
if (strncmp(row[10], "Yes", 3) == 0) {
|
||||
/* get Master_Server_Id values */
|
||||
master_id = atol(row[39]);
|
||||
if (master_id == 0)
|
||||
master_id = -1;
|
||||
}
|
||||
}
|
||||
/* store master_id of current node */
|
||||
memcpy(&database->server->master_id, &master_id, sizeof(long));
|
||||
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
|
||||
/* get variable 'read_only' set by an external component */
|
||||
if (mysql_query(database->con, "SHOW GLOBAL VARIABLES LIKE 'read_only'") == 0
|
||||
&& (result = mysql_store_result(database->con)) != NULL)
|
||||
{
|
||||
num_fields = mysql_num_fields(result);
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
if (strncasecmp(row[1], "OFF", 3) == 0) {
|
||||
ismaster = 1;
|
||||
}
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
|
||||
/* Remove addition info */
|
||||
monitor_clear_pending_status(database, SERVER_STALE_STATUS);
|
||||
|
||||
/* Set the Slave Role */
|
||||
if (isslave)
|
||||
{
|
||||
monitor_set_pending_status(database, SERVER_SLAVE);
|
||||
/* Avoid any possible stale Master state */
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Set replication depth to 1 */
|
||||
database->server->depth = 1;
|
||||
} else {
|
||||
/* Avoid any possible Master/Slave stale state */
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_clear_pending_status(database, SERVER_MASTER);
|
||||
}
|
||||
|
||||
/* Set the Master role */
|
||||
if (isslave && ismaster)
|
||||
{
|
||||
monitor_clear_pending_status(database, SERVER_SLAVE);
|
||||
monitor_set_pending_status(database, SERVER_MASTER);
|
||||
|
||||
/* Set replication depth to 0 */
|
||||
database->server->depth = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point for the monitoring module thread
|
||||
*
|
||||
* @param arg The handle of the monitor
|
||||
*/
|
||||
static void
|
||||
monitorMain(void *arg)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
MONITOR_SERVERS *ptr;
|
||||
int detect_stale_master = handle->detectStaleMaster;
|
||||
MONITOR_SERVERS *root_master;
|
||||
size_t nrounds = 0;
|
||||
|
||||
if (mysql_thread_init())
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Fatal : mysql_thread_init failed in monitor "
|
||||
"module. Exiting.\n")));
|
||||
return;
|
||||
}
|
||||
|
||||
handle->status = MONITOR_RUNNING;
|
||||
while (1)
|
||||
{
|
||||
if (handle->shutdown)
|
||||
{
|
||||
handle->status = MONITOR_STOPPING;
|
||||
mysql_thread_end();
|
||||
handle->status = MONITOR_STOPPED;
|
||||
return;
|
||||
}
|
||||
|
||||
/** Wait base interval */
|
||||
thread_millisleep(MON_BASE_INTERVAL_MS);
|
||||
/**
|
||||
* Calculate how far away the monitor interval is from its full
|
||||
* cycle and if monitor interval time further than the base
|
||||
* interval, then skip monitoring checks. Excluding the first
|
||||
* round.
|
||||
*/
|
||||
if (nrounds != 0 &&
|
||||
((nrounds*MON_BASE_INTERVAL_MS)%handle->interval) >=
|
||||
MON_BASE_INTERVAL_MS)
|
||||
{
|
||||
nrounds += 1;
|
||||
continue;
|
||||
}
|
||||
nrounds += 1;
|
||||
|
||||
/* start from the first server in the list */
|
||||
ptr = handle->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
/* copy server status into monitor pending_status */
|
||||
ptr->pending_status = ptr->server->status;
|
||||
|
||||
/* monitor current node */
|
||||
monitorDatabase(handle, ptr);
|
||||
|
||||
if (mon_status_changed(ptr))
|
||||
{
|
||||
dcb_call_foreach(DCB_REASON_NOT_RESPONDING);
|
||||
}
|
||||
|
||||
if (mon_status_changed(ptr) ||
|
||||
mon_print_fail_status(ptr))
|
||||
{
|
||||
LOGIF(LM, (skygw_log_write_flush(
|
||||
LOGFILE_MESSAGE,
|
||||
"Backend server %s:%d state : %s",
|
||||
ptr->server->name,
|
||||
ptr->server->port,
|
||||
STRSRVSTATUS(ptr->server))));
|
||||
}
|
||||
if (SERVER_IS_DOWN(ptr->server))
|
||||
{
|
||||
/** Increase this server'e error count */
|
||||
ptr->mon_err_count += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/** Reset this server's error count */
|
||||
ptr->mon_err_count = 0;
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
/* Get Master server pointer */
|
||||
root_master = get_current_master(handle);
|
||||
|
||||
/* Update server status from monitor pending status on that server*/
|
||||
|
||||
ptr = handle->databases;
|
||||
while (ptr)
|
||||
{
|
||||
if (! SERVER_IN_MAINT(ptr->server)) {
|
||||
/* If "detect_stale_master" option is On, let's use the previus master */
|
||||
if (detect_stale_master && root_master && (!strcmp(ptr->server->name, root_master->server->name) && ptr->server->port == root_master->server->port) && (ptr->server->status & SERVER_MASTER) && !(ptr->pending_status & SERVER_MASTER)) {
|
||||
/* in this case server->status will not be updated from pending_status */
|
||||
LOGIF(LM, (skygw_log_write_flush(
|
||||
LOGFILE_MESSAGE, "[mysql_mon]: root server [%s:%i] is no longer Master, let's use it again even if it could be a stale master, you have been warned!", ptr->server->name, ptr->server->port)));
|
||||
/* Set the STALE bit for this server in server struct */
|
||||
server_set_status(ptr->server, SERVER_STALE_STATUS);
|
||||
} else {
|
||||
ptr->server->status = ptr->pending_status;
|
||||
}
|
||||
}
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the monitor sampling interval.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param interval The interval to set in monitor struct, in milliseconds
|
||||
*/
|
||||
static void
|
||||
setInterval(void *arg, size_t interval)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->interval, &interval, sizeof(unsigned long));
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/Disable the MySQL Replication Stale Master dectection, allowing a previouvsly detected master to still act as a Master.
|
||||
* This option must be enabled in order to keep the Master when the replication is stopped or removed from slaves.
|
||||
* If the replication is still stopped when MaxSclale is restarted no Master will be available.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param enable To enable it 1, disable it with 0
|
||||
*/
|
||||
static void
|
||||
detectStaleMaster(void *arg, int enable)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
memcpy(&handle->detectStaleMaster, &enable, sizeof(int));
|
||||
}
|
||||
|
||||
static bool mon_status_changed(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
|
||||
if (mon_srv->mon_prev_status != mon_srv->server->status)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
static bool mon_print_fail_status(
|
||||
MONITOR_SERVERS* mon_srv)
|
||||
{
|
||||
bool succp;
|
||||
int errcount = mon_srv->mon_err_count;
|
||||
uint8_t modval;
|
||||
|
||||
modval = 1<<(MIN(errcount/10, 7));
|
||||
|
||||
if (SERVER_IS_DOWN(mon_srv->server) && errcount%modval == 0)
|
||||
{
|
||||
succp = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_set_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status |= bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear a pending status bit in the monior server
|
||||
*
|
||||
* @param server The server to update
|
||||
* @param bit The bit to clear for the server
|
||||
*/
|
||||
static void
|
||||
monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
/*******
|
||||
* This function returns the master server
|
||||
* from a set of MySQL Multi Master monitored servers
|
||||
* and returns the root server (that has SERVER_MASTER bit)
|
||||
* The server is returned even for servers in 'maintenance' mode.
|
||||
*
|
||||
* @param handle The monitor handle
|
||||
* @return The server at root level with SERVER_MASTER bit
|
||||
*/
|
||||
|
||||
static MONITOR_SERVERS *get_current_master(MYSQL_MONITOR *handle) {
|
||||
MONITOR_SERVERS *ptr;
|
||||
|
||||
ptr = handle->databases;
|
||||
|
||||
while (ptr)
|
||||
{
|
||||
/* The server could be in SERVER_IN_MAINT
|
||||
* that means SERVER_IS_RUNNING returns 0
|
||||
* Let's check only for SERVER_IS_DOWN: server is not running
|
||||
*/
|
||||
if (SERVER_IS_DOWN(ptr->server)) {
|
||||
ptr = ptr->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ptr->server->depth == 0) {
|
||||
handle->master = ptr;
|
||||
}
|
||||
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the root master
|
||||
*/
|
||||
|
||||
if (handle->master != NULL) {
|
||||
/* If the root master is in MAINT, return NULL */
|
||||
if (SERVER_IN_MAINT(handle->master->server)) {
|
||||
return NULL;
|
||||
} else {
|
||||
return handle->master;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@
|
||||
* 28/08/14 Massimiliano Pinto Added detectStaleMaster feature: previous detected master will be used again, even if the replication is stopped.
|
||||
* This means both IO and SQL threads are not working on slaves.
|
||||
* This option is not enabled by default.
|
||||
* 10/11/14 Massimiliano Pinto Addition of setNetworkTimeout for connect, read, write
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -65,7 +66,7 @@ extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
static void monitorMain(void *);
|
||||
|
||||
static char *version_str = "V1.3.0";
|
||||
static char *version_str = "V1.4.0";
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_MONITOR,
|
||||
@ -84,6 +85,7 @@ static void setInterval(void *, size_t);
|
||||
static void defaultId(void *, unsigned long);
|
||||
static void replicationHeartbeat(void *, int);
|
||||
static void detectStaleMaster(void *, int);
|
||||
static void setNetworkTimeout(void *, int, int);
|
||||
static bool mon_status_changed(MONITOR_SERVERS* mon_srv);
|
||||
static bool mon_print_fail_status(MONITOR_SERVERS* mon_srv);
|
||||
static MONITOR_SERVERS *getServerByNodeId(MONITOR_SERVERS *, long);
|
||||
@ -103,9 +105,11 @@ static MONITOR_OBJECT MyObject = {
|
||||
defaultUser,
|
||||
diagnostics,
|
||||
setInterval,
|
||||
setNetworkTimeout,
|
||||
defaultId,
|
||||
replicationHeartbeat,
|
||||
detectStaleMaster
|
||||
detectStaleMaster,
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
@ -177,6 +181,9 @@ MYSQL_MONITOR *handle;
|
||||
handle->replicationHeartbeat = 0;
|
||||
handle->detectStaleMaster = 0;
|
||||
handle->master = NULL;
|
||||
handle->connect_timeout=DEFAULT_CONNECT_TIMEOUT;
|
||||
handle->read_timeout=DEFAULT_READ_TIMEOUT;
|
||||
handle->write_timeout=DEFAULT_WRITE_TIMEOUT;
|
||||
spinlock_init(&handle->lock);
|
||||
}
|
||||
handle->tid = (THREAD)thread_start(monitorMain, handle);
|
||||
@ -323,6 +330,9 @@ char *sep;
|
||||
dcb_printf(dcb,"\tMaxScale MonitorId:\t%lu\n", handle->id);
|
||||
dcb_printf(dcb,"\tReplication lag:\t%s\n", (handle->replicationHeartbeat == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb,"\tDetect Stale Master:\t%s\n", (handle->detectStaleMaster == 1) ? "enabled" : "disabled");
|
||||
dcb_printf(dcb,"\tConnect Timeout:\t%i seconds\n", handle->connect_timeout);
|
||||
dcb_printf(dcb,"\tRead Timeout:\t\t%i seconds\n", handle->read_timeout);
|
||||
dcb_printf(dcb,"\tWrite Timeout:\t\t%i seconds\n", handle->write_timeout);
|
||||
dcb_printf(dcb, "\tMonitored servers: ");
|
||||
|
||||
db = handle->databases;
|
||||
@ -378,11 +388,15 @@ char *server_string;
|
||||
{
|
||||
char *dpwd = decryptPassword(passwd);
|
||||
int rc;
|
||||
int read_timeout = 1;
|
||||
int connect_timeout = handle->connect_timeout;
|
||||
int read_timeout = handle->read_timeout;
|
||||
int write_timeout = handle->write_timeout;
|
||||
|
||||
database->con = mysql_init(NULL);
|
||||
|
||||
rc = mysql_options(database->con, MYSQL_OPT_CONNECT_TIMEOUT, (void *)&connect_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_READ_TIMEOUT, (void *)&read_timeout);
|
||||
rc = mysql_options(database->con, MYSQL_OPT_WRITE_TIMEOUT, (void *)&write_timeout);
|
||||
|
||||
if (mysql_real_connect(database->con,
|
||||
database->server->name,
|
||||
@ -864,6 +878,13 @@ static void set_master_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *databas
|
||||
char heartbeat_insert_query[512]="";
|
||||
char heartbeat_purge_query[512]="";
|
||||
|
||||
if (handle->master == NULL) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"[mysql_mon]: set_master_heartbeat called without an available Master server")));
|
||||
return;
|
||||
}
|
||||
|
||||
/* create the maxscale_schema database */
|
||||
if (mysql_query(database->con, "CREATE DATABASE IF NOT EXISTS maxscale_schema")) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -969,6 +990,13 @@ static void set_slave_heartbeat(MYSQL_MONITOR *handle, MONITOR_SERVERS *database
|
||||
MYSQL_RES *result;
|
||||
int num_fields;
|
||||
|
||||
if (handle->master == NULL) {
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"[mysql_mon]: set_slave_heartbeat called without an available Master server")));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the master_timestamp value from maxscale_schema.replication_heartbeat table */
|
||||
|
||||
sprintf(select_heartbeat_query, "SELECT master_timestamp "
|
||||
@ -1203,3 +1231,65 @@ monitor_clear_pending_status(MONITOR_SERVERS *ptr, int bit)
|
||||
{
|
||||
ptr->pending_status &= ~bit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the default id to use in the monitor.
|
||||
*
|
||||
* @param arg The handle allocated by startMonitor
|
||||
* @param type The connect timeout type
|
||||
* @param value The timeout value to set
|
||||
*/
|
||||
static void
|
||||
setNetworkTimeout(void *arg, int type, int value)
|
||||
{
|
||||
MYSQL_MONITOR *handle = (MYSQL_MONITOR *)arg;
|
||||
int max_timeout = (int)(handle->interval/1000);
|
||||
int new_timeout = max_timeout -1;
|
||||
|
||||
if (new_timeout <= 0)
|
||||
new_timeout = DEFAULT_CONNECT_TIMEOUT;
|
||||
|
||||
switch(type) {
|
||||
case MONITOR_CONNECT_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->connect_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->connect_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Connect Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_READ_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->read_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->read_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Read Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
|
||||
case MONITOR_WRITE_TIMEOUT:
|
||||
if (value < max_timeout) {
|
||||
memcpy(&handle->write_timeout, &value, sizeof(int));
|
||||
} else {
|
||||
memcpy(&handle->write_timeout, &new_timeout, sizeof(int));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"warning : Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
|
||||
", lowering to %i seconds", value, max_timeout, new_timeout)));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Monitor setNetworkTimeout received an unsupported action type %i", type)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,8 @@
|
||||
* 28/05/14 Massimiliano Pinto Addition of new fields in MYSQL_MONITOR struct
|
||||
* 24/06/14 Massimiliano Pinto Addition of master field in MYSQL_MONITOR struct and MONITOR_MAX_NUM_SLAVES
|
||||
* 28/08/14 Massimiliano Pinto Addition of detectStaleMaster
|
||||
* 30/10/14 Massimiliano Pinto Addition of disableMasterFailback
|
||||
* 07/11/14 Massimiliano Pinto Addition of NetworkTimeout: connect, read, write
|
||||
*
|
||||
* @endverbatim
|
||||
*/
|
||||
@ -65,8 +67,16 @@ typedef struct {
|
||||
unsigned long id; /**< Monitor ID */
|
||||
int replicationHeartbeat; /**< Monitor flag for MySQL replication heartbeat */
|
||||
int detectStaleMaster; /**< Monitor flag for MySQL replication Stale Master detection */
|
||||
int disableMasterFailback; /**< Monitor flag for Galera Cluster Master failback */
|
||||
MONITOR_SERVERS *master; /**< Master server for MySQL Master/Slave replication */
|
||||
MONITOR_SERVERS *databases; /**< Linked list of servers to monitor */
|
||||
int connect_timeout; /**< Connect timeout in seconds for mysql_real_connect */
|
||||
int read_timeout; /**< Timeout in seconds to read from the server.
|
||||
* There are retries and the total effective timeout value is three times the option value.
|
||||
*/
|
||||
int write_timeout; /**< Timeout in seconds for each attempt to write to the server.
|
||||
* There are retries and the total effective timeout value is two times the option value.
|
||||
*/
|
||||
} MYSQL_MONITOR;
|
||||
|
||||
#define MONITOR_RUNNING 1
|
||||
|
@ -73,6 +73,8 @@ static MONITOR_OBJECT MyObject = {
|
||||
setInterval,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <httpd.h>
|
||||
#include <gw.h>
|
||||
#include <modinfo.h>
|
||||
#include <log_manager.h>
|
||||
|
||||
MODULE_INFO info = {
|
||||
MODULE_API_PROTOCOL,
|
||||
@ -48,6 +49,8 @@ MODULE_INFO info = {
|
||||
"An experimental HTTPD implementation for use in admnistration"
|
||||
};
|
||||
|
||||
extern int lm_enabled_logfiles_bitmask;
|
||||
|
||||
#define ISspace(x) isspace((int)(x))
|
||||
#define HTTP_SERVER_STRING "MaxScale(c) v.1.0.0"
|
||||
static char *version_str = "V1.0.1";
|
||||
@ -171,7 +174,7 @@ HTTPD_session *client_data = NULL;
|
||||
j++;
|
||||
}
|
||||
|
||||
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf))) {
|
||||
while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf) - 1)) {
|
||||
url[i] = buf[j];
|
||||
i++; j++;
|
||||
}
|
||||
@ -236,7 +239,7 @@ HTTPD_session *client_data = NULL;
|
||||
dcb_printf(dcb, "Welcome to HTTPD MaxScale (c) %s\n\n", version_str);
|
||||
|
||||
if (strcmp(url, "/show") == 0) {
|
||||
if (strlen(query_string)) {
|
||||
if (query_string && strlen(query_string)) {
|
||||
if (strcmp(query_string, "dcb") == 0)
|
||||
dprintAllDCBs(dcb);
|
||||
if (strcmp(query_string, "session") == 0)
|
||||
@ -327,25 +330,29 @@ int n_connect = 0;
|
||||
else
|
||||
{
|
||||
atomic_add(&dcb->stats.n_accepts, 1);
|
||||
client = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
client->fd = so;
|
||||
client->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
if((client = dcb_alloc(DCB_ROLE_REQUEST_HANDLER))){
|
||||
client->fd = so;
|
||||
client->remote = strdup(inet_ntoa(addr.sin_addr));
|
||||
memcpy(&client->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
/* we don't need the session */
|
||||
client->session = NULL;
|
||||
/* we don't need the session */
|
||||
client->session = NULL;
|
||||
|
||||
/* create the session data for HTTPD */
|
||||
client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session));
|
||||
client->data = client_data;
|
||||
/* create the session data for HTTPD */
|
||||
client_data = (HTTPD_session *)calloc(1, sizeof(HTTPD_session));
|
||||
client->data = client_data;
|
||||
|
||||
if (poll_add_dcb(client) == -1)
|
||||
{
|
||||
return n_connect;
|
||||
if (poll_add_dcb(client) == -1)
|
||||
{
|
||||
close(so);
|
||||
return n_connect;
|
||||
}
|
||||
n_connect++;
|
||||
}
|
||||
n_connect++;
|
||||
}
|
||||
}
|
||||
|
||||
return n_connect;
|
||||
}
|
||||
|
||||
@ -373,7 +380,8 @@ httpd_listen(DCB *listener, char *config)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int one = 1;
|
||||
int rc;
|
||||
int rc;
|
||||
int syseno = 0;
|
||||
|
||||
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
if (!parse_bindconfig(config, 6442, &addr))
|
||||
@ -385,12 +393,16 @@ int rc;
|
||||
}
|
||||
|
||||
/* socket options */
|
||||
setsockopt(listener->fd,
|
||||
syseno = setsockopt(listener->fd,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
(char *)&one,
|
||||
sizeof(one));
|
||||
|
||||
if(syseno != 0){
|
||||
skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
/* set NONBLOCKING mode */
|
||||
setnonblocking(listener->fd);
|
||||
|
||||
@ -403,9 +415,7 @@ int rc;
|
||||
rc = listen(listener->fd, SOMAXCONN);
|
||||
|
||||
if (rc == 0) {
|
||||
fprintf(stderr,
|
||||
"Listening http connections at %s\n",
|
||||
config);
|
||||
LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening httpd connections at %s", config)));
|
||||
} else {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
@ -439,15 +449,19 @@ static int httpd_get_line(int sock, char *buf, int size) {
|
||||
if (c == '\r') {
|
||||
n = recv(sock, &c, 1, MSG_PEEK);
|
||||
/* DEBUG printf("%02X\n", c); */
|
||||
if ((n > 0) && (c == '\n'))
|
||||
recv(sock, &c, 1, 0);
|
||||
else
|
||||
if ((n > 0) && (c == '\n')) {
|
||||
if(recv(sock, &c, 1, 0) < 0){
|
||||
c = '\n';
|
||||
}
|
||||
} else {
|
||||
c = '\n';
|
||||
}
|
||||
}
|
||||
buf[i] = c;
|
||||
i++;
|
||||
} else
|
||||
} else {
|
||||
c = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
buf[i] = '\0';
|
||||
|
@ -44,6 +44,7 @@
|
||||
* 12/09/2013 Massimiliano Pinto Added checks in gw_read_backend_event() for gw_read_backend_handshake
|
||||
* 27/09/2013 Massimiliano Pinto Changed in gw_read_backend_event the check for dcb_read(), now is if rc < 0
|
||||
* 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support
|
||||
* 10/11/2014 Massimiliano Pinto Client charset is passed to backend
|
||||
*
|
||||
*/
|
||||
#include <modinfo.h>
|
||||
@ -270,6 +271,11 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
|
||||
CHK_SESSION(session);
|
||||
|
||||
if (session == NULL)
|
||||
{
|
||||
rc = 0;
|
||||
goto return_with_lock;
|
||||
}
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
@ -378,7 +384,6 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
ERRACT_REPLY_CLIENT,
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
ss_dassert(!succp);
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [gw_read_backend_event] "
|
||||
@ -388,13 +393,11 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
dcb,
|
||||
dcb->session)));
|
||||
|
||||
if (session != NULL)
|
||||
{
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
dcb_close(dcb);
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
ss_dassert(dcb->dcb_errhandle_called);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
rc = 1;
|
||||
goto return_rc;
|
||||
@ -468,6 +471,7 @@ static int gw_read_backend_event(DCB *dcb) {
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
ss_dassert(dcb->dcb_errhandle_called);
|
||||
dcb_close(dcb);
|
||||
rc = 0;
|
||||
goto return_rc;
|
||||
@ -852,12 +856,17 @@ static int gw_error_backend_event(DCB *dcb)
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
|
||||
/** There are not required backends available, close session. */
|
||||
if (!succp) {
|
||||
/**
|
||||
* If error handler fails it means that routing session can't continue
|
||||
* and it must be closed. In success, only this DCB is closed.
|
||||
*/
|
||||
if (!succp)
|
||||
{
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
ss_dassert(dcb->dcb_errhandle_called);
|
||||
dcb_close(dcb);
|
||||
|
||||
retblock:
|
||||
@ -906,6 +915,9 @@ static int gw_create_backend_connection(
|
||||
/** Copy client flags to backend protocol */
|
||||
protocol->client_capabilities =
|
||||
((MySQLProtocol *)(backend_dcb->session->client->protocol))->client_capabilities;
|
||||
/** Copy client charset to backend protocol */
|
||||
protocol->charset =
|
||||
((MySQLProtocol *)(backend_dcb->session->client->protocol))->charset;
|
||||
|
||||
/*< if succeed, fd > 0, -1 otherwise */
|
||||
rv = gw_do_connect_to_backend(server->name, server->port, &fd);
|
||||
@ -1047,6 +1059,7 @@ gw_backend_hangup(DCB *dcb)
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
ss_dassert(dcb->dcb_errhandle_called);
|
||||
dcb_close(dcb);
|
||||
|
||||
retblock:
|
||||
@ -1078,8 +1091,8 @@ gw_backend_close(DCB *dcb)
|
||||
mysql_protocol_done(dcb);
|
||||
|
||||
/**
|
||||
* If session->state is set to STOPPING the client and the session must
|
||||
* be closed too.
|
||||
* If session->state is STOPPING, start closing client session.
|
||||
* Otherwise only this backend connection is closed.
|
||||
*/
|
||||
if (session != NULL && session->state == SESSION_STATE_STOPPING)
|
||||
{
|
||||
@ -1144,7 +1157,24 @@ static int backend_write_delayqueue(DCB *dcb)
|
||||
localq = dcb->delayq;
|
||||
dcb->delayq = NULL;
|
||||
spinlock_release(&dcb->delayqlock);
|
||||
rc = dcb_write(dcb, localq);
|
||||
|
||||
if (MYSQL_IS_CHANGE_USER(((uint8_t *)GWBUF_DATA(localq))))
|
||||
{
|
||||
MYSQL_session* mses;
|
||||
GWBUF* new_packet;
|
||||
|
||||
mses = (MYSQL_session *)dcb->session->client->data;
|
||||
new_packet = gw_create_change_user_packet(
|
||||
mses,
|
||||
(MySQLProtocol *)dcb->protocol);
|
||||
/**
|
||||
* Remove previous packet which lacks scramble
|
||||
* and append the new.
|
||||
*/
|
||||
localq = gwbuf_consume(localq, GWBUF_LENGTH(localq));
|
||||
localq = gwbuf_append(localq, new_packet);
|
||||
}
|
||||
rc = dcb_write(dcb, localq);
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
@ -1157,42 +1187,42 @@ static int backend_write_delayqueue(DCB *dcb)
|
||||
SESSION *session = dcb->session;
|
||||
|
||||
CHK_SESSION(session);
|
||||
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
|
||||
if (session != NULL)
|
||||
{
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
#if defined(SS_DEBUG)
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Backend write delayqueue error handling.")));
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Backend write delayqueue error handling.")));
|
||||
#endif
|
||||
errbuf = mysql_create_custom_error(
|
||||
1,
|
||||
0,
|
||||
"Failed to write buffered data to back-end server. "
|
||||
"Buffer was empty or back-end was disconnected during "
|
||||
"operation. Attempting to find a new backend.");
|
||||
|
||||
router->handleError(router_instance,
|
||||
rsession,
|
||||
errbuf,
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
errbuf = mysql_create_custom_error(
|
||||
1,
|
||||
0,
|
||||
"Failed to write buffered data to back-end server. "
|
||||
"Buffer was empty or back-end was disconnected during "
|
||||
"operation. Attempting to find a new backend.");
|
||||
|
||||
router->handleError(router_instance,
|
||||
rsession,
|
||||
errbuf,
|
||||
dcb,
|
||||
ERRACT_NEW_CONNECTION,
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
|
||||
if (!succp)
|
||||
{
|
||||
if (session != NULL)
|
||||
{
|
||||
if (!succp)
|
||||
{
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
dcb_close(dcb);
|
||||
}
|
||||
}
|
||||
|
||||
ss_dassert(dcb->dcb_errhandle_called);
|
||||
dcb_close(dcb);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -1253,6 +1283,16 @@ static int gw_change_user(
|
||||
/* get new database name */
|
||||
strcpy(database, (char *)client_auth_packet);
|
||||
|
||||
/* get character set */
|
||||
if (strlen(database)) {
|
||||
client_auth_packet += strlen(database) + 1;
|
||||
} else {
|
||||
client_auth_packet++;
|
||||
}
|
||||
|
||||
if (client_auth_packet && *client_auth_packet)
|
||||
memcpy(&backend_protocol->charset, client_auth_packet, sizeof(int));
|
||||
|
||||
/* save current_database name */
|
||||
strcpy(current_database, current_session->db);
|
||||
|
||||
@ -1266,13 +1306,25 @@ static int gw_change_user(
|
||||
* decode the token and check the password.
|
||||
* Note: if auth_token_len == 0 && auth_token == NULL, user is without password
|
||||
*/
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1);
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client,
|
||||
auth_token,
|
||||
auth_token_len,
|
||||
client_protocol->scramble,
|
||||
sizeof(client_protocol->scramble),
|
||||
username,
|
||||
client_sha1);
|
||||
|
||||
if (auth_ret != 0) {
|
||||
if (!service_refresh_users(backend->session->client->service)) {
|
||||
/* Try authentication again with new repository data */
|
||||
/* Note: if no auth client authentication will fail */
|
||||
auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1);
|
||||
auth_ret = gw_check_mysql_scramble_data(
|
||||
backend->session->client,
|
||||
auth_token, auth_token_len,
|
||||
client_protocol->scramble,
|
||||
sizeof(client_protocol->scramble),
|
||||
username,
|
||||
client_sha1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1336,7 +1388,7 @@ static int gw_change_user(
|
||||
rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol);
|
||||
/*
|
||||
* Now copy new data into user session
|
||||
*/
|
||||
*/
|
||||
strcpy(current_session->user, username);
|
||||
strcpy(current_session->db, database);
|
||||
memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1));
|
||||
@ -1361,8 +1413,8 @@ static GWBUF* process_response_data (
|
||||
GWBUF* readbuf,
|
||||
int nbytes_to_process) /*< number of new bytes read */
|
||||
{
|
||||
int npackets_left = 0; /*< response's packet count */
|
||||
size_t nbytes_left = 0; /*< nbytes to be read for the packet */
|
||||
int npackets_left = 0; /*< response's packet count */
|
||||
size_t nbytes_left = 0; /*< nbytes to be read for the packet */
|
||||
MySQLProtocol* p;
|
||||
GWBUF* outbuf = NULL;
|
||||
|
||||
@ -1506,4 +1558,4 @@ static bool sescmd_response_complete(
|
||||
succp = false;
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@
|
||||
* 07/05/2014 Massimiliano Pinto Added: specific version string in server handshake
|
||||
* 09/09/2014 Massimiliano Pinto Added: 777 permission for socket path
|
||||
* 13/10/2014 Massimiliano Pinto Added: dbname authentication check
|
||||
* 10/11/2014 Massimiliano Pinto Added: client charset added to protocol struct
|
||||
*
|
||||
*/
|
||||
#include <skygw_utils.h>
|
||||
@ -444,6 +445,9 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* get charset */
|
||||
memcpy(&protocol->charset, client_auth_packet + 4 + 4 + 4, sizeof (int));
|
||||
|
||||
/* get the auth token len */
|
||||
memcpy(&auth_token_len,
|
||||
client_auth_packet + 4 + 4 + 4 + 1 + 23 + strlen(username) + 1,
|
||||
@ -475,7 +479,8 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb,
|
||||
auth_token,
|
||||
auth_token_len,
|
||||
protocol->scramble, sizeof(protocol->scramble),
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble),
|
||||
username,
|
||||
stage1_hash);
|
||||
|
||||
@ -487,7 +492,14 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) {
|
||||
if (!service_refresh_users(dcb->service)) {
|
||||
/* Try authentication again with new repository data */
|
||||
/* Note: if no auth client authentication will fail */
|
||||
auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, protocol->scramble, sizeof(protocol->scramble), username, stage1_hash);
|
||||
auth_ret = gw_check_mysql_scramble_data(
|
||||
dcb,
|
||||
auth_token,
|
||||
auth_token_len,
|
||||
protocol->scramble,
|
||||
sizeof(protocol->scramble),
|
||||
username,
|
||||
stage1_hash);
|
||||
}
|
||||
}
|
||||
|
||||
@ -830,38 +842,23 @@ int gw_read_client_event(
|
||||
}
|
||||
|
||||
/** succeed */
|
||||
if (rc)
|
||||
if (rc)
|
||||
{
|
||||
rc = 0; /**< here '0' means success */
|
||||
}
|
||||
else
|
||||
{
|
||||
GWBUF* errbuf;
|
||||
bool succp;
|
||||
|
||||
errbuf = mysql_create_custom_error(
|
||||
1,
|
||||
0,
|
||||
"Write to backend failed. Session closed.");
|
||||
#if defined(SS_DEBUG)
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Client routing error handling.")));
|
||||
#endif
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Routing the query failed. "
|
||||
"Session will be closed.")));
|
||||
|
||||
router->handleError(router_instance,
|
||||
rsession,
|
||||
errbuf,
|
||||
dcb,
|
||||
ERRACT_REPLY_CLIENT,
|
||||
&succp);
|
||||
gwbuf_free(errbuf);
|
||||
ss_dassert(!succp);
|
||||
|
||||
modutil_send_mysql_err_packet(dcb,
|
||||
1,
|
||||
0,
|
||||
2003,
|
||||
"HY000",
|
||||
"Write to backend failed. Session closed.");
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Routing the query failed. "
|
||||
"Session will be closed.")));
|
||||
|
||||
dcb_close(dcb);
|
||||
}
|
||||
}
|
||||
@ -951,6 +948,7 @@ int gw_MySQLListener(
|
||||
char *config_bind)
|
||||
{
|
||||
int l_so;
|
||||
int syseno = 0;
|
||||
struct sockaddr_in serv_addr;
|
||||
struct sockaddr_un local_addr;
|
||||
struct sockaddr *current_addr;
|
||||
@ -999,7 +997,10 @@ int gw_MySQLListener(
|
||||
listen_dcb->fd = -1;
|
||||
|
||||
// socket options
|
||||
setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
if((syseno = setsockopt(l_so, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one))) != 0){
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno))));
|
||||
}
|
||||
|
||||
|
||||
// set NONBLOCKING mode
|
||||
setnonblocking(l_so);
|
||||
@ -1054,9 +1055,7 @@ int gw_MySQLListener(
|
||||
rc = listen(l_so, 10 * SOMAXCONN);
|
||||
|
||||
if (rc == 0) {
|
||||
fprintf(stderr,
|
||||
"Listening MySQL connections at %s\n",
|
||||
config_bind);
|
||||
LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening MySQL connections at %s", config_bind)));
|
||||
} else {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
@ -1079,9 +1078,9 @@ int gw_MySQLListener(
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
conn_open[l_so] = true;
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
listen_dcb->func.accept = gw_MySQLAccept;
|
||||
|
||||
return 1;
|
||||
@ -1112,6 +1111,7 @@ int gw_MySQLAccept(DCB *listener)
|
||||
int sendbuf = GW_BACKEND_SO_SNDBUF;
|
||||
socklen_t optlen = sizeof(sendbuf);
|
||||
int eno = 0;
|
||||
int syseno = 0;
|
||||
int i = 0;
|
||||
|
||||
CHK_DCB(listener);
|
||||
@ -1120,7 +1120,7 @@ int gw_MySQLAccept(DCB *listener)
|
||||
|
||||
retry_accept:
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
if (fail_next_accept > 0)
|
||||
{
|
||||
c_sock = -1;
|
||||
@ -1128,16 +1128,16 @@ int gw_MySQLAccept(DCB *listener)
|
||||
fail_next_accept -= 1;
|
||||
} else {
|
||||
fail_accept_errno = 0;
|
||||
#endif /* SS_DEBUG */
|
||||
#endif /* FAKE_CODE */
|
||||
// new connection from client
|
||||
c_sock = accept(listener->fd,
|
||||
(struct sockaddr *) &client_conn,
|
||||
&client_len);
|
||||
eno = errno;
|
||||
errno = 0;
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
}
|
||||
#endif /* SS_DEBUG */
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
if (c_sock == -1) {
|
||||
|
||||
@ -1212,13 +1212,22 @@ int gw_MySQLAccept(DCB *listener)
|
||||
"%lu [gw_MySQLAccept] Accepted fd %d.",
|
||||
pthread_self(),
|
||||
c_sock)));
|
||||
#endif /* SS_DEBUG */
|
||||
#if defined(FAKE_CODE)
|
||||
conn_open[c_sock] = true;
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
/* set nonblocking */
|
||||
sendbuf = GW_CLIENT_SO_SNDBUF;
|
||||
setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen);
|
||||
|
||||
if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen)) != 0){
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno))));
|
||||
}
|
||||
|
||||
sendbuf = GW_CLIENT_SO_RCVBUF;
|
||||
setsockopt(c_sock, SOL_SOCKET, SO_RCVBUF, &sendbuf, optlen);
|
||||
|
||||
if((syseno = setsockopt(c_sock, SOL_SOCKET, SO_RCVBUF, &sendbuf, optlen)) != 0){
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno))));
|
||||
}
|
||||
setnonblocking(c_sock);
|
||||
|
||||
client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);
|
||||
@ -1374,7 +1383,6 @@ gw_client_close(DCB *dcb)
|
||||
SESSION* session;
|
||||
ROUTER_OBJECT* router;
|
||||
void* router_instance;
|
||||
void* rsession;
|
||||
#if defined(SS_DEBUG)
|
||||
MySQLProtocol* protocol = (MySQLProtocol *)dcb->protocol;
|
||||
if (dcb->state == DCB_STATE_POLLING ||
|
||||
@ -1391,7 +1399,7 @@ gw_client_close(DCB *dcb)
|
||||
* session may be NULL if session_alloc failed.
|
||||
* In that case, router session wasn't created.
|
||||
*/
|
||||
if (session != NULL)
|
||||
if (session != NULL)
|
||||
{
|
||||
CHK_SESSION(session);
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
@ -1400,13 +1408,22 @@ gw_client_close(DCB *dcb)
|
||||
{
|
||||
session->state = SESSION_STATE_STOPPING;
|
||||
}
|
||||
spinlock_release(&session->ses_lock);
|
||||
|
||||
router = session->service->router;
|
||||
router_instance = session->service->router_instance;
|
||||
rsession = session->router_session;
|
||||
/** Close router session and all its connections */
|
||||
router->closeSession(router_instance, rsession);
|
||||
router_instance = session->service->router_instance;
|
||||
router = session->service->router;
|
||||
/**
|
||||
* If router session is being created concurrently router
|
||||
* session might be NULL and it shouldn't be closed.
|
||||
*/
|
||||
if (session->router_session != NULL)
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
/** Close router session and all its connections */
|
||||
router->closeSession(router_instance, session->router_session);
|
||||
}
|
||||
else
|
||||
{
|
||||
spinlock_release(&session->ses_lock);
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
@ -35,6 +35,7 @@
|
||||
* x.y.z.%, x.y.%.%, x.%.%.%
|
||||
* 03/10/2014 Massimiliano Pinto Added netmask for wildcard in IPv4 hosts.
|
||||
* 24/10/2014 Massimiliano Pinto Added Mysql user@host @db authentication support
|
||||
* 10/11/2014 Massimiliano Pinto Charset at connect is passed to backend during authentication
|
||||
*
|
||||
*/
|
||||
|
||||
@ -274,8 +275,7 @@ int gw_read_backend_handshake(
|
||||
payload += 4;
|
||||
|
||||
//Now decode mysql handshake
|
||||
success = gw_decode_mysql_server_handshake(conn,
|
||||
payload);
|
||||
success = gw_decode_mysql_server_handshake(conn, payload);
|
||||
|
||||
if (success < 0) {
|
||||
/* MySQL handshake has not been properly decoded
|
||||
@ -563,6 +563,7 @@ int gw_send_authentication_to_backend(
|
||||
|
||||
char *curr_db = NULL;
|
||||
uint8_t *curr_passwd = NULL;
|
||||
unsigned int charset;
|
||||
|
||||
if (strlen(dbname))
|
||||
curr_db = dbname;
|
||||
@ -574,7 +575,10 @@ int gw_send_authentication_to_backend(
|
||||
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
||||
|
||||
/** Copy client's flags to backend */
|
||||
final_capabilities |= conn->client_capabilities;;
|
||||
final_capabilities |= conn->client_capabilities;
|
||||
|
||||
/* get charset the client sent and use it for connection auth */
|
||||
charset = conn->charset;
|
||||
|
||||
if (compress) {
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||
@ -668,7 +672,7 @@ int gw_send_authentication_to_backend(
|
||||
|
||||
// set the charset
|
||||
payload += 4;
|
||||
*payload = '\x08';
|
||||
*payload = charset;
|
||||
|
||||
payload++;
|
||||
|
||||
@ -736,13 +740,13 @@ int gw_send_authentication_to_backend(
|
||||
*
|
||||
*/
|
||||
int gw_do_connect_to_backend(
|
||||
char *host,
|
||||
int port,
|
||||
int* fd)
|
||||
char *host,
|
||||
int port,
|
||||
int *fd)
|
||||
{
|
||||
struct sockaddr_in serv_addr;
|
||||
int rv;
|
||||
int so = 0;
|
||||
int rv;
|
||||
int so = 0;
|
||||
int bufsize;
|
||||
|
||||
memset(&serv_addr, 0, sizeof serv_addr);
|
||||
@ -750,8 +754,6 @@ int gw_do_connect_to_backend(
|
||||
so = socket(AF_INET,SOCK_STREAM,0);
|
||||
|
||||
if (so < 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Establishing connection to backend server "
|
||||
@ -759,8 +761,8 @@ int gw_do_connect_to_backend(
|
||||
"due %d, %s.",
|
||||
host,
|
||||
port,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
errno,
|
||||
strerror(errno))));
|
||||
rv = -1;
|
||||
goto return_rv;
|
||||
}
|
||||
@ -768,46 +770,62 @@ int gw_do_connect_to_backend(
|
||||
setipaddress(&serv_addr.sin_addr, host);
|
||||
serv_addr.sin_port = htons(port);
|
||||
bufsize = GW_BACKEND_SO_SNDBUF;
|
||||
setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize));
|
||||
|
||||
if(setsockopt(so, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)) != 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Failed to set socket options "
|
||||
"%s:%d failed.\n\t\t Socket configuration failed "
|
||||
"due %d, %s.",
|
||||
host,
|
||||
port,
|
||||
errno,
|
||||
strerror(errno))));
|
||||
rv = -1;
|
||||
/** Close socket */
|
||||
goto close_so;
|
||||
}
|
||||
bufsize = GW_BACKEND_SO_RCVBUF;
|
||||
setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize));
|
||||
|
||||
if(setsockopt(so, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)) != 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Failed to set socket options "
|
||||
"%s:%d failed.\n\t\t Socket configuration failed "
|
||||
"due %d, %s.",
|
||||
host,
|
||||
port,
|
||||
errno,
|
||||
strerror(errno))));
|
||||
rv = -1;
|
||||
/** Close socket */
|
||||
goto close_so;
|
||||
}
|
||||
|
||||
/* set socket to as non-blocking here */
|
||||
setnonblocking(so);
|
||||
rv = connect(so, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
|
||||
|
||||
if (rv != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
|
||||
if (eno == EINPROGRESS) {
|
||||
if (rv != 0)
|
||||
{
|
||||
if (errno == EINPROGRESS)
|
||||
{
|
||||
rv = 1;
|
||||
} else {
|
||||
int rc;
|
||||
int oldfd = so;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Failed to connect backend server %s:%d, "
|
||||
"due %d, %s.",
|
||||
host,
|
||||
port,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
/*< Close newly created socket. */
|
||||
rc = close(so);
|
||||
|
||||
if (rc != 0) {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Failed to "
|
||||
"close socket %d due %d, %s.",
|
||||
oldfd,
|
||||
eno,
|
||||
strerror(eno))));
|
||||
}
|
||||
goto return_rv;
|
||||
errno,
|
||||
strerror(errno))));
|
||||
/** Close socket */
|
||||
goto close_so;
|
||||
}
|
||||
}
|
||||
*fd = so;
|
||||
@ -819,11 +837,26 @@ int gw_do_connect_to_backend(
|
||||
host,
|
||||
port,
|
||||
so)));
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
conn_open[so] = true;
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
return_rv:
|
||||
return rv;
|
||||
|
||||
close_so:
|
||||
/*< Close newly created socket. */
|
||||
if (close(so) != 0)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error: Failed to "
|
||||
"close socket %d due %d, %s.",
|
||||
so,
|
||||
errno,
|
||||
strerror(errno))));
|
||||
}
|
||||
goto return_rv;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1020,193 +1053,226 @@ int mysql_send_custom_error (
|
||||
return dcb->func.write(dcb, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create COM_CHANGE_USER packet and store it to GWBUF
|
||||
*
|
||||
* @param mses MySQL session
|
||||
* @param protocol protocol structure of the backend
|
||||
*
|
||||
* @return GWBUF buffer consisting of COM_CHANGE_USER packet
|
||||
*
|
||||
* @note the function doesn't fail
|
||||
*/
|
||||
GWBUF* gw_create_change_user_packet(
|
||||
MYSQL_session* mses,
|
||||
MySQLProtocol* protocol)
|
||||
{
|
||||
char* db;
|
||||
char* user;
|
||||
uint8_t* pwd;
|
||||
GWBUF* buffer;
|
||||
int compress = 0;
|
||||
uint8_t* payload = NULL;
|
||||
uint8_t* payload_start = NULL;
|
||||
long bytes;
|
||||
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
|
||||
uint32_t server_capabilities = 0;
|
||||
uint32_t final_capabilities = 0;
|
||||
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
||||
char* curr_db = NULL;
|
||||
uint8_t* curr_passwd = NULL;
|
||||
unsigned int charset;
|
||||
|
||||
db = mses->db;
|
||||
user = mses->user;
|
||||
pwd = mses->client_sha1;
|
||||
|
||||
if (strlen(db) > 0)
|
||||
{
|
||||
curr_db = db;
|
||||
}
|
||||
|
||||
if (strlen((char *)pwd) > 0)
|
||||
{
|
||||
curr_passwd = pwd;
|
||||
}
|
||||
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
||||
|
||||
/** Copy client's flags to backend */
|
||||
final_capabilities |= protocol->client_capabilities;
|
||||
|
||||
/* get charset the client sent and use it for connection auth */
|
||||
charset = protocol->charset;
|
||||
|
||||
if (compress)
|
||||
{
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||
#ifdef DEBUG_MYSQL_CONN
|
||||
fprintf(stderr, ">>>> Backend Connection with compression\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (curr_passwd != NULL)
|
||||
{
|
||||
uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
|
||||
/** hash1 is the function input, SHA1(real_password) */
|
||||
memcpy(hash1, pwd, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
/**
|
||||
* hash2 is the SHA1(input data), where
|
||||
* input_data = SHA1(real_password)
|
||||
*/
|
||||
gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2);
|
||||
|
||||
/** dbpass is the HEX form of SHA1(SHA1(real_password)) */
|
||||
gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
/** new_sha is the SHA1(CONCAT(scramble, hash2) */
|
||||
gw_sha1_2_str(protocol->scramble,
|
||||
GW_MYSQL_SCRAMBLE_SIZE,
|
||||
hash2,
|
||||
GW_MYSQL_SCRAMBLE_SIZE,
|
||||
new_sha);
|
||||
|
||||
/** compute the xor in client_scramble */
|
||||
gw_str_xor(client_scramble,
|
||||
new_sha, hash1,
|
||||
GW_MYSQL_SCRAMBLE_SIZE);
|
||||
}
|
||||
if (curr_db == NULL)
|
||||
{
|
||||
final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
}
|
||||
else
|
||||
{
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
}
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH;
|
||||
/**
|
||||
* Protocol MySQL COM_CHANGE_USER for CLIENT_PROTOCOL_41
|
||||
* 1 byte COMMAND
|
||||
*/
|
||||
bytes = 1;
|
||||
|
||||
/** add the user and a terminating char */
|
||||
bytes += strlen(user);
|
||||
bytes++;
|
||||
/**
|
||||
* next will be + 1 (scramble_len) + 20 (fixed_scramble) +
|
||||
* (db + NULL term) + 2 bytes charset
|
||||
*/
|
||||
if (curr_passwd != NULL)
|
||||
{
|
||||
bytes += GW_MYSQL_SCRAMBLE_SIZE;
|
||||
}
|
||||
/** 1 byte for scramble_len */
|
||||
bytes++;
|
||||
/** db name and terminating char */
|
||||
if (curr_db != NULL)
|
||||
{
|
||||
bytes += strlen(curr_db);
|
||||
}
|
||||
bytes++;
|
||||
|
||||
/** the charset */
|
||||
bytes += 2;
|
||||
bytes += strlen("mysql_native_password");
|
||||
bytes++;
|
||||
|
||||
/** the packet header */
|
||||
bytes += 4;
|
||||
|
||||
buffer = gwbuf_alloc(bytes);
|
||||
/**
|
||||
* Set correct type to GWBUF so that it will be handled like session
|
||||
* commands
|
||||
*/
|
||||
buffer->gwbuf_type =
|
||||
GWBUF_TYPE_MYSQL|GWBUF_TYPE_SINGLE_STMT|GWBUF_TYPE_SESCMD;
|
||||
payload = GWBUF_DATA(buffer);
|
||||
memset(payload, '\0', bytes);
|
||||
payload_start = payload;
|
||||
|
||||
/** set packet number to 0 */
|
||||
payload[3] = 0x00;
|
||||
payload += 4;
|
||||
|
||||
/** set the command COM_CHANGE_USER 0x11 */
|
||||
payload[0] = 0x11;
|
||||
payload++;
|
||||
memcpy(payload, user, strlen(user));
|
||||
payload += strlen(user);
|
||||
payload++;
|
||||
|
||||
if (curr_passwd != NULL)
|
||||
{
|
||||
/** set the auth-length */
|
||||
*payload = GW_MYSQL_SCRAMBLE_SIZE;
|
||||
payload++;
|
||||
/**
|
||||
* copy the 20 bytes scramble data after
|
||||
* packet_buffer+36+user+NULL+1 (byte of auth-length)
|
||||
*/
|
||||
memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
payload += GW_MYSQL_SCRAMBLE_SIZE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/** skip the auth-length and write a NULL */
|
||||
payload++;
|
||||
}
|
||||
/** if the db is not NULL append it */
|
||||
if (curr_db != NULL)
|
||||
{
|
||||
memcpy(payload, curr_db, strlen(curr_db));
|
||||
payload += strlen(curr_db);
|
||||
}
|
||||
payload++;
|
||||
/** set the charset, 2 bytes */
|
||||
*payload = charset;
|
||||
payload++;
|
||||
*payload = '\x00';
|
||||
payload++;
|
||||
memcpy(payload, "mysql_native_password", strlen("mysql_native_password"));
|
||||
payload += strlen("mysql_native_password");
|
||||
payload++;
|
||||
/** put here the paylod size: bytes to write - 4 bytes packet header */
|
||||
gw_mysql_set_byte3(payload_start, (bytes-4));
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a MySQL CHANGE_USER packet to backend server
|
||||
*
|
||||
* @param conn MySQL protocol structure
|
||||
* @param dbname The selected database
|
||||
* @param user The selected user
|
||||
* @param passwd The SHA1(real_password): Note real_password is unknown
|
||||
* @param passwd The SHA1(real_password)
|
||||
* @return 1 on success, 0 on failure
|
||||
*/
|
||||
int gw_send_change_user_to_backend(
|
||||
char *dbname,
|
||||
char *user,
|
||||
uint8_t *passwd,
|
||||
MySQLProtocol *conn)
|
||||
char *dbname,
|
||||
char *user,
|
||||
uint8_t *passwd,
|
||||
MySQLProtocol *conn)
|
||||
{
|
||||
int compress = 0;
|
||||
int rv;
|
||||
uint8_t *payload = NULL;
|
||||
uint8_t *payload_start = NULL;
|
||||
long bytes;
|
||||
uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE];
|
||||
uint8_t client_capabilities[4];
|
||||
uint32_t server_capabilities = 0;
|
||||
uint32_t final_capabilities = 0;
|
||||
char dbpass[MYSQL_USER_MAXLEN + 1]="";
|
||||
GWBUF *buffer;
|
||||
DCB *dcb;
|
||||
|
||||
char *curr_db = NULL;
|
||||
uint8_t *curr_passwd = NULL;
|
||||
|
||||
if (strlen(dbname))
|
||||
curr_db = dbname;
|
||||
|
||||
if (strlen((char *)passwd))
|
||||
curr_passwd = passwd;
|
||||
|
||||
dcb = conn->owner_dcb;
|
||||
|
||||
final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities);
|
||||
|
||||
/** Copy client's flags to backend */
|
||||
final_capabilities |= conn->client_capabilities;;
|
||||
|
||||
if (compress) {
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS;
|
||||
#ifdef DEBUG_MYSQL_CONN
|
||||
fprintf(stderr, ">>>> Backend Connection with compression\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
if (curr_passwd != NULL) {
|
||||
uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]="";
|
||||
|
||||
// hash1 is the function input, SHA1(real_password)
|
||||
memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
// hash2 is the SHA1(input data), where input_data = SHA1(real_password)
|
||||
gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2);
|
||||
|
||||
// dbpass is the HEX form of SHA1(SHA1(real_password))
|
||||
gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
// new_sha is the SHA1(CONCAT(scramble, hash2)
|
||||
gw_sha1_2_str(conn->scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha);
|
||||
|
||||
// compute the xor in client_scramble
|
||||
gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
}
|
||||
|
||||
if (curr_db == NULL) {
|
||||
// without db
|
||||
final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
} else {
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB;
|
||||
}
|
||||
|
||||
final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH;
|
||||
|
||||
gw_mysql_set_byte4(client_capabilities, final_capabilities);
|
||||
|
||||
// Protocol MySQL COM_CHANGE_USER for CLIENT_PROTOCOL_41
|
||||
// 1 byte COMMAND
|
||||
bytes = 1;
|
||||
|
||||
// add the user
|
||||
bytes += strlen(user);
|
||||
// NULL byte for user string
|
||||
bytes++;
|
||||
|
||||
// next will be + 1 (scramble_len) + 20 (fixed_scramble) + (dbname + NULL term) + 2 bytes charset
|
||||
|
||||
if (curr_passwd != NULL) {
|
||||
bytes += GW_MYSQL_SCRAMBLE_SIZE;
|
||||
}
|
||||
// 1 byte for scramble_len
|
||||
bytes++;
|
||||
|
||||
if (curr_db != NULL) {
|
||||
bytes += strlen(curr_db);
|
||||
}
|
||||
// NULL byte for dbname string
|
||||
bytes++;
|
||||
|
||||
// the charset
|
||||
bytes += 2;
|
||||
bytes += strlen("mysql_native_password");
|
||||
bytes++;
|
||||
|
||||
// the packet header
|
||||
bytes += 4;
|
||||
|
||||
// allocating the GWBUF
|
||||
buffer = gwbuf_alloc(bytes);
|
||||
/**
|
||||
* Set correct type to GWBUF so that it will be handled like session
|
||||
* commands should
|
||||
*/
|
||||
buffer->gwbuf_type = GWBUF_TYPE_MYSQL|GWBUF_TYPE_SINGLE_STMT|GWBUF_TYPE_SESCMD;
|
||||
payload = GWBUF_DATA(buffer);
|
||||
|
||||
// clearing data
|
||||
memset(payload, '\0', bytes);
|
||||
GWBUF *buffer;
|
||||
int rc;
|
||||
MYSQL_session* mses;
|
||||
|
||||
// save the start pointer
|
||||
payload_start = payload;
|
||||
mses = (MYSQL_session*)conn->owner_dcb->session->client->data;
|
||||
buffer = gw_create_change_user_packet(mses, conn);
|
||||
rc = conn->owner_dcb->func.write(conn->owner_dcb, buffer);
|
||||
|
||||
// set packet # = 1
|
||||
payload[3] = 0x00;
|
||||
payload += 4;
|
||||
|
||||
// set the command COM_CHANGE_USER \x11
|
||||
payload[0] = 0x11;
|
||||
payload++;
|
||||
|
||||
memcpy(payload, user, strlen(user));
|
||||
payload += strlen(user);
|
||||
payload++;
|
||||
|
||||
if (curr_passwd != NULL) {
|
||||
// set the auth-length
|
||||
*payload = GW_MYSQL_SCRAMBLE_SIZE;
|
||||
payload++;
|
||||
|
||||
//copy the 20 bytes scramble data after packet_buffer+36+user+NULL+1 (byte of auth-length)
|
||||
memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE);
|
||||
|
||||
payload += GW_MYSQL_SCRAMBLE_SIZE;
|
||||
|
||||
} else {
|
||||
// skip the auth-length and write a NULL
|
||||
payload++;
|
||||
}
|
||||
|
||||
// if the db is not NULL append it
|
||||
if (curr_db != NULL) {
|
||||
memcpy(payload, curr_db, strlen(curr_db));
|
||||
payload += strlen(curr_db);
|
||||
payload++;
|
||||
} else {
|
||||
// skip the NULL
|
||||
payload++;
|
||||
if (rc != 0)
|
||||
{
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
// set the charset, 2 bytes!!!!
|
||||
*payload = '\x08';
|
||||
payload++;
|
||||
*payload = '\x00';
|
||||
payload++;
|
||||
|
||||
memcpy(payload, "mysql_native_password", strlen("mysql_native_password"));
|
||||
|
||||
payload += strlen("mysql_native_password");
|
||||
payload++;
|
||||
|
||||
// put here the paylod size: bytes to write - 4 bytes packet header
|
||||
gw_mysql_set_byte3(payload_start, (bytes-4));
|
||||
|
||||
rv = dcb->func.write(dcb, buffer);
|
||||
|
||||
if (rv == 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1736,11 +1802,14 @@ void protocol_archive_srv_command(
|
||||
{
|
||||
p->protocol_cmd_history = server_command_copy(s1);
|
||||
}
|
||||
else
|
||||
else /*< scan and count history commands */
|
||||
{
|
||||
len = 1;
|
||||
|
||||
while (h1->scom_next != NULL)
|
||||
{
|
||||
h1 = h1->scom_next;
|
||||
len += 1;
|
||||
}
|
||||
h1->scom_next = server_command_copy(s1);
|
||||
}
|
||||
@ -2033,7 +2102,7 @@ char* get_username_from_auth(
|
||||
|
||||
first_letter = (char *)(data + 4 + 4 + 4 + 1 + 23);
|
||||
|
||||
if (first_letter == '\0')
|
||||
if (*first_letter == '\0')
|
||||
{
|
||||
rval = NULL;
|
||||
goto retblock;
|
||||
|
@ -358,7 +358,8 @@ telnetd_listen(DCB *listener, char *config)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int one = 1;
|
||||
int rc;
|
||||
int rc;
|
||||
int syseno = 0;
|
||||
|
||||
memcpy(&listener->func, &MyObject, sizeof(GWPROTOCOL));
|
||||
|
||||
@ -372,7 +373,12 @@ int rc;
|
||||
}
|
||||
|
||||
// socket options
|
||||
setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
syseno = setsockopt(listener->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
|
||||
|
||||
if(syseno != 0){
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error: Failed to set socket options. Error %d: %s",errno,strerror(errno))));
|
||||
return 0;
|
||||
}
|
||||
// set NONBLOCKING mode
|
||||
setnonblocking(listener->fd);
|
||||
// bind address and port
|
||||
@ -384,9 +390,7 @@ int rc;
|
||||
rc = listen(listener->fd, SOMAXCONN);
|
||||
|
||||
if (rc == 0) {
|
||||
fprintf(stderr,
|
||||
"Listening telnet connections at %s\n",
|
||||
config);
|
||||
LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening telnet connections at %s", config)));
|
||||
} else {
|
||||
int eno = errno;
|
||||
errno = 0;
|
||||
|
@ -1,3 +1,7 @@
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
add_library(testroute SHARED testroute.c)
|
||||
target_link_libraries(testroute log_manager utils)
|
||||
install(TARGETS testroute DESTINATION modules)
|
||||
|
@ -414,7 +414,7 @@ struct subcommand disableoptions[] = {
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
|
||||
static void fail_backendfd(void);
|
||||
static void fail_clientfd(void);
|
||||
@ -456,7 +456,7 @@ struct subcommand failoptions[] = {
|
||||
{0, 0, 0}
|
||||
}
|
||||
};
|
||||
#endif /* SS_DEBUG */
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
static void telnetdAddUser(DCB *, char *, char *);
|
||||
/**
|
||||
@ -502,9 +502,9 @@ static struct {
|
||||
{ "clear", clearoptions },
|
||||
{ "disable", disableoptions },
|
||||
{ "enable", enableoptions },
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
{ "fail", failoptions },
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
{ "list", listoptions },
|
||||
{ "reload", reloadoptions },
|
||||
{ "remove", removeoptions },
|
||||
@ -1113,7 +1113,7 @@ static void disable_log_action(DCB *dcb, char *arg1) {
|
||||
skygw_log_disable(type);
|
||||
}
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
static void fail_backendfd(void)
|
||||
{
|
||||
fail_next_backend_fd = true;
|
||||
@ -1157,4 +1157,4 @@ static void fail_accept(
|
||||
return ;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
|
@ -828,6 +828,17 @@ static void handleError(
|
||||
SESSION *session = backend_dcb->session;
|
||||
session_state_t sesstate;
|
||||
|
||||
/** Don't handle same error twice on same DCB */
|
||||
if (backend_dcb->dcb_errhandle_called)
|
||||
{
|
||||
/** we optimistically assume that previous call succeed */
|
||||
*succp = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
backend_dcb->dcb_errhandle_called = true;
|
||||
}
|
||||
spinlock_acquire(&session->ses_lock);
|
||||
sesstate = session->state;
|
||||
client_dcb = session->client;
|
||||
|
@ -830,8 +830,16 @@ static void* newSession(
|
||||
* Find a backend servers to connect to.
|
||||
* This command requires that rsession's lock is held.
|
||||
*/
|
||||
rses_begin_locked_router_action(client_rses);
|
||||
|
||||
succp = rses_begin_locked_router_action(client_rses);
|
||||
|
||||
if(!succp)
|
||||
{
|
||||
free(client_rses->rses_backend_ref);
|
||||
free(client_rses);
|
||||
client_rses = NULL;
|
||||
goto return_rses;
|
||||
}
|
||||
succp = select_connect_backend_servers(&master_ref,
|
||||
backend_ref,
|
||||
router_nservers,
|
||||
@ -843,7 +851,9 @@ static void* newSession(
|
||||
|
||||
rses_end_locked_router_action(client_rses);
|
||||
|
||||
/** Both Master and at least 1 slave must be found */
|
||||
/**
|
||||
* Master and at least <min_nslaves> slaves must be found
|
||||
*/
|
||||
if (!succp) {
|
||||
free(client_rses->rses_backend_ref);
|
||||
free(client_rses);
|
||||
@ -1029,6 +1039,9 @@ static void freeSession(
|
||||
|
||||
/**
|
||||
* Provide the router with a pointer to a suitable backend dcb.
|
||||
*
|
||||
* As of Nov. 2014, slave which has least connections is always chosen.
|
||||
*
|
||||
* Detect failures in server statuses and reselect backends if necessary.
|
||||
* If name is specified, server name becomes primary selection criteria.
|
||||
*
|
||||
@ -1527,12 +1540,12 @@ skygw_query_type_t is_read_tmp_table(
|
||||
}
|
||||
}
|
||||
|
||||
for(i = 0; i<tsize;i++)
|
||||
{
|
||||
free(tbl[i]);
|
||||
}
|
||||
|
||||
|
||||
if(tbl != NULL){
|
||||
for(i = 0; i<tsize;i++)
|
||||
{
|
||||
free(tbl[i]);
|
||||
}
|
||||
free(tbl);
|
||||
}
|
||||
|
||||
@ -1610,8 +1623,12 @@ void check_create_tmp_table(
|
||||
rses_prop_tmp->rses_prop_type = RSES_PROP_TYPE_TMPTABLES;
|
||||
router_cli_ses->rses_properties[RSES_PROP_TYPE_TMPTABLES] = rses_prop_tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error : Call to malloc() failed.")));
|
||||
}
|
||||
}
|
||||
|
||||
if(rses_prop_tmp){
|
||||
if (rses_prop_tmp->rses_prop_data.temp_tables == NULL)
|
||||
{
|
||||
h = hashtable_alloc(7, hashkeyfun, hashcmpfun);
|
||||
@ -1619,10 +1636,13 @@ void check_create_tmp_table(
|
||||
if (h != NULL)
|
||||
{
|
||||
rses_prop_tmp->rses_prop_data.temp_tables = h;
|
||||
}
|
||||
}else{
|
||||
LOGIF(LE, (skygw_log_write_flush(LOGFILE_ERROR,"Error : Failed to allocate a new hashtable.")));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (hkey &&
|
||||
if (hkey && rses_prop_tmp->rses_prop_data.temp_tables &&
|
||||
hashtable_add(rses_prop_tmp->rses_prop_data.temp_tables,
|
||||
(void *)hkey,
|
||||
(void *)is_temp) == 0) /*< Conflict in hash table */
|
||||
@ -1647,6 +1667,8 @@ void check_create_tmp_table(
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
free(hkey);
|
||||
free(tblname);
|
||||
}
|
||||
@ -1730,7 +1752,10 @@ static int routeQuery(
|
||||
goto retblock;
|
||||
}
|
||||
|
||||
/** Read stored master DCB pointer */
|
||||
/**
|
||||
* Read stored master DCB pointer. If master is not set, routing must
|
||||
* be aborted
|
||||
*/
|
||||
if ((master_dcb = router_cli_ses->rses_master_ref->bref_dcb) == NULL)
|
||||
{
|
||||
char* query_str = modutil_get_query(querybuf);
|
||||
@ -2071,11 +2096,15 @@ static int routeQuery(
|
||||
"route to master "
|
||||
"but couldn't find "
|
||||
"master in a "
|
||||
"suitable state "
|
||||
"failed.")));
|
||||
"suitable state.")));
|
||||
}
|
||||
/**
|
||||
* Master has changed. Return with error indicator.
|
||||
*/
|
||||
rses_end_locked_router_action(router_cli_ses);
|
||||
succp = false;
|
||||
ret = 0;
|
||||
goto retblock;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2645,7 +2674,7 @@ static bool select_connect_backend_servers(
|
||||
const int min_nslaves = 0; /*< not configurable at the time */
|
||||
bool is_synced_master;
|
||||
int (*p)(const void *, const void *);
|
||||
BACKEND* master_host = NULL;
|
||||
BACKEND* master_host;
|
||||
|
||||
if (p_master_ref == NULL || backend_ref == NULL)
|
||||
{
|
||||
@ -2657,46 +2686,32 @@ static bool select_connect_backend_servers(
|
||||
/* get the root Master */
|
||||
master_host = get_root_master(backend_ref, router_nservers);
|
||||
|
||||
/**
|
||||
* Master is already chosen and connected. It means that the function
|
||||
* was called from error handling function or from some other similar
|
||||
* function where session was already established but new slaves needed
|
||||
* to be selected.
|
||||
/**
|
||||
* Existing session : master is already chosen and connected.
|
||||
* The function was called because new slave must be selected to replace
|
||||
* failed one.
|
||||
*/
|
||||
if (*p_master_ref != NULL &&
|
||||
BREF_IS_IN_USE((*p_master_ref)))
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [select_connect_backend_servers] Master %p fd %d found.",
|
||||
pthread_self(),
|
||||
(*p_master_ref)->bref_dcb,
|
||||
(*p_master_ref)->bref_dcb->fd)));
|
||||
|
||||
master_found = true;
|
||||
master_connected = true;
|
||||
|
||||
if (*p_master_ref != NULL)
|
||||
{
|
||||
/**
|
||||
* Ensure that *p_master_ref and master_host point to same backend
|
||||
* and it has a master role.
|
||||
* Ensure that backend reference is in use, stored master is
|
||||
* still current root master.
|
||||
*/
|
||||
ss_dassert(master_host &&
|
||||
((*p_master_ref)->bref_backend->backend_server ==
|
||||
master_host->backend_server) &&
|
||||
(master_host->backend_server->status &
|
||||
(SERVER_MASTER|SERVER_MAINT)) == SERVER_MASTER);
|
||||
}
|
||||
/** New session or master failure case */
|
||||
if (!BREF_IS_IN_USE((*p_master_ref)) ||
|
||||
!SERVER_IS_MASTER((*p_master_ref)->bref_backend->backend_server) ||
|
||||
master_host != (*p_master_ref)->bref_backend)
|
||||
{
|
||||
succp = false;
|
||||
goto return_succp;
|
||||
}
|
||||
master_found = true;
|
||||
master_connected = true;
|
||||
}
|
||||
/**
|
||||
* New session : select master and slaves
|
||||
*/
|
||||
else
|
||||
{
|
||||
LOGIF(LD, (skygw_log_write(
|
||||
LOGFILE_DEBUG,
|
||||
"%lu [select_connect_backend_servers] Session %p doesn't "
|
||||
"currently have a master chosen. Proceeding to master "
|
||||
"selection.",
|
||||
pthread_self(),
|
||||
session)));
|
||||
|
||||
master_found = false;
|
||||
master_connected = false;
|
||||
}
|
||||
@ -2735,11 +2750,6 @@ static bool select_connect_backend_servers(
|
||||
b->backend_conn_count)));
|
||||
}
|
||||
#endif
|
||||
/* assert with master_host */
|
||||
ss_dassert(!master_connected ||
|
||||
(master_host &&
|
||||
((*p_master_ref)->bref_backend->backend_server == master_host->backend_server) &&
|
||||
SERVER_MASTER));
|
||||
/**
|
||||
* Sort the pointer list to servers according to connection counts. As
|
||||
* a consequence those backends having least connections are in the
|
||||
@ -2830,8 +2840,10 @@ static bool select_connect_backend_servers(
|
||||
(max_slave_rlag == MAX_RLAG_UNDEFINED ||
|
||||
(b->backend_server->rlag != MAX_RLAG_NOT_AVAILABLE &&
|
||||
b->backend_server->rlag <= max_slave_rlag)) &&
|
||||
(SERVER_IS_SLAVE(b->backend_server) || SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||
(master_host != NULL && (b->backend_server != master_host->backend_server)))
|
||||
(SERVER_IS_SLAVE(b->backend_server) ||
|
||||
SERVER_IS_RELAY_SERVER(b->backend_server)) &&
|
||||
(master_host != NULL &&
|
||||
(b->backend_server != master_host->backend_server)))
|
||||
{
|
||||
slaves_found += 1;
|
||||
|
||||
@ -2893,6 +2905,12 @@ static bool select_connect_backend_servers(
|
||||
else if (master_host &&
|
||||
(b->backend_server == master_host->backend_server))
|
||||
{
|
||||
/**
|
||||
* *p_master_ref must be assigned with this
|
||||
* backend_ref pointer because its original value
|
||||
* may have been lost when backend references were
|
||||
* sorted (qsort).
|
||||
*/
|
||||
*p_master_ref = &backend_ref[i];
|
||||
|
||||
if (master_connected)
|
||||
@ -4048,7 +4066,7 @@ static void rwsplit_process_router_options(
|
||||
}
|
||||
|
||||
/**
|
||||
* Error Handler routine to resolve backend failures. If it succeeds then there
|
||||
* Error Handler routine to resolve _backend_ failures. If it succeeds then there
|
||||
* are enough operative backends available and connected. Otherwise it fails,
|
||||
* and session is terminated.
|
||||
*
|
||||
@ -4062,7 +4080,6 @@ static void rwsplit_process_router_options(
|
||||
* Even if succp == true connecting to new slave may have failed. succp is to
|
||||
* tell whether router has enough master/slave connections to continue work.
|
||||
*/
|
||||
|
||||
static void handleError (
|
||||
ROUTER* instance,
|
||||
void* router_session,
|
||||
@ -4076,27 +4093,38 @@ static void handleError (
|
||||
ROUTER_CLIENT_SES* rses = (ROUTER_CLIENT_SES *)router_session;
|
||||
|
||||
CHK_DCB(backend_dcb);
|
||||
#if defined(SS_DEBUG)
|
||||
backend_dcb->dcb_errhandle_called = true;
|
||||
#endif
|
||||
/** Don't handle same error twice on same DCB */
|
||||
if (backend_dcb->dcb_errhandle_called)
|
||||
{
|
||||
/** we optimistically assume that previous call succeed */
|
||||
*succp = true;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
backend_dcb->dcb_errhandle_called = true;
|
||||
}
|
||||
session = backend_dcb->session;
|
||||
|
||||
if (session != NULL)
|
||||
CHK_SESSION(session);
|
||||
if (session == NULL || rses == NULL)
|
||||
{
|
||||
*succp = false;
|
||||
return;
|
||||
}
|
||||
CHK_SESSION(session);
|
||||
CHK_CLIENT_RSES(rses);
|
||||
|
||||
switch (action) {
|
||||
case ERRACT_NEW_CONNECTION:
|
||||
{
|
||||
if (rses != NULL)
|
||||
CHK_CLIENT_RSES(rses);
|
||||
|
||||
{
|
||||
if (!rses_begin_locked_router_action(rses))
|
||||
{
|
||||
*succp = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (rses->rses_master_ref->bref_dcb == backend_dcb)
|
||||
if (rses->rses_master_ref->bref_dcb == backend_dcb &&
|
||||
!SERVER_IS_MASTER(rses->rses_master_ref->bref_backend->backend_server))
|
||||
{
|
||||
/** Master failed, can't recover */
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
@ -4195,6 +4223,11 @@ static bool handle_error_new_connection(
|
||||
}
|
||||
CHK_BACKEND_REF(bref);
|
||||
|
||||
/**
|
||||
* If query was sent through the bref and it is waiting for reply from
|
||||
* the backend server it is necessary to send an error to the client
|
||||
* because it is waiting for reply.
|
||||
*/
|
||||
if (BREF_IS_WAITING_RESULT(bref))
|
||||
{
|
||||
DCB* client_dcb;
|
||||
@ -4462,6 +4495,10 @@ static backend_ref_t* get_bref_from_dcb(
|
||||
return bref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls hang-up function for DCB if it is not both running and in
|
||||
* master/slave/joined/ndb role. Called by DCB's callback routine.
|
||||
*/
|
||||
static int router_handle_state_switch(
|
||||
DCB* dcb,
|
||||
DCB_REASON reason,
|
||||
@ -4502,6 +4539,7 @@ return_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static sescmd_cursor_t* backend_ref_get_sescmd_cursor (
|
||||
backend_ref_t* bref)
|
||||
{
|
||||
@ -4623,7 +4661,7 @@ static BACKEND *get_root_master(
|
||||
* Servers are checked even if they are in 'maintenance'
|
||||
*
|
||||
* @param rses pointer to router session
|
||||
* @return pointer to backend reference of the root master
|
||||
* @return pointer to backend reference of the root master or NULL
|
||||
*
|
||||
*/
|
||||
static backend_ref_t* get_root_master_bref(
|
||||
@ -4653,6 +4691,14 @@ static backend_ref_t* get_root_master_bref(
|
||||
bref++;
|
||||
i += 1;
|
||||
}
|
||||
if (candidate_bref == NULL)
|
||||
{
|
||||
LOGIF(LE, (skygw_log_write_flush(
|
||||
LOGFILE_ERROR,
|
||||
"Error : Could not find master among the backend "
|
||||
"servers. Previous master state : %s",
|
||||
STRSRVSTATUS(BREFSRV(rses->rses_master_ref)))));
|
||||
}
|
||||
return candidate_bref;
|
||||
}
|
||||
|
||||
|
@ -1,2 +1,7 @@
|
||||
add_test(NAME ReadWriteSplitTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit.sh testrwsplit.log ${TEST_HOST} ${TEST_PORT_RW} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
if(MYSQLCLIENT_FOUND)
|
||||
add_test(NAME ReadWriteSplitLoginTest COMMAND $<TARGET_FILE:testconnect> 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT_RW} 1.10)
|
||||
endif()
|
||||
|
||||
add_subdirectory(test_hints)
|
6
server/modules/routing/test/CMakeLists.txt
Normal file
6
server/modules/routing/test/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
if(MYSQLCLIENT_FOUND)
|
||||
add_executable(testconnect testconnect.c)
|
||||
message(STATUS "Linking against: ${MYSQLCLIENT_LIBRARIES}")
|
||||
target_link_libraries(testconnect ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread)
|
||||
add_test(NAME ReadConnRouterLoginTest COMMAND $<TARGET_FILE:testconnect> 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT} 1.10)
|
||||
endif()
|
161
server/modules/routing/test/testconnect.c
Normal file
161
server/modules/routing/test/testconnect.c
Normal file
@ -0,0 +1,161 @@
|
||||
#include <my_config.h>
|
||||
#include <mysql.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
MYSQL* server;
|
||||
char *host = NULL, *str_baseline = NULL,*str_test = NULL, *errmsg = NULL;
|
||||
unsigned int port;
|
||||
int rval, iterations,i;
|
||||
clock_t begin,end;
|
||||
size_t offset;
|
||||
struct timeval real_begin,real_end,real_baseline,real_test;
|
||||
time_t time;
|
||||
double baseline, test, ratio, result;
|
||||
|
||||
if(argc < 7){
|
||||
fprintf(stderr,"Usage: %s <iterations> <baseline host> <baseline port> <test host> <test port> <max result ratio>\n",argv[0]);
|
||||
fprintf(stderr,"The ratio is measured as:\ntest time / baseline time\n");
|
||||
fprintf(stderr,"The test fails if this ratio is exceeded.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
;
|
||||
|
||||
if((str_baseline = calloc(256,sizeof(char))) == NULL){
|
||||
return 1;
|
||||
}
|
||||
|
||||
if((str_test = calloc(256,sizeof(char))) == NULL){
|
||||
free(str_baseline);
|
||||
return 1;
|
||||
}
|
||||
|
||||
iterations = atoi(argv[1]);
|
||||
host = strdup(argv[2]);
|
||||
port = atoi(argv[3]);
|
||||
ratio = atof(argv[6]);
|
||||
rval = 0;
|
||||
|
||||
if(ratio <= 0.0){
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**Testing direct connection to master*/
|
||||
|
||||
printf("Connecting to MySQL server through %s:%d.\n",host,port);
|
||||
gettimeofday(&real_begin,NULL);
|
||||
begin = clock();
|
||||
|
||||
if((server = mysql_init(NULL)) == NULL){
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(mysql_real_connect(server,host,"maxuser","maxpwd",NULL,port,NULL,0) == NULL){
|
||||
rval = 1;
|
||||
printf( "Failed to connect to database: Error: %s\n",
|
||||
mysql_error(server));
|
||||
goto report;
|
||||
}
|
||||
|
||||
for(i = 0;i<iterations;i++)
|
||||
{
|
||||
if(mysql_change_user(server,"maxuser","maxpwd",NULL)){
|
||||
rval = 1;
|
||||
printf( "Failed to change user: Error: %s\n",
|
||||
mysql_error(server));
|
||||
goto report;
|
||||
}
|
||||
}
|
||||
|
||||
mysql_close(server);
|
||||
|
||||
end = clock();
|
||||
gettimeofday(&real_end,NULL);
|
||||
baseline = (double)(end - begin)/CLOCKS_PER_SEC;
|
||||
timersub(&real_end,&real_begin,&real_baseline);
|
||||
|
||||
free(host);
|
||||
host = strdup(argv[4]);
|
||||
port = atoi(argv[5]);
|
||||
|
||||
/**Testing connection to master through MaxScale*/
|
||||
|
||||
printf("Connecting to MySQL server through %s:%d.\n",host,port);
|
||||
gettimeofday(&real_begin,NULL);
|
||||
begin = clock();
|
||||
|
||||
if((server = mysql_init(NULL)) == NULL){
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(mysql_real_connect(server,host,"maxuser","maxpwd",NULL,port,NULL,0) == NULL){
|
||||
rval = 1;
|
||||
printf("Failed to connect to database: Error: %s\n",
|
||||
mysql_error(server));
|
||||
goto report;
|
||||
}
|
||||
|
||||
for(i = 0;i<iterations;i++)
|
||||
{
|
||||
if(mysql_change_user(server,"maxuser","maxpwd",NULL)){
|
||||
rval = 1;
|
||||
printf("Failed to change user: Error: %s\n",
|
||||
mysql_error(server));
|
||||
goto report;
|
||||
}
|
||||
}
|
||||
|
||||
mysql_close(server);
|
||||
|
||||
end = clock();
|
||||
gettimeofday(&real_end,NULL);
|
||||
|
||||
test = (double)(end - begin)/CLOCKS_PER_SEC;
|
||||
timersub(&real_end,&real_begin,&real_test);
|
||||
|
||||
report:
|
||||
|
||||
if(rval){
|
||||
|
||||
printf("\nTest failed: Errors during test run.\n");
|
||||
|
||||
}else{
|
||||
|
||||
struct tm *tm;
|
||||
time = real_baseline.tv_sec;
|
||||
tm = localtime(&time);
|
||||
offset = strftime(str_baseline,256*sizeof(char),"%S",tm);
|
||||
sprintf(str_baseline + offset,".%06d",(int)real_baseline.tv_usec);
|
||||
time = real_test.tv_sec;
|
||||
tm = localtime(&time);
|
||||
offset = strftime(str_test,256*sizeof(char),"%S",tm);
|
||||
sprintf(str_test + offset,".%06d",(int)real_test.tv_usec);
|
||||
|
||||
printf("\n\tCPU time in seconds\n\nDirect connection: %f\nThrough MaxScale: %f\n",baseline,test);
|
||||
printf("\n\tReal time in seconds\n\nDirect connection: %s\nThrough MaxScale: %s\n",str_baseline,str_test);
|
||||
|
||||
double base_res = real_baseline.tv_sec + (real_baseline.tv_usec / 1000000.0);
|
||||
double test_res = real_test.tv_sec + (real_test.tv_usec / 1000000.0);
|
||||
result = test_res/base_res;
|
||||
|
||||
if(result > ratio){
|
||||
printf("\nTest failed: Time ratio was %f which exceeded the limit of %f.\n", result, ratio);
|
||||
rval = 1;
|
||||
}else{
|
||||
printf("\nTest passed: Time ratio was %f.\n",result);
|
||||
}
|
||||
}
|
||||
free(str_baseline);
|
||||
free(str_test);
|
||||
free(host);
|
||||
free(errmsg);
|
||||
return rval;
|
||||
}
|
@ -183,8 +183,7 @@ typedef enum skygw_chk_t {
|
||||
((p) == MYSQL_COM_QUIT ? "COM_QUIT" : \
|
||||
((p) == MYSQL_COM_STMT_PREPARE ? "MYSQL_COM_STMT_PREPARE" : \
|
||||
((p) == MYSQL_COM_STMT_EXECUTE ? "MYSQL_COM_STMT_EXECUTE" : \
|
||||
((p) == MYSQL_COM_UNDEFINED ? "MYSQL_COM_UNDEFINED" : \
|
||||
"UNKNOWN MYSQL PACKET TYPE")))))))))))))))))))
|
||||
"UNKNOWN MYSQL PACKET TYPE"))))))))))))))))))
|
||||
|
||||
#define STRDCBSTATE(s) ((s) == DCB_STATE_ALLOC ? "DCB_STATE_ALLOC" : \
|
||||
((s) == DCB_STATE_POLLING ? "DCB_STATE_POLLING" : \
|
||||
@ -263,6 +262,9 @@ typedef enum skygw_chk_t {
|
||||
(SERVER_IS_RELAY_SERVER(s) ? "RUNNING RELAY" : \
|
||||
(SERVER_IS_RUNNING(s) ? "RUNNING (only)" : "NO STATUS")))))))
|
||||
|
||||
#define BREFSRV(b) (b->bref_backend->backend_server)
|
||||
|
||||
|
||||
#define STRHINTTYPE(t) (t == HINT_ROUTE_TO_MASTER ? "HINT_ROUTE_TO_MASTER" : \
|
||||
((t) == HINT_ROUTE_TO_SLAVE ? "HINT_ROUTE_TO_SLAVE" : \
|
||||
((t) == HINT_ROUTE_TO_NAMED_SERVER ? "HINT_ROUTE_TO_NAMED_SERVER" : \
|
||||
@ -528,8 +530,8 @@ typedef enum skygw_chk_t {
|
||||
|
||||
|
||||
|
||||
#if defined(SS_DEBUG)
|
||||
#if defined(FAKE_CODE)
|
||||
bool conn_open[10240];
|
||||
#endif
|
||||
#endif /* FAKE_CODE */
|
||||
|
||||
#endif /* SKYGW_DEBUG_H */
|
||||
|
@ -1696,7 +1696,8 @@ return_succp:
|
||||
}
|
||||
|
||||
static bool file_write_footer(
|
||||
skygw_file_t* file)
|
||||
skygw_file_t* file,
|
||||
bool shutdown)
|
||||
{
|
||||
bool succp = false;
|
||||
size_t wbytes1;
|
||||
@ -1710,10 +1711,20 @@ static bool file_write_footer(
|
||||
const char* header_buf4;
|
||||
|
||||
CHK_FILE(file);
|
||||
header_buf1 = "MaxScale is shut down.\t";
|
||||
|
||||
if (shutdown)
|
||||
{
|
||||
header_buf1 = "MaxScale is shut down.\t";
|
||||
}
|
||||
else
|
||||
{
|
||||
header_buf1 = "Closed file due log rotation.\t";
|
||||
}
|
||||
tslen = get_timestamp_len();
|
||||
header_buf3 = (char *)malloc(tslen);
|
||||
if (header_buf3 == NULL) {
|
||||
|
||||
if (header_buf3 == NULL)
|
||||
{
|
||||
goto return_succp;
|
||||
}
|
||||
tslen = snprint_timestamp(header_buf3, tslen);
|
||||
@ -1729,7 +1740,8 @@ static bool file_write_footer(
|
||||
wbytes1=fwrite((void*)header_buf1, len1, 1, file->sf_file);
|
||||
wbytes4=fwrite((void*)header_buf4, len4, 1, file->sf_file);
|
||||
|
||||
if (wbytes1 != 1 || wbytes3 != 1 || wbytes4 != 1) {
|
||||
if (wbytes1 != 1 || wbytes3 != 1 || wbytes4 != 1)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"* Writing header %s %s to %s failed.\n",
|
||||
header_buf1,
|
||||
@ -1743,20 +1755,30 @@ static bool file_write_footer(
|
||||
|
||||
succp = true;
|
||||
return_succp:
|
||||
if (header_buf3 != NULL) {
|
||||
if (header_buf3 != NULL)
|
||||
{
|
||||
free(header_buf3);
|
||||
}
|
||||
return succp;
|
||||
}
|
||||
|
||||
|
||||
bool skygw_file_write(
|
||||
/**
|
||||
* Write data to a file.
|
||||
*
|
||||
* @param file write target
|
||||
* @param data pointer to contiguous memory buffer
|
||||
* @param nbytes amount of bytes to be written
|
||||
* @param flush ensure that write is permanent
|
||||
*
|
||||
* @return 0 if succeed, errno if failed.
|
||||
*/
|
||||
int skygw_file_write(
|
||||
skygw_file_t* file,
|
||||
void* data,
|
||||
size_t nbytes,
|
||||
bool flush)
|
||||
{
|
||||
bool succp = false;
|
||||
int rc;
|
||||
#if !defined(LAPTOP_TEST)
|
||||
int err = 0;
|
||||
size_t nwritten;
|
||||
@ -1771,13 +1793,14 @@ bool skygw_file_write(
|
||||
nwritten = fwrite(data, nbytes, 1, file->sf_file);
|
||||
|
||||
if (nwritten != 1) {
|
||||
rc = errno;
|
||||
perror("Logfile write.\n");
|
||||
fprintf(stderr,
|
||||
"* Writing %ld bytes, %s to %s failed.\n",
|
||||
"* Writing %ld bytes,\n%s\n to %s failed.\n",
|
||||
nbytes,
|
||||
(char *)data,
|
||||
file->sf_fname);
|
||||
goto return_succp;
|
||||
goto return_rc;
|
||||
}
|
||||
writecount += 1;
|
||||
|
||||
@ -1789,10 +1812,10 @@ bool skygw_file_write(
|
||||
writecount = 0;
|
||||
}
|
||||
#endif
|
||||
succp = true;
|
||||
rc = 0;
|
||||
CHK_FILE(file);
|
||||
return_succp:
|
||||
return succp;
|
||||
return_rc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
skygw_file_t* skygw_file_init(
|
||||
@ -1876,43 +1899,44 @@ return_file:
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
void skygw_file_done(
|
||||
skygw_file_t* file)
|
||||
void skygw_file_close(
|
||||
skygw_file_t* file,
|
||||
bool shutdown)
|
||||
{
|
||||
int fd;
|
||||
int err;
|
||||
|
||||
if (file != NULL) {
|
||||
CHK_FILE(file);
|
||||
|
||||
if (!file_write_footer(file)) {
|
||||
fprintf(stderr,
|
||||
"* Writing header of log file %s failed.\n",
|
||||
file->sf_fname);
|
||||
perror("SkyGW file open\n");
|
||||
}
|
||||
|
||||
fd = fileno(file->sf_file);
|
||||
fsync(fd);
|
||||
err = fclose(file->sf_file);
|
||||
|
||||
if (err != 0) {
|
||||
fprintf(stderr,
|
||||
"* Closing file %s failed : %s.\n",
|
||||
file->sf_fname,
|
||||
strerror(errno));
|
||||
}
|
||||
else
|
||||
int fd;
|
||||
int err;
|
||||
|
||||
if (file != NULL)
|
||||
{
|
||||
CHK_FILE(file);
|
||||
|
||||
if (!file_write_footer(file, shutdown))
|
||||
{
|
||||
fprintf(stderr,
|
||||
"* Writing footer to log file %s failed.\n",
|
||||
file->sf_fname);
|
||||
perror("Write fike footer\n");
|
||||
}
|
||||
fd = fileno(file->sf_file);
|
||||
fsync(fd);
|
||||
|
||||
if ((err = fclose(file->sf_file)) != 0)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"* Closing file %s failed due to %d, %s.\n",
|
||||
file->sf_fname,
|
||||
errno,
|
||||
strerror(errno));
|
||||
}
|
||||
else
|
||||
{
|
||||
ss_dfprintf(stderr, "Closed %s\n", file->sf_fname);
|
||||
free(file->sf_fname);
|
||||
free(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the given needle - user-provided literal - and replace it with
|
||||
* replacement string. Separate user-provided literals from matching table names
|
||||
|
@ -145,8 +145,8 @@ EXTERN_C_BLOCK_END
|
||||
|
||||
/** Skygw file routines */
|
||||
skygw_file_t* skygw_file_init(char* fname, char* symlinkname);
|
||||
void skygw_file_done(skygw_file_t* file);
|
||||
bool skygw_file_write(
|
||||
void skygw_file_close(skygw_file_t* file, bool shutdown);
|
||||
int skygw_file_write(
|
||||
skygw_file_t* file,
|
||||
void* data,
|
||||
size_t nbytes,
|
||||
|
Loading…
x
Reference in New Issue
Block a user