mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 10:14:19 +01:00
6bdc288ff2
Original changeset: ------------------------------------------------------------ revno: 2626 committer: davi@mysql.com/endora.local timestamp: Wed 2008-04-23 09:33:25 -0300 message: Fix for main.ssl and main.ssl_compress test case failures under pool-of-threads. The problem is that the SSL layer has a read buffer and might read more data than requested by the VIO layer. The SSL layer empties the socket buffer which causes the socket to not be signaled for IO if the client is waiting for a command which is sitting in the read buffer. The solution is to retrieve from the transport layer the number of bytes waiting in the read buffer. The data in the read buffer needs to be processed before waiting for more data.
881 lines
21 KiB
C
881 lines
21 KiB
C
/* Copyright (C) 2000 MySQL AB
|
|
|
|
This program 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 of the License.
|
|
|
|
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
|
|
|
|
/*
|
|
Note that we can't have assertion on file descriptors; The reason for
|
|
this is that during mysql shutdown, another thread can close a file
|
|
we are working on. In this case we should just return read errors from
|
|
the file descriptior.
|
|
*/
|
|
|
|
#include "vio_priv.h"
|
|
|
|
int vio_errno(Vio *vio __attribute__((unused)))
|
|
{
|
|
return socket_errno; /* On Win32 this mapped to WSAGetLastError() */
|
|
}
|
|
|
|
|
|
size_t vio_read(Vio * vio, uchar* buf, size_t size)
|
|
{
|
|
size_t r;
|
|
DBUG_ENTER("vio_read");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
|
|
(uint) size));
|
|
|
|
/* Ensure nobody uses vio_read_buff and vio_read simultaneously */
|
|
DBUG_ASSERT(vio->read_end == vio->read_pos);
|
|
#ifdef __WIN__
|
|
r = recv(vio->sd, buf, size,0);
|
|
#else
|
|
errno=0; /* For linux */
|
|
r = read(vio->sd, buf, size);
|
|
#endif /* __WIN__ */
|
|
#ifndef DBUG_OFF
|
|
if (r == (size_t) -1)
|
|
{
|
|
DBUG_PRINT("vio_error", ("Got error %d during read",errno));
|
|
}
|
|
#endif /* DBUG_OFF */
|
|
DBUG_PRINT("exit", ("%ld", (long) r));
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
|
|
/*
|
|
Buffered read: if average read size is small it may
|
|
reduce number of syscalls.
|
|
*/
|
|
|
|
size_t vio_read_buff(Vio *vio, uchar* buf, size_t size)
|
|
{
|
|
size_t rc;
|
|
#define VIO_UNBUFFERED_READ_MIN_SIZE 2048
|
|
DBUG_ENTER("vio_read_buff");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
|
|
(uint) size));
|
|
|
|
if (vio->read_pos < vio->read_end)
|
|
{
|
|
rc= min((size_t) (vio->read_end - vio->read_pos), size);
|
|
memcpy(buf, vio->read_pos, rc);
|
|
vio->read_pos+= rc;
|
|
/*
|
|
Do not try to read from the socket now even if rc < size:
|
|
vio_read can return -1 due to an error or non-blocking mode, and
|
|
the safest way to handle it is to move to a separate branch.
|
|
*/
|
|
}
|
|
else if (size < VIO_UNBUFFERED_READ_MIN_SIZE)
|
|
{
|
|
rc= vio_read(vio, (uchar*) vio->read_buffer, VIO_READ_BUFFER_SIZE);
|
|
if (rc != 0 && rc != (size_t) -1)
|
|
{
|
|
if (rc > size)
|
|
{
|
|
vio->read_pos= vio->read_buffer + size;
|
|
vio->read_end= vio->read_buffer + rc;
|
|
rc= size;
|
|
}
|
|
memcpy(buf, vio->read_buffer, rc);
|
|
}
|
|
}
|
|
else
|
|
rc= vio_read(vio, buf, size);
|
|
DBUG_RETURN(rc);
|
|
#undef VIO_UNBUFFERED_READ_MIN_SIZE
|
|
}
|
|
|
|
|
|
size_t vio_write(Vio * vio, const uchar* buf, size_t size)
|
|
{
|
|
size_t r;
|
|
DBUG_ENTER("vio_write");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
|
|
(uint) size));
|
|
#ifdef __WIN__
|
|
r = send(vio->sd, buf, size,0);
|
|
#else
|
|
r = write(vio->sd, buf, size);
|
|
#endif /* __WIN__ */
|
|
#ifndef DBUG_OFF
|
|
if (r == (size_t) -1)
|
|
{
|
|
DBUG_PRINT("vio_error", ("Got error on write: %d",socket_errno));
|
|
}
|
|
#endif /* DBUG_OFF */
|
|
DBUG_PRINT("exit", ("%u", (uint) r));
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
int vio_blocking(Vio * vio __attribute__((unused)), my_bool set_blocking_mode,
|
|
my_bool *old_mode)
|
|
{
|
|
int r=0;
|
|
DBUG_ENTER("vio_blocking");
|
|
|
|
*old_mode= test(!(vio->fcntl_mode & O_NONBLOCK));
|
|
DBUG_PRINT("enter", ("set_blocking_mode: %d old_mode: %d",
|
|
(int) set_blocking_mode, (int) *old_mode));
|
|
|
|
#if !defined(__WIN__)
|
|
#if !defined(NO_FCNTL_NONBLOCK)
|
|
if (vio->sd >= 0)
|
|
{
|
|
int old_fcntl=vio->fcntl_mode;
|
|
if (set_blocking_mode)
|
|
vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */
|
|
else
|
|
vio->fcntl_mode |= O_NONBLOCK; /* set bit */
|
|
if (old_fcntl != vio->fcntl_mode)
|
|
{
|
|
r= fcntl(vio->sd, F_SETFL, vio->fcntl_mode);
|
|
if (r == -1)
|
|
{
|
|
DBUG_PRINT("info", ("fcntl failed, errno %d", errno));
|
|
vio->fcntl_mode= old_fcntl;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
r= set_blocking_mode ? 0 : 1;
|
|
#endif /* !defined(NO_FCNTL_NONBLOCK) */
|
|
#else /* !defined(__WIN__) */
|
|
if (vio->type != VIO_TYPE_NAMEDPIPE && vio->type != VIO_TYPE_SHARED_MEMORY)
|
|
{
|
|
ulong arg;
|
|
int old_fcntl=vio->fcntl_mode;
|
|
if (set_blocking_mode)
|
|
{
|
|
arg = 0;
|
|
vio->fcntl_mode &= ~O_NONBLOCK; /* clear bit */
|
|
}
|
|
else
|
|
{
|
|
arg = 1;
|
|
vio->fcntl_mode |= O_NONBLOCK; /* set bit */
|
|
}
|
|
if (old_fcntl != vio->fcntl_mode)
|
|
r = ioctlsocket(vio->sd,FIONBIO,(void*) &arg);
|
|
}
|
|
else
|
|
r= test(!(vio->fcntl_mode & O_NONBLOCK)) != set_blocking_mode;
|
|
#endif /* !defined(__WIN__) */
|
|
DBUG_PRINT("exit", ("%d", r));
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
my_bool
|
|
vio_is_blocking(Vio * vio)
|
|
{
|
|
my_bool r;
|
|
DBUG_ENTER("vio_is_blocking");
|
|
r = !(vio->fcntl_mode & O_NONBLOCK);
|
|
DBUG_PRINT("exit", ("%d", (int) r));
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
|
|
int vio_fastsend(Vio * vio __attribute__((unused)))
|
|
{
|
|
int r=0;
|
|
DBUG_ENTER("vio_fastsend");
|
|
|
|
#if defined(IPTOS_THROUGHPUT)
|
|
{
|
|
int tos = IPTOS_THROUGHPUT;
|
|
r= setsockopt(vio->sd, IPPROTO_IP, IP_TOS, (void *) &tos, sizeof(tos));
|
|
}
|
|
#endif /* IPTOS_THROUGHPUT */
|
|
if (!r)
|
|
{
|
|
#ifdef __WIN__
|
|
BOOL nodelay= 1;
|
|
#else
|
|
int nodelay = 1;
|
|
#endif
|
|
|
|
r= setsockopt(vio->sd, IPPROTO_TCP, TCP_NODELAY,
|
|
IF_WIN(const char*, void*) &nodelay,
|
|
sizeof(nodelay));
|
|
|
|
}
|
|
if (r)
|
|
{
|
|
DBUG_PRINT("warning", ("Couldn't set socket option for fast send"));
|
|
r= -1;
|
|
}
|
|
DBUG_PRINT("exit", ("%d", r));
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
int vio_keepalive(Vio* vio, my_bool set_keep_alive)
|
|
{
|
|
int r=0;
|
|
uint opt = 0;
|
|
DBUG_ENTER("vio_keepalive");
|
|
DBUG_PRINT("enter", ("sd: %d set_keep_alive: %d", vio->sd, (int)
|
|
set_keep_alive));
|
|
if (vio->type != VIO_TYPE_NAMEDPIPE)
|
|
{
|
|
if (set_keep_alive)
|
|
opt = 1;
|
|
r = setsockopt(vio->sd, SOL_SOCKET, SO_KEEPALIVE, (char *) &opt,
|
|
sizeof(opt));
|
|
}
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
|
|
my_bool
|
|
vio_should_retry(Vio * vio __attribute__((unused)))
|
|
{
|
|
int en = socket_errno;
|
|
return (en == SOCKET_EAGAIN || en == SOCKET_EINTR ||
|
|
en == SOCKET_EWOULDBLOCK);
|
|
}
|
|
|
|
|
|
my_bool
|
|
vio_was_interrupted(Vio *vio __attribute__((unused)))
|
|
{
|
|
int en= socket_errno;
|
|
return (en == SOCKET_EAGAIN || en == SOCKET_EINTR ||
|
|
en == SOCKET_EWOULDBLOCK || en == SOCKET_ETIMEDOUT);
|
|
}
|
|
|
|
|
|
int vio_close(Vio * vio)
|
|
{
|
|
int r=0;
|
|
DBUG_ENTER("vio_close");
|
|
|
|
if (vio->type != VIO_CLOSED)
|
|
{
|
|
DBUG_ASSERT(vio->type == VIO_TYPE_TCPIP ||
|
|
vio->type == VIO_TYPE_SOCKET ||
|
|
vio->type == VIO_TYPE_SSL);
|
|
|
|
DBUG_ASSERT(vio->sd >= 0);
|
|
if (shutdown(vio->sd, SHUT_RDWR))
|
|
r= -1;
|
|
if (closesocket(vio->sd))
|
|
r= -1;
|
|
}
|
|
if (r)
|
|
{
|
|
DBUG_PRINT("vio_error", ("close() failed, error: %d",socket_errno));
|
|
/* FIXME: error handling (not critical for MySQL) */
|
|
}
|
|
vio->type= VIO_CLOSED;
|
|
vio->sd= -1;
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
|
|
const char *vio_description(Vio * vio)
|
|
{
|
|
return vio->desc;
|
|
}
|
|
|
|
enum enum_vio_type vio_type(Vio* vio)
|
|
{
|
|
return vio->type;
|
|
}
|
|
|
|
my_socket vio_fd(Vio* vio)
|
|
{
|
|
return vio->sd;
|
|
}
|
|
|
|
|
|
my_bool vio_peer_addr(Vio * vio, char *buf, uint16 *port)
|
|
{
|
|
DBUG_ENTER("vio_peer_addr");
|
|
DBUG_PRINT("enter", ("sd: %d", vio->sd));
|
|
if (vio->localhost)
|
|
{
|
|
strmov(buf,"127.0.0.1");
|
|
*port= 0;
|
|
}
|
|
else
|
|
{
|
|
size_socket addrLen = sizeof(vio->remote);
|
|
if (getpeername(vio->sd, (struct sockaddr *) (&vio->remote),
|
|
&addrLen) != 0)
|
|
{
|
|
DBUG_PRINT("exit", ("getpeername gave error: %d", socket_errno));
|
|
DBUG_RETURN(1);
|
|
}
|
|
my_inet_ntoa(vio->remote.sin_addr,buf);
|
|
*port= ntohs(vio->remote.sin_port);
|
|
}
|
|
DBUG_PRINT("exit", ("addr: %s", buf));
|
|
DBUG_RETURN(0);
|
|
}
|
|
|
|
|
|
/*
|
|
Get in_addr for a TCP/IP connection
|
|
|
|
SYNOPSIS
|
|
vio_in_addr()
|
|
vio vio handle
|
|
in put in_addr here
|
|
|
|
NOTES
|
|
one must call vio_peer_addr() before calling this one
|
|
*/
|
|
|
|
void vio_in_addr(Vio *vio, struct in_addr *in)
|
|
{
|
|
DBUG_ENTER("vio_in_addr");
|
|
if (vio->localhost)
|
|
bzero((char*) in, sizeof(*in));
|
|
else
|
|
*in=vio->remote.sin_addr;
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
/**
|
|
Indicate whether there is data to read on a given socket.
|
|
|
|
@note An exceptional condition event and/or errors are
|
|
interpreted as if there is data to read.
|
|
|
|
@param sd A connected socket.
|
|
@param timeout Maximum time in seconds to poll.
|
|
|
|
@retval FALSE There is data to read.
|
|
@retval TRUE There is no data to read.
|
|
*/
|
|
|
|
static my_bool socket_poll_read(my_socket sd, uint timeout)
|
|
{
|
|
#ifdef __WIN__
|
|
int res;
|
|
my_socket fd= sd;
|
|
fd_set readfds, errorfds;
|
|
struct timeval tm;
|
|
DBUG_ENTER("socket_poll_read");
|
|
tm.tv_sec= timeout;
|
|
tm.tv_usec= 0;
|
|
FD_ZERO(&readfds);
|
|
FD_ZERO(&errorfds);
|
|
FD_SET(fd, &readfds);
|
|
FD_SET(fd, &errorfds);
|
|
if ((res= select(fd, &readfds, NULL, &errorfds, &tm) <= 0))
|
|
{
|
|
DBUG_RETURN(res < 0 ? 0 : 1);
|
|
}
|
|
res= FD_ISSET(fd, &readfds) || FD_ISSET(fd, &errorfds);
|
|
DBUG_RETURN(!res);
|
|
#elif defined(HAVE_POLL)
|
|
struct pollfd fds;
|
|
int res;
|
|
DBUG_ENTER("socket_poll_read");
|
|
fds.fd=sd;
|
|
fds.events=POLLIN;
|
|
fds.revents=0;
|
|
if ((res=poll(&fds,1,(int) timeout*1000)) <= 0)
|
|
{
|
|
DBUG_RETURN(res < 0 ? 0 : 1); /* Don't return 1 on errors */
|
|
}
|
|
DBUG_RETURN(fds.revents & (POLLIN | POLLERR | POLLHUP) ? 0 : 1);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
Retrieve the amount of data that can be read from a socket.
|
|
|
|
@param vio A VIO object.
|
|
@param bytes[out] The amount of bytes available.
|
|
|
|
@retval FALSE Success.
|
|
@retval TRUE Failure.
|
|
*/
|
|
|
|
static my_bool socket_peek_read(Vio *vio, uint *bytes)
|
|
{
|
|
#ifdef __WIN__
|
|
int len;
|
|
if (ioctlsocket(vio->sd, FIONREAD, &len))
|
|
return TRUE;
|
|
*bytes= len;
|
|
return FALSE;
|
|
#elif FIONREAD_IN_SYS_IOCTL
|
|
int len;
|
|
if (ioctl(vio->sd, FIONREAD, &len) < 0)
|
|
return TRUE;
|
|
*bytes= len;
|
|
return FALSE;
|
|
#else
|
|
char buf[1024];
|
|
ssize_t res= recv(vio->sd, &buf, sizeof(buf), MSG_PEEK);
|
|
if (res < 0)
|
|
return TRUE;
|
|
*bytes= res;
|
|
return FALSE;
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
Indicate whether there is data to read on a given socket.
|
|
|
|
@remark Errors are interpreted as if there is data to read.
|
|
|
|
@param sd A connected socket.
|
|
@param timeout Maximum time in seconds to wait.
|
|
|
|
@retval FALSE There is data (or EOF) to read. Also FALSE if error.
|
|
@retval TRUE There is _NO_ data to read or timed out.
|
|
*/
|
|
|
|
my_bool vio_poll_read(Vio *vio, uint timeout)
|
|
{
|
|
my_socket sd= vio->sd;
|
|
DBUG_ENTER("vio_poll_read");
|
|
#ifdef HAVE_OPENSSL
|
|
if (vio->type == VIO_TYPE_SSL)
|
|
sd= SSL_get_fd((SSL*) vio->ssl_arg);
|
|
#endif
|
|
DBUG_RETURN(socket_poll_read(sd, timeout));
|
|
}
|
|
|
|
|
|
/**
|
|
Determine if the endpoint of a connection is still available.
|
|
|
|
@remark The socket is assumed to be disconnected if an EOF
|
|
condition is encountered.
|
|
|
|
@param vio The VIO object.
|
|
|
|
@retval TRUE EOF condition not found.
|
|
@retval FALSE EOF condition is signaled.
|
|
*/
|
|
|
|
my_bool vio_is_connected(Vio *vio)
|
|
{
|
|
uint bytes= 0;
|
|
DBUG_ENTER("vio_is_connected");
|
|
|
|
/* In the presence of errors the socket is assumed to be connected. */
|
|
|
|
/*
|
|
The first step of detecting a EOF condition is veryfing
|
|
whether there is data to read. Data in this case would
|
|
be the EOF.
|
|
*/
|
|
if (vio_poll_read(vio, 0))
|
|
DBUG_RETURN(TRUE);
|
|
|
|
/*
|
|
The second step is read() or recv() from the socket returning
|
|
0 (EOF). Unfortunelly, it's not possible to call read directly
|
|
as we could inadvertently read meaningful connection data.
|
|
Simulate a read by retrieving the number of bytes available to
|
|
read -- 0 meaning EOF.
|
|
*/
|
|
if (socket_peek_read(vio, &bytes))
|
|
DBUG_RETURN(TRUE);
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
/* There might be buffered data at the SSL layer. */
|
|
if (!bytes && vio->type == VIO_TYPE_SSL)
|
|
bytes= SSL_pending((SSL*) vio->ssl_arg);
|
|
#endif
|
|
|
|
DBUG_RETURN(bytes ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
void vio_timeout(Vio *vio, uint which, uint timeout)
|
|
{
|
|
#if defined(SO_SNDTIMEO) && defined(SO_RCVTIMEO)
|
|
int r;
|
|
DBUG_ENTER("vio_timeout");
|
|
|
|
{
|
|
#ifdef __WIN__
|
|
/* Windows expects time in milliseconds as int */
|
|
int wait_timeout= (int) timeout * 1000;
|
|
#else
|
|
/* POSIX specifies time as struct timeval. */
|
|
struct timeval wait_timeout;
|
|
wait_timeout.tv_sec= timeout;
|
|
wait_timeout.tv_usec= 0;
|
|
#endif
|
|
|
|
r= setsockopt(vio->sd, SOL_SOCKET, which ? SO_SNDTIMEO : SO_RCVTIMEO,
|
|
IF_WIN(const char*, const void*)&wait_timeout,
|
|
sizeof(wait_timeout));
|
|
|
|
}
|
|
|
|
#ifndef DBUG_OFF
|
|
if (r != 0)
|
|
DBUG_PRINT("error", ("setsockopt failed: %d, errno: %d", r, socket_errno));
|
|
#endif
|
|
|
|
DBUG_VOID_RETURN;
|
|
#else
|
|
/*
|
|
Platforms not suporting setting of socket timeout should either use
|
|
thr_alarm or just run without read/write timeout(s)
|
|
*/
|
|
#endif
|
|
}
|
|
|
|
|
|
#ifdef __WIN__
|
|
|
|
/*
|
|
Finish pending IO on pipe. Honor wait timeout
|
|
*/
|
|
static int pipe_complete_io(Vio* vio, char* buf, size_t size, DWORD timeout_millis)
|
|
{
|
|
DWORD length;
|
|
DWORD ret;
|
|
|
|
DBUG_ENTER("pipe_complete_io");
|
|
|
|
ret= WaitForSingleObject(vio->pipe_overlapped.hEvent, timeout_millis);
|
|
/*
|
|
WaitForSingleObjects will normally return WAIT_OBJECT_O (success, IO completed)
|
|
or WAIT_TIMEOUT.
|
|
*/
|
|
if(ret != WAIT_OBJECT_0)
|
|
{
|
|
CancelIo(vio->hPipe);
|
|
DBUG_PRINT("error",("WaitForSingleObject() returned %d", ret));
|
|
DBUG_RETURN(-1);
|
|
}
|
|
|
|
if (!GetOverlappedResult(vio->hPipe,&(vio->pipe_overlapped),&length, FALSE))
|
|
{
|
|
DBUG_PRINT("error",("GetOverlappedResult() returned last error %d",
|
|
GetLastError()));
|
|
DBUG_RETURN(-1);
|
|
}
|
|
|
|
DBUG_RETURN(length);
|
|
}
|
|
|
|
|
|
size_t vio_read_pipe(Vio * vio, uchar *buf, size_t size)
|
|
{
|
|
DWORD bytes_read;
|
|
DBUG_ENTER("vio_read_pipe");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
|
|
(uint) size));
|
|
|
|
if (!ReadFile(vio->hPipe, buf, (DWORD)size, &bytes_read,
|
|
&(vio->pipe_overlapped)))
|
|
{
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
DBUG_PRINT("error",("ReadFile() returned last error %d",
|
|
GetLastError()));
|
|
DBUG_RETURN((size_t)-1);
|
|
}
|
|
bytes_read= pipe_complete_io(vio, buf, size,vio->read_timeout_millis);
|
|
}
|
|
|
|
DBUG_PRINT("exit", ("%d", bytes_read));
|
|
DBUG_RETURN(bytes_read);
|
|
}
|
|
|
|
|
|
size_t vio_write_pipe(Vio * vio, const uchar* buf, size_t size)
|
|
{
|
|
DWORD bytes_written;
|
|
DBUG_ENTER("vio_write_pipe");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %u", vio->sd, (long) buf,
|
|
(uint) size));
|
|
|
|
if (!WriteFile(vio->hPipe, buf, (DWORD)size, &bytes_written,
|
|
&(vio->pipe_overlapped)))
|
|
{
|
|
if (GetLastError() != ERROR_IO_PENDING)
|
|
{
|
|
DBUG_PRINT("vio_error",("WriteFile() returned last error %d",
|
|
GetLastError()));
|
|
DBUG_RETURN((size_t)-1);
|
|
}
|
|
bytes_written = pipe_complete_io(vio, (char *)buf, size,
|
|
vio->write_timeout_millis);
|
|
}
|
|
|
|
DBUG_PRINT("exit", ("%d", bytes_written));
|
|
DBUG_RETURN(bytes_written);
|
|
}
|
|
|
|
|
|
my_bool vio_is_connected_pipe(Vio *vio)
|
|
{
|
|
if (PeekNamedPipe(vio->hPipe, NULL, 0, NULL, NULL, NULL))
|
|
return TRUE;
|
|
else
|
|
return (GetLastError() != ERROR_BROKEN_PIPE);
|
|
}
|
|
|
|
|
|
int vio_close_pipe(Vio * vio)
|
|
{
|
|
int r;
|
|
DBUG_ENTER("vio_close_pipe");
|
|
|
|
CloseHandle(vio->pipe_overlapped.hEvent);
|
|
DisconnectNamedPipe(vio->hPipe);
|
|
r= CloseHandle(vio->hPipe);
|
|
if (r)
|
|
{
|
|
DBUG_PRINT("vio_error", ("close() failed, error: %d",GetLastError()));
|
|
/* FIXME: error handling (not critical for MySQL) */
|
|
}
|
|
vio->type= VIO_CLOSED;
|
|
vio->sd= -1;
|
|
DBUG_RETURN(r);
|
|
}
|
|
|
|
|
|
void vio_win32_timeout(Vio *vio, uint which , uint timeout_sec)
|
|
{
|
|
DWORD timeout_millis;
|
|
/*
|
|
Windows is measuring timeouts in milliseconds. Check for possible int
|
|
overflow.
|
|
*/
|
|
if (timeout_sec > UINT_MAX/1000)
|
|
timeout_millis= INFINITE;
|
|
else
|
|
timeout_millis= timeout_sec * 1000;
|
|
|
|
/* which == 1 means "write", which == 0 means "read".*/
|
|
if(which)
|
|
vio->write_timeout_millis= timeout_millis;
|
|
else
|
|
vio->read_timeout_millis= timeout_millis;
|
|
}
|
|
|
|
|
|
#ifdef HAVE_SMEM
|
|
|
|
size_t vio_read_shared_memory(Vio * vio, uchar* buf, size_t size)
|
|
{
|
|
size_t length;
|
|
size_t remain_local;
|
|
char *current_postion;
|
|
HANDLE events[2];
|
|
|
|
DBUG_ENTER("vio_read_shared_memory");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf,
|
|
size));
|
|
|
|
remain_local = size;
|
|
current_postion=buf;
|
|
|
|
events[0]= vio->event_server_wrote;
|
|
events[1]= vio->event_conn_closed;
|
|
|
|
do
|
|
{
|
|
if (vio->shared_memory_remain == 0)
|
|
{
|
|
/*
|
|
WaitForMultipleObjects can return next values:
|
|
WAIT_OBJECT_0+0 - event from vio->event_server_wrote
|
|
WAIT_OBJECT_0+1 - event from vio->event_conn_closed. We can't read
|
|
anything
|
|
WAIT_ABANDONED_0 and WAIT_TIMEOUT - fail. We can't read anything
|
|
*/
|
|
if (WaitForMultipleObjects(array_elements(events), events, FALSE,
|
|
vio->read_timeout_millis) != WAIT_OBJECT_0)
|
|
{
|
|
DBUG_RETURN(-1);
|
|
};
|
|
|
|
vio->shared_memory_pos = vio->handle_map;
|
|
vio->shared_memory_remain = uint4korr((ulong*)vio->shared_memory_pos);
|
|
vio->shared_memory_pos+=4;
|
|
}
|
|
|
|
length = size;
|
|
|
|
if (vio->shared_memory_remain < length)
|
|
length = vio->shared_memory_remain;
|
|
if (length > remain_local)
|
|
length = remain_local;
|
|
|
|
memcpy(current_postion,vio->shared_memory_pos,length);
|
|
|
|
vio->shared_memory_remain-=length;
|
|
vio->shared_memory_pos+=length;
|
|
current_postion+=length;
|
|
remain_local-=length;
|
|
|
|
if (!vio->shared_memory_remain)
|
|
{
|
|
if (!SetEvent(vio->event_client_read))
|
|
DBUG_RETURN(-1);
|
|
}
|
|
} while (remain_local);
|
|
length = size;
|
|
|
|
DBUG_PRINT("exit", ("%lu", (ulong) length));
|
|
DBUG_RETURN(length);
|
|
}
|
|
|
|
|
|
size_t vio_write_shared_memory(Vio * vio, const uchar* buf, size_t size)
|
|
{
|
|
size_t length, remain, sz;
|
|
HANDLE pos;
|
|
const uchar *current_postion;
|
|
HANDLE events[2];
|
|
|
|
DBUG_ENTER("vio_write_shared_memory");
|
|
DBUG_PRINT("enter", ("sd: %d buf: 0x%lx size: %d", vio->sd, (long) buf,
|
|
size));
|
|
|
|
remain = size;
|
|
current_postion = buf;
|
|
|
|
events[0]= vio->event_server_read;
|
|
events[1]= vio->event_conn_closed;
|
|
|
|
while (remain != 0)
|
|
{
|
|
if (WaitForMultipleObjects(array_elements(events), events, FALSE,
|
|
vio->write_timeout_millis) != WAIT_OBJECT_0)
|
|
{
|
|
DBUG_RETURN((size_t) -1);
|
|
}
|
|
|
|
sz= (remain > shared_memory_buffer_length ? shared_memory_buffer_length :
|
|
remain);
|
|
|
|
int4store(vio->handle_map,sz);
|
|
pos = vio->handle_map + 4;
|
|
memcpy(pos,current_postion,sz);
|
|
remain-=sz;
|
|
current_postion+=sz;
|
|
if (!SetEvent(vio->event_client_wrote))
|
|
DBUG_RETURN((size_t) -1);
|
|
}
|
|
length = size;
|
|
|
|
DBUG_PRINT("exit", ("%lu", (ulong) length));
|
|
DBUG_RETURN(length);
|
|
}
|
|
|
|
|
|
my_bool vio_is_connected_shared_memory(Vio *vio)
|
|
{
|
|
return (WaitForSingleObject(vio->event_conn_closed, 0) != WAIT_OBJECT_0);
|
|
}
|
|
|
|
|
|
/**
|
|
Close shared memory and DBUG_PRINT any errors that happen on closing.
|
|
@return Zero if all closing functions succeed, and nonzero otherwise.
|
|
*/
|
|
int vio_close_shared_memory(Vio * vio)
|
|
{
|
|
int error_count= 0;
|
|
DBUG_ENTER("vio_close_shared_memory");
|
|
if (vio->type != VIO_CLOSED)
|
|
{
|
|
/*
|
|
Set event_conn_closed for notification of both client and server that
|
|
connection is closed
|
|
*/
|
|
SetEvent(vio->event_conn_closed);
|
|
/*
|
|
Close all handlers. UnmapViewOfFile and CloseHandle return non-zero
|
|
result if they are success.
|
|
*/
|
|
if (UnmapViewOfFile(vio->handle_map) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("UnmapViewOfFile() failed"));
|
|
}
|
|
if (CloseHandle(vio->event_server_wrote) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("CloseHandle(vio->esw) failed"));
|
|
}
|
|
if (CloseHandle(vio->event_server_read) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("CloseHandle(vio->esr) failed"));
|
|
}
|
|
if (CloseHandle(vio->event_client_wrote) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("CloseHandle(vio->ecw) failed"));
|
|
}
|
|
if (CloseHandle(vio->event_client_read) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("CloseHandle(vio->ecr) failed"));
|
|
}
|
|
if (CloseHandle(vio->handle_file_map) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("CloseHandle(vio->hfm) failed"));
|
|
}
|
|
if (CloseHandle(vio->event_conn_closed) == 0)
|
|
{
|
|
error_count++;
|
|
DBUG_PRINT("vio_error", ("CloseHandle(vio->ecc) failed"));
|
|
}
|
|
}
|
|
vio->type= VIO_CLOSED;
|
|
vio->sd= -1;
|
|
DBUG_RETURN(error_count);
|
|
}
|
|
#endif /* HAVE_SMEM */
|
|
#endif /* __WIN__ */
|
|
|
|
|
|
/**
|
|
Number of bytes in the read buffer.
|
|
|
|
@return number of bytes in the read buffer or < 0 if error.
|
|
*/
|
|
|
|
ssize_t vio_pending(Vio *vio)
|
|
{
|
|
#ifdef HAVE_OPENSSL
|
|
SSL *ssl= (SSL*) vio->ssl_arg;
|
|
#endif
|
|
|
|
if (vio->read_pos < vio->read_end)
|
|
return vio->read_end - vio->read_pos;
|
|
|
|
#ifdef HAVE_OPENSSL
|
|
if (ssl)
|
|
return SSL_pending(ssl);
|
|
#endif
|
|
|
|
return 0;
|
|
}
|