* significantly more enjoyable.)
*/
#ifndef lint
-static const char rcsid[] = "$Id: os_win32.c,v 1.8 2000/11/05 17:09:35 robs Exp $";
+static const char rcsid[] = "$Id: os_win32.c,v 1.9 2001/03/26 20:04:56 robs Exp $";
#endif /* not lint */
#include "fcgi_config.h"
#include <assert.h>
#include <stdio.h>
#include <sys/timeb.h>
-#include <windows.h>
+#include <Winsock2.h>
+#include <Windows.h>
#include "fcgios.h"
#define ASSERT assert
-#define WIN32_OPEN_MAX 32 /* XXX: Small hack */
+#define WIN32_OPEN_MAX 128 /* XXX: Small hack */
#define MUTEX_VARNAME "_FCGI_MUTEX_"
-
+#define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE};
-static HANDLE hPipeMutex = INVALID_HANDLE_VALUE;;
-static char pipeMutexEnv[80] = "";
+static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
+
+static BOOLEAN shutdownPending = FALSE;
/*
* An enumeration of the file types
static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
static int listenType = FD_UNUSED;
+
+// XXX This should be a DESCRIPTOR
static HANDLE hListen = INVALID_HANDLE_VALUE;
-static int libInitialized = 0;
+
+static OVERLAPPED listenOverlapped;
+static BOOLEAN libInitialized = FALSE;
\f
/*
* as the caller wanted to get this mapping. This is typically
* only used for mapping stdio handles.
*/
- if ((desiredFd >= 0) &&
- (desiredFd < WIN32_OPEN_MAX)) {
-
- if(fdTable[desiredFd].type == FD_UNUSED) {
+ if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
+ {
+ if (fdTable[desiredFd].type != FD_UNUSED)
+ {
+ return -1;
+ }
index = desiredFd;
- goto found_entry;
- } else {
- return -1;
}
+ else
+ {
+ // See if the entry that matches "fd" is available.
- }
-
- /*
- * Next see if the entry that matches "fd" is available.
- */
- if ((fd > 0) &&
- (fd < WIN32_OPEN_MAX) && (fdTable[fd].type == FD_UNUSED)) {
- index = fd;
- goto found_entry;
- }
+ if (fd <= 0 || fd >= WIN32_OPEN_MAX)
+ {
+ return -1;
+ }
- /*
- * Scan entries for one we can use. Start at 1 (0 fake id fails
- * in some cases). -K*
- */
- for (index = 1; index < WIN32_OPEN_MAX; index++)
- if (fdTable[index].type == FD_UNUSED)
- break;
+ if (fdTable[fd].type == FD_UNUSED)
+ {
+ index = fd;
+ }
+ else
+ {
+ // Find an entry we can use.
+ // Start at 1 (0 fake id fails in some cases).
+
+ for (index = 1; index < WIN32_OPEN_MAX; index++)
+ {
+ if (fdTable[index].type == FD_UNUSED)
+ {
+ break;
+ }
+ }
- /* If no table entries are available, return error. */
- if (index == WIN32_OPEN_MAX) {
- SetLastError(WSAEMFILE);
- DebugBreak();
- return -1;
+ if (index == WIN32_OPEN_MAX)
+ {
+ SetLastError(WSAEMFILE);
+ return -1;
+ }
+ }
}
-found_entry:
fdTable[index].fid.value = fd;
fdTable[index].type = type;
fdTable[index].path = NULL;
ExitThread(0);
}
-\f
+static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
+{
+ HANDLE shutdownEvent = (HANDLE) arg;
+
+ if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
+ {
+ // Assuming it will happen again, all we can do is exit the thread
+ return -1;
+ }
+ else
+ {
+ // "Simple reads and writes to properly-aligned 32-bit variables are atomic"
+ shutdownPending = TRUE;
+
+ // Before an accept() is entered the shutdownPending flag is checked.
+ // If set, OS_Accept() will return -1. If not, it waits
+ // on a connection request for one second, checks the flag, & repeats.
+ // Only one process/thread is allowed to do this at time by
+ // wrapping the accept() with mutex.
+ return 0;
+ }
+}
+
/*
*--------------------------------------------------------------
*
WSADATA wsaData;
int err;
int fakeFd;
- DWORD pipeMode;
DWORD threadId;
char *cLenPtr = NULL;
- char *mutexPtr = NULL;
-
+ char *val = NULL;
+
if(libInitialized)
return 0;
/*
* Initialize windows sockets library.
*/
- wVersion = MAKEWORD(1,1);
+ wVersion = MAKEWORD(2,0);
err = WSAStartup( wVersion, &wsaData );
if (err) {
fprintf(stderr, "Error starting Windows Sockets. Error: %d",
}
/*
+ * If a shutdown event is in the env, save it (I don't see any to
+ * remove it from the environment out from under the application).
+ * Spawn a thread to wait on the shutdown request.
+ */
+ val = getenv(SHUTDOWN_EVENT_NAME);
+ if (val != NULL)
+ {
+ HANDLE shutdownEvent = (HANDLE) atoi(val);
+
+ putenv(SHUTDOWN_EVENT_NAME"=");
+
+ if (! CreateThread(NULL, 0, ShutdownRequestThread,
+ shutdownEvent, 0, NULL))
+ {
+ return -1;
+ }
+ }
+
+ /*
+ * If an accept mutex is in the env, save it and remove it.
+ */
+ val = getenv(MUTEX_VARNAME);
+ if (val != NULL)
+ {
+ acceptMutex = (HANDLE) atoi(val);
+ }
+
+
+ /*
* Determine if this library is being used to listen for FastCGI
* connections. This is communicated by STDIN containing a
* valid handle to a listener object. In this case, both the
*/
if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
(GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
- (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) {
+ (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
+ {
+ DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
+ HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
+
+ // Move the handle to a "low" number
+ if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
+ GetCurrentProcess(), &hListen,
+ 0, TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ return -1;
+ }
+
+ if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
+ {
+ return -1;
+ }
- hListen = GetStdHandle(STD_INPUT_HANDLE);
+ CloseHandle(oldStdIn);
/*
* Set the pipe handle state so that it operates in wait mode.
* XXX: Initial assumption is that SetNamedPipeHandleState will
* fail if this is an IP socket...
*/
- pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
- if(SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) {
+ if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
+ {
listenType = FD_PIPE_SYNC;
- /*
- * Lookup the mutex. If one is found, save it and
- * remove it from the env table if it's not already
- * been done.
- */
- mutexPtr = getenv(MUTEX_VARNAME);
- if(mutexPtr != NULL) {
- hPipeMutex = (HANDLE)atoi(mutexPtr);
- putenv(MUTEX_VARNAME"=");
- }
- } else {
+ listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ }
+ else
+ {
listenType = FD_SOCKET_SYNC;
}
}
int bpLen;
int servLen;
struct sockaddr_in sockAddr;
- char host[1024];
+ char buf[1024];
short port;
int tcp = FALSE;
int flag = 1;
char *tp;
+ HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
+
+ if (mutex == NULL)
+ {
+ return -1;
+ }
+
+ if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
+ {
+ return -1;
+ }
+
+ // This is a nail for listening to more than one port..
+ // This should really be handled by the caller.
+ _snprintf(buf, 1024, MUTEX_VARNAME "=%d", (int) mutex);
+ buf[1023] = '\0';
+ putenv(strdup(buf));
+
+ // There's nothing to be gained (at the moment) by a shutdown Event
+
+ strncpy(buf, bindPath, 1024);
+ buf[1023] = '\0';
- strcpy(host, bindPath);
- if((tp = strchr(host, ':')) != 0) {
+ if((tp = strchr(buf, ':')) != 0) {
*tp++ = 0;
if((port = atoi(tp)) == 0) {
*--tp = ':';
tcp = TRUE;
}
}
- if(tcp && (*host && strcmp(host, "localhost") != 0)) {
+
+ if(tcp && (*buf && strcmp(buf, "localhost") != 0)) {
fprintf(stderr, "To start a service on a TCP port can not "
"specify a host name.\n"
"You should either use \"localhost:<port>\" or "
sa.bInheritHandle = TRUE; /* This will be inherited by the
* FastCGI process
*/
-
- /*
- * Create a mutex to be used to synchronize access to accepting a
- * connection on a named pipe. We don't want to own this at creation
- * time but would rather let the first process that goes for it
- * be able to acquire it.
- */
- hPipeMutex = CreateMutex(NULL, FALSE, NULL);
- if(hPipeMutex == NULL) {
- return -1;
- }
- if(!SetHandleInformation(hPipeMutex, HANDLE_FLAG_INHERIT,
- TRUE)) {
- return -1;
- }
- sprintf(pipeMutexEnv, "%s=%d", MUTEX_VARNAME, (int)hPipeMutex);
- putenv(pipeMutexEnv);
-
/*
* Create a unique name to be used for the socket bind path.
* Make sure that this name is unique and that there's no process
short port;
int tcp = FALSE;
- strcpy(host, bindPath);
- if((tp = strchr(host, ':')) != 0) {
+ strncpy(host, bindPath, 1024);
+ host[1023] = '\0';
+
+ if ((tp = strchr(host, ':')) != 0) {
*tp++ = 0;
if((port = atoi(tp)) == 0) {
*--tp = ':';
tcp = TRUE;
}
}
+
if(tcp == TRUE) {
struct hostent *hp;
if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
case FD_PIPE_ASYNC:
case FD_FILE_SYNC:
case FD_FILE_ASYNC:
- /*
- * CloseHandle returns: TRUE success, 0 failure
- */
- if (CloseHandle(fdTable[fd].fid.fileHandle) == FALSE)
- ret = -1;
break;
- case FD_SOCKET_SYNC:
+
+ case FD_SOCKET_SYNC:
case FD_SOCKET_ASYNC:
/*
* Closing a socket that has an async read outstanding causes a
return 0;
}
+
+static int CALLBACK isAddrOK(LPWSABUF lpCallerId,
+ LPWSABUF dc0,
+ LPQOS dc1,
+ LPQOS dc2,
+ LPWSABUF dc3,
+ LPWSABUF dc4,
+ GROUP *dc5,
+ DWORD dwCallbackData)
+{
+ const char *okAddrs = (char *) dwCallbackData;
+ struct sockaddr *sockaddr = (struct sockaddr *) lpCallerId->buf;
+
+ if (okAddrs == NULL || sockaddr->sa_family != AF_INET)
+ {
+ return TRUE;
+ }
+ else
+ {
+ static const char *token = " ,;:\t";
+ struct sockaddr_in * inet_sockaddr = (struct sockaddr_in *) sockaddr;
+ char *ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
+ char *p = strstr(okAddrs, ipaddr);
+
+ if (p == NULL)
+ {
+ return FALSE;
+ }
+ else if (p == okAddrs)
+ {
+ p += strlen(ipaddr);
+ return (strchr(token, *p) != NULL);
+ }
+ else if (strchr(token, *--p))
+ {
+ p += strlen(ipaddr) + 1;
+ return (strchr(token, *p) != NULL);
+ }
+ else
+ {
+ return FALSE;
+ }
+ }
+}
+
\f
/*
*----------------------------------------------------------------------
int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
{
/* XXX This is broken for listen_sock & fail_on_intr */
- struct sockaddr_in sa;
- int isNewConnection;
int ipcFd = -1;
BOOL pConnected;
- HANDLE hDup;
SOCKET hSock;
- int clilen = sizeof(sa);
- DWORD waitForStatus;
-
- switch(listenType) {
- case FD_PIPE_SYNC:
- waitForStatus = WaitForSingleObject(hPipeMutex,INFINITE);
- switch(waitForStatus) {
- case WAIT_OBJECT_0:
- case WAIT_ABANDONED:
- break;
+ if (shutdownPending)
+ {
+ return -1;
+ }
- case WAIT_FAILED:
- default:
+ // The mutex is to keep other processes (and threads, when supported)
+ // from going into the accept cycle. The accept cycle needs to
+ // periodically break out to check the state of the shutdown flag
+ // and there's no point to having more than one thread do that.
+
+ if (acceptMutex != INVALID_HANDLE_VALUE)
+ {
+ if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
+ {
return -1;
}
-
- /*
- * We have the mutex, go for the connection.
- */
- pConnected = ConnectNamedPipe(hListen, NULL) ?
- TRUE : (GetLastError() == ERROR_PIPE_CONNECTED);
-
- ReleaseMutex(hPipeMutex);
- if(pConnected) {
- /*
- * Success...
- */
- if (!DuplicateHandle(GetCurrentProcess(), hListen,
- GetCurrentProcess(), &hDup, 0,
- TRUE, /* allow inheritance */
- DUPLICATE_SAME_ACCESS)) {
- return -1;
- }
- ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hDup, -1);
- if(ipcFd == -1) {
- DisconnectNamedPipe(hListen);
- CloseHandle(hDup);
+ }
+
+ if (shutdownPending)
+ {
+ if (acceptMutex != INVALID_HANDLE_VALUE)
+ {
+ ReleaseMutex(acceptMutex);
+ }
+ return -1;
+ }
+
+ 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;
+ }
}
- return ipcFd;
- } else {
- return -1;
+ }
+
+ if (acceptMutex != INVALID_HANDLE_VALUE)
+ {
+ ReleaseMutex(acceptMutex);
+ }
+
+ ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
+ if (ipcFd == -1)
+ {
+ DisconnectNamedPipe(hListen);
+ }
}
- break;
+ 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);
+ }
- case FD_SOCKET_SYNC:
- hSock = accept((int)hListen, (struct sockaddr *) &sa, &clilen);
- if(hSock == -1) {
- return -1;
- } else if (sa.sin_family != AF_INET) { /* What are we? */
- closesocket(hSock);
- hSock = (SOCKET)-1;
- return -1;
- } else {
- char *tp1, *tp2;
- int match = 0;
- if (webServerAddrs == NULL)
- isNewConnection = TRUE;
- else {
- tp1 = (char *) malloc(strlen(webServerAddrs)+1);
- ASSERT(tp1 != NULL);
- strcpy(tp1, webServerAddrs);
- while(tp1) {
- if ((tp2 = strchr(tp1, ',')) != NULL)
- *tp2++ = 0;
-
- if (inet_addr(tp1) == sa.sin_addr.s_addr) {
- match = 1;
- break;
- }
- tp1 = tp2;
- }
- free(tp1);
- if (match)
- isNewConnection = TRUE;
- else {
- closesocket(hSock);
- hSock = (SOCKET)-1;
- return -1;
- }
+ if (hSock == -1)
+ {
+ return -1;
+ }
+
+ ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
+ if (ipcFd == -1)
+ {
+ closesocket(hSock);
}
- }
-
- ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
- if(ipcFd == -1) {
- closesocket(hSock);
- }
- return ipcFd;
- break;
-
- case FD_UNUSED:
- default:
- exit(101);
- break;
-
}
+ else
+ {
+ ASSERT(0);
+ }
+
+ return ipcFd;
}
\f
/*