summaryrefslogtreecommitdiff
path: root/sql-common
diff options
context:
space:
mode:
authorIngo Struewing <ingo.struewing@sun.com>2009-09-30 12:28:15 +0200
committerIngo Struewing <ingo.struewing@sun.com>2009-09-30 12:28:15 +0200
commit7dacca0ff0a3c28401500afc807b71ebe9e4594f (patch)
tree91aecbc0399d11fa12af389463f5732e9d27d62b /sql-common
parentccae404afaddf98c16889fdd21c613b551ee5e5e (diff)
downloadmariadb-git-7dacca0ff0a3c28401500afc807b71ebe9e4594f.tar.gz
Bug#37267 - connect() EINPROGRESS failures mishandled in client library
We cann connect() in a non-blocking mode to be able to specify a non-standard timeout. The problem was that we did not fetch the status from the non-blocking connect(). We assumed that poll() would not return a POLLIN flag if the connect failed. But on some platforms this is not true. After a successful poll() we do now retrieve the status value from connect() with getsockopt(...SO_ERROR...). Now we do know if (and how) the connect failed. The test case for my investigation was rpl.rlp_ssl1 on an Ubuntu 9.04 x86_64 machine. Both, IPV4 and IPV6 were active. 'localhost' resolved first for IPV6 and then for IPV4. The connection over IPV6 was blocked. rpl.rlp_ssl1 timed out as it did not notice the failed connect(). The first read() failed, which was interpreted as a master crash and the connection was tried to reestablish with the same result until the retry limit was reached. With the fix, the connect() problem is immediately recognized, and the connect() is retried on the second resolution for 'localhost', which is successful. libmysqld/libmysqld.c: Bug#37267 - connect() EINPROGRESS failures mishandled in client library Changed a DBUG print string to distinguish the two mysql_real_connect() implementations in DBUG traces. mysql-test/include/wait_for_slave_param.inc: Bug#37267 - connect() EINPROGRESS failures mishandled in client library Made timeout value available in error message. mysql-test/suite/rpl/r/rpl_get_master_version_and_clock.result: Bug#37267 - connect() EINPROGRESS failures mishandled in client library Fixed test result. Connect error is now detected as CR_CONN_HOST_ERROR (2003) instead of CR_SERVER_LOST (2013). sql-common/client.c: Bug#37267 - connect() EINPROGRESS failures mishandled in client library Added retrieval of the error code from the non-blocking connect(). Added DBUG. Added comment.
Diffstat (limited to 'sql-common')
-rw-r--r--sql-common/client.c77
1 files changed, 62 insertions, 15 deletions
diff --git a/sql-common/client.c b/sql-common/client.c
index 84029b449af..1c0262f3414 100644
--- a/sql-common/client.c
+++ b/sql-common/client.c
@@ -145,9 +145,12 @@ int my_connect(my_socket fd, const struct sockaddr *name, uint namelen,
uint timeout)
{
#if defined(__WIN__) || defined(__NETWARE__)
- return connect(fd, (struct sockaddr*) name, namelen);
+ DBUG_ENTER("my_connect");
+ DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen));
#else
int flags, res, s_err;
+ DBUG_ENTER("my_connect");
+ DBUG_PRINT("enter", ("fd: %d timeout: %u", fd, timeout));
/*
If they passed us a timeout of zero, we should behave
@@ -155,24 +158,26 @@ int my_connect(my_socket fd, const struct sockaddr *name, uint namelen,
*/
if (timeout == 0)
- return connect(fd, (struct sockaddr*) name, namelen);
+ DBUG_RETURN(connect(fd, (struct sockaddr*) name, namelen));
flags = fcntl(fd, F_GETFL, 0); /* Set socket to not block */
#ifdef O_NONBLOCK
fcntl(fd, F_SETFL, flags | O_NONBLOCK); /* and save the flags.. */
#endif
+ DBUG_PRINT("info", ("connecting non-blocking"));
res= connect(fd, (struct sockaddr*) name, namelen);
+ DBUG_PRINT("info", ("connect result: %d errno: %d", res, errno));
s_err= errno; /* Save the error... */
fcntl(fd, F_SETFL, flags);
if ((res != 0) && (s_err != EINPROGRESS))
{
errno= s_err; /* Restore it */
- return(-1);
+ DBUG_RETURN(-1);
}
if (res == 0) /* Connected quickly! */
- return(0);
- return wait_for_data(fd, timeout);
+ DBUG_RETURN(0);
+ DBUG_RETURN(wait_for_data(fd, timeout));
#endif
}
@@ -191,26 +196,58 @@ static int wait_for_data(my_socket fd, uint timeout)
#ifdef HAVE_POLL
struct pollfd ufds;
int res;
+ DBUG_ENTER("wait_for_data");
+ DBUG_PRINT("info", ("polling"));
ufds.fd= fd;
ufds.events= POLLIN | POLLPRI;
if (!(res= poll(&ufds, 1, (int) timeout*1000)))
{
+ DBUG_PRINT("info", ("poll timed out"));
errno= EINTR;
- return -1;
+ DBUG_RETURN(-1);
}
+ DBUG_PRINT("info",
+ ("poll result: %d errno: %d revents: 0x%02d events: 0x%02d",
+ res, errno, ufds.revents, ufds.events));
if (res < 0 || !(ufds.revents & (POLLIN | POLLPRI)))
- return -1;
- return 0;
+ DBUG_RETURN(-1);
+ /*
+ At this point, we know that something happened on the socket.
+ But this does not means that everything is alright.
+ The connect might have failed. We need to retrieve the error code
+ from the socket layer. We must return success only if we are sure
+ that it was really a success. Otherwise we might prevent the caller
+ from trying another address to connect to.
+ */
+ {
+ int s_err;
+ socklen_t s_len= sizeof(s_err);
+
+ DBUG_PRINT("info", ("Get SO_ERROR from non-blocked connected socket."));
+ res= getsockopt(fd, SOL_SOCKET, SO_ERROR, &s_err, &s_len);
+ DBUG_PRINT("info", ("getsockopt res: %d s_err: %d", res, s_err));
+ if (res)
+ DBUG_RETURN(res);
+ /* getsockopt() was successful, check the retrieved status value. */
+ if (s_err)
+ {
+ errno= s_err;
+ DBUG_RETURN(-1);
+ }
+ /* Status from connect() is zero. Socket is successfully connected. */
+ }
+ DBUG_RETURN(0);
#else
SOCKOPT_OPTLEN_TYPE s_err_size = sizeof(uint);
fd_set sfds;
struct timeval tv;
time_t start_time, now_time;
int res, s_err;
+ DBUG_ENTER("wait_for_data");
if (fd >= FD_SETSIZE) /* Check if wrong error */
- return 0; /* Can't use timeout */
+ DBUG_RETURN(0); /* Can't use timeout */
/*
Our connection is "in progress." We can use the select() call to wait
@@ -250,11 +287,11 @@ static int wait_for_data(my_socket fd, uint timeout)
break;
#endif
if (res == 0) /* timeout */
- return -1;
+ DBUG_RETURN(-1);
now_time= my_time(0);
timeout-= (uint) (now_time - start_time);
if (errno != EINTR || (int) timeout <= 0)
- return -1;
+ DBUG_RETURN(-1);
}
/*
@@ -265,14 +302,14 @@ static int wait_for_data(my_socket fd, uint timeout)
s_err=0;
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*) &s_err, &s_err_size) != 0)
- return(-1);
+ DBUG_RETURN(-1);
if (s_err)
{ /* getsockopt could succeed */
errno = s_err;
- return(-1); /* but return an error... */
+ DBUG_RETURN(-1); /* but return an error... */
}
- return (0); /* ok */
+ DBUG_RETURN(0); /* ok */
#endif /* HAVE_POLL */
}
#endif /* defined(__WIN__) || defined(__NETWARE__) */
@@ -1877,7 +1914,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
init_sigpipe_variables
DBUG_ENTER("mysql_real_connect");
- DBUG_PRINT("enter",("host: %s db: %s user: %s",
+ DBUG_PRINT("enter",("host: %s db: %s user: %s (client)",
host ? host : "(Null)",
db ? db : "(Null)",
user ? user : "(Null)"));
@@ -1927,6 +1964,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
unix_socket=mysql->options.unix_socket;
mysql->server_status=SERVER_STATUS_AUTOCOMMIT;
+ DBUG_PRINT("info", ("Connecting"));
/*
Part 0: Grab a socket and connect it to the server
@@ -1936,6 +1974,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
mysql->options.protocol == MYSQL_PROTOCOL_MEMORY) &&
(!host || !strcmp(host,LOCAL_HOST)))
{
+ DBUG_PRINT("info", ("Using shared memory"));
if ((create_shared_memory(mysql,net, mysql->options.connect_timeout)) ==
INVALID_HANDLE_VALUE)
{
@@ -2034,6 +2073,8 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
}
}
#endif
+ DBUG_PRINT("info", ("net->vio: %p protocol: %d",
+ net->vio, mysql->options.protocol));
if (!net->vio &&
(!mysql->options.protocol ||
mysql->options.protocol == MYSQL_PROTOCOL_TCP))
@@ -2105,6 +2146,11 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
min(sizeof(sock_addr.sin_addr), (size_t) hp->h_length));
DBUG_PRINT("info",("Trying %s...",
(my_inet_ntoa(sock_addr.sin_addr, ipaddr), ipaddr)));
+ /*
+ Here we rely on my_connect() to return success only if the
+ connect attempt was really successful. Otherwise we would stop
+ trying another address, believing we were successful.
+ */
status= my_connect(sock, (struct sockaddr *) &sock_addr,
sizeof(sock_addr), mysql->options.connect_timeout);
}
@@ -2163,6 +2209,7 @@ CLI_MYSQL_REAL_CONNECT(MYSQL *mysql,const char *host, const char *user,
/*
Part 1: Connection established, read and parse first packet
*/
+ DBUG_PRINT("info", ("Read first packet."));
if ((pkt_length=cli_safe_read(mysql)) == packet_error)
{