Merge branch 'develop' into MXS-544
This commit is contained in:
@ -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)
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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 */
|
||||
|
||||
@ -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. */
|
||||
|
||||
|
||||
@ -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
@ -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;
|
||||
}
|
||||
|
||||
@ -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))
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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
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
421
server/core/mlist.c
Normal 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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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:
|
||||
|
||||
183
server/core/query_classifier.c
Normal file
183
server/core/query_classifier.c
Normal 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);
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
366
server/core/slist.c
Normal 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 */
|
||||
|
||||
@ -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
121
server/core/statistics.c
Normal 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;
|
||||
}
|
||||
@ -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
74
server/core/test/logorder.sh
Executable 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
718
server/core/test/testlog.c
Normal 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;
|
||||
}
|
||||
124
server/core/test/testlogorder.c
Normal file
124
server/core/test/testlogorder.c
Normal 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;
|
||||
}
|
||||
@ -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);
|
||||
|
||||
@ -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]);
|
||||
|
||||
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user