Merge branch 'develop' into MXS-544

This commit is contained in:
Markus Makela
2016-02-29 10:18:49 +02:00
128 changed files with 6094 additions and 3949 deletions

View File

@ -1,6 +1,6 @@
add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_ssl.c gw_utils.c hashtable.c hint.c housekeeper.c listener.c load_utils.c maxscale_pcre2.c memlog.c modutil.c monitor.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/log_manager/log_manager.cc ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc)
add_library(maxscale-common SHARED adminusers.c atomic.c buffer.c config.c dbusers.c dcb.c filter.c externcmd.c gwbitmask.c gwdirs.c gw_utils.c hashtable.c hint.c housekeeper.c listener.c load_utils.c log_manager.cc maxscale_pcre2.c memlog.c mlist.c modutil.c monitor.c query_classifier.c poll.c random_jkiss.c resultset.c secrets.c server.c service.c session.c slist.c spinlock.c thread.c users.c utils.c ${CMAKE_SOURCE_DIR}/utils/skygw_utils.cc statistics.c gw_ssl.c )
target_link_libraries(maxscale-common ${EMBEDDED_LIB} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${PCRE_LINK_FLAGS} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++)
target_link_libraries(maxscale-common ${MARIADB_CONNECTOR_LIBRARIES} ${LZMA_LINK_FLAGS} ${PCRE2_LIBRARIES} ${CURL_LIBRARIES} ssl aio pthread crypt dl crypto inih z rt m stdc++)
if(WITH_JEMALLOC)
target_link_libraries(maxscale-common ${JEMALLOC_LIBRARIES})
@ -10,6 +10,7 @@ endif()
add_dependencies(maxscale-common pcre2)
install(TARGETS maxscale-common DESTINATION ${MAXSCALE_LIBDIR})
set_target_properties(maxscale-common PROPERTIES VERSION "1.0.0")
add_executable(maxscale gateway.c)
add_dependencies(maxscale pcre2)

View File

@ -104,7 +104,9 @@ admin_verify(char *username, char *password)
{
return 0;
}
if (strcmp(pw, crypt(password, ADMIN_SALT)) == 0)
struct crypt_data cdata;
cdata.initialized = 0;
if (strcmp(pw, crypt_r(password, ADMIN_SALT, &cdata)) == 0)
{
return 1;
}
@ -191,7 +193,9 @@ admin_add_user(char *uname, char *passwd)
{
return ADMIN_ERR_DUPLICATE;
}
cpasswd = crypt(passwd, ADMIN_SALT);
struct crypt_data cdata;
cdata.initialized = 0;
cpasswd = crypt_r(passwd, ADMIN_SALT, &cdata);
users_add(users, uname, cpasswd);
if ((fp = fopen(fname, "a")) == NULL)
{

View File

@ -176,6 +176,7 @@ static char *monitor_params[] =
"backend_write_timeout",
"available_when_donor",
"disable_master_role_setting",
"use_priority",
NULL
};
@ -1015,6 +1016,22 @@ handle_global_item(const char *name, const char *value)
MXS_ERROR("Invalid timeout value for 'auth_write_timeout': %s", value);
}
}
else if (strcmp(name, "query_classifier") == 0)
{
int len = strlen(value);
int max_len = sizeof(gateway.qc_name) - 1;
if (len <= max_len)
{
strcpy(gateway.qc_name, value);
}
else
{
MXS_ERROR("The length of '%s' is %d, while the maximum length is %d.",
value, len, max_len);
return 0;
}
}
else
{
for (i = 0; lognames[i].name; i++)
@ -1244,6 +1261,9 @@ global_defaults()
{
strncpy(gateway.sysname, uname_data.sysname, _SYSNAME_STR_LENGTH);
}
/* query_classifier */
memset(gateway.qc_name, 0, sizeof(gateway.qc_name));
}
/**
@ -1367,6 +1387,9 @@ process_config_update(CONFIG_CONTEXT *context)
if (auth_all_servers)
{
serviceAuthAllServers(service, config_truth_value(auth_all_servers));
service_set_param_value(service,
config_get_param(obj->parameters, "auth_all_servers"),
auth_all_servers, 0, BOOL_TYPE);
}
if (optimize_wildcard)
{
@ -2236,6 +2259,9 @@ int create_new_service(CONFIG_CONTEXT *obj)
if (auth_all_servers)
{
serviceAuthAllServers(obj->element, config_truth_value(auth_all_servers));
service_set_param_value(service,
config_get_param(obj->parameters, "auth_all_servers"),
auth_all_servers, 0, BOOL_TYPE);
}
char *optimize_wildcard = config_get_value(obj->parameters, "optimize_wildcard");
@ -2658,19 +2684,31 @@ int create_new_monitor(CONFIG_CONTEXT *context, CONFIG_CONTEXT *obj, HASHTABLE*
char *connect_timeout = config_get_value(obj->parameters, "backend_connect_timeout");
if (connect_timeout)
{
monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, atoi(connect_timeout));
if (!monitorSetNetworkTimeout(obj->element, MONITOR_CONNECT_TIMEOUT, atoi(connect_timeout)))
{
MXS_ERROR("Failed to set backend_connect_timeout");
error_count++;
}
}
char *read_timeout = config_get_value(obj->parameters, "backend_read_timeout");
if (read_timeout)
{
monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, atoi(read_timeout));
if (!monitorSetNetworkTimeout(obj->element, MONITOR_READ_TIMEOUT, atoi(read_timeout)))
{
MXS_ERROR("Failed to set backend_read_timeout");
error_count++;
}
}
char *write_timeout = config_get_value(obj->parameters, "backend_write_timeout");
if (write_timeout)
{
monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, atoi(write_timeout));
if (!monitorSetNetworkTimeout(obj->element, MONITOR_WRITE_TIMEOUT, atoi(write_timeout)))
{
MXS_ERROR("Failed to set backend_write_timeout");
error_count++;
}
}
/* get the servers to monitor */

View File

