From 175711a1bc54ac8447aafc2d61c6c9785678101c Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Thu, 13 Nov 2014 17:55:29 +0200 Subject: [PATCH 01/18] Fix to #601, http://bugs.skysql.com/show_bug.cgi?id=601 if (func.auth ==)gw_change_user->gw_send_change_user_to_backend is called before backend has its scramble, auth packet is set to backend's delauqueue instead of writing it to backend. When backend_write_delayqueue is called COM_CHANGE_USER packets are rewritten with backend's current data. --- .../include/mysql_client_server_protocol.h | 7 + server/modules/protocol/mysql_backend.c | 37 +- server/modules/protocol/mysql_client.c | 12 +- server/modules/protocol/mysql_common.c | 388 ++++++++++-------- 4 files changed, 258 insertions(+), 186 deletions(-) diff --git a/server/modules/include/mysql_client_server_protocol.h b/server/modules/include/mysql_client_server_protocol.h index b2b78cf56..f15c9e62b 100644 --- a/server/modules/include/mysql_client_server_protocol.h +++ b/server/modules/include/mysql_client_server_protocol.h @@ -297,6 +297,7 @@ typedef struct { #define MYSQL_GET_STMTOK_NATTR(payload) (gw_mysql_get_byte2(&payload[11])) #define MYSQL_IS_ERROR_PACKET(payload) (MYSQL_GET_COMMAND(payload)==0xff) #define MYSQL_IS_COM_QUIT(payload) (MYSQL_GET_COMMAND(payload)==0x01) +#define MYSQL_IS_CHANGE_USER(payload) (MYSQL_GET_COMMAND(payload)==0x11) #define MYSQL_GET_NATTR(payload) ((int)payload[4]) #endif /** _MYSQL_PROTOCOL_H */ @@ -314,6 +315,7 @@ int gw_send_authentication_to_backend( char *user, uint8_t *passwd, MySQLProtocol *protocol); + const char *gw_mysql_protocol_state2string(int state); int gw_do_connect_to_backend(char *host, int port, int* fd); int mysql_send_com_quit(DCB* dcb, int packet_number, GWBUF* buf); @@ -335,6 +337,11 @@ int gw_send_change_user_to_backend( char *user, uint8_t *passwd, MySQLProtocol *protocol); + +GWBUF* gw_create_change_user_packet( + MYSQL_session* mses, + MySQLProtocol* protocol); + int gw_find_mysql_user_password_sha1( char *username, uint8_t *gateway_password, diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index fcb75e004..33b114649 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -1157,7 +1157,24 @@ static int backend_write_delayqueue(DCB *dcb) localq = dcb->delayq; dcb->delayq = NULL; spinlock_release(&dcb->delayqlock); - rc = dcb_write(dcb, localq); + + if (MYSQL_IS_CHANGE_USER(((uint8_t *)GWBUF_DATA(localq)))) + { + MYSQL_session* mses; + GWBUF* new_packet; + + mses = (MYSQL_session *)dcb->session->client->data; + new_packet = gw_create_change_user_packet( + mses, + (MySQLProtocol *)dcb->protocol); + /** + * Remove previous packet which lacks scramble + * and append the new. + */ + localq = gwbuf_consume(localq, GWBUF_LENGTH(localq)); + localq = gwbuf_append(localq, new_packet); + } + rc = dcb_write(dcb, localq); } if (rc == 0) @@ -1289,13 +1306,25 @@ static int gw_change_user( * decode the token and check the password. * Note: if auth_token_len == 0 && auth_token == NULL, user is without password */ - auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1); + auth_ret = gw_check_mysql_scramble_data(backend->session->client, + auth_token, + auth_token_len, + client_protocol->scramble, + sizeof(client_protocol->scramble), + username, + client_sha1); if (auth_ret != 0) { if (!service_refresh_users(backend->session->client->service)) { /* Try authentication again with new repository data */ /* Note: if no auth client authentication will fail */ - auth_ret = gw_check_mysql_scramble_data(backend->session->client, auth_token, auth_token_len, client_protocol->scramble, sizeof(client_protocol->scramble), username, client_sha1); + auth_ret = gw_check_mysql_scramble_data( + backend->session->client, + auth_token, auth_token_len, + client_protocol->scramble, + sizeof(client_protocol->scramble), + username, + client_sha1); } } @@ -1359,7 +1388,7 @@ static int gw_change_user( rv = gw_send_change_user_to_backend(database, username, client_sha1, backend_protocol); /* * Now copy new data into user session - */ + */ strcpy(current_session->user, username); strcpy(current_session->db, database); memcpy(current_session->client_sha1, client_sha1, sizeof(current_session->client_sha1)); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index f67edb7f3..e248373a2 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -479,7 +479,8 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, - protocol->scramble, sizeof(protocol->scramble), + protocol->scramble, + sizeof(protocol->scramble), username, stage1_hash); @@ -491,7 +492,14 @@ static int gw_mysql_do_authentication(DCB *dcb, GWBUF *queue) { if (!service_refresh_users(dcb->service)) { /* Try authentication again with new repository data */ /* Note: if no auth client authentication will fail */ - auth_ret = gw_check_mysql_scramble_data(dcb, auth_token, auth_token_len, protocol->scramble, sizeof(protocol->scramble), username, stage1_hash); + auth_ret = gw_check_mysql_scramble_data( + dcb, + auth_token, + auth_token_len, + protocol->scramble, + sizeof(protocol->scramble), + username, + stage1_hash); } } diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 374cabfb5..2d5bad11c 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -275,8 +275,7 @@ int gw_read_backend_handshake( payload += 4; //Now decode mysql handshake - success = gw_decode_mysql_server_handshake(conn, - payload); + success = gw_decode_mysql_server_handshake(conn, payload); if (success < 0) { /* MySQL handshake has not been properly decoded @@ -1054,197 +1053,226 @@ int mysql_send_custom_error ( return dcb->func.write(dcb, buf); } +/** + * Create COM_CHANGE_USER packet and store it to GWBUF + * + * @param mses MySQL session + * @param protocol protocol structure of the backend + * + * @return GWBUF buffer consisting of COM_CHANGE_USER packet + * + * @note the function doesn't fail + */ +GWBUF* gw_create_change_user_packet( + MYSQL_session* mses, + MySQLProtocol* protocol) +{ + char* db; + char* user; + uint8_t* pwd; + GWBUF* buffer; + int compress = 0; + uint8_t* payload = NULL; + uint8_t* payload_start = NULL; + long bytes; + uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE]; + uint32_t server_capabilities = 0; + uint32_t final_capabilities = 0; + char dbpass[MYSQL_USER_MAXLEN + 1]=""; + char* curr_db = NULL; + uint8_t* curr_passwd = NULL; + unsigned int charset; + + db = mses->db; + user = mses->user; + pwd = mses->client_sha1; + + if (strlen(db) > 0) + { + curr_db = db; + } + + if (strlen((char *)pwd) > 0) + { + curr_passwd = pwd; + } + final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities); + + /** Copy client's flags to backend */ + final_capabilities |= protocol->client_capabilities; + + /* get charset the client sent and use it for connection auth */ + charset = protocol->charset; + + if (compress) + { + final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS; +#ifdef DEBUG_MYSQL_CONN + fprintf(stderr, ">>>> Backend Connection with compression\n"); +#endif + } + + if (curr_passwd != NULL) + { + uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]=""; + uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]=""; + uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]=""; + + /** hash1 is the function input, SHA1(real_password) */ + memcpy(hash1, pwd, GW_MYSQL_SCRAMBLE_SIZE); + + /** + * hash2 is the SHA1(input data), where + * input_data = SHA1(real_password) + */ + gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2); + + /** dbpass is the HEX form of SHA1(SHA1(real_password)) */ + gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE); + + /** new_sha is the SHA1(CONCAT(scramble, hash2) */ + gw_sha1_2_str(protocol->scramble, + GW_MYSQL_SCRAMBLE_SIZE, + hash2, + GW_MYSQL_SCRAMBLE_SIZE, + new_sha); + + /** compute the xor in client_scramble */ + gw_str_xor(client_scramble, + new_sha, hash1, + GW_MYSQL_SCRAMBLE_SIZE); + } + if (curr_db == NULL) + { + final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; + } + else + { + final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; + } + final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH; + /** + * Protocol MySQL COM_CHANGE_USER for CLIENT_PROTOCOL_41 + * 1 byte COMMAND + */ + bytes = 1; + + /** add the user and a terminating char */ + bytes += strlen(user); + bytes++; + /** + * next will be + 1 (scramble_len) + 20 (fixed_scramble) + + * (db + NULL term) + 2 bytes charset + */ + if (curr_passwd != NULL) + { + bytes += GW_MYSQL_SCRAMBLE_SIZE; + } + /** 1 byte for scramble_len */ + bytes++; + /** db name and terminating char */ + if (curr_db != NULL) + { + bytes += strlen(curr_db); + } + bytes++; + + /** the charset */ + bytes += 2; + bytes += strlen("mysql_native_password"); + bytes++; + + /** the packet header */ + bytes += 4; + + buffer = gwbuf_alloc(bytes); + /** + * Set correct type to GWBUF so that it will be handled like session + * commands + */ + buffer->gwbuf_type = + GWBUF_TYPE_MYSQL|GWBUF_TYPE_SINGLE_STMT|GWBUF_TYPE_SESCMD; + payload = GWBUF_DATA(buffer); + memset(payload, '\0', bytes); + payload_start = payload; + + /** set packet number to 0 */ + payload[3] = 0x00; + payload += 4; + + /** set the command COM_CHANGE_USER 0x11 */ + payload[0] = 0x11; + payload++; + memcpy(payload, user, strlen(user)); + payload += strlen(user); + payload++; + + if (curr_passwd != NULL) + { + /** set the auth-length */ + *payload = GW_MYSQL_SCRAMBLE_SIZE; + payload++; + /** + * copy the 20 bytes scramble data after + * packet_buffer+36+user+NULL+1 (byte of auth-length) + */ + memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE); + payload += GW_MYSQL_SCRAMBLE_SIZE; + } + else + { + /** skip the auth-length and write a NULL */ + payload++; + } + /** if the db is not NULL append it */ + if (curr_db != NULL) + { + memcpy(payload, curr_db, strlen(curr_db)); + payload += strlen(curr_db); + } + payload++; + /** set the charset, 2 bytes */ + *payload = charset; + payload++; + *payload = '\x00'; + payload++; + memcpy(payload, "mysql_native_password", strlen("mysql_native_password")); + payload += strlen("mysql_native_password"); + payload++; + /** put here the paylod size: bytes to write - 4 bytes packet header */ + gw_mysql_set_byte3(payload_start, (bytes-4)); + + return buffer; +} + /** * Write a MySQL CHANGE_USER packet to backend server * * @param conn MySQL protocol structure * @param dbname The selected database * @param user The selected user - * @param passwd The SHA1(real_password): Note real_password is unknown + * @param passwd The SHA1(real_password) * @return 1 on success, 0 on failure */ int gw_send_change_user_to_backend( - char *dbname, - char *user, - uint8_t *passwd, - MySQLProtocol *conn) + char *dbname, + char *user, + uint8_t *passwd, + MySQLProtocol *conn) { - int compress = 0; - int rv; - uint8_t *payload = NULL; - uint8_t *payload_start = NULL; - long bytes; - uint8_t client_scramble[GW_MYSQL_SCRAMBLE_SIZE]; - uint8_t client_capabilities[4]; - uint32_t server_capabilities = 0; - uint32_t final_capabilities = 0; - char dbpass[MYSQL_USER_MAXLEN + 1]=""; - GWBUF *buffer; - DCB *dcb; - - char *curr_db = NULL; - uint8_t *curr_passwd = NULL; - unsigned int charset; - - if (strlen(dbname)) - curr_db = dbname; - - if (strlen((char *)passwd)) - curr_passwd = passwd; - - dcb = conn->owner_dcb; - - final_capabilities = gw_mysql_get_byte4((uint8_t *)&server_capabilities); - - /** Copy client's flags to backend */ - final_capabilities |= conn->client_capabilities; - - /* get charset the client sent and use it for connection auth */ - charset = conn->charset; - - if (compress) { - final_capabilities |= GW_MYSQL_CAPABILITIES_COMPRESS; -#ifdef DEBUG_MYSQL_CONN - fprintf(stderr, ">>>> Backend Connection with compression\n"); -#endif - } - - if (curr_passwd != NULL) { - uint8_t hash1[GW_MYSQL_SCRAMBLE_SIZE]=""; - uint8_t hash2[GW_MYSQL_SCRAMBLE_SIZE]=""; - uint8_t new_sha[GW_MYSQL_SCRAMBLE_SIZE]=""; - - // hash1 is the function input, SHA1(real_password) - memcpy(hash1, passwd, GW_MYSQL_SCRAMBLE_SIZE); - - // hash2 is the SHA1(input data), where input_data = SHA1(real_password) - gw_sha1_str(hash1, GW_MYSQL_SCRAMBLE_SIZE, hash2); - - // dbpass is the HEX form of SHA1(SHA1(real_password)) - gw_bin2hex(dbpass, hash2, GW_MYSQL_SCRAMBLE_SIZE); - - // new_sha is the SHA1(CONCAT(scramble, hash2) - gw_sha1_2_str(conn->scramble, GW_MYSQL_SCRAMBLE_SIZE, hash2, GW_MYSQL_SCRAMBLE_SIZE, new_sha); - - // compute the xor in client_scramble - gw_str_xor(client_scramble, new_sha, hash1, GW_MYSQL_SCRAMBLE_SIZE); - - } - - if (curr_db == NULL) { - // without db - final_capabilities &= ~GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; - } else { - final_capabilities |= GW_MYSQL_CAPABILITIES_CONNECT_WITH_DB; - } - - final_capabilities |= GW_MYSQL_CAPABILITIES_PLUGIN_AUTH; - - gw_mysql_set_byte4(client_capabilities, final_capabilities); - - // Protocol MySQL COM_CHANGE_USER for CLIENT_PROTOCOL_41 - // 1 byte COMMAND - bytes = 1; - - // add the user - bytes += strlen(user); - // NULL byte for user string - bytes++; - - // next will be + 1 (scramble_len) + 20 (fixed_scramble) + (dbname + NULL term) + 2 bytes charset - - if (curr_passwd != NULL) { - bytes += GW_MYSQL_SCRAMBLE_SIZE; - } - // 1 byte for scramble_len - bytes++; - - if (curr_db != NULL) { - bytes += strlen(curr_db); - } - // NULL byte for dbname string - bytes++; - - // the charset - bytes += 2; - bytes += strlen("mysql_native_password"); - bytes++; - - // the packet header - bytes += 4; - - // allocating the GWBUF - buffer = gwbuf_alloc(bytes); - /** - * Set correct type to GWBUF so that it will be handled like session - * commands should - */ - buffer->gwbuf_type = GWBUF_TYPE_MYSQL|GWBUF_TYPE_SINGLE_STMT|GWBUF_TYPE_SESCMD; - payload = GWBUF_DATA(buffer); - - // clearing data - memset(payload, '\0', bytes); + GWBUF *buffer; + int rc; + MYSQL_session* mses; - // save the start pointer - payload_start = payload; + mses = (MYSQL_session*)conn->owner_dcb->session->client->data; + buffer = gw_create_change_user_packet(mses, conn); + rc = conn->owner_dcb->func.write(conn->owner_dcb, buffer); - // set packet # = 1 - payload[3] = 0x00; - payload += 4; - - // set the command COM_CHANGE_USER \x11 - payload[0] = 0x11; - payload++; - - memcpy(payload, user, strlen(user)); - payload += strlen(user); - payload++; - - if (curr_passwd != NULL) { - // set the auth-length - *payload = GW_MYSQL_SCRAMBLE_SIZE; - payload++; - - //copy the 20 bytes scramble data after packet_buffer+36+user+NULL+1 (byte of auth-length) - memcpy(payload, client_scramble, GW_MYSQL_SCRAMBLE_SIZE); - - payload += GW_MYSQL_SCRAMBLE_SIZE; - - } else { - // skip the auth-length and write a NULL - payload++; - } - - // if the db is not NULL append it - if (curr_db != NULL) { - memcpy(payload, curr_db, strlen(curr_db)); - payload += strlen(curr_db); - payload++; - } else { - // skip the NULL - payload++; + if (rc != 0) + { + rc = 1; } - - // set the charset, 2 bytes!!!! - *payload = charset; - payload++; - *payload = '\x00'; - payload++; - - memcpy(payload, "mysql_native_password", strlen("mysql_native_password")); - - payload += strlen("mysql_native_password"); - payload++; - - // put here the paylod size: bytes to write - 4 bytes packet header - gw_mysql_set_byte3(payload_start, (bytes-4)); - - rv = dcb->func.write(dcb, buffer); - - if (rv == 0) - return 0; - else - return 1; + return rc; } /** From 12b06a503e198d39013a9f18fe6e9065e3d9d263 Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 13 Nov 2014 17:15:17 +0100 Subject: [PATCH 02/18] Removed NULL pointer printing Removed NULL pointer printing --- server/core/service.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/core/service.c b/server/core/service.c index d88013a37..b9294bd27 100644 --- a/server/core/service.c +++ b/server/core/service.c @@ -227,7 +227,7 @@ GWPROTOCOL *funcs; LOGFILE_ERROR, "Error : Unable to load users from %s:%d for " "service %s.", - port->address, + (port->address == NULL ? "0.0.0.0" : port->address), port->port, service->name))); hashtable_free(service->users->data); From 238c476d3a5b1350a94138b1d3957477fb7c60fe Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Thu, 13 Nov 2014 17:41:50 +0100 Subject: [PATCH 03/18] getUsers try selecting a Master server getUsers try selecting a Master server if available. Next users reload will connect to master server --- server/core/dbusers.c | 62 ++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/server/core/dbusers.c b/server/core/dbusers.c index 662f480a3..7d6acfd98 100644 --- a/server/core/dbusers.c +++ b/server/core/dbusers.c @@ -468,26 +468,56 @@ getUsers(SERVICE *service, USERS *users) server = service->databases; dpwd = decryptPassword(service_passwd); - while (server != NULL && (mysql_real_connect(con, - server->name, - service_user, - dpwd, - NULL, - server->port, - NULL, - 0) == NULL)) - { - server = server->nextdb; + /* Select a server with Master bit, if available */ + while (server != NULL && !(server->status & SERVER_MASTER)) { + server = server->nextdb; } + + /* Try loading data from master server */ + if (server != NULL && (mysql_real_connect(con, server->name, service_user, dpwd, NULL, server->port, NULL, 0) != NULL)) { + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "Dbusers : Loading data from backend database with Master role [%s:%i] " + "for service [%s]", + server->name, + server->port, + service->name))); + } else { + /* load data from other servers via loop */ + server = service->databases; + + while (server != NULL && (mysql_real_connect(con, + server->name, + service_user, + dpwd, + NULL, + server->port, + NULL, + 0) == NULL)) + { + server = server->nextdb; + } + + if (server != NULL) { + LOGIF(LD, (skygw_log_write_flush( + LOGFILE_DEBUG, + "Dbusers : Loading data from backend database [%s:%i] " + "for service [%s]", + server->name, + server->port, + service->name))); + } + } + free(dpwd); if (server == NULL) { LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : Unable to get user data from backend database " - "for service [%s]. Missing server information.", - service->name))); + LOGFILE_ERROR, + "Error : Unable to get user data from backend database " + "for service [%s]. Missing server information.", + service->name))); mysql_close(con); return -1; } @@ -663,8 +693,8 @@ getUsers(SERVICE *service, USERS *users) /* load all mysql database names */ dbnames = getDatabases(service, con); - LOGIF(LM, (skygw_log_write( - LOGFILE_MESSAGE, + LOGIF(LD, (skygw_log_write( + LOGFILE_DEBUG, "Loaded %d MySQL Database Names for service [%s]", dbnames, service->name))); From b37fb35669cc2280f920baf17b653bc96648fb4f Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 14 Nov 2014 10:03:11 +0100 Subject: [PATCH 04/18] Removed PID print to stderr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed PID print to stderr. it’s already included id message log --- server/core/gateway.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 0cf2cd666..090b433a3 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -1851,8 +1851,6 @@ static int write_pid_file(char *home_dir) { /* close file */ close(fd); - - fprintf(stderr, "MaxScale PID %s in pidfile %s\n", pidstr, pidfile); } /* success */ From 949bbc99d9fd8e8f51ac6b8f6580c8cc2c90fe2d Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 14 Nov 2014 10:11:15 +0100 Subject: [PATCH 05/18] Messages moved from stderr to MESSAGE log Messages moved from stderr to MESSAGE log --- server/modules/protocol/httpd.c | 6 +++--- server/modules/protocol/telnetd.c | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/server/modules/protocol/httpd.c b/server/modules/protocol/httpd.c index f5e347924..4b4588b16 100644 --- a/server/modules/protocol/httpd.c +++ b/server/modules/protocol/httpd.c @@ -49,6 +49,8 @@ MODULE_INFO info = { "An experimental HTTPD implementation for use in admnistration" }; +extern int lm_enabled_logfiles_bitmask; + #define ISspace(x) isspace((int)(x)) #define HTTP_SERVER_STRING "MaxScale(c) v.1.0.0" static char *version_str = "V1.0.1"; @@ -413,9 +415,7 @@ int syseno = 0; rc = listen(listener->fd, SOMAXCONN); if (rc == 0) { - fprintf(stderr, - "Listening http connections at %s\n", - config); + LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening httpd connections at %s", config))); } else { int eno = errno; errno = 0; diff --git a/server/modules/protocol/telnetd.c b/server/modules/protocol/telnetd.c index e8c208c20..f78a8e0d3 100644 --- a/server/modules/protocol/telnetd.c +++ b/server/modules/protocol/telnetd.c @@ -390,9 +390,7 @@ int syseno = 0; rc = listen(listener->fd, SOMAXCONN); if (rc == 0) { - fprintf(stderr, - "Listening telnet connections at %s\n", - config); + LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening telnet connections at %s", config))); } else { int eno = errno; errno = 0; From d090eee09b6bacfe3cb5aa427366b93672cf4a8c Mon Sep 17 00:00:00 2001 From: MassimilianoPinto Date: Fri, 14 Nov 2014 10:19:04 +0100 Subject: [PATCH 06/18] Messages moved from stderr to MESSAGE log Messages moved from stderr to MESSAGE log --- server/modules/protocol/mysql_client.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 99747fd7e..49d382bbc 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1055,9 +1055,7 @@ int gw_MySQLListener( rc = listen(l_so, 10 * SOMAXCONN); if (rc == 0) { - fprintf(stderr, - "Listening MySQL connections at %s\n", - config_bind); + LOGIF(LM, (skygw_log_write_flush(LOGFILE_MESSAGE,"Listening MySQL connections at %s", config_bind))); } else { int eno = errno; errno = 0; From 4389cb06da42389bffd144cca425e5fbbfdde98c Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Fri, 14 Nov 2014 10:36:32 +0000 Subject: [PATCH 07/18] Add the CLI to the template configuration file --- server/MaxScale_template.cnf | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/server/MaxScale_template.cnf b/server/MaxScale_template.cnf index c322fec8f..6c980b03b 100644 --- a/server/MaxScale_template.cnf +++ b/server/MaxScale_template.cnf @@ -131,6 +131,24 @@ service=HTTPD Router protocol=HTTPD port=6444 +# Enable the maxadmin interface to MaxScale +# +# Listen on the default port of 6603 and restrict +# to connections from localhost only. +# Remove the address=localhost entry to enable +# maxadmin connections from any host + +[CLI] +type=service +router=cli + +[CLI Listener] +type=listener +service=CLI +protocol=maxscaled +address=localhost +port=6603 + # Definition of the servers [server1] From 77aa9827fe93f1361d23833ab371cde5192d8c6f Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 14 Nov 2014 13:06:35 +0200 Subject: [PATCH 08/18] Fix candidate to bug #615, http://bugs.skysql.com/show_bug.cgi?id=615 --- server/modules/protocol/mysql_backend.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index 33b114649..aae579d09 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -539,10 +539,13 @@ static int gw_read_backend_event(DCB *dcb) { goto return_rc; } } - /*< - * If dcb->session->client is freed already it may be NULL. + /** + * Check that session is operable, and that client DCB is + * still listening the socket for replies. */ - if (dcb->session->client != NULL) + if (dcb->session->state == SESSION_STATE_ROUTER_READY && + dcb->session->client != NULL && + dcb->session->client->state == DCB_STATE_POLLING) { client_protocol = SESSION_PROTOCOL(dcb->session, MySQLProtocol); @@ -571,6 +574,10 @@ static int gw_read_backend_event(DCB *dcb) { rc = 1; } } + else /*< session is closing; replying to client isn't possible */ + { + gwbuf_free(read_buffer); + } } return_rc: From 81009e7d19d03425ba59b2ff8c5cb9047f4ac847 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 14 Nov 2014 15:20:15 +0200 Subject: [PATCH 09/18] Fix candidate to bug #615 Mark backend reference closed in handleError --- .../routing/readwritesplit/readwritesplit.c | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index eca2a5615..d403217ca 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -267,7 +267,11 @@ static bool handle_error_new_connection( ROUTER_CLIENT_SES* rses, DCB* backend_dcb, GWBUF* errmsg); -static void handle_error_reply_client(SESSION* ses, GWBUF* errmsg); +static void handle_error_reply_client( + SESSION* ses, + ROUTER_CLIENT_SES* rses, + DCB* backend_dcb, + GWBUF* errmsg); static backend_ref_t* get_root_master_bref(ROUTER_CLIENT_SES* rses); @@ -4116,7 +4120,7 @@ static void handleError ( switch (action) { case ERRACT_NEW_CONNECTION: - { + { if (!rses_begin_locked_router_action(rses)) { *succp = false; @@ -4134,7 +4138,6 @@ static void handleError ( *succp = false; rses_end_locked_router_action(rses); - return; } /** * This is called in hope of getting replacement for @@ -4150,7 +4153,10 @@ static void handleError ( case ERRACT_REPLY_CLIENT: { - handle_error_reply_client(session, errmsgbuf); + handle_error_reply_client(session, + rses, + backend_dcb, + errmsgbuf); *succp = false; /*< no new backend servers were made available */ break; } @@ -4163,16 +4169,29 @@ static void handleError ( static void handle_error_reply_client( - SESSION* ses, - GWBUF* errmsg) + SESSION* ses, + ROUTER_CLIENT_SES* rses, + DCB* backend_dcb, + GWBUF* errmsg) { session_state_t sesstate; DCB* client_dcb; + backend_ref_t* bref; spinlock_acquire(&ses->ses_lock); sesstate = ses->state; client_dcb = ses->client; spinlock_release(&ses->ses_lock); + + /** + * If bref exists, mark it closed + */ + if ((bref = get_bref_from_dcb(rses, backend_dcb)) != NULL) + { + CHK_BACKEND_REF(bref); + bref_clear_state(bref, BREF_IN_USE); + bref_set_state(bref, BREF_CLOSED); + } if (sesstate == SESSION_STATE_ROUTER_READY) { From e35ad0c5d3c3182fa408cfe1a2868e3ed11c0cbd Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 14 Nov 2014 17:50:07 +0200 Subject: [PATCH 10/18] Fix candidate for bug #615 Removed invalid debug assert --- server/modules/routing/readwritesplit/readwritesplit.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index d403217ca..fd47a3c19 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1216,7 +1216,6 @@ static bool get_dcb( "the service %s.", rses->router->service->name))); } - ss_dassert(succp); } if (btype == BE_MASTER) @@ -1689,9 +1688,9 @@ void check_create_tmp_table( * for buffering the partial query, a later call to the query router will * contain the remainder, or part thereof of the query. * - * @param instance The query router instance - * @param session The session associated with the client - * @param queue MaxScale buffer queue with the packets received + * @param instance The query router instance + * @param router_session The session associated with the client + * @param querybuf MaxScale buffer queue with received packet * * @return if succeed 1, otherwise 0 * If routeQuery fails, it means that router session has failed. From a16d1d445e697b9291495cb7512671e26ad56685 Mon Sep 17 00:00:00 2001 From: Mark Riddoch Date: Fri, 14 Nov 2014 18:02:25 +0000 Subject: [PATCH 11/18] Addition of flush log and flush logs command --- server/modules/routing/debugcmd.c | 76 +++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index 31f3c5953..9dc269cd2 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -490,6 +490,81 @@ struct subcommand removeoptions[] = { } }; +/** + * User command to flush a single logfile + * + * @param pdcb The stream to write output to + * @param logname The name of the log + */ +static void +flushlog(DCB *pdcb, char *logname) +{ + if (logname == NULL) + { + } + else if (!strcasecmp(logname, "error")) + { + skygw_log_rotate(LOGFILE_ERROR); + } + else if (!strcasecmp(logname, "message")) + { + skygw_log_rotate(LOGFILE_MESSAGE); + } + else if (!strcasecmp(logname, "trace")) + { + skygw_log_rotate(LOGFILE_TRACE); + } + else if (!strcasecmp(logname, "debug")) + { + skygw_log_rotate(LOGFILE_DEBUG); + } + else + { + dcb_printf(pdcb, "Unexpected logfile name, expected " + "error, message, trace oe debug.\n"); + } +} + +/** + * User command to flush all logfiles + * + * @param pdcb The stream to write output to + */ +static void +flushlogs(DCB *pdcb) +{ + skygw_log_rotate(LOGFILE_ERROR); + skygw_log_rotate(LOGFILE_MESSAGE); + skygw_log_rotate(LOGFILE_TRACE); + skygw_log_rotate(LOGFILE_DEBUG); +} + + +/** + * The subcommands of the flush command + */ +struct subcommand flushoptions[] = { + { + "log", + 1, + flushlog, + "Flush the content of a log file, close that log, rename it and open a new log file", + "Flush the content of a log file, close that log, rename it and open a new log file", + {ARG_TYPE_STRING, 0, 0} + }, + { + "logs", + 0, + flushlogs, + "Flush the content of all log files, close that logs, rename them and open a new log files", + "Flush the content of all log files, close that logs, rename them and open a new log files", + {0, 0, 0} + }, + { + NULL, 0, NULL, NULL, NULL, {0, 0, 0} + } +}; + /** * The debug command table @@ -505,6 +580,7 @@ static struct { #if defined(FAKE_CODE) { "fail", failoptions }, #endif /* FAKE_CODE */ + { "flush", flushoptions }, { "list", listoptions }, { "reload", reloadoptions }, { "remove", removeoptions }, From 8c1e7172f3eca41623ad6a90737682b1af2a3b2f Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 14 Nov 2014 20:48:33 +0200 Subject: [PATCH 12/18] Added debug logging for #615 --- server/core/dcb.c | 4 ++++ server/modules/protocol/mysql_backend.c | 4 ++++ server/modules/protocol/mysql_client.c | 4 ++++ server/modules/routing/readwritesplit/readwritesplit.c | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/server/core/dcb.c b/server/core/dcb.c index 26ab592c5..244b50c06 100644 --- a/server/core/dcb.c +++ b/server/core/dcb.c @@ -1119,6 +1119,10 @@ dcb_close(DCB *dcb) CHK_DCB(dcb); + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [dcb_close]", + pthread_self()))); + /*< * dcb_close may be called for freshly created dcb, in which case * it only needs to be freed. diff --git a/server/modules/protocol/mysql_backend.c b/server/modules/protocol/mysql_backend.c index aae579d09..96fe9377f 100644 --- a/server/modules/protocol/mysql_backend.c +++ b/server/modules/protocol/mysql_backend.c @@ -1089,6 +1089,10 @@ gw_backend_close(DCB *dcb) session = dcb->session; CHK_SESSION(session); + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [gw_backend_close]", + pthread_self()))); + quitbuf = mysql_create_com_quit(NULL, 0); gwbuf_set_type(quitbuf, GWBUF_TYPE_MYSQL); diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 49d382bbc..77ef7c9ef 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -1392,6 +1392,10 @@ gw_client_close(DCB *dcb) CHK_PROTOCOL(protocol); } #endif + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [gw_client_close]", + pthread_self()))); + mysql_protocol_done(dcb); session = dcb->session; diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index fd47a3c19..e3c308242 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -912,6 +912,10 @@ static void closeSession( ROUTER_CLIENT_SES* router_cli_ses; backend_ref_t* backend_ref; + LOGIF(LD, (skygw_log_write(LOGFILE_DEBUG, + "%lu [RWSplit:closeSession]", + pthread_self()))); + /** * router session can be NULL if newSession failed and it is discarding * its connections and DCB's. From e790437fc04771a98936a50c514096b794459a77 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 14 Nov 2014 22:35:23 +0200 Subject: [PATCH 13/18] Fixed debug assert. If session is closed its router session pointer is nullified and it is not error. --- .../routing/readwritesplit/readwritesplit.c | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index e3c308242..fbbd444a4 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -934,18 +934,7 @@ static void closeSession( if (!router_cli_ses->rses_closed && rses_begin_locked_router_action(router_cli_ses)) { - int i = 0; - /** - * session must be moved to SESSION_STATE_STOPPING state before - * router session is closed. - */ -#if defined(SS_DEBUG) - SESSION* ses = get_session_by_router_ses((void*)router_cli_ses); - - ss_dassert(ses != NULL); - ss_dassert(ses->state == SESSION_STATE_STOPPING); -#endif - + int i; /** * This sets router closed. Nobody is allowed to use router * whithout checking this first. @@ -955,8 +944,17 @@ static void closeSession( for (i=0; irses_nbackends; i++) { backend_ref_t* bref = &backend_ref[i]; - DCB* dcb = bref->bref_dcb; - + DCB* dcb = bref->bref_dcb; +#if defined(SS_DEBUG) + /** + * session must be moved to SESSION_STATE_STOPPING state before + * router session is closed. + */ + if (dcb->session != NULL) + { + ss_dassert(dcb->session->state == SESSION_STATE_STOPPING); + } +#endif /** Close those which had been connected */ if (BREF_IS_IN_USE(bref)) { From 419fda5514354003b86822cdd9ff7204c50982ec Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Fri, 14 Nov 2014 23:13:40 +0200 Subject: [PATCH 14/18] Fixed false positive debug assertion --- .../routing/readwritesplit/readwritesplit.c | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index fbbd444a4..54fe66f2b 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -945,21 +945,21 @@ static void closeSession( { backend_ref_t* bref = &backend_ref[i]; DCB* dcb = bref->bref_dcb; -#if defined(SS_DEBUG) - /** - * session must be moved to SESSION_STATE_STOPPING state before - * router session is closed. - */ - if (dcb->session != NULL) - { - ss_dassert(dcb->session->state == SESSION_STATE_STOPPING); - } -#endif /** Close those which had been connected */ if (BREF_IS_IN_USE(bref)) { CHK_DCB(dcb); - /** Clean operation counter in bref and in SERVER */ +#if defined(SS_DEBUG) + /** + * session must be moved to SESSION_STATE_STOPPING state before + * router session is closed. + */ + if (dcb->session != NULL) + { + ss_dassert(dcb->session->state == SESSION_STATE_STOPPING); + } +#endif + /** Clean operation counter in bref and in SERVER */ while (BREF_IS_WAITING_RESULT(bref)) { bref_clear_state(bref, BREF_WAITING_RESULT); From 4f136fa4d2f02542bf5dadc0bf5a376c9c956cb9 Mon Sep 17 00:00:00 2001 From: VilhoRaatikka Date: Sat, 15 Nov 2014 23:57:09 +0200 Subject: [PATCH 15/18] Fixed backend selection logic in get_dcb. If slave is not found write a warning to error log and fall through to master selection. If master has changed or previous master's state has changed, routing fails. --- server/modules/protocol/mysql_common.c | 6 +- .../routing/readwritesplit/readwritesplit.c | 95 ++++++++----------- 2 files changed, 43 insertions(+), 58 deletions(-) diff --git a/server/modules/protocol/mysql_common.c b/server/modules/protocol/mysql_common.c index 2d5bad11c..ce6f1c033 100644 --- a/server/modules/protocol/mysql_common.c +++ b/server/modules/protocol/mysql_common.c @@ -1790,13 +1790,13 @@ void protocol_archive_srv_command( } s1 = &p->protocol_command; - +#if defined(EXTRA_SS_DEBUG) LOGIF(LT, (skygw_log_write( LOGFILE_TRACE, "Move command %s from fd %d to command history.", STRPACKETTYPE(s1->scom_cmd), p->owner_dcb->fd))); - +#endif /** Copy to history list */ if ((h1 = p->protocol_cmd_history) == NULL) { @@ -1874,7 +1874,7 @@ void protocol_add_srv_command( STRPACKETTYPE(cmd), p->owner_dcb->fd))); -#if defined(SS_DEBUG) +#if defined(EXTRA_SS_DEBUG) c = &p->protocol_command; while (c != NULL && c->scom_cmd != MYSQL_COM_UNDEFINED) diff --git a/server/modules/routing/readwritesplit/readwritesplit.c b/server/modules/routing/readwritesplit/readwritesplit.c index 54fe66f2b..22d57683e 100644 --- a/server/modules/routing/readwritesplit/readwritesplit.c +++ b/server/modules/routing/readwritesplit/readwritesplit.c @@ -1177,38 +1177,15 @@ static bool get_dcb( "Warning : No slaves available " "for the service %s.", rses->router->service->name))); - } - - btype = BE_MASTER; - - if (BREF_IS_IN_USE(master_bref)) - { - *p_dcb = master_bref->bref_dcb; - succp = true; - - ss_dassert(master_bref->bref_dcb->state != DCB_STATE_ZOMBIE); - - ss_dassert( - (master_bref->bref_backend && - (master_bref->bref_backend->backend_server == - master_bref->bref_backend->backend_server)) && - smallest_nconn == -1); - - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Using master %s:%d instead.", - master_bref->bref_backend->backend_server->name, - master_bref->bref_backend->backend_server->port))); - } - else - { - LOGIF(LE, (skygw_log_write_flush( - LOGFILE_ERROR, - "Error : No master is availabe either. " - "Unable to find backend server for " - "routing."))); - } + } + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Warning : Using master %s:%d.", + master_bref->bref_backend->backend_server->name, + master_bref->bref_backend->backend_server->port))); + btype = BE_MASTER; } + /** Found slave, correct the status flag */ else if (rses->router->available_slaves == false) { rses->router->available_slaves = true; @@ -1217,25 +1194,31 @@ static bool get_dcb( "At least one slave has become available for " "the service %s.", rses->router->service->name))); + goto return_succp; } } if (btype == BE_MASTER) { - for (i=0; irses_nbackends; i++) - { - BACKEND* b = backend_ref[i].bref_backend; - - if (BREF_IS_IN_USE((&backend_ref[i])) && - (master_bref->bref_backend && - (b->backend_server == - master_bref->bref_backend->backend_server))) - { - *p_dcb = backend_ref[i].bref_dcb; - succp = true; - goto return_succp; - } - } + if (BREF_IS_IN_USE(master_bref) && + SERVER_IS_MASTER(master_bref->bref_backend->backend_server)) + { + *p_dcb = master_bref->bref_dcb; + succp = true; + /** if bref is in use DCB should not be closed */ + ss_dassert(master_bref->bref_dcb->state != DCB_STATE_ZOMBIE); + } + else + { + LOGIF(LE, (skygw_log_write_flush( + LOGFILE_ERROR, + "Error : Server at %s:%d should be master but " + "is %s instead and can't be chosen to master.", + master_bref->bref_backend->backend_server->name, + master_bref->bref_backend->backend_server->port, + STRSRVSTATUS(master_bref->bref_backend->backend_server)))); + succp = false; + } } return_succp: @@ -4138,16 +4121,18 @@ static void handleError ( "Session will be closed."))); *succp = false; - rses_end_locked_router_action(rses); } - /** - * This is called in hope of getting replacement for - * failed slave(s). - */ - *succp = handle_error_new_connection(inst, - rses, - backend_dcb, - errmsgbuf); + else + { + /** + * This is called in hope of getting replacement for + * failed slave(s). + */ + *succp = handle_error_new_connection(inst, + rses, + backend_dcb, + errmsgbuf); + } rses_end_locked_router_action(rses); break; } @@ -4716,7 +4701,7 @@ static backend_ref_t* get_root_master_bref( LOGIF(LE, (skygw_log_write_flush( LOGFILE_ERROR, "Error : Could not find master among the backend " - "servers. Previous master state : %s", + "servers. Previous master's state : %s", STRSRVSTATUS(BREFSRV(rses->rses_master_ref))))); } return candidate_bref; From df9acee69a56174c3873071ca967acc9a3a93178 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 17 Nov 2014 12:44:25 +0200 Subject: [PATCH 16/18] Fix to bug# 616: Added checks that only one block buffer (the first one) is being moved at any one time. --- log_manager/log_manager.cc | 46 +++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/log_manager/log_manager.cc b/log_manager/log_manager.cc index 0e923d9d1..a875bd2ad 100644 --- a/log_manager/log_manager.cc +++ b/log_manager/log_manager.cc @@ -1042,21 +1042,46 @@ static char* blockbuf_get_writepos( }else if(bb->bb_state == BB_CLEARED){ /** - *Move the full buffer to the end of the list + *Move the cleared buffer to the end of the list if it is the first one in the list */ simple_mutex_unlock(&bb->bb_mutex); simple_mutex_lock(&bb_list->mlist_mutex, true); - if(node->mlnode_next){ - bb_list->mlist_first = node->mlnode_next; - bb_list->mlist_last->mlnode_next = node; - node->mlnode_next = NULL; - bb_list->mlist_last = node; - node = bb_list->mlist_first; - } + if(node == bb_list->mlist_first) + { - bb->bb_state = BB_READY; + if(bb_list->mlist_nodecount > 1 && + node != bb_list->mlist_last){ + bb_list->mlist_last->mlnode_next = bb_list->mlist_first; + bb_list->mlist_first = bb_list->mlist_first->mlnode_next; + bb_list->mlist_last->mlnode_next->mlnode_next = NULL; + bb_list->mlist_last = bb_list->mlist_last->mlnode_next; + } + + ss_dassert(node == bb_list->mlist_last); + + simple_mutex_unlock(&bb_list->mlist_mutex); + simple_mutex_lock(&bb->bb_mutex, true); + + bb->bb_state = BB_READY; + + simple_mutex_unlock(&bb->bb_mutex); + simple_mutex_lock(&bb_list->mlist_mutex, true); + + } + else + { + if(node->mlnode_next){ + node = node->mlnode_next; + }else{ + node = bb_list->mlist_first; + } + continue; + } + + + }else if (bb->bb_state == BB_READY){ /** @@ -2701,7 +2726,8 @@ static void filewriter_done( * by file writer which traverses the list and accesses block buffers * included in list nodes. * List modifications are protected with version numbers. - * Before modification, version is increased by one to be odd. After the + * Before + modification, version is increased by one to be odd. After the * completion, it is increased again to even. List can be read only when * version is even and read is consistent only if version hasn't changed * during the read. From 4708e9720832071474b03528afc038b60bd5bea7 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 17 Nov 2014 12:47:24 +0200 Subject: [PATCH 17/18] Added a 'testall-valgrind' target to CMake that runs the full test suite with Valgrind. --- CMakeLists.txt | 19 +++++++++++++++++++ FindValgrind.cmake | 13 +++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 FindValgrind.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a9d8cbef1..2f2990397 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,10 @@ set_maxscale_version() set(CMAKE_INSTALL_PREFIX "${INSTALL_DIR}" CACHE INTERNAL "Prefix prepended to install directories." FORCE) +message(STATUS "$CMAKE_MODULE_PATH:${CMAKE_MODULE_PATH}") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/") +message(STATUS "CMAKE_MODULE_PATH:${CMAKE_MODULE_PATH}") +find_package(Valgrind) project(MaxScale) check_deps() @@ -149,11 +153,13 @@ set(CPACK_RPM_USER_FILELIST "%ignore /etc/init.d") set(CPACK_RPM_USER_FILELIST "%ignore /etc/ld.so.conf.d") set(CPACK_RPM_USER_FILELIST "%ignore /etc") include(CPack) + add_custom_target(buildtests COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DBUILD_TYPE=Debug -DINSTALL_DIR=${CMAKE_BINARY_DIR} -DINSTALL_SYSTEM_FILES=N ${CMAKE_SOURCE_DIR} COMMAND make COMMENT "Building test suite..." VERBATIM ) + add_custom_target(testall COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DBUILD_TYPE=Debug -DINSTALL_DIR=${CMAKE_BINARY_DIR} -DINSTALL_SYSTEM_FILES=N ${CMAKE_SOURCE_DIR} COMMAND make install @@ -185,3 +191,16 @@ if(DOXYGEN_FOUND) COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygate) endif() + +# Testall target with Valgrind +if(VALGRIND_FOUND) +add_custom_target(testall-valgrind + COMMAND ${CMAKE_COMMAND} -DDEPS_OK=Y -DBUILD_TESTS=Y -DBUILD_TYPE=Debug -DINSTALL_DIR=${CMAKE_BINARY_DIR} -DINSTALL_SYSTEM_FILES=N ${CMAKE_SOURCE_DIR} + COMMAND make install + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CMAKE_SOURCE_DIR}/server/test/MaxScale_test.cnf ${CMAKE_BINARY_DIR}/etc/MaxScale.cnf + COMMAND /bin/sh -c "valgrind --track-fds=yes --leak-check=full --show-leak-kinds=all --log-file=${CMAKE_BINARY_DIR}/valgrind.log ${CMAKE_BINARY_DIR}/bin/maxscale -c ${CMAKE_BINARY_DIR} &>/dev/null" + COMMAND /bin/sh -c "make test || echo \"Test results written to: ${CMAKE_BINARY_DIR}/Testing/Temporary/\"" + COMMAND killall maxscale + COMMENT "Running full test suite with Valgrind..." VERBATIM) + +endif() \ No newline at end of file diff --git a/FindValgrind.cmake b/FindValgrind.cmake new file mode 100644 index 000000000..977d0028a --- /dev/null +++ b/FindValgrind.cmake @@ -0,0 +1,13 @@ +# This CMake file tries to find the Valgrind executable +# The following variables are set: +# VALGRIND_FOUND - System has Valgrind +# VALGRIND_EXECUTABLE - The Valgrind executable file +find_program(VALGRIND_EXECUTABLE valgrind) +if(VALGRIND_EXECUTABLE STREQUAL "VALGRIND_EXECUTABLE-NOTFOUND") + message(STATUS "Valgrind not found.") + set(VALGRIND_FOUND FALSE CACHE INTERNAL "") + unset(VALGRIND_EXECUTABLE) +else() + message(STATUS "Valgrind found: ${VALGRIND_EXECUTABLE}") + set(VALGRIND_FOUND TRUE CACHE INTERNAL "") +endif() \ No newline at end of file From a96f2fe57bc211717425606b8b1adc9e25588388 Mon Sep 17 00:00:00 2001 From: Markus Makela Date: Mon, 17 Nov 2014 12:52:21 +0200 Subject: [PATCH 18/18] Fixed some typos and testing messages from CMake output. --- CMakeLists.txt | 3 +-- server/modules/routing/readwritesplit/test/CMakeLists.txt | 2 +- server/modules/routing/test/CMakeLists.txt | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f2990397..8ef2ec311 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,9 +8,8 @@ set_maxscale_version() set(CMAKE_INSTALL_PREFIX "${INSTALL_DIR}" CACHE INTERNAL "Prefix prepended to install directories." FORCE) -message(STATUS "$CMAKE_MODULE_PATH:${CMAKE_MODULE_PATH}") + set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/") -message(STATUS "CMAKE_MODULE_PATH:${CMAKE_MODULE_PATH}") find_package(Valgrind) project(MaxScale) diff --git a/server/modules/routing/readwritesplit/test/CMakeLists.txt b/server/modules/routing/readwritesplit/test/CMakeLists.txt index d2af1e9b4..7e3505dda 100644 --- a/server/modules/routing/readwritesplit/test/CMakeLists.txt +++ b/server/modules/routing/readwritesplit/test/CMakeLists.txt @@ -1,7 +1,7 @@ add_test(NAME ReadWriteSplitTest COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/rwsplit.sh testrwsplit.log ${TEST_HOST} ${TEST_PORT_RW} ${TEST_MASTER_ID} ${TEST_USER} ${TEST_PASSWORD} ${CMAKE_CURRENT_SOURCE_DIR}) if(MYSQLCLIENT_FOUND) - add_test(NAME ReadWriteSplitLoginTest COMMAND $ 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT_RW} 1.10) + add_test(NAME ReadWriteSplitAuthTest COMMAND $ 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT_RW} 1.10) endif() add_subdirectory(test_hints) \ No newline at end of file diff --git a/server/modules/routing/test/CMakeLists.txt b/server/modules/routing/test/CMakeLists.txt index 2eedb914d..19c51b4a1 100644 --- a/server/modules/routing/test/CMakeLists.txt +++ b/server/modules/routing/test/CMakeLists.txt @@ -2,5 +2,5 @@ if(MYSQLCLIENT_FOUND) add_executable(testconnect testconnect.c) message(STATUS "Linking against: ${MYSQLCLIENT_LIBRARIES}") target_link_libraries(testconnect ${MYSQLCLIENT_LIBRARIES} ssl crypto dl z m rt pthread) - add_test(NAME ReadConnRouterLoginTest COMMAND $ 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT} 1.10) + add_test(NAME ReadConnRouterAuthTest COMMAND $ 10000 ${TEST_HOST} ${MASTER_PORT} ${TEST_HOST} ${TEST_PORT} 1.10) endif() \ No newline at end of file