/*
* os_win32.c --
*
- *
- * Copyright (c) 1995 Open Market, Inc.
- * All rights reserved.
- *
- * This file contains proprietary and confidential information and
- * remains the unpublished property of Open Market, Inc. Use,
- * disclosure, or reproduction is prohibited except as permitted by
- * express written license agreement with Open Market, Inc.
- *
* Bill Snapper
* snapper@openmarket.com
*
* (Special thanks to Karen and Bill. They made my job much easier and
* significantly more enjoyable.)
+ *
+ * Copyright (c) 1996 Open Market, Inc.
+ *
+ * See the file "LICENSE" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#ifndef lint
-static const char rcsid[] = "$Id: os_win32.c,v 1.15 2001/06/19 17:11:39 robs Exp $";
+static const char rcsid[] = "$Id: os_win32.c,v 1.35 2004/01/31 17:47:07 robs Exp $";
#endif /* not lint */
#define WIN32_LEAN_AND_MEAN
#include <assert.h>
#include <stdio.h>
#include <sys/timeb.h>
+#include <process.h>
+#include <signal.h>
#define DLLAPI __declspec(dllexport)
-#include "fcgios.h"
+
#include "fcgimisc.h"
+#include "fcgios.h"
#define WIN32_OPEN_MAX 128 /* XXX: Small hack */
static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
INVALID_HANDLE_VALUE};
+// This is a nail for listening to more than one port..
static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
static BOOLEAN shutdownPending = FALSE;
+static BOOLEAN shutdownNow = FALSE;
/*
* An enumeration of the file types
static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
-static enum FILE_TYPE listenType = FD_UNUSED;
+static FILE_TYPE listenType = FD_UNUSED;
// XXX This should be a DESCRIPTOR
static HANDLE hListen = INVALID_HANDLE_VALUE;
-static OVERLAPPED listenOverlapped;
static BOOLEAN libInitialized = FALSE;
/*
*
*--------------------------------------------------------------
*/
-static void StdinThread(LPDWORD startup){
-
+static void StdinThread(void * startup)
+{
int doIo = TRUE;
- int fd;
- int bytesRead;
+ unsigned long fd;
+ unsigned long bytesRead;
POVERLAPPED_REQUEST pOv;
+ // Touch the arg to prevent warning
+ startup = NULL;
+
while(doIo) {
/*
* Block until a request to read from stdin comes in or a
ExitThread(0);
}
-static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
+void OS_ShutdownPending(void)
+{
+ shutdownPending = TRUE;
+}
+
+static void ShutdownRequestThread(void * arg)
{
HANDLE shutdownEvent = (HANDLE) arg;
- if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
+ WaitForSingleObject(shutdownEvent, INFINITE);
+
+ shutdownPending = TRUE;
+
+ // emulate the unix behaviour
+ raise(SIGTERM);
+
+ if (listenType == FD_PIPE_SYNC)
{
- // 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;
+ // Its a hassle to get ConnectNamedPipe to return early,
+ // so just wack the whole process - yes, this will toast
+ // any requests in progress, but at least its a clean
+ // shutdown (its better than TerminateProcess())
+ exit(0);
}
+
+ // FD_SOCKET_SYNC: When in Accept(), select() is used to poll
+ // the shutdownPending flag - yeah this isn't pretty either
+ // but its only one process doing it if an Accept mutex is used.
+ // This at least buys no toasted requests.
}
/*
WSADATA wsaData;
int err;
int fakeFd;
- DWORD threadId;
char *cLenPtr = NULL;
char *val = NULL;
{
HANDLE shutdownEvent = (HANDLE) atoi(val);
- putenv(SHUTDOWN_EVENT_NAME"=");
-
- if (! CreateThread(NULL, 0, ShutdownRequestThread,
- shutdownEvent, 0, NULL))
+ if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1)
{
return -1;
}
}
- /*
- * If an accept mutex is in the env, save it and remove it.
- */
- val = getenv(MUTEX_VARNAME);
- if (val != NULL)
+ if (acceptMutex == INVALID_HANDLE_VALUE)
{
- acceptMutex = (HANDLE) atoi(val);
+ /* If an accept mutex is in the env, use 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
if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
{
listenType = FD_PIPE_SYNC;
- listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
}
else
{
*/
if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
atoi(cLenPtr) > 0) {
- hStdinThread = CreateThread(NULL, 8192,
- (LPTHREAD_START_ROUTINE)&StdinThread,
- NULL, 0, &threadId);
- if (hStdinThread == NULL) {
+ hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL);
+ if (hStdinThread == (HANDLE) -1) {
printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
GetLastError());
return -1;
strncpy(buf, p, 6);
buf[5] = '\0';
- port = atoi(buf);
+ port = (short) atoi(buf);
}
return port;
{
int pseudoFd = -1;
short port = getPort(bindPath);
- HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
- char * mutexEnvString;
- if (mutex == NULL)
+ if (acceptMutex == INVALID_HANDLE_VALUE)
{
- return -1;
- }
-
- if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
- {
- return -1;
+ acceptMutex = CreateMutex(NULL, FALSE, NULL);
+ if (acceptMutex == NULL) return -2;
+ if (! SetHandleInformation(acceptMutex, HANDLE_FLAG_INHERIT, TRUE)) return -3;
}
- // This is a nail for listening to more than one port..
- // This should really be handled by the caller.
-
- mutexEnvString = malloc(strlen(MUTEX_VARNAME) + 7);
- sprintf(mutexEnvString, MUTEX_VARNAME "=%d", (int) mutex);
- putenv(mutexEnvString);
-
// There's nothing to be gained (at the moment) by a shutdown Event
if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
listenSock = socket(AF_INET, SOCK_STREAM, 0);
if (listenSock == INVALID_SOCKET)
{
- return -1;
+ return -4;
}
- if (! bind(listenSock, (struct sockaddr *) &sockAddr, sockLen)
- || ! listen(listenSock, backlog))
+ if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) )
{
- return -1;
+ return -12;
+ }
+
+ if (listen(listenSock, backlog))
+ {
+ return -5;
}
pseudoFd = Win32NewDescriptor(listenType, listenSock, -1);
if (pseudoFd == -1)
{
closesocket(listenSock);
- return -1;
+ return -6;
}
hListen = (HANDLE) listenSock;
if (! pipePath)
{
- return -1;
+ return -7;
}
strcpy(pipePath, bindPathPrefix);
strcat(pipePath, bindPath);
hListenPipe = CreateNamedPipe(pipePath,
- PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+ PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
PIPE_UNLIMITED_INSTANCES,
4096, 4096, 0, NULL);
if (hListenPipe == INVALID_HANDLE_VALUE)
{
- return -1;
+ return -8;
}
if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
{
- return -1;
+ return -9;
}
pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1);
if (pseudoFd == -1)
{
CloseHandle(hListenPipe);
- return -1;
+ return -10;
}
hListen = (HANDLE) hListenPipe;
ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
+ if (shutdownNow) return -1;
+
switch (fdTable[fd].type)
{
case FD_FILE_SYNC:
ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
+ if (shutdownNow) return -1;
+
switch (fdTable[fd].type)
{
case FD_FILE_SYNC:
*
*--------------------------------------------------------------
*/
-int OS_Close(int fd)
+int OS_Close(int fd, int shutdown_ok)
{
int ret = 0;
case FD_PIPE_ASYNC:
case FD_FILE_SYNC:
case FD_FILE_ASYNC:
- break;
+
+ break;
- case FD_SOCKET_SYNC:
+ case FD_SOCKET_SYNC:
case FD_SOCKET_ASYNC:
- /*
- * Closing a socket that has an async read outstanding causes a
- * tcp reset and possible data loss. The shutdown call seems to
- * prevent this.
- */
- shutdown(fdTable[fd].fid.sock, 2);
- /*
- * closesocket returns: 0 success, SOCKET_ERROR failure
- */
- if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
- ret = -1;
- break;
+
+ /*
+ * shutdown() the send side and then read() from client until EOF
+ * or a timeout expires. This is done to minimize the potential
+ * that a TCP RST will be sent by our TCP stack in response to
+ * receipt of additional data from the client. The RST would
+ * cause the client to discard potentially useful response data.
+ */
+
+ if (shutdown_ok)
+ {
+ if (shutdown(fdTable[fd].fid.sock, SD_SEND) == 0)
+ {
+ struct timeval tv;
+ fd_set rfds;
+ int sock = fdTable[fd].fid.sock;
+ int rv;
+ char trash[1024];
+
+ FD_ZERO(&rfds);
+
+ do
+ {
+#pragma warning( disable : 4127 )
+ FD_SET((unsigned) sock, &rfds);
+#pragma warning( default : 4127 )
+
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ rv = select(sock + 1, &rfds, NULL, NULL, &tv);
+ }
+ while (rv > 0 && recv(sock, trash, sizeof(trash), 0) > 0);
+ }
+ }
+
+ closesocket(fdTable[fd].fid.sock);
+
+ break;
+
default:
- return -1; /* fake failure */
+
+ ret = -1; /* fake failure */
}
Win32FreeDescriptor(fd);
*/
int OS_DoIo(struct timeval *tmo)
{
- int fd;
- int bytes;
+ unsigned long fd;
+ unsigned long bytes;
POVERLAPPED_REQUEST pOv;
struct timeb tb;
int ms;
return 0;
}
-
-static int CALLBACK isAddrOK(LPWSABUF lpCallerId,
- LPWSABUF dc0,
- LPQOS dc1,
- LPQOS dc2,
- LPWSABUF dc3,
- LPWSABUF dc4,
- GROUP *dc5,
- DWORD dwCallbackData)
+static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs)
{
- const char *okAddrs = (char *) dwCallbackData;
- struct sockaddr *sockaddr = (struct sockaddr *) lpCallerId->buf;
+ static const char *token = " ,;:\t";
+ char *ipaddr;
+ char *p;
+
+ if (okAddrs == NULL) return TRUE;
+
+ ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
+ p = strstr(okAddrs, ipaddr);
- if (okAddrs == NULL || sockaddr->sa_family != AF_INET)
+ if (p == NULL) return FALSE;
+
+ if (p == okAddrs)
{
- return TRUE;
+ p += strlen(ipaddr);
+ return (strchr(token, *p) != NULL);
}
- 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;
- }
+ if (strchr(token, *--p) != NULL)
+ {
+ p += strlen(ipaddr) + 1;
+ return (strchr(token, *p) != NULL);
}
+
+ return FALSE;
}
-static printLastError(const char * text)
+#ifndef NO_WSAACEPT
+static int CALLBACK isAddrOKCallback(LPWSABUF lpCallerId,
+ LPWSABUF dc0,
+ LPQOS dc1,
+ LPQOS dc2,
+ LPWSABUF dc3,
+ LPWSABUF dc4,
+ GROUP *dc5,
+ DWORD data)
+{
+ struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf;
+
+ // Touch the args to avoid warnings
+ dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; dc5 = NULL;
+
+ if ((void *) data == NULL) return CF_ACCEPT;
+
+ if (sockaddr->sin_family != AF_INET) return CF_ACCEPT;
+
+ return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT;
+}
+#endif
+
+static void printLastError(const char * text)
{
LPVOID buf;
{
int ipcFd = -1;
- if (! ConnectNamedPipe(hListen, &listenOverlapped))
+ if (! ConnectNamedPipe(hListen, NULL))
{
switch (GetLastError())
{
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;
+ // The NamedPipe was opened with an Overlapped structure
+ // and there is a pending io operation. mod_fastcgi
+ // did this in 2.2.12 (fcgi_pm.c v1.52).
case ERROR_PIPE_LISTENING:
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)
+
+ for (;;)
{
- if (shutdownPending)
+ struct sockaddr sockaddr;
+ int sockaddrLen = sizeof(sockaddr);
+
+ for (;;)
{
- OS_LibShutdown();
- return -1;
+ const struct timeval timeout = {1, 0};
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+
+#pragma warning( disable : 4127 )
+ FD_SET((unsigned int) hListen, &readfds);
+#pragma warning( default : 4127 )
+
+ if (select(0, &readfds, NULL, NULL, &timeout) == 0)
+ {
+ if (shutdownPending)
+ {
+ OS_LibShutdown();
+ return -1;
+ }
+ }
+ else
+ {
+ break;
+ }
}
- }
-
- hSock = (webServerAddrs == NULL)
- ? accept((SOCKET) hListen,
- &sockaddr,
- &sockaddrLen)
- : WSAAccept((unsigned int) hListen,
- &sockaddr,
- &sockaddrLen,
- isAddrOK,
- (DWORD) webServerAddrs);
+#if NO_WSAACEPT
+ hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen);
+
+ if (hSock == INVALID_SOCKET)
+ {
+ break;
+ }
+
+ if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs))
+ {
+ break;
+ }
+
+ closesocket(hSock);
+#else
+ hSock = WSAAccept((unsigned int) hListen,
+ &sockaddr,
+ &sockaddrLen,
+ isAddrOKCallback,
+ (DWORD) webServerAddrs);
+
+ if (hSock != INVALID_SOCKET)
+ {
+ break;
+ }
+
+ if (WSAGetLastError() != WSAECONNREFUSED)
+ {
+ break;
+ }
+#endif
+ }
if (hSock == INVALID_SOCKET)
{
- // Can I use FormatMessage()?
+ /* Use FormatMessage() */
fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError());
return -1;
}
{
int ipcFd = -1;
+ // Touch args to prevent warnings
+ listen_sock = 0; fail_on_intr = 0;
+
// @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)
{
*
*----------------------------------------------------------------------
*/
-int OS_IpcClose(int ipcFd)
+int OS_IpcClose(int ipcFd, int shutdown)
{
- if (ipcFd == -1)
- return 0;
+ if (ipcFd == -1) return 0;
/*
* Catch it if fd is a bogus value
ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
ASSERT(fdTable[ipcFd].type != FD_UNUSED);
- switch(listenType) {
-
+ switch (listenType)
+ {
case FD_PIPE_SYNC:
- /*
- * Make sure that the client (ie. a Web Server in this case) has
- * read all data from the pipe before we disconnect.
- */
- if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
- return -1;
- if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
- OS_Close(ipcFd);
- return 0;
- } else {
- return -1;
- }
- break;
+ /*
+ * Make sure that the client (ie. a Web Server in this case) has
+ * read all data from the pipe before we disconnect.
+ */
+ if (! FlushFileBuffers(fdTable[ipcFd].fid.fileHandle)) return -1;
+
+ if (! DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) return -1;
+
+ /* fall through */
case FD_SOCKET_SYNC:
- OS_Close(ipcFd);
- return 0;
- break;
+
+ OS_Close(ipcFd, shutdown);
+ break;
case FD_UNUSED:
default:
- exit(106);
- break;
+
+ exit(106);
+ break;
}
+
+ return 0;
}
/*
*/
int OS_IsFcgi(int sock)
{
- // 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);
+ // Touch args to prevent warnings
+ sock = 0;
+
+ /* XXX This is broken for sock */
+
+ return (listenType != FD_UNUSED);
}
/*