@ -27,7 +27,8 @@
* 24/06/2013 Massimiliano Pinto Initial implementation
* 08/08/2013 Massimiliano Pinto Fixed bug for invalid memory access in row[1]+1 when row[1] is ""
* 06/02/2014 Massimiliano Pinto Mysql user root selected based on configuration flag
* 26/02/2014 Massimiliano Pinto Addd: replace_mysql_users() routine may replace users' table based on a checksum
* 26/02/2014 Massimiliano Pinto Addd: replace_mysql_users() routine may replace users' table
* based on a checksum
* 28/02/2014 Massimiliano Pinto Added Mysql user@host authentication
* 29/09/2014 Massimiliano Pinto Added Mysql user@host authentication with wildcard in IPv4 hosts:
* x.y.z.%, x.y.%.%, x.%.%.%
@ -53,72 +54,52 @@
#include <mysqld_error.h>
#include <regex.h>
#define USERS_QUERY_NO_ROOT " AND user NOT IN ('root')"
/** Alternate query which resolves user grants at the table level */
#if 0
#define LOAD_MYSQL_USERS_QUERY \
"SELECT DISTINCT \
user.user AS user, \
user.host AS host, \
user.password AS password, \
concat(user.user,user.host,user.password, \
IF((user.Select_priv+0)||find_in_set('Select',Coalesce(tp.Table_priv,0)),'Y','N') , \
COALESCE( db.db,tp.db, '')) AS userdata, \
user.Select_priv AS anydb, \
COALESCE( db.db,tp.db, NULL) AS db \
FROM \
mysql.user LEFT JOIN \
mysql.db ON user.user=db.user AND user.host=db.host LEFT JOIN \
mysql.tables_priv tp ON user.user=tp.user AND user.host=tp.host \
WHERE user.user IS NOT NULL AND user.user <> ''"
#else
#define LOAD_MYSQL_USERS_QUERY "SELECT \
user, host, password, concat(user,host,password,Select_priv) AS userdata, \
Select_priv AS anydb FROM mysql.user WHERE user IS NOT NULL AND user <> ''"
#endif
/** Don't include the root user */
#define USERS_QUERY_NO_ROOT " AND user.user NOT IN ('root')"
/** User count without databases */
#define MYSQL_USERS_COUNT "SELECT COUNT(1) AS nusers FROM mysql.user"
#define MYSQL_USERS_WITH_DB_ORDER " ORDER BY host DESC"
/** Order by host name */
#define MYSQL_USERS_ORDER_BY " ORDER BY host DESC "
#define LOAD_MYSQL_USERS_WITH_DB_QUERY "SELECT \
/** Normal password column name */
#define MYSQL_PASSWORD "password"
/** MySQL 5.7 password column name */
#define MYSQL57_PASSWORD "authentication_string"
/** Query template which resolves user grants and access to databases at the table level */
#define MYSQL_USERS_DB_QUERY_TEMPLATE \
"SELECT DISTINCT \
user.user AS user, \
user.host AS host, \
user.password AS password, \
concat(user.user,user.host,user.password,user.Select_priv,IFNULL(db,'')) AS userdata, \
user.%s AS password, \
concat(user.user,user.host,user.%s, \
IF((user.Select_priv+0)||find_in_set('Select',Coalesce(tp.Table_priv,0)),'Y','N') , \
COALESCE( db.db,tp.db, '')) AS userdata, \
user.Select_priv AS anydb, \
db.db AS db \
FROM mysql.user LEFT JOIN mysql.db \
ON user.user=db.user AND user.host=db.host \
WHERE user.user IS NOT NULL" MYSQL_USERS_WITH_DB_ORDER
COALESCE( db.db,tp.db, NULL) AS db \
FROM \
mysql.user LEFT JOIN \
mysql.db ON user.user=db.user AND user.host=db.host LEFT JOIN \
mysql.tables_priv tp ON user.user=tp.user AND user.host=tp.host \
WHERE user.user IS NOT NULL AND user.user <> ''"
#define LOAD_MYSQL57_USERS_WITH_DB_QUERY "SELECT \
user.user AS user, \
user.host AS host, \
user.authentication_string AS password, \
concat(user.user,user.host,user.authentication_string,user.Select_priv,IFNULL(db,'')) AS userdata, \
user.Select_priv AS anydb, \
db.db AS db \
FROM mysql.user LEFT JOIN mysql.db \
ON user.user=db.user AND user.host=db.host \
WHERE user.user IS NOT NULL" MYSQL_USERS_WITH_DB_ORDER
#define MYSQL_USERS_QUERY_TEMPLATE "SELECT \
user, host, %s, concat(user, host, %s, Select_priv) AS userdata, \
Select_priv AS anydb FROM mysql.user WHERE user.user IS NOT NULL AND user.user <> ''"
#define MYSQL_USERS_WITH_DB_COUNT "SELECT COUNT(1) AS nusers_db FROM \
(" LOAD_MYSQL_USERS_WITH_DB_QUERY ") AS tbl_count"
/** User count query split into two parts. This way the actual query used to
* fetch the users can be inserted as a subquery between the START and END
* portions of them. */
#define MYSQL_USERS_COUNT_TEMPLATE_START "SELECT COUNT(1) AS nusers_db FROM ("
#define MYSQL_USERS_COUNT_TEMPLATE_END ") AS tbl_count"
#define MYSQL57_USERS_WITH_DB_COUNT "SELECT COUNT(1) AS nusers_db FROM \
(" LOAD_MYSQL57_USERS_WITH_DB_QUERY ") AS tbl_count"
#define LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT "SELECT * \
FROM (" LOAD_MYSQL_USERS_WITH_DB_QUERY ") AS t1 \
WHERE user NOT IN ('root')" MYSQL_USERS_WITH_DB_ORDER
#define LOAD_MYSQL57_USERS_WITH_DB_QUERY_NO_ROOT "SELECT * \
FROM (" LOAD_MYSQL57_USERS_WITH_DB_QUERY ") AS t1 \
WHERE user NOT IN ('root')" MYSQL_USERS_WITH_DB_ORDER
/** The maximum possible length of the query */
#define MAX_QUERY_STR_LEN strlen(MYSQL_USERS_COUNT_TEMPLATE_START \
MYSQL_USERS_COUNT_TEMPLATE_END MYSQL_USERS_DB_QUERY_TEMPLATE \
MYSQL_USERS_ORDER_BY) + strlen(MYSQL57_PASSWORD) * 2 + 1
#define LOAD_MYSQL_DATABASE_NAMES "SELECT * \
FROM ( (SELECT COUNT(1) AS ndbs \
@ -130,58 +111,98 @@
MaxScale authentication will proceed without including database permissions. \
To correct this GRANT SHOW DATABASES ON *.* privilege to the user %s."
static int getUsers(SERVICE *service, USERS *users);
static int add_databases(SERVICE *service, MYSQL *con);
static int add_wildcard_users(USERS *users, char* name, char* host,
char* password, char* anydb, char* db, HASHTABLE* hash);
static void *dbusers_keyread(int fd);
static int dbusers_keywrite(int fd, void *key);
static void *dbusers_valueread(int fd);
static int dbusers_valuewrite(int fd, void *value);
static int get_all_users(SERVICE *service, USERS *users);
static int get_databases(SERVICE *, MYSQL *);
static int get_users(SERVICE *service, USERS *users);
static MYSQL *gw_mysql_init(void);
static int gw_mysql_set_timeouts(MYSQL* handle);
static bool host_has_singlechar_wildcard(const char *host);
static bool host_matches_singlechar_wildcard(const char* user, const char* wild);
static bool is_ipaddress(const char* host);
static char *mysql_format_user_entry(void *data);
static char *mysql_format_user_entry(void *data);
static int normalize_hostname(const char *input_host, char *output_host);
static int resource_add(HASHTABLE *, char *, char *);
static HASHTABLE *resource_alloc();
static void *resource_fetch(HASHTABLE *, char *);
static void resource_free(HASHTABLE *resource);
static int uh_cmpfun(void* v1, void* v2);
static int uh_hfun(void* key);
static void *uh_keydup(void* key);
static void uh_keyfree(void* key);
static int uh_hfun(void* key);
char *mysql_users_fetch(USERS *users, MYSQL_USER_HOST *key);
char *mysql_format_user_entry(void *data);
int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host,
char *passwd, char *anydb, char *db);
static int getDatabases(SERVICE *, MYSQL *);
HASHTABLE *resource_alloc();
void resource_free(HASHTABLE *resource);
void *resource_fetch(HASHTABLE *, char *);
int resource_add(HASHTABLE *, char *, char *);
int resource_hash(char *);
static int normalize_hostname(char *input_host, char *output_host);
int wildcard_db_grant(char* str);
int add_wildcard_users(USERS *users, char* name, char* host, char* password,
char* anydb, char* db, HASHTABLE* hash);
static int gw_mysql_set_timeouts(MYSQL* handle);
static int wildcard_db_grant(char* str);
/**
* Get the user data query.
* Get the user data query with databases
*
* @param server_version Server version string
* @param include_root Include root user
* @return Users query
* @param buffer Destination where the query is written. Must be at least
* MAX_QUERY_STR_LEN bytes long
* @return Users query with databases included
*/
const char* get_mysql_users_query(char* server_version, bool include_root)
static char* get_users_db_query(const char* server_version, bool include_root, char* buffer)
{
const char* rval;
if (strstr(server_version, "5.7."))
{
rval = include_root ? LOAD_MYSQL57_USERS_WITH_DB_QUERY :
LOAD_MYSQL57_USERS_WITH_DB_QUERY_NO_ROOT;
}
else
{
rval = include_root ? LOAD_MYSQL_USERS_WITH_DB_QUERY :
LOAD_MYSQL_USERS_WITH_DB_QUERY_NO_ROOT;
}
return rval;
const char* password = strstr(server_version, "5.7.") ?
MYSQL57_PASSWORD : MYSQL_PASSWORD;
int nchars = snprintf(buffer, MAX_QUERY_STR_LEN, MYSQL_USERS_DB_QUERY_TEMPLATE
"%s" MYSQL_USERS_ORDER_BY, password, password,
include_root ? "" : USERS_QUERY_NO_ROOT);
ss_dassert(nchars < MAX_QUERY_STR_LEN);
(void) nchars;
return buffer;
}
/**
* Get the user count query.
* Get the user data query
*
* @param server_version Server version string
* @return User vount query
* */
const char* get_mysq_users_db_count_query(char* server_version)
* @param include_root Include root user
* @param buffer Destination where the query is written. Must be at least
* MAX_QUERY_STR_LEN bytes long
* @return Users query
*/
static char* get_users_query(const char* server_version, bool include_root, char* buffer)
{
return strstr(server_version, "5.7.") ?
MYSQL57_USERS_WITH_DB_COUNT : MYSQL_USERS_WITH_DB_COUNT;
const char* password = strstr(server_version, "5.7.") ?
MYSQL57_PASSWORD : MYSQL_PASSWORD;
int nchars = snprintf(buffer, MAX_QUERY_STR_LEN, MYSQL_USERS_QUERY_TEMPLATE "%s"
MYSQL_USERS_ORDER_BY, password, password,
include_root ? "" : USERS_QUERY_NO_ROOT);
ss_dassert(nchars < MAX_QUERY_STR_LEN);
(void) nchars;
return buffer;
}
/**
* Get the user count query
*
* @param server_version Server version string
* @param buffer Destination where the query is written. Must be at least
* MAX_QUERY_STR_LEN bytes long
* @return User count query
* */
static char* get_usercount_query(const char* server_version, bool include_root, char* buffer)
{
const char* password = strstr(server_version, "5.7.") ?
MYSQL57_PASSWORD : MYSQL_PASSWORD;
int nchars = snprintf(buffer, MAX_QUERY_STR_LEN, MYSQL_USERS_COUNT_TEMPLATE_START
MYSQL_USERS_DB_QUERY_TEMPLATE "%s" MYSQL_USERS_ORDER_BY
MYSQL_USERS_COUNT_TEMPLATE_END, password, password,
include_root ? "" : USERS_QUERY_NO_ROOT);
ss_dassert(nchars < MAX_QUERY_STR_LEN);
(void) nchars;
return buffer;
}
/**
@ -191,7 +212,7 @@ const char* get_mysq_users_db_count_query(char* server_version)
* @param wildcardhost Host address in the grant
* @return True if the host address matches
*/
bool host_matches_singlechar_wildcard(const char* user, const char* wild)
static bool host_matches_singlechar_wildcard(const char* user, const char* wild)
{
while (*user != '\0' && *wild != '\0')
{
@ -215,7 +236,7 @@ bool host_matches_singlechar_wildcard(const char* user, const char* wild)
int
load_mysql_users(SERVICE *service)
{
return getUsers(service, service->users);
return get_users(service, service->users);
}
/**
@ -239,7 +260,7 @@ reload_mysql_users(SERVICE *service)
oldresources = service->resources;
i = getUsers(service, newusers);
i = get_users(service, newusers);
spinlock_acquire(&service->spin);
oldusers = service->users;
@ -279,7 +300,7 @@ replace_mysql_users(SERVICE *service)
oldresources = service->resources;
/* load db users ad db grants */
i = getUsers(service, newusers);
i = get_users(service, newusers);
if (i <= 0)
{
@ -332,7 +353,7 @@ replace_mysql_users(SERVICE *service)
* @param host IP address to check
* @return True if the address is a valid, MySQL type IP address
*/
bool is_ipaddress(const char* host)
static bool is_ipaddress(const char* host)
{
while (*host != '\0')
{
@ -351,7 +372,7 @@ bool is_ipaddress(const char* host)
* @param host Hostname to check
* @return True if the hostname is a valid IP address with a single character wildcard
*/
bool host_has_singlechar_wildcard(const char *host)
static bool host_has_singlechar_wildcard(const char *host)
{
const char* chrptr = host;
bool retval = false;
@ -388,8 +409,8 @@ bool host_has_singlechar_wildcard(const char *host)
* @return 1 on success, 0 on failure and -1 on duplicate user
*/
int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host,
char *passwd, char *anydb, char *db)
int add_mysql_users_with_host_ipv4(USERS *users, const char *user, const char *host,
char *passwd, const char *anydb, const char *db)
{
struct sockaddr_in serv_addr;
MYSQL_USER_HOST key;
@ -504,7 +525,7 @@ int add_mysql_users_with_host_ipv4(USERS *users, char *user, char *host,
* @return -1 on any error or the number of users inserted (0 means no users at all)
*/
static int
addDatabases(SERVICE *service, MYSQL *con)
add_databases(SERVICE *service, MYSQL *con)
{
MYSQL_ROW row;
MYSQL_RES *result = NULL;
@ -609,7 +630,7 @@ addDatabases(SERVICE *service, MYSQL *con)
* @return -1 on any error or the number of users inserted (0 means no users at all)
*/
static int
getDatabases(SERVICE *service, MYSQL *con)
get_databases(SERVICE *service, MYSQL *con)
{
MYSQL_ROW row;
MYSQL_RES *result = NULL;
@ -715,7 +736,7 @@ getDatabases(SERVICE *service, MYSQL *con)
* @return -1 on any error or the number of users inserted
*/
static int
getAllUsers(SERVICE *service, USERS *users)
get_all_users(SERVICE *service, USERS *users)
{
MYSQL *con = NULL;
MYSQL_ROW row;
@ -725,7 +746,7 @@ getAllUsers(SERVICE *service, USERS *users)
char *dpwd = NULL;
int total_users = 0;
SERVER_REF *server;
const char *users_query;
const char *userquery;
char *tmp;
unsigned char hash[SHA_DIGEST_LENGTH] = "";
char *users_data = NULL;
@ -769,32 +790,13 @@ getAllUsers(SERVICE *service, USERS *users)
while (server != NULL)
{
con = gw_mysql_init();
con = mysql_init(NULL);
if (con == NULL)
if (!con)
{
MXS_ERROR("mysql_init: %s", mysql_error(con));
goto cleanup;
}
/** Set read, write and connect timeout values */
if (gw_mysql_set_timeouts(con))
{
MXS_ERROR("Failed to set timeout values for backend connection.");
mysql_close(con);
goto cleanup;
}
if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL))
{
MXS_ERROR("Failed to set external connection. "
"It is needed for backend server connections.");
mysql_close(con);
goto cleanup;
}
while (!service->svc_do_shutdown &&
server != NULL &&
(mysql_real_connect(con,
@ -819,7 +821,7 @@ getAllUsers(SERVICE *service, USERS *users)
goto cleanup;
}
addDatabases(service, con);
add_databases(service, con);
mysql_close(con);
server = server->next;
}
@ -828,28 +830,10 @@ getAllUsers(SERVICE *service, USERS *users)
while (server != NULL)
{
con = gw_mysql_init();
con = mysql_init(NULL);
if (con == NULL)
if (!con)
{
MXS_ERROR("mysql_init: %s", mysql_error(con));
goto cleanup;
}
/** Set read, write and connect timeout values */
if (gw_mysql_set_timeouts(con))
{
MXS_ERROR("Failed to set timeout values for backend connection.");
mysql_close(con);
goto cleanup;
}
if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL))
{
MXS_ERROR("Failed to set external connection. "
"It is needed for backend server connections.");
mysql_close(con);
goto cleanup;
}
@ -880,9 +864,12 @@ getAllUsers(SERVICE *service, USERS *users)
goto cleanup;
}
}
char querybuffer[MAX_QUERY_STR_LEN];
/** Count users. Start with users and db grants for users */
const char *user_with_db_count = get_mysq_users_db_count_query(server->server->server_string);
if (mysql_query(con, user_with_db_count))
const char *usercount = get_usercount_query(server->server->server_string,
service->enable_root, querybuffer);
if (mysql_query(con, usercount))
{
if (mysql_errno(con) != ER_TABLEACCESS_DENIED_ERROR)
{
@ -934,11 +921,11 @@ getAllUsers(SERVICE *service, USERS *users)
goto cleanup;
}
users_query = get_mysql_users_query(server->server->server_string,
service->enable_root);
userquery = get_users_db_query(server->server->server_string,
service->enable_root, querybuffer);
/* send first the query that fetches users and db grants */
if (mysql_query(con, users_query))
if (mysql_query(con, userquery))
{
/*
* An error occurred executing the query
@ -969,17 +956,10 @@ getAllUsers(SERVICE *service, USERS *users)
MXS_ERROR(ERROR_NO_SHOW_DATABASES, service->name, service_user);
/* check for root user select */
if (service->enable_root)
{
users_query = LOAD_MYSQL_USERS_QUERY " ORDER BY HOST DESC";
}
else
{
users_query = LOAD_MYSQL_USERS_QUERY USERS_QUERY_NO_ROOT " ORDER BY HOST DESC";
}
userquery = get_users_query(server->server->server_string,
service->enable_root, querybuffer);
if (mysql_query(con, users_query))
if (mysql_query(con, userquery))
{
MXS_ERROR("Loading users for service [%s] encountered "
"error: [%s], code %i",
@ -1244,7 +1224,7 @@ cleanup:
* @return -1 on any error or the number of users inserted
*/
static int
getUsers(SERVICE *service, USERS *users)
get_users(SERVICE *service, USERS *users)
{
MYSQL *con = NULL;
MYSQL_ROW row;
@ -1254,7 +1234,7 @@ getUsers(SERVICE *service, USERS *users)
char *dpwd;
int total_users = 0;
SERVER_REF *server;
const char *users_query;
const char *userquery;
unsigned char hash[SHA_DIGEST_LENGTH] = "";
char *users_data = NULL;
int nusers = 0;
@ -1276,31 +1256,16 @@ getUsers(SERVICE *service, USERS *users)
if (service->users_from_all)
{
return getAllUsers(service, users);
return get_all_users(service, users);
}
con = mysql_init(NULL);
con = gw_mysql_init();
if (con == NULL)
if (!con)
{
MXS_ERROR("mysql_init: %s", mysql_error(con));
return -1;
}
/** Set read, write and connect timeout values */
if (gw_mysql_set_timeouts(con))
{
MXS_ERROR("Failed to set timeout values for backend connection.");
mysql_close(con);
return -1;
}
if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL))
{
MXS_ERROR("Failed to set external connection. "
"It is needed for backend server connections.");
mysql_close(con);
return -1;
}
/**
* Attempt to connect to one of the databases database or until we run
* out of databases
@ -1383,9 +1348,11 @@ getUsers(SERVICE *service, USERS *users)
}
}
const char *user_with_db_count = get_mysq_users_db_count_query(server->server->server_string);
char querybuffer[MAX_QUERY_STR_LEN];
const char *usercount = get_usercount_query(server->server->server_string,
service->enable_root, querybuffer);
/** Count users. Start with users and db grants for users */
if (mysql_query(con, user_with_db_count))
if (mysql_query(con, usercount))
{
if (mysql_errno(con) != ER_TABLEACCESS_DENIED_ERROR)
{
@ -1434,10 +1401,10 @@ getUsers(SERVICE *service, USERS *users)
return -1;
}
users_query = get_mysql_users_query(server->server->server_string,
service->enable_root);
userquery = get_users_db_query(server->server->server_string,
service->enable_root, querybuffer);
/* send first the query that fetches users and db grants */
if (mysql_query(con, users_query))
if (mysql_query(con, userquery))
{
/*
* An error occurred executing the query
@ -1461,20 +1428,13 @@ getUsers(SERVICE *service, USERS *users)
/*
* We have got ER_TABLEACCESS_DENIED_ERROR
* try loading users from mysql.user without DB names.
*/
*/
MXS_ERROR(ERROR_NO_SHOW_DATABASES, service->name, service_user);
/* check for root user select */
if (service->enable_root)
{
users_query = LOAD_MYSQL_USERS_QUERY " ORDER BY HOST DESC";
}
else
{
users_query = LOAD_MYSQL_USERS_QUERY USERS_QUERY_NO_ROOT " ORDER BY HOST DESC";
}
userquery = get_users_query(server->server->server_string,
service->enable_root, querybuffer);
if (mysql_query(con, users_query))
if (mysql_query(con, userquery))
{
MXS_ERROR("Loading users for service [%s] encountered error: "
"[%s], code %i", service->name, mysql_error(con),
@ -1524,7 +1484,7 @@ getUsers(SERVICE *service, USERS *users)
if (db_grants)
{
/* load all mysql database names */
dbnames = getDatabases(service, con);
dbnames = get_databases(service, con);
MXS_DEBUG("Loaded %d MySQL Database Names for service [%s]",
dbnames, service->name);
}
@ -1999,7 +1959,7 @@ static void uh_keyfree(void* key)
* @param data Input data
* @return the MySQL user@host
*/
char *mysql_format_user_entry(void *data)
static char *mysql_format_user_entry(void *data)
{
MYSQL_USER_HOST *entry;
char *mysql_user;
@ -2068,7 +2028,7 @@ char *mysql_format_user_entry(void *data)
*
* @param resources The resources table to remove
*/
void
static void
resource_free(HASHTABLE *resources)
{
if (resources)
@ -2082,7 +2042,7 @@ resource_free(HASHTABLE *resources)
*
* @return The database names table
*/
HASHTABLE *
static HASHTABLE *
resource_alloc()
{
HASHTABLE *resources;
@ -2106,7 +2066,7 @@ resource_alloc()
* @param value The value for resource (not used)
* @return The number of resources dded to the table
*/
int
static int
resource_add(HASHTABLE *resources, char *key, char *value)
{
return hashtable_add(resources, key, value);
@ -2119,7 +2079,7 @@ resource_add(HASHTABLE *resources, char *key, char *value)
* @param key The database name to fetch
* @return The database esists or NULL if not found
*/
void *
static void *
resource_fetch(HASHTABLE *resources, char *key)
{
return hashtable_fetch(resources, key);
@ -2139,7 +2099,7 @@ resource_fetch(HASHTABLE *resources, char *key)
* @param output_host The normalized hostname (buffer must be preallocated)
* @return The calculated netmask or -1 on failure
*/
static int normalize_hostname(char *input_host, char *output_host)
static int normalize_hostname(const char *input_host, char *output_host)
{
int netmask, bytes, bits = 0, found_wildcard = 0;
char *p, *lasts, *tmp;
@ -2215,6 +2175,47 @@ static int normalize_hostname(char *input_host, char *output_host)
return netmask;
}
/**
* Returns a MYSQL object suitably configured.
*
* @return An object or NULL if something fails.
*/
MYSQL *gw_mysql_init()
{
MYSQL* con = mysql_init(NULL);
if (con)
{
if (gw_mysql_set_timeouts(con) == 0)
{
// MYSQL_OPT_USE_REMOTE_CONNECTION must be set if the embedded
// libary is used. With Connector-C (at least 2.2.1) the call
// fails.
#if !defined(LIBMARIADB)
if (mysql_options(con, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL) != 0)
{
MXS_ERROR("Failed to set external connection. "
"It is needed for backend server connections.");
mysql_close(con);
con = NULL;
}
#endif
}
else
{
MXS_ERROR("Failed to set timeout values for backend connection.");
mysql_close(con);
con = NULL;
}
}
else
{
MXS_ERROR("mysql_init: %s", mysql_error(NULL));
}
return con;
}
/**
* Set read, write and connect timeout values for MySQL database connection.
*
@ -2448,7 +2449,7 @@ dbusers_valueread(int fd)
* @return The number of entries saved
*/
int
dbusers_save(USERS *users, char *filename)
dbusers_save(USERS *users, const char *filename)
{
return hashtable_save(users->data, filename, dbusers_keywrite, dbusers_valuewrite);
}
@ -2461,7 +2462,7 @@ dbusers_save(USERS *users, char *filename)
* @return The number of entries loaded
*/
int
dbusers_load(USERS *users, char *filename)
dbusers_load(USERS *users, const char *filename)
{
return hashtable_load(users->data, filename, dbusers_keyread, dbusers_valueread);
}
@ -2471,7 +2472,7 @@ dbusers_load(USERS *users, char *filename)
* @param str Database grant
* @return 1 if the name contains the '%' wildcard character, 0 if it does not
*/
int wildcard_db_grant(char* str)
static int wildcard_db_grant(char* str)
{
char* ptr = str;
@ -2498,8 +2499,8 @@ int wildcard_db_grant(char* str)
* @param hash Hashtable with all database names
* @return number of unique grants generated from wildcard database name
*/
int add_wildcard_users(USERS *users, char* name, char* host, char* password,
char* anydb, char* db, HASHTABLE* hash)
static int add_wildcard_users(USERS *users, char* name, char* host, char* password,
char* anydb, char* db, HASHTABLE* hash)
{
HASHITERATOR* iter;
HASHTABLE* ht = hash;
@ -2580,7 +2581,6 @@ bool check_service_permissions(SERVICE* service)
MYSQL_RES* res;
char *user, *password, *dpasswd;
SERVER_REF* server;
int conn_timeout = 1;
bool rval = true;
if (is_internal_service(service->routerModule))
@ -2605,15 +2605,14 @@ bool check_service_permissions(SERVICE* service)
dpasswd = decryptPassword(password);
if ((mysql = mysql_init(NULL)) == NULL)
mysql = gw_mysql_init();
if (!mysql)
{
MXS_ERROR("[%s] MySQL connection initialization failed.", __FUNCTION__);
free(dpasswd);
return false;
}
mysql_options(mysql, MYSQL_OPT_USE_REMOTE_CONNECTION, NULL);
mysql_options(mysql, MYSQL_OPT_CONNECT_TIMEOUT, &conn_timeout);
/** Connect to the first server. This assumes all servers have identical
* user permissions. */

View File

@ -932,7 +932,7 @@ int dcb_read(DCB *dcb,
* @param dcb The DCB to read from
* @return -1 on error, otherwise the total number of bytes available
*/
static inline int
static int
dcb_bytes_readable(DCB *dcb)
{
int bytesavailable;
@ -965,7 +965,7 @@ dcb_bytes_readable(DCB *dcb)
* @param nreadtotal Number of bytes that have been read
* @return -1 on error, 0 for conditions not treated as error
*/
static inline int
static int
dcb_read_no_bytes_available(DCB *dcb, int nreadtotal)
{
/** Handle closed client socket */
@ -1000,7 +1000,7 @@ dcb_read_no_bytes_available(DCB *dcb, int nreadtotal)
* @param nsingleread To be set as the number of bytes read this time
* @return GWBUF* buffer containing new data, or null.
*/
static inline GWBUF *
static GWBUF *
dcb_basic_read(DCB *dcb, int bytesavailable, int maxbytes, int nreadtotal, int *nsingleread)
{
GWBUF *buffer;

File diff suppressed because it is too large Load Diff

View File

@ -85,6 +85,17 @@ void set_datadir(char* param)
maxscaledatadir = param;
}
/**
* Set the data directory
* @param str Path to directory
*/
void set_process_datadir(char* param)
{
free(processdatadir);
clean_up_pathname(param);
processdatadir = param;
}
/**
* Set the library directory. Modules will be loaded from here.
* @param str Path to directory
@ -96,6 +107,18 @@ void set_libdir(char* param)
libdir = param;
}
/**
* Set the executable directory. Internal processes will look for executables
* from here.
* @param str Path to directory
*/
void set_execdir(char* param)
{
free(execdir);
clean_up_pathname(param);
execdir = param;
}
/**
* Get the directory with all the modules.
* @return The module directory
@ -115,14 +138,23 @@ char* get_cachedir()
}
/**
* Get the service cache directory
* @return The path to the cache directory
* Get the MaxScale data directory
* @return The path to the data directory
*/
char* get_datadir()
{
return maxscaledatadir ? maxscaledatadir : (char*) default_datadir;
}
/**
* Get the process specific data directory
* @return The path to the process specific directory
*/
char* get_process_datadir()
{
return processdatadir ? processdatadir : (char*) default_datadir;
}
/**
* Get the configuration file directory
* @return The path to the configuration file directory
@ -158,3 +190,12 @@ char* get_langdir()
{
return langdir ? langdir : (char*) default_langdir;
}
/**
* Get the directory with the executables.
* @return The executables directory
*/
char* get_execdir()
{
return execdir ? execdir : (char*) default_execdir;
}

View File

@ -711,7 +711,7 @@ hashtable_iterator_free(HASHITERATOR *iter)
* @return Number of entries written or -1 on error
*/
int
hashtable_save(HASHTABLE *table, char *filename,
hashtable_save(HASHTABLE *table, const char *filename,
int (*keywrite)(int, void*),
int (*valuewrite)(int, void*))
{
@ -771,7 +771,7 @@ hashtable_save(HASHTABLE *table, char *filename,
* @return Number of entries read or -1 on error
*/
int
hashtable_load(HASHTABLE *table, char *filename,
hashtable_load(HASHTABLE *table, const char *filename,
void *(*keyread)(int),
void *(*valueread)(int))
{

View File

@ -20,6 +20,7 @@
#include <housekeeper.h>
#include <thread.h>
#include <spinlock.h>
#include <log_manager.h>
/**
* @file housekeeper.c Provide a mechanism to run periodic tasks
@ -54,6 +55,7 @@ static SPINLOCK tasklock = SPINLOCK_INIT;
static int do_shutdown = 0;
long hkheartbeat = 0; /*< One heartbeat is 100 milliseconds */
static THREAD hk_thr_handle;
static void hkthread(void *);
@ -63,7 +65,10 @@ static void hkthread(void *);
void
hkinit()
{
thread_start(hkthread, NULL);
if (thread_start(&hk_thr_handle, hkthread, NULL) == NULL)
{
MXS_ERROR("Failed to start housekeeper thread.");
}
}
/**
@ -84,7 +89,7 @@ hkinit()
* if the task was added, otherwise 0
*/
int
hktask_add(char *name, void (*taskfn)(void *), void *data, int frequency)
hktask_add(const char *name, void (*taskfn)(void *), void *data, int frequency)
{
HKTASK *task, *ptr;
@ -150,7 +155,7 @@ hktask_add(char *name, void (*taskfn)(void *), void *data, int frequency)
*
*/
int
hktask_oneshot(char *name, void (*taskfn)(void *), void *data, int when)
hktask_oneshot(const char *name, void (*taskfn)(void *), void *data, int when)
{
HKTASK *task, *ptr;
@ -203,7 +208,7 @@ hktask_oneshot(char *name, void (*taskfn)(void *), void *data, int when)
* @return Returns 0 if the task could not be removed
*/
int
hktask_remove(char *name)
hktask_remove(const char *name)
{
HKTASK *ptr, *lptr = NULL;

View File

@ -198,6 +198,12 @@ load_module(const char *module, const char *type)
MXS_ERROR("Module '%s' does not implement the filter API.", module);
fatal = 1;
}
if (strcmp(type, MODULE_QUERY_CLASSIFIER) == 0
&& mod_info->modapi != MODULE_API_QUERY_CLASSIFIER)
{
MXS_ERROR("Module '%s' does not implement the query classifier API.", module);
fatal = 1;
}
if (fatal)
{
dlclose(dlhandle);
@ -345,6 +351,14 @@ unregister_module(const char *module)
{
ptr = ptr->next;
}
/*<
* Remove the module to be be freed from the list.
*/
if (ptr && (ptr->next == mod))
{
ptr->next = ptr->next->next;
}
}
/*<

2829
server/core/log_manager.cc Normal file

File diff suppressed because it is too large Load Diff

421
server/core/mlist.c Normal file
View File

@ -0,0 +1,421 @@
/*
* 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
*/
#include <mlist.h>
static void mlist_free_memory(mlist_t* ml, char* name);
static mlist_node_t* mlist_node_init(void* data, mlist_cursor_t* cursor);
//static mlist_node_t* mlist_node_get_next(mlist_node_t* curr_node);
//static mlist_node_t* mlist_get_first(mlist_t* list);
//static mlist_cursor_t* mlist_get_cursor(mlist_t* list);
/**
* @node Cut off nodes of the list.
*
* Parameters:
* @param ml - <usage>
* <description>
*
* @return Pointer to the first of the detached nodes.
*
*
* @details (write detailed description here)
*
*/
mlist_node_t* mlist_detach_nodes(mlist_t* ml)
{
mlist_node_t* node;
CHK_MLIST(ml);
node = ml->mlist_first;
ml->mlist_first = NULL;
ml->mlist_last = NULL;
ml->mlist_nodecount = 0;
return node;
}
/**
* @node Create a list with rwlock and optional read-only cursor
*
* Parameters:
* @param listp - <usage>
* <description>
*
* @param cursor - <usage>
* <description>
*
* @param name - <usage>
* <description>
*
* @return Address of mlist_t struct.
*
*
* @details Cursor must protect its reads with read lock, and after
* acquiring read lock reader must check whether the list is deleted
* (mlist_deleted).
*
*/
mlist_t* mlist_init(mlist_t* listp, mlist_cursor_t** cursor, char* name,
void (*datadel)(void*), int maxnodes)
{
mlist_cursor_t* c;
mlist_t* list;
if (cursor != NULL)
{
ss_dassert(*cursor == NULL);
}
/** listp is not NULL if caller wants flat list */
if (listp == NULL)
{
list = (mlist_t*) calloc(1, sizeof (mlist_t));
}
else
{
/** Caller wants list flat, memory won't be freed */
list = listp;
list->mlist_flat = true;
}
ss_dassert(list != NULL);
if (list == NULL)
{
fprintf(stderr, "* Allocating memory for mlist failed\n");
mlist_free_memory(list, name);
goto return_list;
}
list->mlist_chk_top = CHK_NUM_MLIST;
list->mlist_chk_tail = CHK_NUM_MLIST;
/** Set size limit for list. 0 means unlimited */
list->mlist_nodecount_max = maxnodes;
/** Set data deletion callback fun */
list->mlist_datadel = datadel;
if (name != NULL)
{
list->mlist_name = name;
}
/** Create mutex, return NULL if fails. */
if (simple_mutex_init(&list->mlist_mutex, "writebuf mutex") == NULL)
{
ss_dfprintf(stderr, "* Creating rwlock for mlist failed\n");
mlist_free_memory(list, name);
list = NULL;
goto return_list;
}
/** Create cursor for reading the list */
if (cursor != NULL)
{
c = mlist_cursor_init(list);
if (c == NULL)
{
simple_mutex_done(&list->mlist_mutex);
mlist_free_memory(list, name);
list = NULL;
goto return_list;
}
CHK_MLIST_CURSOR(c);
*cursor = c;
}
list->mlist_versno = 2; /*< vresno != 0 means that list is initialized */
CHK_MLIST(list);
return_list:
return list;
}
/**
* @node Free mlist memory allocations. name must be explicitly
* set if mlist has one.
*
* Parameters:
* @param ml - <usage>
* <description>
*
* @param name - <usage>
* <description>
*
* @return void
*
*
* @details (write detailed description here)
*
*/
static void mlist_free_memory(mlist_t* ml, char* name)
{
mlist_node_t* node;
/** name */
if (name != NULL)
{
free(name);
}
if (ml != NULL)
{
/** list data */
while (ml->mlist_first != NULL)
{
/** Scan list and free nodes and data inside nodes */
node = ml->mlist_first->mlnode_next;
mlist_node_done(ml->mlist_first);
ml->mlist_first = node;
}
/** list structure */
if (!ml->mlist_flat)
{
free(ml);
}
}
}
void* mlist_node_get_data(mlist_node_t* node)
{
CHK_MLIST_NODE(node);
return node->mlnode_data;
}
void mlist_node_done(mlist_node_t* n)
{
CHK_MLIST_NODE(n);
if (n->mlnode_data != NULL)
{
if (n->mlnode_list->mlist_datadel != NULL)
{
(n->mlnode_list->mlist_datadel(n->mlnode_data));
}
free(n->mlnode_data);
}
free(n);
}
/**
* @node Mark list as deleted and free the memory.
*
* Parameters:
* @param list - <usage>
* <description>
*
* @return void
*
*
* @details (write detailed description here)
*
*/
void mlist_done(mlist_t* list)
{
CHK_MLIST(list);
simple_mutex_lock(&list->mlist_mutex, true);
list->mlist_deleted = true;
simple_mutex_unlock(&list->mlist_mutex);
simple_mutex_done(&list->mlist_mutex);
mlist_free_memory(list, list->mlist_name);
}
/**
* @node Adds data to list by allocating node for it. Checks list size limit.
*
* Parameters:
* @param list - <usage>
* <description>
*
* @param data - <usage>
* <description>
*
* @return true, if succeed, false, if list had node limit and it is full.
*
*
* @details (write detailed description here)
*
*/
bool mlist_add_data_nomutex(mlist_t* list, void* data)
{
bool succp;
succp = mlist_add_node_nomutex(list, mlist_node_init(data, NULL));
return succp;
}
static mlist_node_t* mlist_node_init(void* data, mlist_cursor_t* cursor)
{
mlist_node_t* node;
node = (mlist_node_t*) calloc(1, sizeof (mlist_node_t));
node->mlnode_chk_top = CHK_NUM_MLIST_NODE;
node->mlnode_chk_tail = CHK_NUM_MLIST_NODE;
node->mlnode_data = data;
CHK_MLIST_NODE(node);
if (cursor != NULL)
{
cursor->mlcursor_pos = node;
}
return node;
}
mlist_node_t* mlist_detach_first(mlist_t* ml)
{
mlist_node_t* node;
CHK_MLIST(ml);
node = ml->mlist_first;
CHK_MLIST_NODE(node);
ml->mlist_first = node->mlnode_next;
node->mlnode_next = NULL;
ml->mlist_nodecount -= 1;
if (ml->mlist_nodecount == 0)
{
ml->mlist_last = NULL;
}
else
{
CHK_MLIST_NODE(ml->mlist_first);
}
CHK_MLIST(ml);
return (node);
}
/**
* @node Add new node to end of list if there is space for it.
*
* Parameters:
* @param list - <usage>
* <description>
*
* @param newnode - <usage>
* <description>
*
* @param add_last - <usage>
* <description>
*
* @return true, if succeede, false, if list size limit was exceeded.
*
*
* @details (write detailed description here)
*
*/
bool mlist_add_node_nomutex(mlist_t* list, mlist_node_t* newnode)
{
bool succp = false;
CHK_MLIST(list);
CHK_MLIST_NODE(newnode);
ss_dassert(!list->mlist_deleted);
/** List is full already. */
if (list->mlist_nodecount == list->mlist_nodecount_max)
{
goto return_succp;
}
/** Find location for new node */
if (list->mlist_last != NULL)
{
ss_dassert(!list->mlist_last->mlnode_deleted);
CHK_MLIST_NODE(list->mlist_last);
CHK_MLIST_NODE(list->mlist_first);
ss_dassert(list->mlist_last->mlnode_next == NULL);
list->mlist_last->mlnode_next = newnode;
}
else
{
list->mlist_first = newnode;
}
list->mlist_last = newnode;
newnode->mlnode_list = list;
list->mlist_nodecount += 1;
succp = true;
return_succp:
CHK_MLIST(list);
return succp;
}
/**
* mlist_cursor_t
*/
mlist_cursor_t* mlist_cursor_init(mlist_t* list)
{
CHK_MLIST(list);
mlist_cursor_t* c;
/** acquire shared lock to the list */
simple_mutex_lock(&list->mlist_mutex, true);
c = (mlist_cursor_t *) calloc(1, sizeof (mlist_cursor_t));
if (c == NULL)
{
simple_mutex_unlock(&list->mlist_mutex);
goto return_cursor;
}
c->mlcursor_chk_top = CHK_NUM_MLIST_CURSOR;
c->mlcursor_chk_tail = CHK_NUM_MLIST_CURSOR;
c->mlcursor_list = list;
/** Set cursor position if list is not empty */
if (list->mlist_first != NULL)
{
c->mlcursor_pos = list->mlist_first;
}
simple_mutex_unlock(&list->mlist_mutex);
CHK_MLIST_CURSOR(c);
return_cursor:
return c;
}
void* mlist_cursor_get_data_nomutex(mlist_cursor_t* mc)
{
CHK_MLIST_CURSOR(mc);
return (mc->mlcursor_pos->mlnode_data);
}
bool mlist_cursor_move_to_first(mlist_cursor_t* mc)
{
bool succp = false;
mlist_t* list;
CHK_MLIST_CURSOR(mc);
list = mc->mlcursor_list;
CHK_MLIST(list);
simple_mutex_lock(&list->mlist_mutex, true);
if (mc->mlcursor_list->mlist_deleted)
{
simple_mutex_unlock(&list->mlist_mutex);
return false;
}
/** Set position point to first node */
mc->mlcursor_pos = list->mlist_first;
if (mc->mlcursor_pos != NULL)
{
CHK_MLIST_NODE(mc->mlcursor_pos);
succp = true;
}
simple_mutex_unlock(&list->mlist_mutex);
return succp;
}

View File

@ -186,12 +186,22 @@ void monitorStartAll()
void
monitorStop(MONITOR *monitor)
{
spinlock_acquire(&monitor->lock);
if (monitor->state != MONITOR_STATE_STOPPED)
{
monitor->state = MONITOR_STATE_STOPPING;
monitor->module->stopMonitor(monitor);
monitor->state = MONITOR_STATE_STOPPED;
MONITOR_SERVERS* db = monitor->databases;
while (db)
{
mysql_close(db->con);
db->con = NULL;
db = db->next;
}
}
spinlock_release(&monitor->lock);
}
/**
@ -401,60 +411,39 @@ monitorSetInterval(MONITOR *mon, unsigned long interval)
* @param type The timeout handling type
* @param value The timeout to set
*/
void
monitorSetNetworkTimeout(MONITOR *mon, int type, int value) {
bool
monitorSetNetworkTimeout(MONITOR *mon, int type, int value)
{
bool rval = true;
int max_timeout = (int)(mon->interval/1000);
int new_timeout = max_timeout -1;
if (new_timeout <= 0)
if (value > 0)
{
new_timeout = DEFAULT_CONNECT_TIMEOUT;
switch (type)
{
case MONITOR_CONNECT_TIMEOUT:
mon->connect_timeout = value;
break;
case MONITOR_READ_TIMEOUT:
mon->read_timeout = value;
break;
case MONITOR_WRITE_TIMEOUT:
mon->write_timeout = value;
break;
default:
MXS_ERROR("Monitor setNetworkTimeout received an unsupported action type %i", type);
rval = false;
break;
}
}
switch(type) {
case MONITOR_CONNECT_TIMEOUT:
if (value < max_timeout)
{
memcpy(&mon->connect_timeout, &value, sizeof(int));
}
else
{
memcpy(&mon->connect_timeout, &new_timeout, sizeof(int));
MXS_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(&mon->read_timeout, &value, sizeof(int));
}
else
{
memcpy(&mon->read_timeout, &new_timeout, sizeof(int));
MXS_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(&mon->write_timeout, &value, sizeof(int));
}
else
{
memcpy(&mon->write_timeout, &new_timeout, sizeof(int));
MXS_WARNING("Monitor Write Timeout %i is greater than monitor interval ~%i seconds"
", lowering to %i seconds", value, max_timeout, new_timeout);
}
break;
default:
MXS_ERROR("Monitor setNetworkTimeout received an unsupported action type %i", type);
break;
else
{
MXS_ERROR("Negative value for monitor timeout.");
rval = false;
}
return rval;
}
/**

View File

@ -36,6 +36,8 @@
#include <mysql.h>
#include <resultset.h>
#include <session.h>
#include <statistics.h>
#include <query_classifier.h>
#define PROFILE_POLL 0
@ -156,21 +158,21 @@ static THREAD_DATA *thread_data = NULL; /*< Status of each thread */
*/
static struct
{
int n_read; /*< Number of read events */
int n_write; /*< Number of write events */
int n_error; /*< Number of error events */
int n_hup; /*< Number of hangup events */
int n_accept; /*< Number of accept events */
int n_polls; /*< Number of poll cycles */
int n_pollev; /*< Number of polls returning events */
int n_nbpollev; /*< Number of polls returning events */
int n_nothreads; /*< Number of times no threads are polling */
int n_fds[MAXNFDS]; /*< Number of wakeups with particular n_fds value */
int evq_length; /*< Event queue length */
int evq_pending; /*< Number of pending descriptors in event queue */
int evq_max; /*< Maximum event queue length */
int wake_evqpending;/*< Woken from epoll_wait with pending events in queue */
int blockingpolls; /*< Number of epoll_waits with a timeout specified */
ts_stats_t *n_read; /*< Number of read events */
ts_stats_t *n_write; /*< Number of write events */
ts_stats_t *n_error; /*< Number of error events */
ts_stats_t *n_hup; /*< Number of hangup events */
ts_stats_t *n_accept; /*< Number of accept events */
ts_stats_t *n_polls; /*< Number of poll cycles */
ts_stats_t *n_pollev; /*< Number of polls returning events */
ts_stats_t *n_nbpollev; /*< Number of polls returning events */
ts_stats_t *n_nothreads; /*< Number of times no threads are polling */
int n_fds[MAXNFDS]; /*< Number of wakeups with particular n_fds value */
int evq_length; /*< Event queue length */
int evq_pending; /*< Number of pending descriptors in event queue */
int evq_max; /*< Maximum event queue length */
int wake_evqpending; /*< Woken from epoll_wait with pending events in queue */
ts_stats_t *blockingpolls; /*< Number of epoll_waits with a timeout specified */
} pollStats;
#define N_QUEUE_TIMES 30
@ -230,6 +232,22 @@ poll_init()
thread_data[i].state = THREAD_STOPPED;
}
}
if ((pollStats.n_read = ts_stats_alloc()) == NULL ||
(pollStats.n_write = ts_stats_alloc()) == NULL ||
(pollStats.n_error = ts_stats_alloc()) == NULL ||
(pollStats.n_hup = ts_stats_alloc()) == NULL ||
(pollStats.n_accept = ts_stats_alloc()) == NULL ||
(pollStats.n_polls = ts_stats_alloc()) == NULL ||
(pollStats.n_pollev = ts_stats_alloc()) == NULL ||
(pollStats.n_nbpollev = ts_stats_alloc()) == NULL ||
(pollStats.n_nothreads = ts_stats_alloc()) == NULL ||
(pollStats.blockingpolls = ts_stats_alloc()) == NULL)
{
perror("Fatal error: Memory allocation failed.");
exit(-1);
}
#if MUTEX_EPOLL
simple_mutex_init(&epoll_wait_mutex, "epoll_wait_mutex");
#endif
@ -540,6 +558,8 @@ poll_waitevents(void *arg)
intptr_t thread_id = (intptr_t)arg;
int poll_spins = 0;
ts_stats_set_thread_id(thread_id);
/** Add this thread to the bitmask of running polling threads */
bitmask_set(&poll_mask, thread_id);
if (thread_data)
@ -547,9 +567,6 @@ poll_waitevents(void *arg)
thread_data[thread_id].state = THREAD_IDLE;
}
/** Init mysql thread context for use with a mysql handle and a parser */
mysql_thread_init();
while (1)
{
if (pollStats.evq_pending == 0 && timeout_bias < 10)
@ -570,7 +587,7 @@ poll_waitevents(void *arg)
thread_data[thread_id].state = THREAD_POLLING;
}
atomic_add(&pollStats.n_polls, 1);
ts_stats_add(pollStats.n_polls, 1);
if ((nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 0)) == -1)
{
atomic_add(&n_waiting, -1);
@ -593,7 +610,7 @@ poll_waitevents(void *arg)
*/
else if (nfds == 0 && pollStats.evq_pending == 0 && poll_spins++ > number_poll_spins)
{
atomic_add(&pollStats.blockingpolls, 1);
ts_stats_add(pollStats.blockingpolls, 1);
nfds = epoll_wait(epoll_fd,
events,
MAX_EVENTS,
@ -611,7 +628,7 @@ poll_waitevents(void *arg)
if (n_waiting == 0)
{
atomic_add(&pollStats.n_nothreads, 1);
ts_stats_add(pollStats.n_nothreads, 1);
}
#if MUTEX_EPOLL
simple_mutex_unlock(&epoll_wait_mutex);
@ -622,13 +639,13 @@ poll_waitevents(void *arg)
timeout_bias = 1;
if (poll_spins <= number_poll_spins + 1)
{
atomic_add(&pollStats.n_nbpollev, 1);
ts_stats_add(pollStats.n_nbpollev, 1);
}
poll_spins = 0;
MXS_DEBUG("%lu [poll_waitevents] epoll_wait found %d fds",
pthread_self(),
nfds);
atomic_add(&pollStats.n_pollev, 1);
ts_stats_add(pollStats.n_pollev, 1);
if (thread_data)
{
thread_data[thread_id].n_fds = nfds;
@ -733,8 +750,6 @@ poll_waitevents(void *arg)
thread_data[thread_id].state = THREAD_STOPPED;
}
bitmask_clear(&poll_mask, thread_id);
/** Release mysql thread context */
mysql_thread_end();
return;
}
if (thread_data)
@ -915,7 +930,7 @@ process_pollq(int thread_id)
if (eno == 0)
{
atomic_add(&pollStats.n_write, 1);
ts_stats_add(pollStats.n_write, 1);
/** Read session id to thread's local storage */
dcb_get_ses_log_info(dcb,
&mxs_log_tls.li_sesid,
@ -947,7 +962,7 @@ process_pollq(int thread_id)
"Accept in fd %d",
pthread_self(),
dcb->fd);
atomic_add(&pollStats.n_accept, 1);
ts_stats_add(pollStats.n_accept, 1);
dcb_get_ses_log_info(dcb,
&mxs_log_tls.li_sesid,
&mxs_log_tls.li_enabled_priorities);
@ -964,7 +979,7 @@ process_pollq(int thread_id)
pthread_self(),
dcb,
dcb->fd);
atomic_add(&pollStats.n_read, 1);
ts_stats_add(pollStats.n_read, 1);
/** Read session id to thread's local storage */
dcb_get_ses_log_info(dcb,
&mxs_log_tls.li_sesid,
@ -1012,7 +1027,7 @@ process_pollq(int thread_id)
eno,
strerror_r(eno, errbuf, sizeof(errbuf)));
}
atomic_add(&pollStats.n_error, 1);
ts_stats_add(pollStats.n_error, 1);
/** Read session id to thread's local storage */
dcb_get_ses_log_info(dcb,
&mxs_log_tls.li_sesid,
@ -1037,7 +1052,7 @@ process_pollq(int thread_id)
dcb->fd,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)));
atomic_add(&pollStats.n_hup, 1);
ts_stats_add(pollStats.n_hup, 1);
spinlock_acquire(&dcb->dcb_initlock);
if ((dcb->flags & DCBF_HUNG) == 0)
{
@ -1073,7 +1088,7 @@ process_pollq(int thread_id)
dcb->fd,
eno,
strerror_r(eno, errbuf, sizeof(errbuf)));
atomic_add(&pollStats.n_hup, 1);
ts_stats_add(pollStats.n_hup, 1);
spinlock_acquire(&dcb->dcb_initlock);
if ((dcb->flags & DCBF_HUNG) == 0)
{
@ -1224,7 +1239,6 @@ spin_reporter(void *dcb, char *desc, int value)
dcb_printf((DCB *)dcb, "\t%-40s %d\n", desc, value);
}
/**
* Debug routine to print the polling statistics
*
@ -1237,25 +1251,25 @@ dprintPollStats(DCB *dcb)
dcb_printf(dcb, "\nPoll Statistics.\n\n");
dcb_printf(dcb, "No. of epoll cycles: %d\n",
pollStats.n_polls);
ts_stats_sum(pollStats.n_polls));
dcb_printf(dcb, "No. of epoll cycles with wait: %d\n",
pollStats.blockingpolls);
ts_stats_sum(pollStats.blockingpolls));
dcb_printf(dcb, "No. of epoll calls returning events: %d\n",
pollStats.n_pollev);
ts_stats_sum(pollStats.n_pollev));
dcb_printf(dcb, "No. of non-blocking calls returning events: %d\n",
pollStats.n_nbpollev);
ts_stats_sum(pollStats.n_nbpollev));
dcb_printf(dcb, "No. of read events: %d\n",
pollStats.n_read);
ts_stats_sum(pollStats.n_read));
dcb_printf(dcb, "No. of write events: %d\n",
pollStats.n_write);
ts_stats_sum(pollStats.n_write));
dcb_printf(dcb, "No. of error events: %d\n",
pollStats.n_error);
ts_stats_sum(pollStats.n_error));
dcb_printf(dcb, "No. of hangup events: %d\n",
pollStats.n_hup);
ts_stats_sum(pollStats.n_hup));
dcb_printf(dcb, "No. of accept events: %d\n",
pollStats.n_accept);
ts_stats_sum(pollStats.n_accept));
dcb_printf(dcb, "No. of times no threads polling: %d\n",
pollStats.n_nothreads);
ts_stats_sum(pollStats.n_nothreads));
dcb_printf(dcb, "Current event queue length: %d\n",
pollStats.evq_length);
dcb_printf(dcb, "Maximum event queue length: %d\n",
@ -1811,15 +1825,15 @@ poll_get_stat(POLL_STAT stat)
switch (stat)
{
case POLL_STAT_READ:
return pollStats.n_read;
return ts_stats_sum(pollStats.n_read);
case POLL_STAT_WRITE:
return pollStats.n_write;
return ts_stats_sum(pollStats.n_write);
case POLL_STAT_ERROR:
return pollStats.n_error;
return ts_stats_sum(pollStats.n_error);
case POLL_STAT_HANGUP:
return pollStats.n_hup;
return ts_stats_sum(pollStats.n_hup);
case POLL_STAT_ACCEPT:
return pollStats.n_accept;
return ts_stats_sum(pollStats.n_accept);
case POLL_STAT_EVQ_LEN:
return pollStats.evq_length;
case POLL_STAT_EVQ_PENDING:

View File

@ -0,0 +1,183 @@
/**
* @section LICENCE
*
* 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
*
* @file
*
*/
#include <query_classifier.h>
#include <log_manager.h>
#include <modules.h>
//#define QC_TRACE_ENABLED
#undef QC_TRACE_ENABLED
#if defined(QC_TRACE_ENABLED)
#define QC_TRACE() MXS_NOTICE(__func__)
#else
#define QC_TRACE()
#endif
static const char default_qc_name[] = "qc_mysqlembedded";
static QUERY_CLASSIFIER* classifier;
bool qc_init(const char* plugin_name)
{
QC_TRACE();
ss_dassert(!classifier);
if (!plugin_name || (*plugin_name == 0))
{
MXS_NOTICE("No query classifier specified, using default '%s'.", default_qc_name);
plugin_name = default_qc_name;
}
bool success = false;
void* module = load_module(plugin_name, MODULE_QUERY_CLASSIFIER);
if (module)
{
classifier = (QUERY_CLASSIFIER*) module;
MXS_NOTICE("%s loaded.", plugin_name);
success = classifier->qc_init();
}
else
{
MXS_ERROR("Could not load %s.", plugin_name);
}
return success;
}
void qc_end(void)
{
QC_TRACE();
ss_dassert(classifier);
classifier->qc_end();
classifier = NULL;
}
bool qc_thread_init(void)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_thread_init();
}
void qc_thread_end(void)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_thread_end();
}
qc_query_type_t qc_get_type(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_type(query);
}
qc_query_op_t qc_get_operation(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_operation(query);
}
char* qc_get_created_table_name(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_created_table_name(query);
}
bool qc_is_drop_table_query(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_is_drop_table_query(query);
}
bool qc_is_real_query(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_is_real_query(query);
}
char** qc_get_table_names(GWBUF* query, int* tblsize, bool fullnames)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_table_names(query, tblsize, fullnames);
}
char* qc_get_canonical(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_canonical(query);
}
bool qc_query_has_clause(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_query_has_clause(query);
}
char* qc_get_qtype_str(qc_query_type_t qtype)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_qtype_str(qtype);
}
char* qc_get_affected_fields(GWBUF* query)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_affected_fields(query);
}
char** qc_get_database_names(GWBUF* query, int* sizep)
{
QC_TRACE();
ss_dassert(classifier);
return classifier->qc_get_database_names(query, sizep);
}

View File

@ -68,6 +68,7 @@
#include <gw.h>
#include <gwdirs.h>
#include <math.h>
#include <version.h>
/** To be used with configuration type checks */
typedef struct typelib_st

View File

@ -138,12 +138,8 @@ session_alloc(SERVICE *service, DCB *client_dcb)
if (session->router_session == NULL)
{
session->state = SESSION_STATE_TO_BE_FREED;
MXS_ERROR("%lu [%s] Error : Failed to create %s session because router"
"could not establish a new router session, see earlier error.",
pthread_self(),
__func__,
service->name);
MXS_ERROR("Failed to create new router session for service '%s'. "
"See previous errors for more details.", service->name);
}
/*
* Pending filter chain being setup set the head of the chain to

366
server/core/slist.c Normal file
View File

@ -0,0 +1,366 @@
/*
* 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
*/
#include <slist.h>
#include <atomic.h>
static slist_cursor_t* slist_cursor_init(slist_t* list);
static slist_t* slist_init_ex(bool create_cursors);
static slist_node_t* slist_node_init(void* data, slist_cursor_t* cursor);
static void slist_add_node(slist_t* list, slist_node_t* node);
#if defined(NOT_USED)
static slist_node_t* slist_node_get_next(slist_node_t* curr_node);
static slist_node_t* slist_get_first(slist_t* list);
static slist_cursor_t* slist_get_cursor(slist_t* list);
#endif /*< NOT_USED */
/** End of static function declarations */
static slist_t* slist_init_ex(bool create_cursors)
{
slist_t* list;
list = (slist_t*) calloc(1, sizeof (slist_t));
list->slist_chk_top = CHK_NUM_SLIST;
list->slist_chk_tail = CHK_NUM_SLIST;
if (create_cursors)
{
list->slist_cursors_list = slist_init_ex(false);
}
return list;
}
static slist_node_t* slist_node_init(void* data, slist_cursor_t* cursor)
{
slist_node_t* node;
node = (slist_node_t*) calloc(1, sizeof (slist_node_t));
node->slnode_chk_top = CHK_NUM_SLIST_NODE;
node->slnode_chk_tail = CHK_NUM_SLIST_NODE;
node->slnode_data = data;
CHK_SLIST_NODE(node);
if (cursor != NULL)
{
node->slnode_cursor_refcount += 1;
cursor->slcursor_pos = node;
}
return node;
}
static void slist_add_node(slist_t* list, slist_node_t* node)
{
CHK_SLIST(list);
CHK_SLIST_NODE(node);
if (list->slist_tail != NULL)
{
CHK_SLIST_NODE(list->slist_tail);
CHK_SLIST_NODE(list->slist_head);
ss_dassert(list->slist_tail->slnode_next == NULL);
list->slist_tail->slnode_next = node;
}
else
{
list->slist_head = node;
}
list->slist_tail = node;
node->slnode_list = list;
list->slist_nelems += 1;
CHK_SLIST(list);
}
#if defined(NOT_USED)
static slist_node_t* slist_node_get_next(slist_node_t* curr_node)
{
CHK_SLIST_NODE(curr_node);
if (curr_node->slnode_next != NULL)
{
CHK_SLIST_NODE(curr_node->slnode_next);
return (curr_node->slnode_next);
}
return NULL;
}
static slist_node_t* slist_get_first(slist_t* list)
{
CHK_SLIST(list);
if (list->slist_head != NULL)
{
CHK_SLIST_NODE(list->slist_head);
return list->slist_head;
}
return NULL;
}
static slist_cursor_t* slist_get_cursor(slist_t* list)
{
CHK_SLIST(list);
slist_cursor_t* c;
c = slist_cursor_init(list);
return c;
}
#endif /*< NOT_USED */
static slist_cursor_t* slist_cursor_init(slist_t* list)
{
CHK_SLIST(list);
slist_cursor_t* c;
c = (slist_cursor_t *) calloc(1, sizeof (slist_cursor_t));
c->slcursor_chk_top = CHK_NUM_SLIST_CURSOR;
c->slcursor_chk_tail = CHK_NUM_SLIST_CURSOR;
c->slcursor_list = list;
/** Set cursor position is list is not empty */
if (list->slist_head != NULL)
{
list->slist_head->slnode_cursor_refcount += 1;
c->slcursor_pos = list->slist_head;
}
/** Add cursor to cursor list */
slist_add_node(list->slist_cursors_list, slist_node_init(c, NULL));
CHK_SLIST_CURSOR(c);
return c;
}
/**
* @node Create a cursor and a list with cursors supported. 19.6.2013 :
* supports only cursor per list.
*
* Parameters:
* @param void - <usage>
* <description>
*
* @return returns a pointer to cursor, which is not positioned
* because the list is empty.
*
*
* @details (write detailed description here)
*
*/
slist_cursor_t* slist_init(void)
{
slist_t* list;
slist_cursor_t* slc;
list = slist_init_ex(true);
CHK_SLIST(list);
slc = slist_cursor_init(list);
CHK_SLIST_CURSOR(slc);
return slc;
}
/**
* @node moves cursor to the first node of list.
*
* Parameters:
* @param c - <usage>
* <description>
*
* @return true if there is first node in the list
* false is the list is empty.
*
*
* @details (write detailed description here)
*
*/
bool slcursor_move_to_begin(slist_cursor_t* c)
{
bool succp = true;
slist_t* list;
CHK_SLIST_CURSOR(c);
list = c->slcursor_list;
CHK_SLIST(list);
c->slcursor_pos = list->slist_head;
if (c->slcursor_pos == NULL)
{
succp = false;
}
return succp;
}
/**
* @node moves cursor to next node
*
* Parameters:
* @param c - <usage>
* <description>
*
* @return true in success, false is there is no next node on the list.
*
*
* @details (write detailed description here)
*
*/
bool slcursor_step_ahead(slist_cursor_t* c)
{
bool succp = false;
slist_node_t* node;
CHK_SLIST_CURSOR(c);
CHK_SLIST_NODE(c->slcursor_pos);
node = c->slcursor_pos->slnode_next;
if (node != NULL)
{
CHK_SLIST_NODE(node);
c->slcursor_pos = node;
succp = true;
}
return succp;
}
void* slcursor_get_data(slist_cursor_t* c)
{
slist_node_t* node;
void* data = NULL;
CHK_SLIST_CURSOR(c);
node = c->slcursor_pos;
if (node != NULL)
{
CHK_SLIST_NODE(node);
data = node->slnode_data;
}
return data;
}
/**
* @node Add data to the list by using cursor.
*
* Parameters:
* @param c - <usage>
* <description>
*
* @param data - <usage>
* <description>
*
* @return void
*
*
* @details (write detailed description here)
*
*/
void slcursor_add_data(slist_cursor_t* c, void* data)
{
slist_t* list;
slist_node_t* pos;
CHK_SLIST_CURSOR(c);
list = c->slcursor_list;
CHK_SLIST(list);
if (c->slcursor_pos != NULL)
{
CHK_SLIST_NODE(c->slcursor_pos);
}
ss_dassert(list->slist_tail->slnode_next == NULL);
pos = slist_node_init(data, c);
slist_add_node(list, pos);
CHK_SLIST(list);
CHK_SLIST_CURSOR(c);
}
/**
* Remove the node currently pointed by the cursor from the slist. This does not delete the data in the
* node but will delete the structure pointing to that data. This is useful when
* the user wants to free the allocated memory. After node removal, the cursor
* will point to the node before the removed node.
* @param c Cursor pointing to the data node to be removed
*/
void slcursor_remove_data(slist_cursor_t* c)
{
slist_node_t* node = c->slcursor_pos;
int havemore = slist_size(c);
slcursor_move_to_begin(c);
if (node == c->slcursor_pos)
{
c->slcursor_list->slist_head = c->slcursor_list->slist_head->slnode_next;
slcursor_move_to_begin(c);
atomic_add((int*) &node->slnode_list->slist_nelems, -1);
atomic_add((int*) &node->slnode_cursor_refcount, -1);
if (node->slnode_cursor_refcount == 0)
{
free(node);
}
return;
}
while (havemore)
{
if (c->slcursor_pos->slnode_next == node)
{
c->slcursor_pos->slnode_next = node->slnode_next;
atomic_add((int*) &node->slnode_list->slist_nelems, -1);
atomic_add((int*) &node->slnode_cursor_refcount, -1);
if (node->slnode_cursor_refcount == 0)
{
free(node);
}
return;
}
havemore = slcursor_step_ahead(c);
}
}
/**
* Return the size of the slist.
* @param c slist cursor which refers to a list
* @return nummber of elements in the list
*/
size_t slist_size(slist_cursor_t* c)
{
return c->slcursor_list->slist_nelems;
}
void slist_done(slist_cursor_t* c)
{
bool succp;
void* data;
succp = slcursor_move_to_begin(c);
while (succp)
{
data = slcursor_get_data(c);
free(data);
succp = slcursor_step_ahead(c);
}
free(c->slcursor_list);
free(c);
}
/** End of list implementation */

View File

@ -87,7 +87,7 @@ spinlock_acquire(SPINLOCK *lock)
}
}
lock->acquired++;
lock->owner = THREAD_SHELF();
lock->owner = thread_self();
atomic_add(&(lock->waiting), -1);
#endif
}
@ -112,7 +112,7 @@ spinlock_acquire_nowait(SPINLOCK *lock)
#endif
#if SPINLOCK_PROFILE
lock->acquired++;
lock->owner = THREAD_SHELF();
lock->owner = thread_self();
#endif
return TRUE;
}

121
server/core/statistics.c Normal file
View File

@ -0,0 +1,121 @@
/*
* 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 2016
*/
#include <statistics.h>
#include <maxconfig.h>
#include <string.h>
#include <platform.h>
thread_local int current_thread_id = 0;
static int thread_count = 0;
static bool initialized = false;
/**
* Initialize the statistics gathering
*/
void ts_stats_init()
{
ss_dassert(!initialized);
thread_count = config_threadcount();
initialized = true;
}
/**
* End the statistics gathering
*/
void ts_stats_end()
{
ss_dassert(initialized);
}
/**
* Create a new statistics object
*
* @return New stats_t object or NULL if memory allocation failed
*/
ts_stats_t ts_stats_alloc()
{
ss_dassert(initialized);
return calloc(thread_count, sizeof(int));
}
/**
* Free a statistics object
*
* @param stats Stats to free
*/
void ts_stats_free(ts_stats_t stats)
{
ss_dassert(initialized);
free(stats);
}
/**
* Set the current thread id
*
* This should only be called only once by each thread.
* @param id Thread id
*/
void ts_stats_set_thread_id(int id)
{
ss_dassert(initialized);
current_thread_id = id;
}
/**
* Add @c value to @c stats
*
* @param stats Statistics to add to
* @param value Value to add
*/
void ts_stats_add(ts_stats_t stats, int value)
{
ss_dassert(initialized);
((int*)stats)[current_thread_id] += value;
}
/**
* Assign a value to the statistics
*
* This sets the value for the current thread only.
* @param stats Statistics to set
* @param value Value to set to
*/
void ts_stats_set(ts_stats_t stats, int value)
{
ss_dassert(initialized);
((int*)stats)[current_thread_id] = value;
}
/**
* Read the total value of the statistics object
*
* @param stats Statistics to read
* @return Value of statistics
*/
int ts_stats_sum(ts_stats_t stats)
{
ss_dassert(initialized);
int sum = 0;
for (int i = 0; i < thread_count; i++)
{
sum += ((int*)stats)[i];
}
return sum;
}

View File

@ -5,6 +5,8 @@ add_executable(test_dcb testdcb.c)
add_executable(test_filter testfilter.c)
add_executable(test_hash testhash.c)
add_executable(test_hint testhint.c)
add_executable(test_log testlog.c)
add_executable(test_logorder testlogorder.c)
add_executable(test_modutil testmodutil.c)
add_executable(test_mysql_users test_mysql_users.c)
add_executable(test_poll testpoll.c)
@ -19,24 +21,28 @@ target_link_libraries(test_adminusers maxscale-common)
target_link_libraries(test_buffer maxscale-common)
target_link_libraries(test_dcb maxscale-common)
target_link_libraries(test_filter maxscale-common)
target_link_libraries(test_hash maxscale-common )
target_link_libraries(test_hint maxscale-common )
target_link_libraries(test_hash maxscale-common)
target_link_libraries(test_hint maxscale-common)
target_link_libraries(test_log maxscale-common)
target_link_libraries(test_logorder maxscale-common)
target_link_libraries(test_modutil maxscale-common)
target_link_libraries(test_mysql_users MySQLClient maxscale-common)
target_link_libraries(test_poll maxscale-common)
target_link_libraries(test_server maxscale-common)
target_link_libraries(test_service maxscale-common)
target_link_libraries(test_spinlock maxscale-common )
target_link_libraries(test_spinlock maxscale-common)
target_link_libraries(test_users maxscale-common)
target_link_libraries(testfeedback maxscale-common)
target_link_libraries(testmaxscalepcre2 maxscale-common )
target_link_libraries(testmemlog maxscale-common )
target_link_libraries(testmaxscalepcre2 maxscale-common)
target_link_libraries(testmemlog maxscale-common)
add_test(Internal-TestAdminUsers test_adminusers)
add_test(Internal-TestBuffer test_buffer)
add_test(Internal-TestDCB test_dcb)
add_test(Internal-TestFilter test_filter)
add_test(Internal-TestHash test_hash)
add_test(Internal-TestHint test_hint)
add_test(Internal-TestLog test_log)
add_test(NAME Internal-TestLogOrder COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/logorder.sh 200 0 1000 ${CMAKE_CURRENT_BINARY_DIR}/logorder.log)
add_test(Internal-TestMaxScalePCRE2 testmaxscalepcre2)
add_test(Internal-TestMemlog testmemlog)
add_test(Internal-TestModutil test_modutil)

74
server/core/test/logorder.sh Executable file
View File

@ -0,0 +1,74 @@
#! /bin/bash
if [[ $# -lt 4 ]]
then
echo "Usage: logorder.sh <iterations> <frequency of flushes> <message size> <log file>"
echo "To disable log flushing, use 0 for flush frequency"
exit
fi
if [ $# -eq 5 ]
then
TDIR=$5
else
TDIR=$PWD
fi
rm $TDIR/*.log
#Create large messages
$TDIR/testorder $1 $2 $3
TESTLOG=$4
MCOUNT=$1
BLOCKS=`cat $TDIR/skygw_err1.log |tr -s ' '|grep -o 'block:[[:digit:]]\+'|cut -d ':' -f 2`
MESSAGES=`cat $TDIR/skygw_err1.log |tr -s ' '|grep -o 'message|[[:digit:]]\+'|cut -d '|' -f 2`
prev=0
error=0
all_errors=0
for i in $BLOCKS
do
if [[ $i -le $prev ]]
then
error=1
all_errors=1
echo "block mismatch: $i was after $prev." >> $TESTLOG
fi
prev=$i
done
if [[ error -eq 0 ]]
then
echo "Block buffers were in order" >> $TESTLOG
else
echo "Error: block buffers were written in the wrong order" >> $TESTLOG
fi
prev=0
error=0
for i in $MESSAGES
do
if [[ $i -ne $(( prev + 1 )) ]]
then
error=1
all_errors=1
echo "message mismatch: $i was after $prev." >> $TESTLOG
fi
prev=$i
done
if [[ error -eq 0 ]]
then
echo "Block buffer messages were in order" >> $TESTLOG
else
echo "Error: block buffer messages were written in the wrong order" >> $TESTLOG
fi
cat $TESTLOG
exit $all_errors

718
server/core/test/testlog.c Normal file
View File

@ -0,0 +1,718 @@
/*
* 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
@brief (brief description)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <skygw_utils.h>
#include <log_manager.h>
typedef struct thread_st
{
skygw_message_t* mes;
simple_mutex_t* mtx;
size_t* nactive;
pthread_t tid;
} thread_t;
static void* thr_run(void* data);
static void* thr_run_morelog(void* data);
#define MAX_NTHR 256
#define NITER 100
#if 1
# define TEST1
#endif
#if 0
# define TEST2
#endif
#define TEST3
#define TEST4
const char USAGE[]=
"usage: %s [-t <#threads>]\n"
"\n"
"-t: Number of threads. Default is %d.\n";
const int N_THR = 4;
#define TEST_ERROR(msg)\
do { fprintf(stderr, "[%s:%d]: %s\n", basename(__FILE__), __LINE__, msg); } while (false)
static void skygw_log_enable(int priority)
{
mxs_log_set_priority_enabled(priority, true);
}
static void skygw_log_disable(int priority)
{
mxs_log_set_priority_enabled(priority, false);
}
int main(int argc, char* argv[])
{
int err = 0;
char* logstr;
int i;
bool succp;
skygw_message_t* mes;
simple_mutex_t* mtx;
size_t nactive;
thread_t** thr = NULL;
time_t t;
struct tm tm;
char c;
int nthr = N_THR;
while ((c = getopt(argc, argv, "t:")) != -1)
{
switch (c) {
case 't':
nthr = atoi(optarg);
if (nthr <= 0)
{
err = 1;
}
break;
default:
err = 1;
break;
}
}
if (err != 0)
{
fprintf(stderr, USAGE, argv[0], N_THR);
err = 1;
goto return_err;
}
printf("Using %d threads.\n", nthr);
thr = (thread_t **)calloc(1, nthr*sizeof(thread_t*));
if (thr == NULL)
{
fprintf(stderr, "Failed to allocate memory for thread "
"structure. Exiting.\n");
err = 1;
goto return_err;
}
i = atexit(mxs_log_finish);
if (i != 0)
{
fprintf(stderr, "Couldn't register exit function.\n");
}
succp = mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
if (!succp)
{
fprintf(stderr, "Log manager initialization failed.\n");
}
ss_dassert(succp);
t = time(NULL);
localtime_r(&t, &tm);
err = MXS_ERROR("%04d %02d/%02d %02d.%02d.%02d",
tm.tm_year+1900,
tm.tm_mon+1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("First write with flush.");
err = MXS_ERROR("%s", logstr);
logstr = ("Second write with flush.");
err = MXS_ERROR("%s", logstr);
logstr = ("Third write, no flush.");
err = MXS_ERROR("%s", logstr);
logstr = ("Fourth write, no flush. Next flush only.");
err = MXS_ERROR("%s", logstr);
err = mxs_log_flush();
logstr = "My name is %s %d years and %d months.";
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
err = MXS_INFO(logstr, "TraceyTracey", 3, 7);
mxs_log_flush();
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = "My name is Tracey Tracey 47 years and 7 months.";
err = MXS_INFO("%s", logstr);
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = "My name is Stacey %s";
err = MXS_INFO(logstr, " ");
mxs_log_finish();
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = "My name is Philip";
err = MXS_INFO("%s", logstr);
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = "Philip.";
err = MXS_INFO("%s", logstr);
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = "Ph%dlip.";
err = MXS_INFO(logstr, 1);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("A terrible error has occurred!");
err = MXS_ERROR("%s", logstr);
logstr = ("Hi, how are you?");
err = MXS_NOTICE("%s", logstr);
logstr = ("I'm doing fine!");
err = MXS_NOTICE("%s", logstr);
logstr = ("Rather more surprising, at least at first sight, is the fact that a reference to "
"a[i] can also be written as *(a+i). In evaluating a[i], C converts it to *(a+i) "
"immediately; the two forms are equivalent. Applying the operators & to both parts "
"of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the "
"address of the i-th element beyond a.");
err = MXS_ERROR("%s", logstr);
logstr = ("I was wondering, you know, it has been such a lovely weather whole morning and I "
"thought that would you like to come to my place and have a little piece of cheese "
"with us. Just me and my mom - and you, of course. Then, if you wish, we could "
"listen to the radio and keep company for our little Steven, my mom's cat, you know.");
err = MXS_NOTICE("%s", logstr);
mxs_log_finish();
#if defined(TEST1)
mes = skygw_message_init();
mtx = simple_mutex_init(NULL, "testmtx");
/** Test starts */
fprintf(stderr, "\nStarting test #1 \n");
/** 1 */
for (i = 0; i < nthr; i++)
{
thr[i] = (thread_t*)calloc(1, sizeof(thread_t));
thr[i]->mes = mes;
thr[i]->mtx = mtx;
thr[i]->nactive = &nactive;
}
nactive = nthr;
for (i = 0; i < nthr; i++)
{
pthread_t p;
pthread_create(&p, NULL, thr_run, thr[i]);
thr[i]->tid = p;
}
do
{
skygw_message_wait(mes);
simple_mutex_lock(mtx, true);
if (nactive > 0)
{
simple_mutex_unlock(mtx);
continue;
}
break;
} while(true);
for (i = 0; i < nthr; i++)
{
pthread_join(thr[i]->tid, NULL);
}
/** This is to release memory */
mxs_log_finish();
simple_mutex_unlock(mtx);
for (i = 0; i < nthr; i++)
{
free(thr[i]);
}
#endif
#if defined(TEST2)
fprintf(stderr, "\nStarting test #2 \n");
/** 2 */
for (i = 0; i < nthr; i++)
{
thr[i] = (thread_t*)calloc(1, sizeof(thread_t));
thr[i]->mes = mes;
thr[i]->mtx = mtx;
thr[i]->nactive = &nactive;
}
nactive = nthr;
fprintf(stderr,
"\nLaunching %d threads, each iterating %d times.",
nthr,
NITER);
for (i = 0; i < nthr; i++)
{
pthread_t p;
pthread_create(&p, NULL, thr_run_morelog, thr[i]);
thr[i]->tid = p;
}
fprintf(stderr, ".. done");
fprintf(stderr, "\nStarting to wait threads.\n");
do
{
skygw_message_wait(mes);
simple_mutex_lock(mtx, true);
if (nactive > 0)
{
simple_mutex_unlock(mtx);
continue;
}
break;
} while(true);
for (i = 0; i < nthr; i++)
{
pthread_join(thr[i]->tid, NULL);
}
/** This is to release memory */
mxs_log_finish();
simple_mutex_unlock(mtx);
fprintf(stderr, "\nFreeing thread memory.");
for (i = 0; i < nthr; i++)
{
free(thr[i]);
}
/** Test ended here */
skygw_message_done(mes);
simple_mutex_done(mtx);
#endif /* TEST 2 */
#if defined(TEST3)
/**
* Test enable/disable log.
*/
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
succp = mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
ss_dassert(succp);
logstr = ("\tTEST 3 - test enabling and disabling logs.");
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
skygw_log_disable(LOG_INFO);
logstr = ("1.\tWrite once to ERROR and twice to MESSAGE log.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
skygw_log_enable(LOG_INFO);
logstr = ("2.\tWrite to once to ERROR, twice to MESSAGE and "
"three times to TRACE log.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
skygw_log_disable(LOG_ERR);
logstr = ("3.\tWrite to once to MESSAGE and twice to TRACE log.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
skygw_log_disable(LOG_NOTICE);
skygw_log_disable(LOG_INFO);
logstr = ("4.\tWrite to none.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
skygw_log_enable(LOG_ERR);
skygw_log_enable(LOG_NOTICE);
logstr = ("4.\tWrite once to ERROR and twice to MESSAGE log.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
mxs_log_finish();
#endif /* TEST 3 */
#if defined(TEST4)
succp = mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
ss_dassert(succp);
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = ("\tTEST 4 - test spreading logs down to other logs.");
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
logstr = ("1.\tWrite to ERROR and thus also to MESSAGE and TRACE logs.");
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
logstr = ("2.\tWrite to MESSAGE and thus to TRACE logs.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
skygw_log_enable(LOG_INFO);
logstr = ("3.\tWrite to TRACE log only.");
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
skygw_log_disable(LOG_NOTICE);
logstr = ("4.\tWrite to ERROR and thus also to TRACE log. "
"MESSAGE is disabled.");
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
logstr = ("5.\tThis should not appear anywhere since MESSAGE "
"is disabled.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
mxs_log_finish();
succp = mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
ss_dassert(succp);
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
logstr = ("6.\tWrite to ERROR and thus also to MESSAGE and TRACE logs.");
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
logstr = ("7.\tWrite to MESSAGE and thus to TRACE logs.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
logstr = ("8.\tWrite to TRACE log only.");
skygw_log_enable(LOG_INFO);
err = MXS_INFO("%s", logstr);
ss_dassert(err == 0);
skygw_log_disable(LOG_NOTICE);
logstr = ("9.\tWrite to ERROR and thus also to TRACE log. "
"MESSAGE is disabled");
err = MXS_ERROR("%s", logstr);
ss_dassert(err == 0);
logstr = ("10.\tThis should not appear anywhere since MESSAGE is "
"disabled.");
err = MXS_NOTICE("%s", logstr);
ss_dassert(err == 0);
skygw_log_enable(LOG_NOTICE);
err = MXS_ERROR("11.\tWrite to all logs some formattings : "
"%d %s %d",
(int)3,
"foo",
(int)3);
err = MXS_ERROR("12.\tWrite to MESSAGE and TRACE log some "
"formattings "
": %d %s %d",
(int)3,
"foo",
(int)3);
err = MXS_ERROR("13.\tWrite to TRACE log some formattings "
": %d %s %d",
(int)3,
"foo",
(int)3);
ss_dassert(err == 0);
mxs_log_finish();
#endif /* TEST 4 */
fprintf(stderr, ".. done.\n");
return_err:
if (thr != NULL)
{
free(thr);
}
return err;
}
static void* thr_run(void* data)
{
thread_t* td = (thread_t *)data;
char* logstr;
int err;
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
mxs_log_flush();
logstr = ("Hi, how are you?");
err = MXS_NOTICE("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_flush();
logstr = ("I was wondering, you know, it has been such a lovely weather whole morning and "
"I thought that would you like to come to my place and have a little piece of "
"cheese with us. Just me and my mom - and you, of course. Then, if you wish, "
"we could listen to the radio and keep company for our little Steven, my mom's "
"cat, you know.");
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
err = MXS_NOTICE("%s", logstr);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("Testing. One, two, three\n");
err = MXS_ERROR("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
mxs_log_flush();
logstr = ("For automatic and register variables, it is done each time the function or block is entered.");
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
err = MXS_INFO("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("Rather more surprising, at least at first sight, is the fact that a reference "
"to a[i] can also be written as *(a+i). In evaluating a[i], C converts it to *(a+i) "
"immediately; the two forms are equivalent. Applying the operatos & to both parts "
"of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the "
"address of the i-th element beyond a.");
err = MXS_ERROR("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
mxs_log_finish();
mxs_log_flush();
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("..and you?");
err = MXS_NOTICE("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("For automatic and register variables, it is done each time the function or block is entered.");
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
err = MXS_INFO("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("Rather more surprising, at least at first sight, is the fact that a reference to "
"a[i] can also be written as *(a+i). In evaluating a[i], C converts it to *(a+i) "
"immediately; the two forms are equivalent. Applying the operatos & to both parts "
"of this equivalence, it follows that &a[i] and a+i are also identical: a+i is the "
"address of the i-th element beyond a.");
err = MXS_ERROR("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("..... and you too?");
err = MXS_NOTICE("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
mxs_log_flush();
logstr = ("For automatic and register variables, it is done each time the function or block is entered.");
#if !defined(SS_DEBUG)
skygw_log_enable(LOG_INFO);
#endif
err = MXS_INFO("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("Testing. One, two, three, four\n");
err = MXS_ERROR("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
logstr = ("Testing. One, two, three, .. where was I?\n");
err = MXS_ERROR("%s", logstr);
if (err != 0)
{
TEST_ERROR("Error, log write failed.");
}
ss_dassert(err == 0);
mxs_log_finish();
mxs_log_init(NULL, "/tmp", MXS_LOG_TARGET_FS);
mxs_log_finish();
simple_mutex_lock(td->mtx, true);
*td->nactive -= 1;
simple_mutex_unlock(td->mtx);
skygw_message_send(td->mes);
return NULL;
}
static int nstr(char** str_arr)
{
int i;
for (i = 0; str_arr[i] != NULL; i++)
{
}
return i;
}
char* logs[] = {
"foo",
"bar",
"done",
"critical test logging",
"longer test l o g g g i n g",
"reeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeally looooooooooooooooooooooooooooooooooooooo"
"ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong line",
"shoorter one",
"two",
"scrap : 834nuft984pnw8ynup4598yp8wup8upwn48t5gpn45",
"more the same : f98uft5p8ut2p44449upnt5",
"asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd987987asdasd98987",
NULL
};
static void* thr_run_morelog(void* data)
{
thread_t* td = (thread_t *)data;
int err;
int i;
int nmsg;
nmsg = nstr(logs);
for (i = 0; i < NITER; i++)
{
char* str = logs[rand() % nmsg];
err = MXS_LOG_MESSAGE((int)(rand() % (LOG_DEBUG+1)),
"%s - iteration # %d",
str,
i);
if (err != 0)
{
fprintf(stderr, "Error, log write failed.\n");
}
}
simple_mutex_lock(td->mtx, true);
*td->nactive -= 1;
simple_mutex_unlock(td->mtx);
skygw_message_send(td->mes);
return NULL;
}

View File

@ -0,0 +1,124 @@
/*
* 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
*/
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <skygw_utils.h>
#include <log_manager.h>
static void skygw_log_enable(int priority)
{
mxs_log_set_priority_enabled(priority, true);
}
static void skygw_log_disable(int priority)
{
mxs_log_set_priority_enabled(priority, false);
}
int main(int argc, char** argv)
{
int iterations = 0, i, interval = 10;
int block_size;
int succp, err = 0;
char cwd[1024];
char tmp[2048];
char *message;
long msg_index = 1;
struct timespec ts1;
ts1.tv_sec = 0;
memset(cwd, 0, 1024);
if (argc < 4)
{
fprintf(stderr,
"Log Manager Log Order Test\n"
"Writes an ascending number into the error log to determine if log writes are in order.\n"
"Usage:\t testorder <iterations> <frequency of log flushes> <size of message in bytes>\n");
return 1;
}
block_size = atoi(argv[3]);
if (block_size < 1 || block_size > 1024)
{
fprintf(stderr,"Message size too small or large, must be at least 1 byte long and "
"must not exceed 1024 bytes.");
return 1;
}
if (getcwd(cwd, sizeof(cwd)) == NULL ||
(message = (char*)malloc(sizeof(char) * block_size)) == NULL)
{
fprintf(stderr,"Fatal Error, exiting...");
return 1;
}
memset(tmp, 0, 1024);
sprintf(tmp, "%s", cwd);
iterations = atoi(argv[1]);
interval = atoi(argv[2]);
succp = mxs_log_init(NULL, tmp, MXS_LOG_TARGET_FS);
if (!succp)
{
fprintf(stderr,"Error, log manager initialization failed.\n");
}
ss_dassert(succp);
skygw_log_disable(LOG_INFO);
skygw_log_disable(LOG_NOTICE);
skygw_log_disable(LOG_DEBUG);
for (i = 0; i < iterations; i++)
{
sprintf(message, "message|%ld", msg_index++);
int msgsize = block_size - strlen(message);
if (msgsize < 0 || msgsize > 8192)
{
fprintf(stderr,"Error: Message too long");
break;
}
memset(message + strlen(message), ' ', msgsize);
memset(message + block_size - 1, '\0', 1);
if (interval > 0 && i % interval == 0)
{
err = MXS_ERROR("%s", message);
}
else
{
err = MXS_ERROR("%s", message);
}
if (err)
{
fprintf(stderr,"Error: log_manager returned %d",err);
break;
}
ts1.tv_nsec = 100 * 1000000;
nanosleep(&ts1, NULL);
}
mxs_log_flush();
mxs_log_finish();
free(message);
return 0;
}

View File

@ -40,6 +40,7 @@
#include <errno.h>
#include <poll.h>
#include <dcb.h>
#include <test_utils.h>
/**
* test1 Allocate a service and do lots of other things
@ -56,6 +57,7 @@ int result;
/* Poll tests */
ss_dfprintf(stderr,
"testpoll : Initialise the polling system.");
init_test_env(NULL);
poll_init();
ss_dfprintf(stderr, "\t..done\nAdd a DCB");
dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER);

View File

@ -111,7 +111,7 @@ static int
test2()
{
SPINLOCK lck;
void *handle;
THREAD handle;
struct timespec sleeptime;
sleeptime.tv_sec = 10;
@ -120,7 +120,7 @@ struct timespec sleeptime;
acquire_time = 0;
spinlock_init(&lck);
spinlock_acquire(&lck);
handle = thread_start(test2_helper, (void *)&lck);
thread_start(&handle, test2_helper, (void *)&lck);
nanosleep(&sleeptime, NULL);
spinlock_release(&lck);
thread_wait(handle);
@ -206,7 +206,7 @@ static int
test3()
{
// SPINLOCK lck;
void *handle[THREADS];
THREAD handle[THREADS];
int i;
int tnum[THREADS];
time_t rawtime;
@ -220,7 +220,7 @@ time_t rawtime;
for (i = 0; i<THREADS; i++) {
threadrun[i] = 0;
tnum[i] = i;
handle[i] = thread_start(test3_helper, &tnum[i]);
thread_start(&handle[i], test3_helper, &tnum[i]);
}
for (i = 0; i<THREADS; i++) {
fprintf(stderr, "spinlock_test 3 thread %d ran %d times, no wait %d times before waits.\n", i, threadrun[i], nowait[i]);

View File

@ -16,7 +16,7 @@
* Copyright MariaDB Corporation Ab 2013-2014
*/
#include <thread.h>
#include <pthread.h>
/**
* @file thread.c - Implementation of thread related operations
*
@ -33,20 +33,18 @@
/**
* Start a polling thread
*
* @param entry The entry point to call
* @param arg The argument to pass the thread entry point
* @return The thread handle
* @param thd Pointer to the THREAD object
* @param entry The entry point to call
* @param arg The argument to pass the thread entry point
* @return The thread handle or NULL if an error occurred
*/
void *
thread_start(void (*entry)(void *), void *arg)
THREAD *thread_start(THREAD *thd, void (*entry)(void *), void *arg)
{
pthread_t thd;
if (pthread_create(&thd, NULL, (void *(*)(void *))entry, arg) != 0)
if (pthread_create(thd, NULL, (void *(*)(void *))entry, arg) != 0)
{
return NULL;
}
return (void *)thd;
return thd;
}
/**
@ -55,7 +53,7 @@ thread_start(void (*entry)(void *), void *arg)
* @param thd The thread handle
*/
void
thread_wait(void *thd)
thread_wait(THREAD thd)
{
void *rval;
@ -71,7 +69,6 @@ void
thread_millisleep(int ms)
{
struct timespec req;
req.tv_sec = ms / 1000;
req.tv_nsec = (ms % 1000) * 1000000;
nanosleep(&req, NULL);