X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=libfcgi%2Fos_win32.c;h=cb3550fc35fa1c867de2e38fdd88a568228322a0;hb=85d8cc6daa62cac18eebbeb6644e17d921541c24;hp=0b86c7d52f9a53a436f2538d76717cee063c68ba;hpb=63a3820b7d9220f9863ebdd1fb2df09f148dd4f8;p=catagits%2Ffcgi2.git diff --git a/libfcgi/os_win32.c b/libfcgi/os_win32.c index 0b86c7d..cb3550f 100755 --- a/libfcgi/os_win32.c +++ b/libfcgi/os_win32.c @@ -1,23 +1,19 @@ /* * 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.16 2001/06/19 21:03:13 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 @@ -27,10 +23,13 @@ static const char rcsid[] = "$Id: os_win32.c,v 1.16 2001/06/19 21:03:13 robs Exp #include #include #include +#include +#include #define DLLAPI __declspec(dllexport) -#include "fcgios.h" + #include "fcgimisc.h" +#include "fcgios.h" #define WIN32_OPEN_MAX 128 /* XXX: Small hack */ @@ -51,9 +50,11 @@ static HANDLE hStdinThread = INVALID_HANDLE_VALUE; 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 @@ -117,12 +118,11 @@ typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST; 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; /* @@ -220,13 +220,16 @@ static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd) * *-------------------------------------------------------------- */ -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 @@ -258,27 +261,35 @@ static void StdinThread(LPDWORD startup){ 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. } /* @@ -302,7 +313,6 @@ int OS_LibInit(int stdioFds[3]) WSADATA wsaData; int err; int fakeFd; - DWORD threadId; char *cLenPtr = NULL; char *val = NULL; @@ -345,25 +355,22 @@ int OS_LibInit(int stdioFds[3]) { 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 @@ -410,7 +417,6 @@ int OS_LibInit(int stdioFds[3]) if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) { listenType = FD_PIPE_SYNC; - listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } else { @@ -483,10 +489,8 @@ int OS_LibInit(int stdioFds[3]) */ 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("

OS_LibInit Failed to create STDIN thread! ERROR: %d

\r\n\r\n", GetLastError()); return -1; @@ -651,7 +655,7 @@ static short getPort(const char * bindPath) strncpy(buf, p, 6); buf[5] = '\0'; - port = atoi(buf); + port = (short) atoi(buf); } return port; @@ -679,26 +683,14 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) { 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))) @@ -726,13 +718,17 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) 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); @@ -740,7 +736,7 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) if (pseudoFd == -1) { closesocket(listenSock); - return -1; + return -6; } hListen = (HANDLE) listenSock; @@ -752,14 +748,14 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) 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); @@ -768,12 +764,12 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) 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); @@ -781,7 +777,7 @@ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) if (pseudoFd == -1) { CloseHandle(hListenPipe); - return -1; + return -10; } hListen = (HANDLE) hListenPipe; @@ -940,6 +936,8 @@ int OS_Read(int fd, char * buf, size_t len) ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); + if (shutdownNow) return -1; + switch (fdTable[fd].type) { case FD_FILE_SYNC: @@ -1001,6 +999,8 @@ int OS_Write(int fd, char * buf, size_t len) ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX); + if (shutdownNow) return -1; + switch (fdTable[fd].type) { case FD_FILE_SYNC: @@ -1352,7 +1352,7 @@ int OS_AsyncWrite(int fd, int offset, void *buf, int len, * *-------------------------------------------------------------- */ -int OS_Close(int fd) +int OS_Close(int fd, int shutdown_ok) { int ret = 0; @@ -1367,24 +1367,53 @@ int OS_Close(int fd) 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); @@ -1440,8 +1469,8 @@ int OS_CloseRead(int 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; @@ -1483,52 +1512,58 @@ int OS_DoIo(struct timeval *tmo) 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; - if (okAddrs == NULL || sockaddr->sa_family != AF_INET) + ipaddr = inet_ntoa(inet_sockaddr->sin_addr); + p = strstr(okAddrs, ipaddr); + + 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; @@ -1552,7 +1587,7 @@ static int acceptNamedPipe() { int ipcFd = -1; - if (! ConnectNamedPipe(hListen, &listenOverlapped)) + if (! ConnectNamedPipe(hListen, NULL)) { switch (GetLastError()) { @@ -1565,19 +1600,9 @@ static int acceptNamedPipe() 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: @@ -1605,39 +1630,75 @@ static int acceptNamedPipe() 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; } @@ -1670,6 +1731,9 @@ int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) { 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. @@ -1733,10 +1797,9 @@ int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) * *---------------------------------------------------------------------- */ -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 @@ -1744,33 +1807,32 @@ int OS_IpcClose(int ipcFd) 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; } /* @@ -1790,10 +1852,12 @@ int OS_IpcClose(int ipcFd) */ 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); } /*