5 * Copyright (c) 1995 Open Market, Inc.
8 * This file contains proprietary and confidential information and
9 * remains the unpublished property of Open Market, Inc. Use,
10 * disclosure, or reproduction is prohibited except as permitted by
11 * express written license agreement with Open Market, Inc.
14 * snapper@openmarket.com
16 * (Special thanks to Karen and Bill. They made my job much easier and
17 * significantly more enjoyable.)
20 static const char rcsid[] = "$Id: os_win32.c,v 1.14 2001/06/06 16:03:41 robs Exp $";
23 #include "fcgi_config.h"
25 #define DLLAPI __declspec(dllexport)
29 #include <sys/timeb.h>
37 #define WIN32_OPEN_MAX 128 /* XXX: Small hack */
39 #define MUTEX_VARNAME "_FCGI_MUTEX_"
40 #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
41 #define LOCALHOST "localhost"
43 static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
44 static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
45 static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
47 static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
48 INVALID_HANDLE_VALUE};
50 static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
52 static BOOLEAN shutdownPending = FALSE;
55 * An enumeration of the file types
56 * supported by the FD_TABLE structure.
58 * XXX: Not all currently supported. This allows for future
78 * Structure used to map file handle and socket handle
79 * values into values that can be used to create unix-like
80 * select bitmaps, read/write for both sockets/files.
87 unsigned long instance;
89 int offset; /* only valid for async file writes */
90 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
91 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
92 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
93 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
97 * XXX Note there is no dyanmic sizing of this table, so if the
98 * number of open file descriptors exceeds WIN32_OPEN_MAX the
101 static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
103 static CRITICAL_SECTION fdTableCritical;
105 struct OVERLAPPED_REQUEST {
106 OVERLAPPED overlapped;
107 unsigned long instance; /* file instance (won't match after a close) */
108 OS_AsyncProc procPtr; /* callback routine */
109 ClientData clientData; /* callback argument */
110 ClientData clientData1; /* additional clientData */
112 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
114 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
116 static enum FILE_TYPE listenType = FD_UNUSED;
118 // XXX This should be a DESCRIPTOR
119 static HANDLE hListen = INVALID_HANDLE_VALUE;
121 static OVERLAPPED listenOverlapped;
122 static BOOLEAN libInitialized = FALSE;
125 *--------------------------------------------------------------
127 * Win32NewDescriptor --
129 * Set up for I/O descriptor masquerading.
132 * Returns "fake id" which masquerades as a UNIX-style "small
133 * non-negative integer" file/socket descriptor.
134 * Win32_* routine below will "do the right thing" based on the
135 * descriptor's actual type. -1 indicates failure.
138 * Entry in fdTable is reserved to represent the socket/file.
140 *--------------------------------------------------------------
142 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
146 EnterCriticalSection(&fdTableCritical);
149 * If desiredFd is set, try to get this entry (this is used for
150 * mapping stdio handles). Otherwise try to get the fd entry.
151 * If this is not available, find a the first empty slot. .
153 if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
155 if (fdTable[desiredFd].type == FD_UNUSED)
162 if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
170 for (i = 1; i < WIN32_OPEN_MAX; ++i)
172 if (fdTable[i].type == FD_UNUSED)
183 fdTable[index].fid.value = fd;
184 fdTable[index].type = type;
185 fdTable[index].path = NULL;
186 fdTable[index].Errno = NO_ERROR;
187 fdTable[index].status = 0;
188 fdTable[index].offset = -1;
189 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
190 fdTable[index].hMapMutex = NULL;
191 fdTable[index].ovList = NULL;
194 LeaveCriticalSection(&fdTableCritical);
199 *--------------------------------------------------------------
203 * This thread performs I/O on stadard input. It is needed
204 * because you can't guarantee that all applications will
205 * create standard input with sufficient access to perform
206 * asynchronous I/O. Since we don't want to block the app
207 * reading from stdin we make it look like it's using I/O
208 * completion ports to perform async I/O.
211 * Data is read from stdin and posted to the io completion
217 *--------------------------------------------------------------
219 static void StdinThread(LPDWORD startup){
224 POVERLAPPED_REQUEST pOv;
228 * Block until a request to read from stdin comes in or a
229 * request to terminate the thread arrives (fd = -1).
231 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
232 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
237 ASSERT((fd == STDIN_FILENO) || (fd == -1));
242 ASSERT(pOv->clientData1 != NULL);
244 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
246 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
247 STDIN_FILENO, (LPOVERLAPPED)pOv);
257 static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
259 HANDLE shutdownEvent = (HANDLE) arg;
261 if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
263 // Assuming it will happen again, all we can do is exit the thread
268 // "Simple reads and writes to properly-aligned 32-bit variables are atomic"
269 shutdownPending = TRUE;
271 // Before an accept() is entered the shutdownPending flag is checked.
272 // If set, OS_Accept() will return -1. If not, it waits
273 // on a connection request for one second, checks the flag, & repeats.
274 // Only one process/thread is allowed to do this at time by
275 // wrapping the accept() with mutex.
281 *--------------------------------------------------------------
285 * Set up the OS library for use.
288 * Returns 0 if success, -1 if not.
291 * Sockets initialized, pseudo file descriptors setup, etc.
293 *--------------------------------------------------------------
295 int OS_LibInit(int stdioFds[3])
302 char *cLenPtr = NULL;
308 InitializeCriticalSection(&fdTableCritical);
311 * Initialize windows sockets library.
313 wVersion = MAKEWORD(2,0);
314 err = WSAStartup( wVersion, &wsaData );
316 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
322 * Create the I/O completion port to be used for our I/O queue.
324 if (hIoCompPort == INVALID_HANDLE_VALUE) {
325 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
327 if(hIoCompPort == INVALID_HANDLE_VALUE) {
328 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
335 * If a shutdown event is in the env, save it (I don't see any to
336 * remove it from the environment out from under the application).
337 * Spawn a thread to wait on the shutdown request.
339 val = getenv(SHUTDOWN_EVENT_NAME);
342 HANDLE shutdownEvent = (HANDLE) atoi(val);
344 putenv(SHUTDOWN_EVENT_NAME"=");
346 if (! CreateThread(NULL, 0, ShutdownRequestThread,
347 shutdownEvent, 0, NULL))
354 * If an accept mutex is in the env, save it and remove it.
356 val = getenv(MUTEX_VARNAME);
359 acceptMutex = (HANDLE) atoi(val);
364 * Determine if this library is being used to listen for FastCGI
365 * connections. This is communicated by STDIN containing a
366 * valid handle to a listener object. In this case, both the
367 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
368 * the starting process.
370 * The trick is determining if this is a pipe or a socket...
372 * XXX: Add the async accept test to determine socket or handle to a
375 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
376 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
377 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
379 DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
380 HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
382 // Move the handle to a "low" number
383 if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
384 GetCurrentProcess(), &hListen,
385 0, TRUE, DUPLICATE_SAME_ACCESS))
390 if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
395 CloseHandle(oldStdIn);
398 * Set the pipe handle state so that it operates in wait mode.
400 * NOTE: The listenFd is not mapped to a pseudo file descriptor
401 * as all work done on it is contained to the OS library.
403 * XXX: Initial assumption is that SetNamedPipeHandleState will
404 * fail if this is an IP socket...
406 if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
408 listenType = FD_PIPE_SYNC;
409 listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
413 listenType = FD_SOCKET_SYNC;
418 * If there are no stdioFds passed in, we're done.
420 if(stdioFds == NULL) {
426 * Setup standard input asynchronous I/O. There is actually a separate
427 * thread spawned for this purpose. The reason for this is that some
428 * web servers use anonymous pipes for the connection between itself
429 * and a CGI application. Anonymous pipes can't perform asynchronous
430 * I/O or use I/O completion ports. Therefore in order to present a
431 * consistent I/O dispatch model to an application we emulate I/O
432 * completion port behavior by having the standard input thread posting
433 * messages to the hIoCompPort which look like a complete overlapped
434 * I/O structure. This keeps the event dispatching simple from the
435 * application perspective.
437 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
439 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
440 HANDLE_FLAG_INHERIT, 0)) {
442 * XXX: Causes error when run from command line. Check KB
443 err = GetLastError();
449 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
450 (int)stdioHandles[STDIN_FILENO],
451 STDIN_FILENO)) == -1) {
455 * Set stdin equal to our pseudo FD and create the I/O completion
456 * port to be used for async I/O.
458 stdioFds[STDIN_FILENO] = fakeFd;
462 * Create the I/O completion port to be used for communicating with
463 * the thread doing I/O on standard in. This port will carry read
464 * and possibly thread termination requests to the StdinThread.
466 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
467 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
469 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
470 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
477 * Create the thread that will read stdin if the CONTENT_LENGTH
480 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
482 hStdinThread = CreateThread(NULL, 8192,
483 (LPTHREAD_START_ROUTINE)&StdinThread,
485 if (hStdinThread == NULL) {
486 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
493 * STDOUT will be used synchronously.
495 * XXX: May want to convert this so that it could be used for OVERLAPPED
496 * I/O later. If so, model it after the Stdin I/O as stdout is
497 * also incapable of async I/O on some servers.
499 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
500 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
501 HANDLE_FLAG_INHERIT, FALSE)) {
506 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
507 (int)stdioHandles[STDOUT_FILENO],
508 STDOUT_FILENO)) == -1) {
512 * Set stdout equal to our pseudo FD
514 stdioFds[STDOUT_FILENO] = fakeFd;
517 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
518 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
519 HANDLE_FLAG_INHERIT, FALSE)) {
523 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
524 (int)stdioHandles[STDERR_FILENO],
525 STDERR_FILENO)) == -1) {
529 * Set stderr equal to our pseudo FD
531 stdioFds[STDERR_FILENO] = fakeFd;
538 *--------------------------------------------------------------
542 * Shutdown the OS library.
548 * Memory freed, handles closed.
550 *--------------------------------------------------------------
552 void OS_LibShutdown()
555 if(hIoCompPort != INVALID_HANDLE_VALUE) {
556 CloseHandle(hIoCompPort);
557 hIoCompPort = INVALID_HANDLE_VALUE;
560 if(hStdinCompPort != INVALID_HANDLE_VALUE) {
561 CloseHandle(hStdinCompPort);
562 hStdinCompPort = INVALID_HANDLE_VALUE;
566 * Shutdown the socket library.
573 *--------------------------------------------------------------
575 * Win32FreeDescriptor --
577 * Free I/O descriptor entry in fdTable.
580 * Frees I/O descriptor entry in fdTable.
585 *--------------------------------------------------------------
587 static void Win32FreeDescriptor(int fd)
589 /* Catch it if fd is a bogus value */
590 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
592 EnterCriticalSection(&fdTableCritical);
594 if (fdTable[fd].type != FD_UNUSED)
596 switch (fdTable[fd].type)
601 /* Free file path string */
602 ASSERT(fdTable[fd].path != NULL);
603 free(fdTable[fd].path);
604 fdTable[fd].path = NULL;
611 ASSERT(fdTable[fd].path == NULL);
613 fdTable[fd].type = FD_UNUSED;
614 fdTable[fd].path = NULL;
615 fdTable[fd].Errno = NO_ERROR;
616 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
618 if (fdTable[fd].hMapMutex != NULL)
620 CloseHandle(fdTable[fd].hMapMutex);
621 fdTable[fd].hMapMutex = NULL;
625 LeaveCriticalSection(&fdTableCritical);
630 static short getPort(const char * bindPath)
633 char * p = strchr(bindPath, ':');
649 * OS_CreateLocalIpcFd --
651 * This procedure is responsible for creating the listener pipe
652 * on Windows NT for local process communication. It will create a
653 * named pipe and return a file descriptor to it to the caller.
656 * Listener pipe created. This call returns either a valid
657 * pseudo file descriptor or -1 on error.
660 * Listener pipe and IPC address are stored in the FCGI info
662 * 'errno' will set on errors (-1 is returned).
664 *----------------------------------------------------------------------
666 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
669 short port = getPort(bindPath);
670 HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
671 char * mutexEnvString;
678 if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
683 // This is a nail for listening to more than one port..
684 // This should really be handled by the caller.
686 mutexEnvString = malloc(strlen(MUTEX_VARNAME) + 7);
687 sprintf(mutexEnvString, MUTEX_VARNAME "=%d", (int) mutex);
688 putenv(mutexEnvString);
690 // There's nothing to be gained (at the moment) by a shutdown Event
692 if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
694 fprintf(stderr, "To start a service on a TCP port can not "
695 "specify a host name.\n"
696 "You should either use \"localhost:<port>\" or "
697 " just use \":<port>.\"\n");
701 listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC;
706 struct sockaddr_in sockAddr;
707 int sockLen = sizeof(sockAddr);
709 memset(&sockAddr, 0, sizeof(sockAddr));
710 sockAddr.sin_family = AF_INET;
711 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
712 sockAddr.sin_port = htons(port);
714 listenSock = socket(AF_INET, SOCK_STREAM, 0);
715 if (listenSock == INVALID_SOCKET)
720 if (! bind(listenSock, (struct sockaddr *) &sockAddr, sockLen)
721 || ! listen(listenSock, backlog))
726 pseudoFd = Win32NewDescriptor(listenType, listenSock, -1);
730 closesocket(listenSock);
734 hListen = (HANDLE) listenSock;
738 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
739 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
746 strcpy(pipePath, bindPathPrefix);
747 strcat(pipePath, bindPath);
749 hListenPipe = CreateNamedPipe(pipePath,
750 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
751 PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
752 PIPE_UNLIMITED_INSTANCES,
753 4096, 4096, 0, NULL);
757 if (hListenPipe == INVALID_HANDLE_VALUE)
762 if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
767 pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1);
771 CloseHandle(hListenPipe);
775 hListen = (HANDLE) hListenPipe;
782 *----------------------------------------------------------------------
786 * Create the pipe pathname connect to the remote application if
790 * -1 if fail or a valid handle if connection succeeds.
793 * Remote connection established.
795 *----------------------------------------------------------------------
797 int OS_FcgiConnect(char *bindPath)
799 short port = getPort(bindPath);
806 struct sockaddr_in sockAddr;
807 int sockLen = sizeof(sockAddr);
810 if (*bindPath != ':')
812 char * p = strchr(bindPath, ':');
813 int len = p - bindPath + 1;
816 strncpy(host, bindPath, len);
820 hp = gethostbyname(host ? host : LOCALHOST);
829 fprintf(stderr, "Unknown host: %s\n", bindPath);
833 memset(&sockAddr, 0, sizeof(sockAddr));
834 sockAddr.sin_family = AF_INET;
835 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
836 sockAddr.sin_port = htons(port);
838 sock = socket(AF_INET, SOCK_STREAM, 0);
839 if (sock == INVALID_SOCKET)
844 if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen))
850 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1);
859 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
867 strcpy(pipePath, bindPathPrefix);
868 strcat(pipePath, bindPath);
870 hPipe = CreateFile(pipePath,
871 GENERIC_WRITE | GENERIC_READ,
872 FILE_SHARE_READ | FILE_SHARE_WRITE,
875 FILE_FLAG_OVERLAPPED,
880 if( hPipe == INVALID_HANDLE_VALUE)
885 pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1);
894 * Set stdin equal to our pseudo FD and create the I/O completion
895 * port to be used for async I/O.
897 if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1))
899 Win32FreeDescriptor(pseudoFd);
909 *--------------------------------------------------------------
913 * Pass through to the appropriate NT read function.
916 * Returns number of byes read. Mimics unix read:.
917 * n bytes read, 0 or -1 failure: errno contains actual error
922 *--------------------------------------------------------------
924 int OS_Read(int fd, char * buf, size_t len)
929 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
931 switch (fdTable[fd].type)
938 if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL))
944 fdTable[fd].Errno = GetLastError();
950 case FD_SOCKET_ASYNC:
952 ret = recv(fdTable[fd].fid.sock, buf, len, 0);
953 if (ret == SOCKET_ERROR)
955 fdTable[fd].Errno = WSAGetLastError();
970 *--------------------------------------------------------------
974 * Perform a synchronous OS write.
977 * Returns number of bytes written. Mimics unix write:
978 * n bytes written, 0 or -1 failure (??? couldn't find man page).
983 *--------------------------------------------------------------
985 int OS_Write(int fd, char * buf, size_t len)
990 ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
992 switch (fdTable[fd].type)
999 if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL))
1005 fdTable[fd].Errno = GetLastError();
1010 case FD_SOCKET_SYNC:
1011 case FD_SOCKET_ASYNC:
1013 ret = send(fdTable[fd].fid.sock, buf, len, 0);
1014 if (ret == SOCKET_ERROR)
1016 fdTable[fd].Errno = WSAGetLastError();
1031 *----------------------------------------------------------------------
1035 * Spawns a new server listener process, and stores the information
1036 * relating to the child in the supplied record. A wait handler is
1037 * registered on the child's completion. This involves creating
1038 * a process on NT and preparing a command line with the required
1039 * state (currently a -childproc flag and the server socket to use
1040 * for accepting connections).
1043 * 0 if success, -1 if error.
1046 * Child process spawned.
1048 *----------------------------------------------------------------------
1050 int OS_SpawnChild(char *execPath, int listenFd)
1052 STARTUPINFO StartupInfo;
1053 PROCESS_INFORMATION pInfo;
1056 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
1057 StartupInfo.cb = sizeof (STARTUPINFO);
1058 StartupInfo.lpReserved = NULL;
1059 StartupInfo.lpReserved2 = NULL;
1060 StartupInfo.cbReserved2 = 0;
1061 StartupInfo.lpDesktop = NULL;
1064 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
1065 * the new process. The fact that there is a stdin and NULL handles
1066 * for stdout and stderr tells the FastCGI process that this is a
1067 * FastCGI process and not a CGI process.
1069 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1071 * XXX: Do I have to dup the handle before spawning the process or is
1072 * it sufficient to use the handle as it's reference counted
1075 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
1076 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
1077 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
1080 * Make the listener socket inheritable.
1082 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
1089 * XXX: Might want to apply some specific security attributes to the
1092 success = CreateProcess(execPath, /* LPCSTR address of module name */
1093 NULL, /* LPCSTR address of command line */
1094 NULL, /* Process security attributes */
1095 NULL, /* Thread security attributes */
1096 TRUE, /* Inheritable Handes inherited. */
1097 0, /* DWORD creation flags */
1098 NULL, /* Use parent environment block */
1099 NULL, /* Address of current directory name */
1100 &StartupInfo, /* Address of STARTUPINFO */
1101 &pInfo); /* Address of PROCESS_INFORMATION */
1110 *--------------------------------------------------------------
1112 * OS_AsyncReadStdin --
1114 * This initiates an asynchronous read on the standard
1115 * input handle. This handle is not guaranteed to be
1116 * capable of performing asynchronous I/O so we send a
1117 * message to the StdinThread to do the synchronous read.
1120 * -1 if error, 0 otherwise.
1123 * Asynchronous message is queued to the StdinThread and an
1124 * overlapped structure is allocated/initialized.
1126 *--------------------------------------------------------------
1128 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1129 ClientData clientData)
1131 POVERLAPPED_REQUEST pOv;
1133 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1135 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1137 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1138 pOv->clientData1 = (ClientData)buf;
1139 pOv->instance = fdTable[STDIN_FILENO].instance;
1140 pOv->procPtr = procPtr;
1141 pOv->clientData = clientData;
1143 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1149 *--------------------------------------------------------------
1153 * This initiates an asynchronous read on the file
1154 * handle which may be a socket or named pipe.
1156 * We also must save the ProcPtr and ClientData, so later
1157 * when the io completes, we know who to call.
1159 * We don't look at any results here (the ReadFile may
1160 * return data if it is cached) but do all completion
1161 * processing in OS_Select when we get the io completion
1162 * port done notifications. Then we call the callback.
1165 * -1 if error, 0 otherwise.
1168 * Asynchronous I/O operation is queued for completion.
1170 *--------------------------------------------------------------
1172 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1173 OS_AsyncProc procPtr, ClientData clientData)
1176 POVERLAPPED_REQUEST pOv;
1179 * Catch any bogus fd values
1181 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1183 * Confirm that this is an async fd
1185 ASSERT(fdTable[fd].type != FD_UNUSED);
1186 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1187 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1188 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1190 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1192 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1194 * Only file offsets should be non-zero, but make sure.
1196 if (fdTable[fd].type == FD_FILE_ASYNC)
1197 if (fdTable[fd].offset >= 0)
1198 pOv->overlapped.Offset = fdTable[fd].offset;
1200 pOv->overlapped.Offset = offset;
1201 pOv->instance = fdTable[fd].instance;
1202 pOv->procPtr = procPtr;
1203 pOv->clientData = clientData;
1206 * ReadFile returns: TRUE success, FALSE failure
1208 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1209 (LPOVERLAPPED)pOv)) {
1210 fdTable[fd].Errno = GetLastError();
1211 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1212 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1213 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1216 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1217 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1220 fdTable[fd].Errno = 0;
1226 *--------------------------------------------------------------
1230 * This initiates an asynchronous write on the "fake" file
1231 * descriptor (which may be a file, socket, or named pipe).
1232 * We also must save the ProcPtr and ClientData, so later
1233 * when the io completes, we know who to call.
1235 * We don't look at any results here (the WriteFile generally
1236 * completes immediately) but do all completion processing
1237 * in OS_DoIo when we get the io completion port done
1238 * notifications. Then we call the callback.
1241 * -1 if error, 0 otherwise.
1244 * Asynchronous I/O operation is queued for completion.
1246 *--------------------------------------------------------------
1248 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1249 OS_AsyncProc procPtr, ClientData clientData)
1252 POVERLAPPED_REQUEST pOv;
1255 * Catch any bogus fd values
1257 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1259 * Confirm that this is an async fd
1261 ASSERT(fdTable[fd].type != FD_UNUSED);
1262 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1263 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1264 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1266 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1268 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1270 * Only file offsets should be non-zero, but make sure.
1272 if (fdTable[fd].type == FD_FILE_ASYNC)
1274 * Only file opened via OS_AsyncWrite with
1275 * O_APPEND will have an offset != -1.
1277 if (fdTable[fd].offset >= 0)
1279 * If the descriptor has a memory mapped file
1280 * handle, take the offsets from there.
1282 if (fdTable[fd].hMapMutex != NULL) {
1284 * Wait infinitely; this *should* not cause problems.
1286 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1289 * Retrieve the shared offset values.
1291 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1292 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1295 * Update the shared offset values for the next write
1297 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1298 *(fdTable[fd].offsetLowPtr) += len;
1300 ReleaseMutex(fdTable[fd].hMapMutex);
1302 pOv->overlapped.Offset = fdTable[fd].offset;
1304 pOv->overlapped.Offset = offset;
1305 pOv->instance = fdTable[fd].instance;
1306 pOv->procPtr = procPtr;
1307 pOv->clientData = clientData;
1310 * WriteFile returns: TRUE success, FALSE failure
1312 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1313 (LPOVERLAPPED)pOv)) {
1314 fdTable[fd].Errno = GetLastError();
1315 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1316 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1319 fdTable[fd].Errno = 0;
1321 if (fdTable[fd].offset >= 0)
1322 fdTable[fd].offset += len;
1327 *--------------------------------------------------------------
1331 * Closes the descriptor with routine appropriate for
1332 * descriptor's type.
1335 * Socket or file is closed. Return values mimic Unix close:
1336 * 0 success, -1 failure
1339 * Entry in fdTable is marked as free.
1341 *--------------------------------------------------------------
1343 int OS_Close(int fd)
1348 * Catch it if fd is a bogus value
1350 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1351 ASSERT(fdTable[fd].type != FD_UNUSED);
1353 switch (fdTable[fd].type) {
1360 case FD_SOCKET_SYNC:
1361 case FD_SOCKET_ASYNC:
1363 * Closing a socket that has an async read outstanding causes a
1364 * tcp reset and possible data loss. The shutdown call seems to
1367 shutdown(fdTable[fd].fid.sock, 2);
1369 * closesocket returns: 0 success, SOCKET_ERROR failure
1371 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1375 return -1; /* fake failure */
1378 Win32FreeDescriptor(fd);
1383 *--------------------------------------------------------------
1387 * Cancel outstanding asynchronous reads and prevent subsequent
1388 * reads from completing.
1391 * Socket or file is shutdown. Return values mimic Unix shutdown:
1392 * 0 success, -1 failure
1394 *--------------------------------------------------------------
1396 int OS_CloseRead(int fd)
1401 * Catch it if fd is a bogus value
1403 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1404 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1405 || fdTable[fd].type == FD_SOCKET_SYNC);
1407 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1413 *--------------------------------------------------------------
1417 * This function was formerly OS_Select. It's purpose is
1418 * to pull I/O completion events off the queue and dispatch
1419 * them to the appropriate place.
1425 * Handlers are called.
1427 *--------------------------------------------------------------
1429 int OS_DoIo(struct timeval *tmo)
1433 POVERLAPPED_REQUEST pOv;
1440 * We can loop in here, but not too long, as wait handlers
1442 * For cgi stdin, apparently select returns when io completion
1443 * ports don't, so don't wait the full timeout.
1446 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1450 ms_last = tb.time*1000 + tb.millitm;
1452 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1454 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1455 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1456 err = WSAGetLastError();
1457 return 0; /* timeout */
1460 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1461 /* call callback if descriptor still valid */
1463 if(pOv->instance == fdTable[fd].instance)
1464 (*pOv->procPtr)(pOv->clientData, bytes);
1468 ms -= (tb.time*1000 + tb.millitm - ms_last);
1469 ms_last = tb.time*1000 + tb.millitm;
1475 static int CALLBACK isAddrOK(LPWSABUF lpCallerId,
1482 DWORD dwCallbackData)
1484 const char *okAddrs = (char *) dwCallbackData;
1485 struct sockaddr *sockaddr = (struct sockaddr *) lpCallerId->buf;
1487 if (okAddrs == NULL || sockaddr->sa_family != AF_INET)
1493 static const char *token = " ,;:\t";
1494 struct sockaddr_in * inet_sockaddr = (struct sockaddr_in *) sockaddr;
1495 char *ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
1496 char *p = strstr(okAddrs, ipaddr);
1502 else if (p == okAddrs)
1504 p += strlen(ipaddr);
1505 return (strchr(token, *p) != NULL);
1507 else if (strchr(token, *--p))
1509 p += strlen(ipaddr) + 1;
1510 return (strchr(token, *p) != NULL);
1520 *----------------------------------------------------------------------
1524 * Accepts a new FastCGI connection. This routine knows whether
1525 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1528 * -1 if the operation fails, otherwise this is a valid IPC fd.
1531 * New IPC connection is accepted.
1533 *----------------------------------------------------------------------
1535 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1537 /* XXX This is broken for listen_sock & fail_on_intr */
1542 if (shutdownPending)
1547 // The mutex is to keep other processes (and threads, when supported)
1548 // from going into the accept cycle. The accept cycle needs to
1549 // periodically break out to check the state of the shutdown flag
1550 // and there's no point to having more than one thread do that.
1552 if (acceptMutex != INVALID_HANDLE_VALUE)
1554 if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
1560 if (shutdownPending)
1562 if (acceptMutex != INVALID_HANDLE_VALUE)
1564 ReleaseMutex(acceptMutex);
1569 if (listenType == FD_PIPE_SYNC)
1571 pConnected = ConnectNamedPipe(hListen, &listenOverlapped)
1573 : (GetLastError() == ERROR_PIPE_CONNECTED);
1577 while (WaitForSingleObject(listenOverlapped.hEvent, 1000) == WAIT_TIMEOUT)
1579 if (shutdownPending)
1581 if (acceptMutex != INVALID_HANDLE_VALUE)
1583 ReleaseMutex(acceptMutex);
1591 if (acceptMutex != INVALID_HANDLE_VALUE)
1593 ReleaseMutex(acceptMutex);
1596 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
1599 DisconnectNamedPipe(hListen);
1602 else if (listenType == FD_SOCKET_SYNC)
1604 struct sockaddr sockaddr;
1605 int sockaddrLen = sizeof(sockaddr);
1607 const struct timeval timeout = {1, 0};
1610 FD_SET((unsigned int) hListen, &readfds);
1612 while (select(0, &readfds, NULL, NULL, &timeout) == 0)
1614 if (shutdownPending)
1620 hSock = (webServerAddrs == NULL)
1621 ? accept((SOCKET) hListen,
1624 : WSAAccept((unsigned int) hListen,
1628 (DWORD) webServerAddrs);
1630 if (acceptMutex != INVALID_HANDLE_VALUE)
1632 ReleaseMutex(acceptMutex);
1640 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1655 *----------------------------------------------------------------------
1659 * OS IPC routine to close an IPC connection.
1665 * IPC connection is closed.
1667 *----------------------------------------------------------------------
1669 int OS_IpcClose(int ipcFd)
1675 * Catch it if fd is a bogus value
1677 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1678 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1680 switch(listenType) {
1684 * Make sure that the client (ie. a Web Server in this case) has
1685 * read all data from the pipe before we disconnect.
1687 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1689 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1697 case FD_SOCKET_SYNC:
1710 *----------------------------------------------------------------------
1714 * Determines whether this process is a FastCGI process or not.
1717 * Returns 1 if FastCGI, 0 if not.
1722 *----------------------------------------------------------------------
1724 int OS_IsFcgi(int sock)
1726 // This is still broken. There is not currently a way to differentiate
1727 // a CGI from a FCGI pipe (try the Unix method).
1729 return (fdTable[sock].type != FD_UNUSED);
1733 *----------------------------------------------------------------------
1737 * Sets selected flag bits in an open file descriptor. Currently
1738 * this is only to put a SOCKET into non-blocking mode.
1740 *----------------------------------------------------------------------
1742 void OS_SetFlags(int fd, int flags)
1744 unsigned long pLong = 1L;
1747 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1748 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1750 exit(WSAGetLastError());
1752 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1753 hIoCompPort, fd, 1)) {
1754 err = GetLastError();
1758 fdTable[fd].type = FD_SOCKET_ASYNC;