* significantly more enjoyable.)
*/
#ifndef lint
-static const char rcsid[] = "$Id: os_win32.c,v 1.11 2001/03/27 14:03:21 robs Exp $";
+static const char rcsid[] = "$Id: os_win32.c,v 1.15 2001/06/19 17:11:39 robs Exp $";
#endif /* not lint */
-#include "fcgi_config.h"
-
-#define DLLAPI __declspec(dllexport)
-
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <sys/timeb.h>
-#include <Winsock2.h>
-#include <Windows.h>
+#define DLLAPI __declspec(dllexport)
#include "fcgios.h"
-
-#define ASSERT assert
+#include "fcgimisc.h"
#define WIN32_OPEN_MAX 128 /* XXX: Small hack */
+/*
+ * millisecs to wait for a client connection before checking the
+ * shutdown flag (then go back to waiting for a connection, etc).
+ */
+#define ACCEPT_TIMEOUT 1000
+
#define MUTEX_VARNAME "_FCGI_MUTEX_"
#define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
#define LOCALHOST "localhost"
LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
};
-typedef struct FD_TABLE *PFD_TABLE;
-
/*
* XXX Note there is no dyanmic sizing of this table, so if the
* number of open file descriptors exceeds WIN32_OPEN_MAX the
*/
static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
+static CRITICAL_SECTION fdTableCritical;
+
struct OVERLAPPED_REQUEST {
OVERLAPPED overlapped;
unsigned long instance; /* file instance (won't match after a close) */
*/
static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
{
- int index;
+ int index = -1;
+
+ EnterCriticalSection(&fdTableCritical);
/*
- * If the "desiredFd" is not -1, try to get this entry for our
- * pseudo file descriptor. If this is not available, return -1
- * as the caller wanted to get this mapping. This is typically
- * only used for mapping stdio handles.
+ * If desiredFd is set, try to get this entry (this is used for
+ * mapping stdio handles). Otherwise try to get the fd entry.
+ * If this is not available, find a the first empty slot. .
*/
if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
{
- if (fdTable[desiredFd].type != FD_UNUSED)
+ if (fdTable[desiredFd].type == FD_UNUSED)
{
- return -1;
+ index = desiredFd;
}
- index = desiredFd;
}
- else
+ else if (fd > 0)
{
- // See if the entry that matches "fd" is available.
-
- if (fd <= 0 || fd >= WIN32_OPEN_MAX)
- {
- return -1;
- }
-
- if (fdTable[fd].type == FD_UNUSED)
+ if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
{
index = fd;
}
else
{
- // Find an entry we can use.
- // Start at 1 (0 fake id fails in some cases).
+ int i;
- for (index = 1; index < WIN32_OPEN_MAX; index++)
+ for (i = 1; i < WIN32_OPEN_MAX; ++i)
{
- if (fdTable[index].type == FD_UNUSED)
+ if (fdTable[i].type == FD_UNUSED)
{
+ index = i;
break;
}
}
-
- if (index == WIN32_OPEN_MAX)
- {
- SetLastError(WSAEMFILE);
- return -1;
- }
}
}
+
+ if (index != -1)
+ {
+ fdTable[index].fid.value = fd;
+ fdTable[index].type = type;
+ fdTable[index].path = NULL;
+ fdTable[index].Errno = NO_ERROR;
+ fdTable[index].status = 0;
+ fdTable[index].offset = -1;
+ fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
+ fdTable[index].hMapMutex = NULL;
+ fdTable[index].ovList = NULL;
+ }
- fdTable[index].fid.value = fd;
- fdTable[index].type = type;
- fdTable[index].path = NULL;
- fdTable[index].Errno = NO_ERROR;
- fdTable[index].status = 0;
- fdTable[index].offset = -1;
- fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
- fdTable[index].hMapMutex = NULL;
- fdTable[index].ovList = NULL;
-
+ LeaveCriticalSection(&fdTableCritical);
return index;
}
if(libInitialized)
return 0;
+ InitializeCriticalSection(&fdTableCritical);
+
/*
* Initialize windows sockets library.
*/
void OS_LibShutdown()
{
- if(hIoCompPort != INVALID_HANDLE_VALUE) {
+ if (hIoCompPort != INVALID_HANDLE_VALUE)
+ {
CloseHandle(hIoCompPort);
- hIoCompPort = INVALID_HANDLE_VALUE;
+ hIoCompPort = INVALID_HANDLE_VALUE;
}
- if(hStdinCompPort != INVALID_HANDLE_VALUE) {
+ if (hStdinCompPort != INVALID_HANDLE_VALUE)
+ {
CloseHandle(hStdinCompPort);
- hStdinCompPort = INVALID_HANDLE_VALUE;
+ hStdinCompPort = INVALID_HANDLE_VALUE;
}
- /*
- * Shutdown the socket library.
- */
+ if (acceptMutex != INVALID_HANDLE_VALUE)
+ {
+ ReleaseMutex(acceptMutex);
+ }
+
+ DisconnectNamedPipe(hListen);
+
+ CancelIo(hListen);
+
+
WSACleanup();
- return;
}
/*
{
/* Catch it if fd is a bogus value */
ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
- ASSERT(fdTable[fd].type != FD_UNUSED);
- switch (fdTable[fd].type) {
- case FD_FILE_SYNC:
- case FD_FILE_ASYNC:
- /* Free file path string */
- ASSERT(fdTable[fd].path != NULL);
- free(fdTable[fd].path);
- fdTable[fd].path = NULL;
- break;
- default:
- /*
- * Break through to generic fdTable free-descriptor code
- */
- break;
+ EnterCriticalSection(&fdTableCritical);
+
+ if (fdTable[fd].type != FD_UNUSED)
+ {
+ switch (fdTable[fd].type)
+ {
+ case FD_FILE_SYNC:
+ case FD_FILE_ASYNC:
+
+ /* Free file path string */
+ ASSERT(fdTable[fd].path != NULL);
+ free(fdTable[fd].path);
+ fdTable[fd].path = NULL;
+ break;
+
+ default:
+ break;
+ }
+ ASSERT(fdTable[fd].path == NULL);
+
+ fdTable[fd].type = FD_UNUSED;
+ fdTable[fd].path = NULL;
+ fdTable[fd].Errno = NO_ERROR;
+ fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
+
+ if (fdTable[fd].hMapMutex != NULL)
+ {
+ CloseHandle(fdTable[fd].hMapMutex);
+ fdTable[fd].hMapMutex = NULL;
+ }
}
- ASSERT(fdTable[fd].path == NULL);
- fdTable[fd].type = FD_UNUSED;
- fdTable[fd].path = NULL;
- fdTable[fd].Errno = NO_ERROR;
- fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
- if (fdTable[fd].hMapMutex != NULL) {
- CloseHandle(fdTable[fd].hMapMutex);
- fdTable[fd].hMapMutex = NULL;
- }
+
+ LeaveCriticalSection(&fdTableCritical);
+
return;
}
int OS_Read(int fd, char * buf, size_t len)
{
DWORD bytesRead;
- int ret;
+ int ret = -1;
- /*
- * Catch any bogus fd values
- */
ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
- switch (fdTable[fd].type) {
+ switch (fdTable[fd].type)
+ {
case FD_FILE_SYNC:
case FD_FILE_ASYNC:
case FD_PIPE_SYNC:
case FD_PIPE_ASYNC:
- bytesRead = fd;
- /*
- * ReadFile returns: TRUE success, FALSE failure
- */
- if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
- NULL)) {
- fdTable[fd].Errno = GetLastError();
- return -1;
+
+ if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL))
+ {
+ ret = bytesRead;
+ }
+ else
+ {
+ fdTable[fd].Errno = GetLastError();
}
- return bytesRead;
+
+ break;
case FD_SOCKET_SYNC:
case FD_SOCKET_ASYNC:
- /* winsock recv returns n bytes recv'ed, SOCKET_ERROR failure */
- /*
- * XXX: Test this with ReadFile. If it works, remove this code
- * to simplify the routine.
- */
- if ((ret = recv(fdTable[fd].fid.sock, buf, len, 0)) ==
- SOCKET_ERROR) {
- fdTable[fd].Errno = WSAGetLastError();
- return -1;
+
+ ret = recv(fdTable[fd].fid.sock, buf, len, 0);
+ if (ret == SOCKET_ERROR)
+ {
+ fdTable[fd].Errno = WSAGetLastError();
+ ret = -1;
}
- return ret;
- default:
- return -1;
+
+ break;
+
+ default:
+
+ ASSERT(0);
}
+
+ return ret;
}
/*
int OS_Write(int fd, char * buf, size_t len)
{
DWORD bytesWritten;
- int ret;
+ int ret = -1;
- /*
- * Catch any bogus fd values
- */
- ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
- ASSERT((fdTable[fd].type > FD_UNUSED) &&
- (fdTable[fd].type <= FD_PIPE_ASYNC));
+ ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
- switch (fdTable[fd].type) {
+ switch (fdTable[fd].type)
+ {
case FD_FILE_SYNC:
case FD_FILE_ASYNC:
case FD_PIPE_SYNC:
case FD_PIPE_ASYNC:
- bytesWritten = fd;
- /*
- * WriteFile returns: TRUE success, FALSE failure
- */
- if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len,
- &bytesWritten, NULL)) {
- fdTable[fd].Errno = GetLastError();
- return -1;
+
+ if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL))
+ {
+ ret = bytesWritten;
+ }
+ else
+ {
+ fdTable[fd].Errno = GetLastError();
}
- return bytesWritten;
+
+ break;
+
case FD_SOCKET_SYNC:
case FD_SOCKET_ASYNC:
- /* winsock send returns n bytes written, SOCKET_ERROR failure */
- /*
- * XXX: Test this with WriteFile. If it works, remove this code
- * to simplify the routine.
- */
- if ((ret = send(fdTable[fd].fid.sock, buf, len, 0)) ==
- SOCKET_ERROR) {
- fdTable[fd].Errno = WSAGetLastError();
- return -1;
+
+ ret = send(fdTable[fd].fid.sock, buf, len, 0);
+ if (ret == SOCKET_ERROR)
+ {
+ fdTable[fd].Errno = WSAGetLastError();
+ ret = -1;
}
- return ret;
- default:
- return -1;
+
+ break;
+
+ default:
+
+ ASSERT(0);
}
+
+ return ret;
}
/*
}
}
}
+
+static printLastError(const char * text)
+{
+ LPVOID buf;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ GetLastError(),
+ 0,
+ (LPTSTR) &buf,
+ 0,
+ NULL
+ );
+
+ fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf);
+ LocalFree(buf);
+}
+
+static int acceptNamedPipe()
+{
+ int ipcFd = -1;
+
+ if (! ConnectNamedPipe(hListen, &listenOverlapped))
+ {
+ switch (GetLastError())
+ {
+ case ERROR_PIPE_CONNECTED:
+
+ // A client connected after CreateNamedPipe but
+ // before ConnectNamedPipe. Its a good connection.
+
+ break;
+
+ case ERROR_IO_PENDING:
+
+ // Wait for a connection to complete.
+
+ while (WaitForSingleObject(listenOverlapped.hEvent,
+ ACCEPT_TIMEOUT) == WAIT_TIMEOUT)
+ {
+ if (shutdownPending)
+ {
+ OS_LibShutdown();
+ return -1;
+ }
+ }
+
+ break;
+
+ case ERROR_PIPE_LISTENING:
+
+ // The pipe handle is in nonblocking mode.
+
+ case ERROR_NO_DATA:
+
+ // The previous client closed its handle (and we failed
+ // to call DisconnectNamedPipe)
+
+ default:
+
+ printLastError("unexpected ConnectNamedPipe() error");
+ }
+ }
+
+ ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
+ if (ipcFd == -1)
+ {
+ DisconnectNamedPipe(hListen);
+ }
+
+ return ipcFd;
+}
+
+static int acceptSocket(const char *webServerAddrs)
+{
+ struct sockaddr sockaddr;
+ int sockaddrLen = sizeof(sockaddr);
+ fd_set readfds;
+ const struct timeval timeout = {1, 0};
+ SOCKET hSock;
+ int ipcFd = -1;
+
+ FD_ZERO(&readfds);
+ FD_SET((unsigned int) hListen, &readfds);
+ while (select(0, &readfds, NULL, NULL, &timeout) == 0)
+ {
+ if (shutdownPending)
+ {
+ OS_LibShutdown();
+ return -1;
+ }
+ }
+
+ hSock = (webServerAddrs == NULL)
+ ? accept((SOCKET) hListen,
+ &sockaddr,
+ &sockaddrLen)
+ : WSAAccept((unsigned int) hListen,
+ &sockaddr,
+ &sockaddrLen,
+ isAddrOK,
+ (DWORD) webServerAddrs);
+
+
+ if (hSock == INVALID_SOCKET)
+ {
+ // Can I use FormatMessage()?
+ fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError());
+ return -1;
+ }
+
+ ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
+ if (ipcFd == -1)
+ {
+ closesocket(hSock);
+ }
+
+ return ipcFd;
+}
+
/*
*----------------------------------------------------------------------
*
* OS_Accept --
*
- * Accepts a new FastCGI connection. This routine knows whether
- * we're dealing with TCP based sockets or NT Named Pipes for IPC.
+ * Accepts a new FastCGI connection. This routine knows whether
+ * we're dealing with TCP based sockets or NT Named Pipes for IPC.
+ *
+ * fail_on_intr is ignored in the Win lib.
*
* Results:
* -1 if the operation fails, otherwise this is a valid IPC fd.
*
- * Side effects:
- * New IPC connection is accepted.
- *
*----------------------------------------------------------------------
*/
int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
{
- /* XXX This is broken for listen_sock & fail_on_intr */
int ipcFd = -1;
- BOOL pConnected;
- SOCKET hSock;
+
+ // @todo Muliple listen sockets and sockets other than 0 are not
+ // supported due to the use of globals.
+ if (listen_sock != (int) hListen)
+ {
+ fprintf(stderr, "illegal listen_sock value (%d) for OS_Accept()\n", listen_sock);
+ return -1;
+ }
if (shutdownPending)
{
+ OS_LibShutdown();
return -1;
}
{
if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
{
+ printLastError("WaitForSingleObject() failed");
return -1;
}
}
if (shutdownPending)
{
- if (acceptMutex != INVALID_HANDLE_VALUE)
- {
- ReleaseMutex(acceptMutex);
- }
- return -1;
+ OS_LibShutdown();
}
-
- if (listenType == FD_PIPE_SYNC)
+ else if (listenType == FD_PIPE_SYNC)
{
- pConnected = ConnectNamedPipe(hListen, &listenOverlapped)
- ? TRUE
- : (GetLastError() == ERROR_PIPE_CONNECTED);
-
- if (! pConnected)
- {
- while (WaitForSingleObject(listenOverlapped.hEvent, 1000) == WAIT_TIMEOUT)
- {
- if (shutdownPending)
- {
- if (acceptMutex != INVALID_HANDLE_VALUE)
- {
- ReleaseMutex(acceptMutex);
- }
- CancelIo(hListen);
- return -1;
- }
- }
- }
-
- if (acceptMutex != INVALID_HANDLE_VALUE)
- {
- ReleaseMutex(acceptMutex);
- }
-
- ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
- if (ipcFd == -1)
- {
- DisconnectNamedPipe(hListen);
- }
+ ipcFd = acceptNamedPipe();
}
else if (listenType == FD_SOCKET_SYNC)
{
- struct sockaddr sockaddr;
- int sockaddrLen = sizeof(sockaddr);
- fd_set readfds;
- const struct timeval timeout = {1, 0};
-
- FD_ZERO(&readfds);
- FD_SET((unsigned int) hListen, &readfds);
-
- while (select(0, &readfds, NULL, NULL, &timeout) == 0)
- {
- if (shutdownPending)
- {
- return -1;
- }
- }
-
- hSock = (webServerAddrs == NULL)
- ? accept((SOCKET) hListen,
- &sockaddr,
- &sockaddrLen)
- : WSAAccept((unsigned int) hListen,
- &sockaddr,
- &sockaddrLen,
- isAddrOK,
- (DWORD) webServerAddrs);
-
- if (acceptMutex != INVALID_HANDLE_VALUE)
- {
- ReleaseMutex(acceptMutex);
- }
-
- if (hSock == -1)
- {
- return -1;
- }
-
- ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
- if (ipcFd == -1)
- {
- closesocket(hSock);
- }
+ ipcFd = acceptSocket(webServerAddrs);
}
else
{
- ASSERT(0);
+ fprintf(stderr, "unknown listenType (%d)\n", listenType);
}
+ if (acceptMutex != INVALID_HANDLE_VALUE)
+ {
+ ReleaseMutex(acceptMutex);
+ }
+
return ipcFd;
}
*/
int OS_IsFcgi(int sock)
{
- /* XXX This is broken for sock */
- if(listenType == FD_UNUSED) {
- return FALSE;
- } else {
- return TRUE;
- }
+ // This is still broken. There is not currently a way to differentiate
+ // a CGI from a FCGI pipe (try the Unix method).
+
+ return (fdTable[sock].type != FD_UNUSED);
}
/*