From a7c3cd5f30e60c501f862176a8ddde41f81ceb34 Mon Sep 17 00:00:00 2001 From: vraatikka Date: Tue, 24 Sep 2013 15:04:12 +0300 Subject: [PATCH] Related to bug #217, added command 'fail accept ' to debug client's commands. Reproduce #217 by connecting with telnet to debug client interface and execute command: fail accept 23 and try to execute read query, for example, with mysql -h 127.0.0.1 -P 4008 -u maxuser -pmaxpwd -e 'select current_user(), @@server_id' --- server/core/gateway.c | 2 + server/include/dcb.h | 2 + server/modules/protocol/mysql_client.c | 330 ++++++++++++------------- server/modules/routing/debugcmd.c | 83 ++++++- 4 files changed, 234 insertions(+), 183 deletions(-) diff --git a/server/core/gateway.c b/server/core/gateway.c index 04a6f54bc..b558dd0d4 100644 --- a/server/core/gateway.c +++ b/server/core/gateway.c @@ -216,6 +216,8 @@ memset(dcb_fake_write_errno, 0, sizeof(unsigned char)*1024); memset(dcb_fake_write_ev, 0, sizeof(__int32_t)*1024); fail_next_backend_fd = false; fail_next_client_fd = false; +fail_next_accept = false; +fail_accept_errno = 0; #endif l = atexit(skygw_logmanager_exit); diff --git a/server/include/dcb.h b/server/include/dcb.h index 8e73d3a7e..84725dcfc 100644 --- a/server/include/dcb.h +++ b/server/include/dcb.h @@ -190,6 +190,8 @@ unsigned char dcb_fake_write_errno[1024]; __int32_t dcb_fake_write_ev[1024]; bool fail_next_backend_fd; bool fail_next_client_fd; +bool fail_next_accept; +int fail_accept_errno; #endif /* A few useful macros */ diff --git a/server/modules/protocol/mysql_client.c b/server/modules/protocol/mysql_client.c index 75811e68c..8d7a3e57d 100644 --- a/server/modules/protocol/mysql_client.c +++ b/server/modules/protocol/mysql_client.c @@ -127,7 +127,11 @@ mysql_send_ok(DCB *dcb, int packet_number, int in_affected_rows, const char* mys affected_rows = in_affected_rows; - mysql_payload_size = sizeof(field_count) + sizeof(affected_rows) + sizeof(insert_id) + sizeof(mysql_server_status) + sizeof(mysql_warning_count); + mysql_payload_size = sizeof(field_count) + + sizeof(affected_rows) + + sizeof(insert_id) + + sizeof(mysql_server_status) + + sizeof(mysql_warning_count); if (mysql_message != NULL) { mysql_payload_size += strlen(mysql_message); @@ -513,7 +517,6 @@ int gw_read_client_event(DCB* dcb) { int rc = 0; CHK_DCB(dcb); - protocol = DCB_PROTOCOL(dcb, MySQLProtocol); CHK_PROTOCOL(protocol); /** @@ -611,7 +614,6 @@ int gw_read_client_event(DCB* dcb) { * Read all the data that is available into a chain of buffers */ { - /* int len; */ GWBUF *queue = NULL; GWBUF *gw_buffer = NULL; uint8_t *ptr_buff = NULL; @@ -639,34 +641,20 @@ int gw_read_client_event(DCB* dcb) { /* Now, we are assuming in the first buffer there is * the information form mysql command */ queue = gw_buffer; - /* len = GWBUF_LENGTH(queue); */ ptr_buff = GWBUF_DATA(queue); /* get mysql commang at fifth byte */ if (ptr_buff) { mysql_command = ptr_buff[4]; } - - if (mysql_command == '\x03') { - /** - * SQL Trace here. - * Length can be calculated and it must be passed as - * argument. - */ - /* this is a standard MySQL query !!!! */ - } /** - * Routing Client input to Backend + * Without rsession there is no access to backend. + * COM_QUIT : close client dcb + * else : write custom error to client dcb. */ - /* Do not route the query without session! */ if(rsession == NULL) { + /** COM_QUIT */ if (mysql_command == '\x01') { - /** - * COM_QUIT handling - * - * fprintf(stderr, "COM_QUIT received with - * no connected backends from %i\n", dcb->fd); - */ skygw_log_write_flush( LOGFILE_DEBUG, "%lu [gw_read_client_event] Client read " @@ -682,63 +670,52 @@ int gw_read_client_event(DCB* dcb) { dcb, 1, 0, - "Connection to backend lost"); + "Query routing failed. Connection to " + "backend lost"); protocol->state = MYSQL_IDLE; } rc = 1; goto return_rc; } - /* We can route the query */ - /* COM_QUIT handling */ + /** Route COM_QUIT to backend */ if (mysql_command == '\x01') { - skygw_log_write_flush( - LOGFILE_DEBUG, - "%lu [gw_read_client_event] Before routeQuery. " - "dcb %p.", - pthread_self(), - dcb); - /** - * fprintf(stderr, "COM_QUIT received from %i and - * passed to backed\n", dcb->fd); - * this will propagate COM_QUIT to backend(s) - * fprintf(stderr, "<<< Routing the COM_QUIT ...\n"); - */ router->routeQuery(router_instance, rsession, queue); skygw_log_write_flush( LOGFILE_DEBUG, - "%lu [gw_read_client_event] After routeQuery. " - "dcb %p.", + "%lu [gw_read_client_event] Routed COM_QUIT to " + "backend. Close client dcb %p", pthread_self(), dcb); - /* close client connection */ - (dcb->func).close(dcb); + /** close client connection */ + + (dcb->func).close(dcb); rc = 1; - goto return_rc; } - - /* MySQL Command Routing */ - protocol->state = MYSQL_ROUTING; - - /* writing in the backend buffer queue, via routeQuery */ - //fprintf(stderr, "<<< Routing the Query ...\n"); - rc = router->routeQuery(router_instance, rsession, queue); - - if (rc == 1) { - protocol->state = MYSQL_WAITING_RESULT; - } else { - mysql_send_custom_error(dcb, - 1, - 0, - "Connection to backend lost."); - protocol->state = MYSQL_IDLE; - goto return_rc; + else + { + /** Route other commands to backend */ + protocol->state = MYSQL_ROUTING; + rc = router->routeQuery(router_instance, rsession, queue); + /** succeed */ + if (rc == 1) { + protocol->state = MYSQL_WAITING_RESULT; + rc = 0; /**< here '0' means success */ + } else { + mysql_send_custom_error(dcb, + 1, + 0, + "Query routing failed. " + "Connection to backend " + "lost."); + protocol->state = MYSQL_IDLE; + } } - } + goto return_rc; + } /* MYSQL_IDLE, MYSQL_WAITING_RESULT */ break; default: - // todo break; } rc = 0; @@ -950,75 +927,84 @@ int gw_MySQLAccept(DCB *listener) &addrlen); eno = errno; - - if (c_sock == -1) { +#if defined(SS_DEBUG) + if (fail_next_accept) { + c_sock = -1; + eno = fail_accept_errno; + i = 10; + fail_next_accept = false; + fail_accept_errno = 0; + } +#endif /* SS_DEBUG */ + + if (c_sock == -1) { + + if (eno == EAGAIN || + eno == EWOULDBLOCK) + { + rc = 1; + /* We have processed all incoming connections. */ + break; + } + else if (eno == ENFILE) + { + /** + * Exceeded system's max. number of files limit. + */ + skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [gw_MySQLAccept] Error %d, %s.", + pthread_self(), + eno, + strerror(eno)); + i++; + usleep(100*i*i); + + if (i<10) { + goto retry_accept; + } + rc = 1; + goto return_rc; + } + else if (eno == EMFILE) + { + /** + * Exceeded processes max. number of files limit. + */ + skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [gw_MySQLAccept] Error %d, %s.", + pthread_self(), + eno, + strerror(eno)); + i++; + usleep(100*i*i); - if (eno == EAGAIN || - eno == EWOULDBLOCK) - { - rc = 1; - /* We have processed all incoming connections. */ - break; - } - else if (eno == ENFILE) - { - /** - * Exceeded system's max. number of files limit. - */ - skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_MySQLAccept] Error %d, %s.", - pthread_self(), - eno, - strerror(eno)); - i++; - usleep(100*i*i); - - if (i<10) { - goto retry_accept; - } - rc = 1; - goto return_rc; - } - else if (eno == EMFILE) - { - /** - * Exceeded processes max. number of files limit. - */ - skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_MySQLAccept] Error %d, %s.", - pthread_self(), - eno, - strerror(eno)); - i++; - usleep(100*i*i); - - if (i<10) { - goto retry_accept; - } - rc = 1; - goto return_rc; - } - else - { - /** - * Other error. - */ - skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_MySQLAccept] Error %d, %s.", - pthread_self(), - eno, - strerror(eno)); - ss_dassert(false); - break; - } /* if (eno == ..) */ + if (i<10) { + goto retry_accept; + } + rc = 1; + goto return_rc; + } + else + { + /** + * Other error. + */ + skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [gw_MySQLAccept] Error %d, %s.", + pthread_self(), + eno, + strerror(eno)); + ss_dassert(false); + break; + } /* if (eno == ..) */ } /* if (c_sock == -1) */ /* reset counter */ i = 0; - listener->stats.n_accepts++; + listener->stats.n_accepts++; #if defined(SS_DEBUG) skygw_log_write_flush( LOGFILE_TRACE, @@ -1027,20 +1013,20 @@ int gw_MySQLAccept(DCB *listener) c_sock); conn_open[c_sock] = true; #endif - fprintf(stderr, + fprintf(stderr, "Processing %i connection fd %i for listener %i\n", listener->stats.n_accepts, c_sock, listener->fd); - // set nonblocking + // set nonblocking - setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen); - setnonblocking(c_sock); + setsockopt(c_sock, SOL_SOCKET, SO_SNDBUF, &sendbuf, optlen); + setnonblocking(c_sock); - client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); - client_dcb->service = listener->session->service; - client_dcb->fd = c_sock; - client_dcb->remote = strdup(inet_ntoa(local.sin_addr)); + client_dcb = dcb_alloc(DCB_ROLE_REQUEST_HANDLER); + client_dcb->service = listener->session->service; + client_dcb->fd = c_sock; + client_dcb->remote = strdup(inet_ntoa(local.sin_addr)); protocol = mysql_protocol_init(client_dcb, c_sock); ss_dassert(protocol != NULL); @@ -1058,46 +1044,46 @@ int gw_MySQLAccept(DCB *listener) goto return_rc; } client_dcb->protocol = protocol; - // assign function poiters to "func" field - memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); - //send handshake to the client_dcb - MySQLSendHandshake(client_dcb); + // assign function poiters to "func" field + memcpy(&client_dcb->func, &MyObject, sizeof(GWPROTOCOL)); + //send handshake to the client_dcb + MySQLSendHandshake(client_dcb); - // client protocol state change - protocol->state = MYSQL_AUTH_SENT; + // client protocol state change + protocol->state = MYSQL_AUTH_SENT; - /** - * Set new descriptor to event set. At the same time, - * change state to DCB_STATE_POLLING so that - * thread which wakes up sees correct state. - */ - if (poll_add_dcb(client_dcb) == -1) - { - /** delete client_dcb */ - dcb_close(client_dcb); + /** + * Set new descriptor to event set. At the same time, + * change state to DCB_STATE_POLLING so that + * thread which wakes up sees correct state. + */ + if (poll_add_dcb(client_dcb) == -1) + { + /** delete client_dcb */ + dcb_close(client_dcb); - /** Previous state is recovered in poll_add_dcb. */ - skygw_log_write_flush( - LOGFILE_ERROR, - "%lu [gw_MySQLAccept] Failed to add dcb %p for " - "fd %d to epoll set.", - pthread_self(), - client_dcb, - client_dcb->fd); - rc = 1; - goto return_rc; - } - else - { - skygw_log_write( - LOGFILE_TRACE, - "%lu [gw_MySQLAccept] Added dcb %p for fd " - "%d to epoll set.", - pthread_self(), - client_dcb, - client_dcb->fd); - } - } /**< while 1 */ + /** Previous state is recovered in poll_add_dcb. */ + skygw_log_write_flush( + LOGFILE_ERROR, + "%lu [gw_MySQLAccept] Failed to add dcb %p for " + "fd %d to epoll set.", + pthread_self(), + client_dcb, + client_dcb->fd); + rc = 1; + goto return_rc; + } + else + { + skygw_log_write( + LOGFILE_TRACE, + "%lu [gw_MySQLAccept] Added dcb %p for fd " + "%d to epoll set.", + pthread_self(), + client_dcb, + client_dcb->fd); + } + } /**< while 1 */ #if defined(SS_DEBUG) if (rc == 0) { CHK_DCB(client_dcb); @@ -1105,9 +1091,9 @@ int gw_MySQLAccept(DCB *listener) CHK_PROTOCOL(protocol); } #endif -return_rc: - return rc; -} + return_rc: + return rc; + } /* */ diff --git a/server/modules/routing/debugcmd.c b/server/modules/routing/debugcmd.c index c06c24e3f..348dec469 100644 --- a/server/modules/routing/debugcmd.c +++ b/server/modules/routing/debugcmd.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -189,10 +190,21 @@ static void disable_log_action(DCB *, char *); * * The subcommands of the enable command * */ struct subcommand enableoptions[] = { - { "log", 1, enable_log_action, "Enable Log options for MaxScale, options trace | error | message E.g. enable log message.", - {ARG_TYPE_STRING, 0, 0} }, - { NULL, 0, NULL, NULL, - {0, 0, 0} } + { + "log", + 1, + enable_log_action, + "Enable Log options for MaxScale, options trace | error | " + "message E.g. enable log message.", + {ARG_TYPE_STRING, 0, 0} + }, + { + NULL, + 0, + NULL, + NULL, + {0, 0, 0} + } }; @@ -200,17 +212,28 @@ struct subcommand enableoptions[] = { * * The subcommands of the disable command * */ struct subcommand disableoptions[] = { - { "log", 1, disable_log_action, "Disable Log for MaxScale, Options: trace | error | message E.g. disable log trace", - {ARG_TYPE_STRING, 0, 0} }, - { NULL, 0, NULL, NULL, - {0, 0, 0} } + { + "log", + 1, + disable_log_action, + "Disable Log for MaxScale, Options: trace | error | message E.g. " + "disable log trace", + {ARG_TYPE_STRING, 0, 0} + }, + { + NULL, + 0, + NULL, + NULL, + {0, 0, 0} + } }; #if defined(SS_DEBUG) static void fail_backendfd(void); static void fail_clientfd(void); - +static void fail_accept(DCB* dcb, char* arg1); /** * * The subcommands of the fail command * */ @@ -229,6 +252,13 @@ struct subcommand failoptions[] = { "Fail client socket for next operation.", {ARG_TYPE_STRING, 0, 0} }, + { + "accept", + 1, + fail_accept, + "Fail to accept next client connection.", + {ARG_TYPE_STRING, 0, 0} + }, { NULL, 0, @@ -735,11 +765,42 @@ static void disable_log_action(DCB *dcb, char *arg1) { #if defined(SS_DEBUG) static void fail_backendfd(void) { - fail_next_backend_fd = TRUE; + fail_next_backend_fd = true; } static void fail_clientfd(void) { - fail_next_client_fd = TRUE; + fail_next_client_fd = true; +} + +static void fail_accept( + DCB* dcb, + char* arg1) +{ + fail_accept_errno = atoi(arg1); + + switch(fail_accept_errno) { + case EAGAIN: +// case EWOULDBLOCK: + case EBADF: + case EINTR: + case EINVAL: + case EMFILE: + case ENFILE: + case ENOTSOCK: + case EOPNOTSUPP: + case ENOBUFS: + case ENOMEM: + case EPROTO: + fail_next_accept = true; + break; + + default: + dcb_printf(dcb, + "[%d, %s] is not valid errno for accept.\n", + fail_accept_errno, + strerror(fail_accept_errno)); + return ; + } } #endif