1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-22 23:02:54 +03:00
2004-08-29 04:13:13 +00:00

467 lines
11 KiB
C

/*-------------------------------------------------------------------------
*
* socket.c
* Microsoft Windows Win32 Socket Functions
*
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/win32/socket.c,v 1.3 2004/08/29 04:12:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#undef socket
#undef accept
#undef connect
#undef select
#undef recv
#undef send
/*
* Blocking socket functions implemented so they listen on both
* the socket and the signal event, required for signal handling.
*/
/*
* Convert the last socket error code into errno
*/
static void TranslateSocketError(void) {
switch (WSAGetLastError()) {
case WSANOTINITIALISED:
case WSAENETDOWN:
case WSAEINPROGRESS:
case WSAEINVAL:
case WSAESOCKTNOSUPPORT:
case WSAEFAULT:
case WSAEINVALIDPROVIDER:
case WSAEINVALIDPROCTABLE:
case WSAEMSGSIZE:
errno = EINVAL;
break;
case WSAEAFNOSUPPORT:
errno = EAFNOSUPPORT;
break;
case WSAEMFILE:
errno = EMFILE;
break;
case WSAENOBUFS:
errno = ENOBUFS;
break;
case WSAEPROTONOSUPPORT:
case WSAEPROTOTYPE:
errno = EPROTONOSUPPORT;
break;
case WSAECONNREFUSED:
errno = ECONNREFUSED;
break;
case WSAEINTR:
errno = EINTR;
break;
case WSAENOTSOCK:
errno = EBADFD;
break;
case WSAEOPNOTSUPP:
errno = EOPNOTSUPP;
break;
case WSAEWOULDBLOCK:
errno = EWOULDBLOCK;
break;
case WSAEACCES:
errno = EACCES;
break;
case WSAENOTCONN:
case WSAENETRESET:
case WSAECONNRESET:
case WSAESHUTDOWN:
case WSAECONNABORTED:
case WSAEDISCON:
errno = ECONNREFUSED; /*ENOTCONN?*/
break;
default:
ereport(NOTICE,
(errmsg_internal("Unknown win32 socket error code: %i",WSAGetLastError())));
errno = EINVAL;
}
}
static int pgwin32_poll_signals(void) {
if (WaitForSingleObject(pgwin32_signal_event,0) == WAIT_OBJECT_0) {
pgwin32_dispatch_queued_signals();
errno = EINTR;
return 1;
}
return 0;
}
static int pgwin32_waitforsinglesocket(SOCKET s, int what) {
static HANDLE waitevent = INVALID_HANDLE_VALUE;
HANDLE events[2];
int r;
if (waitevent == INVALID_HANDLE_VALUE) {
waitevent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (waitevent == INVALID_HANDLE_VALUE)
ereport(ERROR,
(errmsg_internal("Failed to create socket waiting event: %i",(int)GetLastError())));
}
else
if (!ResetEvent(waitevent))
ereport(ERROR,
(errmsg_internal("Failed to reset socket waiting event: %i",(int)GetLastError())));
if (WSAEventSelect(s, waitevent, what) == SOCKET_ERROR) {
TranslateSocketError();
return 0;
}
events[0] = pgwin32_signal_event;
events[1] = waitevent;
r = WaitForMultipleObjects(2, events, FALSE, INFINITE);
if (r == WAIT_OBJECT_0) {
pgwin32_dispatch_queued_signals();
errno = EINTR;
return 0;
}
if (r == WAIT_OBJECT_0+1)
return 1;
ereport(ERROR,
(errmsg_internal("Bad return from WaitForMultipleObjects: %i (%i)",r,(int)GetLastError())));
return 0;
}
/*
* Create a socket, setting it to overlapped and non-blocking
*/
SOCKET pgwin32_socket(int af, int type, int protocol) {
SOCKET s;
unsigned long on = 1;
s = WSASocket(af, type, protocol, NULL, 0, WSA_FLAG_OVERLAPPED);
if (s == INVALID_SOCKET) {
TranslateSocketError();
return INVALID_SOCKET;
}
if (ioctlsocket(s, FIONBIO, &on)) {
TranslateSocketError();
return INVALID_SOCKET;
}
errno = 0;
return s;
}
SOCKET pgwin32_accept(SOCKET s, struct sockaddr* addr, int* addrlen) {
SOCKET rs;
/* Poll for signals, but don't return with EINTR, since we don't
handle that in pqcomm.c */
pgwin32_poll_signals();
rs = WSAAccept(s, addr, addrlen, NULL, 0);
if (rs == INVALID_SOCKET) {
TranslateSocketError();
return INVALID_SOCKET;
}
return rs;
}
/* No signal delivery during connect. */
int pgwin32_connect(SOCKET s, const struct sockaddr *addr, int addrlen) {
int r;
r = WSAConnect(s, addr, addrlen, NULL, NULL, NULL, NULL);
if (r == 0)
return 0;
if (WSAGetLastError() != WSAEWOULDBLOCK) {
TranslateSocketError();
return -1;
}
while (pgwin32_waitforsinglesocket(s, FD_CONNECT) == 0) {
/* Loop endlessly as long as we are just delivering signals */
}
return 0;
}
int pgwin32_recv(SOCKET s, char *buf, int len, int f) {
WSABUF wbuf;
int r;
DWORD b;
DWORD flags = f;
if (pgwin32_poll_signals())
return -1;
wbuf.len = len;
wbuf.buf = buf;
r = WSARecv(s, &wbuf, 1, &b, &flags, NULL, NULL);
if (r != SOCKET_ERROR && b > 0)
/* Read succeeded right away */
return b;
if (r == SOCKET_ERROR &&
WSAGetLastError() != WSAEWOULDBLOCK) {
TranslateSocketError();
return -1;
}
/* No error, zero bytes (win2000+) or error+WSAEWOULDBLOCK (<=nt4) */
if (pgwin32_waitforsinglesocket(s, FD_READ | FD_CLOSE | FD_ACCEPT) == 0)
return -1;
r = WSARecv(s, &wbuf, 1, &b, &flags, NULL, NULL);
if (r == SOCKET_ERROR) {
TranslateSocketError();
return -1;
}
return b;
}
int pgwin32_send(SOCKET s, char *buf, int len, int flags) {
WSABUF wbuf;
int r;
DWORD b;
if (pgwin32_poll_signals())
return -1;
wbuf.len = len;
wbuf.buf = buf;
r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL);
if (r != SOCKET_ERROR && b > 0)
/* Write succeeded right away */
return b;
if (r == SOCKET_ERROR &&
WSAGetLastError() != WSAEWOULDBLOCK) {
TranslateSocketError();
return -1;
}
/* No error, zero bytes (win2000+) or error+WSAEWOULDBLOCK (<=nt4) */
if (pgwin32_waitforsinglesocket(s, FD_WRITE | FD_CLOSE) == 0)
return -1;
r = WSASend(s, &wbuf, 1, &b, flags, NULL, NULL);
if (r == SOCKET_ERROR) {
TranslateSocketError();
return -1;
}
return b;
}
/*
* Wait for activity on one or more sockets.
* While waiting, allow signals to run
*
* NOTE! Currently does not implement exceptfds check,
* since it is not used in postgresql!
*/
int pgwin32_select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, const struct timeval* timeout) {
WSAEVENT events[FD_SETSIZE*2]; /* worst case is readfds totally different
* from writefds, so 2*FD_SETSIZE sockets */
SOCKET sockets[FD_SETSIZE*2];
int numevents=0;
int i;
int r;
DWORD timeoutval = WSA_INFINITE;
FD_SET outreadfds;
FD_SET outwritefds;
int nummatches = 0;
Assert(exceptfds == NULL);
if (pgwin32_poll_signals())
return -1;
FD_ZERO(&outreadfds);
FD_ZERO(&outwritefds);
/* Write FDs are different in the way that it is only flagged by
* WSASelectEvent() if we have tried to write to them first. So try
* an empty write */
if (writefds) {
for (i = 0; i < writefds->fd_count; i++) {
char c;
WSABUF buf;
DWORD sent;
buf.buf = &c;
buf.len = 0;
r = WSASend(writefds->fd_array[i], &buf, 1, &sent, 0, NULL, NULL);
if (r == 0) /* Completed - means things are fine! */
FD_SET(writefds->fd_array[i], &outwritefds);
else { /* Not completed */
if (WSAGetLastError() != WSAEWOULDBLOCK)
/* Not completed, and not just "would block", so
* an error occured */
FD_SET(writefds->fd_array[i], &outwritefds);
}
}
if (outwritefds.fd_count > 0) {
memcpy(writefds,&outwritefds,sizeof(fd_set));
if (readfds)
FD_ZERO(readfds);
return outwritefds.fd_count;
}
}
/* Now set up for an actual select */
if (timeout != NULL) {
/* timeoutval is in milliseconds */
timeoutval = timeout->tv_sec*1000 + timeout->tv_usec / 1000;
}
if (readfds != NULL) {
for (i=0; i < readfds->fd_count; i++) {
events[numevents] = WSACreateEvent();
sockets[numevents] = readfds->fd_array[i];
numevents++;
}
}
if (writefds != NULL) {
for (i=0; i < writefds->fd_count; i++) {
if (!readfds ||
!FD_ISSET(writefds->fd_array[i], readfds)) {
/* If the socket is not in the read list */
events[numevents] = WSACreateEvent();
sockets[numevents] = writefds->fd_array[i];
numevents++;
}
}
}
for (i=0; i < numevents; i++) {
int flags = 0;
if (readfds && FD_ISSET(sockets[i],readfds))
flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
if (writefds && FD_ISSET(sockets[i],writefds))
flags |= FD_WRITE | FD_CLOSE;
if (WSAEventSelect(sockets[i], events[i], flags) == SOCKET_ERROR) {
TranslateSocketError();
for (i = 0; i < numevents; i++)
WSACloseEvent(events[i]);
return -1;
}
}
events[numevents] = pgwin32_signal_event;
r = WaitForMultipleObjectsEx(numevents+1, events, FALSE, timeoutval, FALSE);
if (r != WSA_WAIT_TIMEOUT && r != (WAIT_OBJECT_0+numevents)) {
/* We scan all events, even those not signalled, in case more
* than one event has been tagged but Wait.. can only return one.
*/
WSANETWORKEVENTS resEvents;
for (i=0; i < numevents; i++) {
ZeroMemory(&resEvents,sizeof(resEvents));
if (WSAEnumNetworkEvents(sockets[i],events[i],&resEvents) == SOCKET_ERROR)
ereport(FATAL,
(errmsg_internal("failed to enumerate network events: %i",(int)GetLastError())));
/* Read activity? */
if (readfds && FD_ISSET(sockets[i], readfds)) {
if ((resEvents.lNetworkEvents & FD_READ) ||
(resEvents.lNetworkEvents & FD_ACCEPT) ||
(resEvents.lNetworkEvents & FD_CLOSE)) {
FD_SET(sockets[i],&outreadfds);
nummatches++;
}
}
/* Write activity? */
if (writefds && FD_ISSET(sockets[i], writefds)) {
if ((resEvents.lNetworkEvents & FD_WRITE) ||
(resEvents.lNetworkEvents & FD_CLOSE)) {
FD_SET(sockets[i],&outwritefds);
nummatches++;
}
}
}
}
/* Clean up all handles */
for (i = 0; i < numevents; i++) {
WSAEventSelect(sockets[i], events[i], 0);
WSACloseEvent(events[i]);
}
if (r == WSA_WAIT_TIMEOUT) {
if (readfds)
FD_ZERO(readfds);
if (writefds)
FD_ZERO(writefds);
return 0;
}
if (r == WAIT_OBJECT_0+numevents) {
pgwin32_dispatch_queued_signals();
errno = EINTR;
if (readfds)
FD_ZERO(readfds);
if (writefds)
FD_ZERO(writefds);
return -1;
}
/* Overwrite socket sets with our resulting values */
if (readfds)
memcpy(readfds, &outreadfds, sizeof(fd_set));
if (writefds)
memcpy(writefds, &outwritefds, sizeof(fd_set));
return nummatches;
}
/*
* Return win32 error string, since strerror can't
* handle winsock codes
*/
static char wserrbuf[256];
const char *
pgwin32_socket_strerror(int err)
{
static HANDLE handleDLL = INVALID_HANDLE_VALUE;
if (handleDLL == INVALID_HANDLE_VALUE) {
handleDLL = LoadLibraryEx("netmsg.dll", NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
if (handleDLL == NULL)
ereport(FATAL,
(errmsg_internal("Failed to load netmsg.dll: %i",(int)GetLastError())));
}
ZeroMemory(&wserrbuf, sizeof(wserrbuf));
if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE,
handleDLL,
err,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
wserrbuf,
sizeof(wserrbuf)-1,
NULL) == 0) {
/* Failed to get id */
sprintf(wserrbuf,"Unknown winsock error %i",err);
}
return wserrbuf;
}