📄 os_win32.c
字号:
/* * 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.) */#ifndef lintstatic const char rcsid[] = "$Id: os_win32.c,v 1.33 2002/03/05 18:15:15 robs Exp $";#endif /* not lint */#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 <process.h>#define DLLAPI __declspec(dllexport)#include "fcgimisc.h"#include "fcgios.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"static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;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 * supported by the FD_TABLE structure. * * XXX: Not all currently supported. This allows for future * functionality. */typedef enum { FD_UNUSED, FD_FILE_SYNC, FD_FILE_ASYNC, FD_SOCKET_SYNC, FD_SOCKET_ASYNC, FD_PIPE_SYNC, FD_PIPE_ASYNC} FILE_TYPE;typedef union { HANDLE fileHandle; SOCKET sock; unsigned int value;} DESCRIPTOR;/* * Structure used to map file handle and socket handle * values into values that can be used to create unix-like * select bitmaps, read/write for both sockets/files. */struct FD_TABLE { DESCRIPTOR fid; FILE_TYPE type; char *path; DWORD Errno; unsigned long instance; int status; int offset; /* only valid for async file writes */ LPDWORD offsetHighPtr; /* pointers to offset high and low words */ LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */ HANDLE hMapMutex; /* mutex handle for multi-proc offset update */ LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */};/* * XXX Note there is no dyanmic sizing of this table, so if the * number of open file descriptors exceeds WIN32_OPEN_MAX the * app will blow up. */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) */ OS_AsyncProc procPtr; /* callback routine */ ClientData clientData; /* callback argument */ ClientData clientData1; /* additional clientData */};typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";static FILE_TYPE listenType = FD_UNUSED;// XXX This should be a DESCRIPTORstatic HANDLE hListen = INVALID_HANDLE_VALUE;static BOOLEAN libInitialized = FALSE;/* *-------------------------------------------------------------- * * Win32NewDescriptor -- * * Set up for I/O descriptor masquerading. * * Results: * Returns "fake id" which masquerades as a UNIX-style "small * non-negative integer" file/socket descriptor. * Win32_* routine below will "do the right thing" based on the * descriptor's actual type. -1 indicates failure. * * Side effects: * Entry in fdTable is reserved to represent the socket/file. * *-------------------------------------------------------------- */static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd){ int index = -1; EnterCriticalSection(&fdTableCritical); /* * 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) { index = desiredFd; } } else if (fd > 0) { if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED) { index = fd; } else { int i; for (i = 1; i < WIN32_OPEN_MAX; ++i) { if (fdTable[i].type == FD_UNUSED) { index = i; break; } } } } 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; } LeaveCriticalSection(&fdTableCritical); return index;}/* *-------------------------------------------------------------- * * StdinThread-- * * This thread performs I/O on stadard input. It is needed * because you can't guarantee that all applications will * create standard input with sufficient access to perform * asynchronous I/O. Since we don't want to block the app * reading from stdin we make it look like it's using I/O * completion ports to perform async I/O. * * Results: * Data is read from stdin and posted to the io completion * port. * * Side effects: * None. * *-------------------------------------------------------------- */static void StdinThread(void * startup) { int doIo = TRUE; 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 * request to terminate the thread arrives (fd = -1). */ if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd, (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) { doIo = 0; break; } ASSERT((fd == STDIN_FILENO) || (fd == -1)); if(fd == -1) { doIo = 0; break; } ASSERT(pOv->clientData1 != NULL); if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead, &bytesRead, NULL)) { PostQueuedCompletionStatus(hIoCompPort, bytesRead, STDIN_FILENO, (LPOVERLAPPED)pOv); } else { doIo = 0; break; } } ExitThread(0);}void OS_ShutdownPending(void){ shutdownPending = TRUE;}static void ShutdownRequestThread(void * arg){ HANDLE shutdownEvent = (HANDLE) arg; WaitForSingleObject(shutdownEvent, INFINITE); shutdownPending = TRUE; if (listenType == FD_PIPE_SYNC) { // 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.}/* *-------------------------------------------------------------- * * OS_LibInit -- * * Set up the OS library for use. * * Results: * Returns 0 if success, -1 if not. * * Side effects: * Sockets initialized, pseudo file descriptors setup, etc. * *-------------------------------------------------------------- */int OS_LibInit(int stdioFds[3]){ WORD wVersion; WSADATA wsaData; int err; int fakeFd; char *cLenPtr = NULL; char *val = NULL; if(libInitialized) return 0; InitializeCriticalSection(&fdTableCritical); /* * Initialize windows sockets library. */ wVersion = MAKEWORD(2,0); err = WSAStartup( wVersion, &wsaData ); if (err) { fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); exit(111); } /* * Create the I/O completion port to be used for our I/O queue. */ if (hIoCompPort == INVALID_HANDLE_VALUE) { hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hIoCompPort == INVALID_HANDLE_VALUE) { printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * 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); if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1) { return -1; } } if (acceptMutex == INVALID_HANDLE_VALUE) { /* 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 * valid handle to a listener object. In this case, both the * "stdout" and "stderr" handles will be INVALID (ie. closed) by * the starting process. * * The trick is determining if this is a pipe or a socket... * * XXX: Add the async accept test to determine socket or handle to a * pipe!!! */ if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_ERROR_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; } CloseHandle(oldStdIn); /* * Set the pipe handle state so that it operates in wait mode. * * NOTE: The listenFd is not mapped to a pseudo file descriptor * as all work done on it is contained to the OS library. * * XXX: Initial assumption is that SetNamedPipeHandleState will * fail if this is an IP socket... */ if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) { listenType = FD_PIPE_SYNC; } else { listenType = FD_SOCKET_SYNC; } } /* * If there are no stdioFds passed in, we're done. */ if(stdioFds == NULL) { libInitialized = 1; return 0; } /* * Setup standard input asynchronous I/O. There is actually a separate * thread spawned for this purpose. The reason for this is that some * web servers use anonymous pipes for the connection between itself * and a CGI application. Anonymous pipes can't perform asynchronous * I/O or use I/O completion ports. Therefore in order to present a * consistent I/O dispatch model to an application we emulate I/O * completion port behavior by having the standard input thread posting * messages to the hIoCompPort which look like a complete overlapped * I/O structure. This keeps the event dispatching simple from the * application perspective. */ stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDIN_FILENO], HANDLE_FLAG_INHERIT, 0)) {/* * XXX: Causes error when run from command line. Check KB err = GetLastError(); DebugBreak(); exit(99); */ } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDIN_FILENO], STDIN_FILENO)) == -1) { return -1; } else { /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ stdioFds[STDIN_FILENO] = fakeFd; } /* * Create the I/O completion port to be used for communicating with
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -