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.23 2001/09/06 20:08:33 robs Exp $";
23 #define WIN32_LEAN_AND_MEAN
29 #include <sys/timeb.h>
31 #define DLLAPI __declspec(dllexport)
36 #define WIN32_OPEN_MAX 128 /* XXX: Small hack */
39 * millisecs to wait for a client connection before checking the
40 * shutdown flag (then go back to waiting for a connection, etc).
42 #define ACCEPT_TIMEOUT 1000
44 #define MUTEX_VARNAME "_FCGI_MUTEX_"
45 #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
46 #define LOCALHOST "localhost"
48 static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
49 static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
50 static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
52 static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
53 INVALID_HANDLE_VALUE};
55 static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
57 static BOOLEAN shutdownPending = FALSE;
58 static BOOLEAN shutdownNow = FALSE;
61 * An enumeration of the file types
62 * supported by the FD_TABLE structure.
64 * XXX: Not all currently supported. This allows for future
84 * Structure used to map file handle and socket handle
85 * values into values that can be used to create unix-like
86 * select bitmaps, read/write for both sockets/files.
93 unsigned long instance;
95 int offset; /* only valid for async file writes */
96 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
97 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
98 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
99 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
103 * XXX Note there is no dyanmic sizing of this table, so if the
104 * number of open file descriptors exceeds WIN32_OPEN_MAX the
107 static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
109 static CRITICAL_SECTION fdTableCritical;
111 struct OVERLAPPED_REQUEST {
112 OVERLAPPED overlapped;
113 unsigned long instance; /* file instance (won't match after a close) */
114 OS_AsyncProc procPtr; /* callback routine */
115 ClientData clientData; /* callback argument */
116 ClientData clientData1; /* additional clientData */
118 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
120 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
122 static FILE_TYPE listenType = FD_UNUSED;
124 // XXX This should be a DESCRIPTOR
125 static HANDLE hListen = INVALID_HANDLE_VALUE;
127 static OVERLAPPED listenOverlapped;
128 static BOOLEAN libInitialized = FALSE;
131 *--------------------------------------------------------------
133 * Win32NewDescriptor --
135 * Set up for I/O descriptor masquerading.
138 * Returns "fake id" which masquerades as a UNIX-style "small
139 * non-negative integer" file/socket descriptor.
140 * Win32_* routine below will "do the right thing" based on the
141 * descriptor's actual type. -1 indicates failure.
144 * Entry in fdTable is reserved to represent the socket/file.
146 *--------------------------------------------------------------
148 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
152 EnterCriticalSection(&fdTableCritical);
155 * If desiredFd is set, try to get this entry (this is used for
156 * mapping stdio handles). Otherwise try to get the fd entry.
157 * If this is not available, find a the first empty slot. .
159 if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
161 if (fdTable[desiredFd].type == FD_UNUSED)
168 if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
176 for (i = 1; i < WIN32_OPEN_MAX; ++i)
178 if (fdTable[i].type == FD_UNUSED)
189 fdTable[index].fid.value = fd;
190 fdTable[index].type = type;
191 fdTable[index].path = NULL;
192 fdTable[index].Errno = NO_ERROR;
193 fdTable[index].status = 0;
194 fdTable[index].offset = -1;
195 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
196 fdTable[index].hMapMutex = NULL;
197 fdTable[index].ovList = NULL;
200 LeaveCriticalSection(&fdTableCritical);
205 *--------------------------------------------------------------
209 * This thread performs I/O on stadard input. It is needed
210 * because you can't guarantee that all applications will
211 * create standard input with sufficient access to perform
212 * asynchronous I/O. Since we don't want to block the app
213 * reading from stdin we make it look like it's using I/O
214 * completion ports to perform async I/O.
217 * Data is read from stdin and posted to the io completion
223 *--------------------------------------------------------------
225 static void StdinThread(LPDWORD startup){
229 unsigned long bytesRead;
230 POVERLAPPED_REQUEST pOv;
232 // Touch the arg to prevent warning
237 * Block until a request to read from stdin comes in or a
238 * request to terminate the thread arrives (fd = -1).
240 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
241 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
246 ASSERT((fd == STDIN_FILENO) || (fd == -1));
251 ASSERT(pOv->clientData1 != NULL);
253 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
255 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
256 STDIN_FILENO, (LPOVERLAPPED)pOv);
266 void OS_Shutdown(void)
269 OS_ShutdownPending();
272 void OS_ShutdownPending(void)
274 shutdownPending = TRUE;
277 /* XXX Need a shutdown now event */
278 static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
280 HANDLE shutdownEvent = (HANDLE) arg;
282 if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
284 // Assuming it will happen again, all we can do is exit the thread
289 // "Simple reads and writes to properly-aligned 32-bit variables are atomic"
290 shutdownPending = TRUE;
292 // Before an accept() is entered the shutdownPending flag is checked.
293 // If set, OS_Accept() will return -1. If not, it waits
294 // on a connection request for one second, checks the flag, & repeats.
295 // Only one process/thread is allowed to do this at time by
296 // wrapping the accept() with mutex.
302 *--------------------------------------------------------------
306 * Set up the OS library for use.
309 * Returns 0 if success, -1 if not.
312 * Sockets initialized, pseudo file descriptors setup, etc.
314 *--------------------------------------------------------------
316 int OS_LibInit(int stdioFds[3])
323 char *cLenPtr = NULL;
329 InitializeCriticalSection(&fdTableCritical);
332 * Initialize windows sockets library.
334 wVersion = MAKEWORD(2,0);
335 err = WSAStartup( wVersion, &wsaData );
337 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
343 * Create the I/O completion port to be used for our I/O queue.
345 if (hIoCompPort == INVALID_HANDLE_VALUE) {
346 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
348 if(hIoCompPort == INVALID_HANDLE_VALUE) {
349 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
356 * If a shutdown event is in the env, save it (I don't see any to
357 * remove it from the environment out from under the application).
358 * Spawn a thread to wait on the shutdown request.
360 val = getenv(SHUTDOWN_EVENT_NAME);
363 HANDLE shutdownEvent = (HANDLE) atoi(val);
365 if (! CreateThread(NULL, 0, ShutdownRequestThread,
366 shutdownEvent, 0, NULL))
373 * If an accept mutex is in the env, save it and remove it.
375 val = getenv(MUTEX_VARNAME);
378 acceptMutex = (HANDLE) atoi(val);
383 * Determine if this library is being used to listen for FastCGI
384 * connections. This is communicated by STDIN containing a
385 * valid handle to a listener object. In this case, both the
386 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
387 * the starting process.
389 * The trick is determining if this is a pipe or a socket...
391 * XXX: Add the async accept test to determine socket or handle to a
394 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
395 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
396 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
398 DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
399 HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
401 // Move the handle to a "low" number
402 if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
403 GetCurrentProcess(), &hListen,
404 0, TRUE, DUPLICATE_SAME_ACCESS))
409 if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
414 CloseHandle(oldStdIn);
417 * Set the pipe handle state so that it operates in wait mode.
419 * NOTE: The listenFd is not mapped to a pseudo file descriptor
420 * as all work done on it is contained to the OS library.
422 * XXX: Initial assumption is that SetNamedPipeHandleState will
423 * fail if this is an IP socket...
425 if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
427 listenType = FD_PIPE_SYNC;
428 listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
432 listenType = FD_SOCKET_SYNC;
437 * If there are no stdioFds passed in, we're done.
439 if(stdioFds == NULL) {
445 * Setup standard input asynchronous I/O. There is actually a separate
446 * thread spawned for this purpose. The reason for this is that some
447 * web servers use anonymous pipes for the connection between itself
448 * and a CGI application. Anonymous pipes can't perform asynchronous
449 * I/O or use I/O completion ports. Therefore in order to present a
450 * consistent I/O dispatch model to an application we emulate I/O
451 * completion port behavior by having the standard input thread posting
452 * messages to the hIoCompPort which look like a complete overlapped
453 * I/O structure. This keeps the event dispatching simple from the
454 * application perspective.
456 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
458 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
459 HANDLE_FLAG_INHERIT, 0)) {
461 * XXX: Causes error when run from command line. Check KB
462 err = GetLastError();
468 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
469 (int)stdioHandles[STDIN_FILENO],
470 STDIN_FILENO)) == -1) {
474 * Set stdin equal to our pseudo FD and create the I/O completion
475 * port to be used for async I/O.
477 stdioFds[STDIN_FILENO] = fakeFd;
481 * Create the I/O completion port to be used for communicating with
482 * the thread doing I/O on standard in. This port will carry read
483 * and possibly thread termination requests to the StdinThread.
485 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
486 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
488 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
489 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
496 * Create the thread that will read stdin if the CONTENT_LENGTH
499 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
501 hStdinThread = CreateThread(NULL, 8192,
502 (LPTHREAD_START_ROUTINE)&StdinThread,
504 if (hStdinThread == NULL) {
505 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
512 * STDOUT will be used synchronously.
514 * XXX: May want to convert this so that it could be used for OVERLAPPED
515 * I/O later. If so, model it after the Stdin I/O as stdout is
516 * also incapable of async I/O on some servers.
518 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
519 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
520 HANDLE_FLAG_INHERIT, FALSE)) {
525 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
526 (int)stdioHandles[STDOUT_FILENO],
527 STDOUT_FILENO)) == -1) {
531 * Set stdout equal to our pseudo FD
533 stdioFds[STDOUT_FILENO] = fakeFd;
536 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
537 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
538 HANDLE_FLAG_INHERIT, FALSE)) {
542 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
543 (int)stdioHandles[STDERR_FILENO],
544 STDERR_FILENO)) == -1) {
548 * Set stderr equal to our pseudo FD
550 stdioFds[STDERR_FILENO] = fakeFd;
557 *--------------------------------------------------------------
561 * Shutdown the OS library.
567 * Memory freed, handles closed.
569 *--------------------------------------------------------------
571 void OS_LibShutdown()
574 if (hIoCompPort != INVALID_HANDLE_VALUE)
576 CloseHandle(hIoCompPort);
577 hIoCompPort = INVALID_HANDLE_VALUE;
580 if (hStdinCompPort != INVALID_HANDLE_VALUE)
582 CloseHandle(hStdinCompPort);
583 hStdinCompPort = INVALID_HANDLE_VALUE;
586 if (acceptMutex != INVALID_HANDLE_VALUE)
588 ReleaseMutex(acceptMutex);
591 DisconnectNamedPipe(hListen);
600 *--------------------------------------------------------------
602 * Win32FreeDescriptor --
604 * Free I/O descriptor entry in fdTable.
607 * Frees I/O descriptor entry in fdTable.
612 *--------------------------------------------------------------
614 static void Win32FreeDescriptor(int fd)
616 /* Catch it if fd is a bogus value */
617 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
619 EnterCriticalSection(&fdTableCritical);
621 if (fdTable[fd].type != FD_UNUSED)
623 switch (fdTable[fd].type)
628 /* Free file path string */
629 ASSERT(fdTable[fd].path != NULL);
630 free(fdTable[fd].path);
631 fdTable[fd].path = NULL;
638 ASSERT(fdTable[fd].path == NULL);
640 fdTable[fd].type = FD_UNUSED;
641 fdTable[fd].path = NULL;
642 fdTable[fd].Errno = NO_ERROR;
643 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
645 if (fdTable[fd].hMapMutex != NULL)
647 CloseHandle(fdTable[fd].hMapMutex);
648 fdTable[fd].hMapMutex = NULL;
652 LeaveCriticalSection(&fdTableCritical);
657 static short getPort(const char * bindPath)
660 char * p = strchr(bindPath, ':');
669 port = (short) atoi(buf);
676 * OS_CreateLocalIpcFd --
678 * This procedure is responsible for creating the listener pipe
679 * on Windows NT for local process communication. It will create a
680 * named pipe and return a file descriptor to it to the caller.
683 * Listener pipe created. This call returns either a valid
684 * pseudo file descriptor or -1 on error.
687 * Listener pipe and IPC address are stored in the FCGI info
689 * 'errno' will set on errors (-1 is returned).
691 *----------------------------------------------------------------------
693 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
696 short port = getPort(bindPath);
697 HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
698 char * mutexEnvString;
705 if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
710 // This is a nail for listening to more than one port..
711 // This should really be handled by the caller.
713 mutexEnvString = malloc(strlen(MUTEX_VARNAME) + 7);
714 sprintf(mutexEnvString, MUTEX_VARNAME "=%d", (int) mutex);
715 putenv(mutexEnvString);
717 // There's nothing to be gained (at the moment) by a shutdown Event
719 if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
721 fprintf(stderr, "To start a service on a TCP port can not "
722 "specify a host name.\n"
723 "You should either use \"localhost:<port>\" or "
724 " just use \":<port>.\"\n");
728 listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC;
733 struct sockaddr_in sockAddr;
734 int sockLen = sizeof(sockAddr);
736 memset(&sockAddr, 0, sizeof(sockAddr));
737 sockAddr.sin_family = AF_INET;
738 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
739 sockAddr.sin_port = htons(port);
741 listenSock = socket(AF_INET, SOCK_STREAM, 0);
742 if (listenSock == INVALID_SOCKET)
747 if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) )
752 if (listen(listenSock, backlog))
757 pseudoFd = Win32NewDescriptor(listenType, listenSock, -1);
761 closesocket(listenSock);
765 hListen = (HANDLE) listenSock;
769 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
770 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
777 strcpy(pipePath, bindPathPrefix);
778 strcat(pipePath, bindPath);
780 hListenPipe = CreateNamedPipe(pipePath,
781 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
782 PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
783 PIPE_UNLIMITED_INSTANCES,
784 4096, 4096, 0, NULL);
788 if (hListenPipe == INVALID_HANDLE_VALUE)
793 if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
798 pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1);
802 CloseHandle(hListenPipe);
806 hListen = (HANDLE) hListenPipe;
813 *----------------------------------------------------------------------
817 * Create the pipe pathname connect to the remote application if
821 * -1 if fail or a valid handle if connection succeeds.
824 * Remote connection established.
826 *----------------------------------------------------------------------
828 int OS_FcgiConnect(char *bindPath)
830 short port = getPort(bindPath);
837 struct sockaddr_in sockAddr;
838 int sockLen = sizeof(sockAddr);
841 if (*bindPath != ':')
843 char * p = strchr(bindPath, ':');
844 int len = p - bindPath + 1;
847 strncpy(host, bindPath, len);
851 hp = gethostbyname(host ? host : LOCALHOST);
860 fprintf(stderr, "Unknown host: %s\n", bindPath);
864 memset(&sockAddr, 0, sizeof(sockAddr));
865 sockAddr.sin_family = AF_INET;
866 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
867 sockAddr.sin_port = htons(port);
869 sock = socket(AF_INET, SOCK_STREAM, 0);
870 if (sock == INVALID_SOCKET)
875 if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen))
881 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1);
890 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
898 strcpy(pipePath, bindPathPrefix);
899 strcat(pipePath, bindPath);
901 hPipe = CreateFile(pipePath,
902 GENERIC_WRITE | GENERIC_READ,
903 FILE_SHARE_READ | FILE_SHARE_WRITE,
906 FILE_FLAG_OVERLAPPED,
911 if( hPipe == INVALID_HANDLE_VALUE)
916 pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1);
925 * Set stdin equal to our pseudo FD and create the I/O completion
926 * port to be used for async I/O.
928 if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1))
930 Win32FreeDescriptor(pseudoFd);
940 *--------------------------------------------------------------
944 * Pass through to the appropriate NT read function.
947 * Returns number of byes read. Mimics unix read:.
948 * n bytes read, 0 or -1 failure: errno contains actual error
953 *--------------------------------------------------------------
955 int OS_Read(int fd, char * buf, size_t len)
960 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
962 if (shutdownNow) return -1;
964 switch (fdTable[fd].type)
971 if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL))
977 fdTable[fd].Errno = GetLastError();
983 case FD_SOCKET_ASYNC:
985 ret = recv(fdTable[fd].fid.sock, buf, len, 0);
986 if (ret == SOCKET_ERROR)
988 fdTable[fd].Errno = WSAGetLastError();
1003 *--------------------------------------------------------------
1007 * Perform a synchronous OS write.
1010 * Returns number of bytes written. Mimics unix write:
1011 * n bytes written, 0 or -1 failure (??? couldn't find man page).
1016 *--------------------------------------------------------------
1018 int OS_Write(int fd, char * buf, size_t len)
1023 ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
1025 if (shutdownNow) return -1;
1027 switch (fdTable[fd].type)
1034 if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL))
1040 fdTable[fd].Errno = GetLastError();
1045 case FD_SOCKET_SYNC:
1046 case FD_SOCKET_ASYNC:
1048 ret = send(fdTable[fd].fid.sock, buf, len, 0);
1049 if (ret == SOCKET_ERROR)
1051 fdTable[fd].Errno = WSAGetLastError();
1066 *----------------------------------------------------------------------
1070 * Spawns a new server listener process, and stores the information
1071 * relating to the child in the supplied record. A wait handler is
1072 * registered on the child's completion. This involves creating
1073 * a process on NT and preparing a command line with the required
1074 * state (currently a -childproc flag and the server socket to use
1075 * for accepting connections).
1078 * 0 if success, -1 if error.
1081 * Child process spawned.
1083 *----------------------------------------------------------------------
1085 int OS_SpawnChild(char *execPath, int listenFd)
1087 STARTUPINFO StartupInfo;
1088 PROCESS_INFORMATION pInfo;
1091 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
1092 StartupInfo.cb = sizeof (STARTUPINFO);
1093 StartupInfo.lpReserved = NULL;
1094 StartupInfo.lpReserved2 = NULL;
1095 StartupInfo.cbReserved2 = 0;
1096 StartupInfo.lpDesktop = NULL;
1099 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
1100 * the new process. The fact that there is a stdin and NULL handles
1101 * for stdout and stderr tells the FastCGI process that this is a
1102 * FastCGI process and not a CGI process.
1104 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1106 * XXX: Do I have to dup the handle before spawning the process or is
1107 * it sufficient to use the handle as it's reference counted
1110 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
1111 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
1112 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
1115 * Make the listener socket inheritable.
1117 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
1124 * XXX: Might want to apply some specific security attributes to the
1127 success = CreateProcess(execPath, /* LPCSTR address of module name */
1128 NULL, /* LPCSTR address of command line */
1129 NULL, /* Process security attributes */
1130 NULL, /* Thread security attributes */
1131 TRUE, /* Inheritable Handes inherited. */
1132 0, /* DWORD creation flags */
1133 NULL, /* Use parent environment block */
1134 NULL, /* Address of current directory name */
1135 &StartupInfo, /* Address of STARTUPINFO */
1136 &pInfo); /* Address of PROCESS_INFORMATION */
1145 *--------------------------------------------------------------
1147 * OS_AsyncReadStdin --
1149 * This initiates an asynchronous read on the standard
1150 * input handle. This handle is not guaranteed to be
1151 * capable of performing asynchronous I/O so we send a
1152 * message to the StdinThread to do the synchronous read.
1155 * -1 if error, 0 otherwise.
1158 * Asynchronous message is queued to the StdinThread and an
1159 * overlapped structure is allocated/initialized.
1161 *--------------------------------------------------------------
1163 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1164 ClientData clientData)
1166 POVERLAPPED_REQUEST pOv;
1168 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1170 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1172 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1173 pOv->clientData1 = (ClientData)buf;
1174 pOv->instance = fdTable[STDIN_FILENO].instance;
1175 pOv->procPtr = procPtr;
1176 pOv->clientData = clientData;
1178 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1184 *--------------------------------------------------------------
1188 * This initiates an asynchronous read on the file
1189 * handle which may be a socket or named pipe.
1191 * We also must save the ProcPtr and ClientData, so later
1192 * when the io completes, we know who to call.
1194 * We don't look at any results here (the ReadFile may
1195 * return data if it is cached) but do all completion
1196 * processing in OS_Select when we get the io completion
1197 * port done notifications. Then we call the callback.
1200 * -1 if error, 0 otherwise.
1203 * Asynchronous I/O operation is queued for completion.
1205 *--------------------------------------------------------------
1207 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1208 OS_AsyncProc procPtr, ClientData clientData)
1211 POVERLAPPED_REQUEST pOv;
1214 * Catch any bogus fd values
1216 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1218 * Confirm that this is an async fd
1220 ASSERT(fdTable[fd].type != FD_UNUSED);
1221 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1222 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1223 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1225 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1227 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1229 * Only file offsets should be non-zero, but make sure.
1231 if (fdTable[fd].type == FD_FILE_ASYNC)
1232 if (fdTable[fd].offset >= 0)
1233 pOv->overlapped.Offset = fdTable[fd].offset;
1235 pOv->overlapped.Offset = offset;
1236 pOv->instance = fdTable[fd].instance;
1237 pOv->procPtr = procPtr;
1238 pOv->clientData = clientData;
1241 * ReadFile returns: TRUE success, FALSE failure
1243 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1244 (LPOVERLAPPED)pOv)) {
1245 fdTable[fd].Errno = GetLastError();
1246 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1247 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1248 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1251 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1252 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1255 fdTable[fd].Errno = 0;
1261 *--------------------------------------------------------------
1265 * This initiates an asynchronous write on the "fake" file
1266 * descriptor (which may be a file, socket, or named pipe).
1267 * We also must save the ProcPtr and ClientData, so later
1268 * when the io completes, we know who to call.
1270 * We don't look at any results here (the WriteFile generally
1271 * completes immediately) but do all completion processing
1272 * in OS_DoIo when we get the io completion port done
1273 * notifications. Then we call the callback.
1276 * -1 if error, 0 otherwise.
1279 * Asynchronous I/O operation is queued for completion.
1281 *--------------------------------------------------------------
1283 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1284 OS_AsyncProc procPtr, ClientData clientData)
1287 POVERLAPPED_REQUEST pOv;
1290 * Catch any bogus fd values
1292 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1294 * Confirm that this is an async fd
1296 ASSERT(fdTable[fd].type != FD_UNUSED);
1297 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1298 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1299 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1301 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1303 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1305 * Only file offsets should be non-zero, but make sure.
1307 if (fdTable[fd].type == FD_FILE_ASYNC)
1309 * Only file opened via OS_AsyncWrite with
1310 * O_APPEND will have an offset != -1.
1312 if (fdTable[fd].offset >= 0)
1314 * If the descriptor has a memory mapped file
1315 * handle, take the offsets from there.
1317 if (fdTable[fd].hMapMutex != NULL) {
1319 * Wait infinitely; this *should* not cause problems.
1321 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1324 * Retrieve the shared offset values.
1326 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1327 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1330 * Update the shared offset values for the next write
1332 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1333 *(fdTable[fd].offsetLowPtr) += len;
1335 ReleaseMutex(fdTable[fd].hMapMutex);
1337 pOv->overlapped.Offset = fdTable[fd].offset;
1339 pOv->overlapped.Offset = offset;
1340 pOv->instance = fdTable[fd].instance;
1341 pOv->procPtr = procPtr;
1342 pOv->clientData = clientData;
1345 * WriteFile returns: TRUE success, FALSE failure
1347 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1348 (LPOVERLAPPED)pOv)) {
1349 fdTable[fd].Errno = GetLastError();
1350 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1351 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1354 fdTable[fd].Errno = 0;
1356 if (fdTable[fd].offset >= 0)
1357 fdTable[fd].offset += len;
1362 *--------------------------------------------------------------
1366 * Closes the descriptor with routine appropriate for
1367 * descriptor's type.
1370 * Socket or file is closed. Return values mimic Unix close:
1371 * 0 success, -1 failure
1374 * Entry in fdTable is marked as free.
1376 *--------------------------------------------------------------
1378 int OS_Close(int fd)
1383 * Catch it if fd is a bogus value
1385 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1386 ASSERT(fdTable[fd].type != FD_UNUSED);
1388 switch (fdTable[fd].type) {
1395 case FD_SOCKET_SYNC:
1396 case FD_SOCKET_ASYNC:
1398 * Closing a socket that has an async read outstanding causes a
1399 * tcp reset and possible data loss. The shutdown call seems to
1402 shutdown(fdTable[fd].fid.sock, 2);
1404 * closesocket returns: 0 success, SOCKET_ERROR failure
1406 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1410 return -1; /* fake failure */
1413 Win32FreeDescriptor(fd);
1418 *--------------------------------------------------------------
1422 * Cancel outstanding asynchronous reads and prevent subsequent
1423 * reads from completing.
1426 * Socket or file is shutdown. Return values mimic Unix shutdown:
1427 * 0 success, -1 failure
1429 *--------------------------------------------------------------
1431 int OS_CloseRead(int fd)
1436 * Catch it if fd is a bogus value
1438 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1439 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1440 || fdTable[fd].type == FD_SOCKET_SYNC);
1442 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1448 *--------------------------------------------------------------
1452 * This function was formerly OS_Select. It's purpose is
1453 * to pull I/O completion events off the queue and dispatch
1454 * them to the appropriate place.
1460 * Handlers are called.
1462 *--------------------------------------------------------------
1464 int OS_DoIo(struct timeval *tmo)
1467 unsigned long bytes;
1468 POVERLAPPED_REQUEST pOv;
1475 * We can loop in here, but not too long, as wait handlers
1477 * For cgi stdin, apparently select returns when io completion
1478 * ports don't, so don't wait the full timeout.
1481 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1485 ms_last = tb.time*1000 + tb.millitm;
1487 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1489 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1490 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1491 err = WSAGetLastError();
1492 return 0; /* timeout */
1495 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1496 /* call callback if descriptor still valid */
1498 if(pOv->instance == fdTable[fd].instance)
1499 (*pOv->procPtr)(pOv->clientData, bytes);
1503 ms -= (tb.time*1000 + tb.millitm - ms_last);
1504 ms_last = tb.time*1000 + tb.millitm;
1509 static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs)
1511 static const char *token = " ,;:\t";
1515 if (okAddrs == NULL) return TRUE;
1517 ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
1518 p = strstr(okAddrs, ipaddr);
1520 if (p == NULL) return FALSE;
1524 p += strlen(ipaddr);
1525 return (strchr(token, *p) != NULL);
1528 if (strchr(token, *--p) != NULL)
1530 p += strlen(ipaddr) + 1;
1531 return (strchr(token, *p) != NULL);
1538 static int CALLBACK isAddrOKCallback(LPWSABUF lpCallerId,
1547 struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf;
1549 // Touch the args to avoid warnings
1550 dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; dc5 = NULL;
1552 if ((void *) data == NULL) return CF_ACCEPT;
1554 if (sockaddr->sin_family != AF_INET) return CF_ACCEPT;
1556 return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT;
1560 static printLastError(const char * text)
1565 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1566 FORMAT_MESSAGE_FROM_SYSTEM |
1567 FORMAT_MESSAGE_IGNORE_INSERTS,
1576 fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf);
1580 static int acceptNamedPipe()
1584 if (! ConnectNamedPipe(hListen, &listenOverlapped))
1586 switch (GetLastError())
1588 case ERROR_PIPE_CONNECTED:
1590 // A client connected after CreateNamedPipe but
1591 // before ConnectNamedPipe. Its a good connection.
1595 case ERROR_IO_PENDING:
1597 // Wait for a connection to complete.
1599 while (WaitForSingleObject(listenOverlapped.hEvent,
1600 ACCEPT_TIMEOUT) == WAIT_TIMEOUT)
1602 if (shutdownPending)
1611 case ERROR_PIPE_LISTENING:
1613 // The pipe handle is in nonblocking mode.
1617 // The previous client closed its handle (and we failed
1618 // to call DisconnectNamedPipe)
1622 printLastError("unexpected ConnectNamedPipe() error");
1626 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
1629 DisconnectNamedPipe(hListen);
1635 static int acceptSocket(const char *webServerAddrs)
1642 struct sockaddr sockaddr;
1643 int sockaddrLen = sizeof(sockaddr);
1647 const struct timeval timeout = {1, 0};
1651 FD_SET((unsigned int) hListen, &readfds);
1653 if (select(0, &readfds, NULL, NULL, &timeout) == 0)
1655 if (shutdownPending)
1668 hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen);
1670 if (hSock == INVALID_SOCKET)
1675 if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs))
1682 hSock = WSAAccept((unsigned int) hListen,
1686 (DWORD) webServerAddrs);
1688 if (hSock != INVALID_SOCKET)
1693 if (WSAGetLastError() != WSAECONNREFUSED)
1700 if (hSock == INVALID_SOCKET)
1702 /* Use FormatMessage() */
1703 fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError());
1707 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1717 *----------------------------------------------------------------------
1721 * Accepts a new FastCGI connection. This routine knows whether
1722 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1724 * fail_on_intr is ignored in the Win lib.
1727 * -1 if the operation fails, otherwise this is a valid IPC fd.
1729 *----------------------------------------------------------------------
1731 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1735 // Touch args to prevent warnings
1736 listen_sock = 0; fail_on_intr = 0;
1738 // @todo Muliple listen sockets and sockets other than 0 are not
1739 // supported due to the use of globals.
1741 if (shutdownPending)
1747 // The mutex is to keep other processes (and threads, when supported)
1748 // from going into the accept cycle. The accept cycle needs to
1749 // periodically break out to check the state of the shutdown flag
1750 // and there's no point to having more than one thread do that.
1752 if (acceptMutex != INVALID_HANDLE_VALUE)
1754 if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
1756 printLastError("WaitForSingleObject() failed");
1761 if (shutdownPending)
1765 else if (listenType == FD_PIPE_SYNC)
1767 ipcFd = acceptNamedPipe();
1769 else if (listenType == FD_SOCKET_SYNC)
1771 ipcFd = acceptSocket(webServerAddrs);
1775 fprintf(stderr, "unknown listenType (%d)\n", listenType);
1778 if (acceptMutex != INVALID_HANDLE_VALUE)
1780 ReleaseMutex(acceptMutex);
1787 *----------------------------------------------------------------------
1791 * OS IPC routine to close an IPC connection.
1797 * IPC connection is closed.
1799 *----------------------------------------------------------------------
1801 int OS_IpcClose(int ipcFd)
1807 * Catch it if fd is a bogus value
1809 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1810 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1812 switch(listenType) {
1816 * Make sure that the client (ie. a Web Server in this case) has
1817 * read all data from the pipe before we disconnect.
1819 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1821 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1829 case FD_SOCKET_SYNC:
1842 *----------------------------------------------------------------------
1846 * Determines whether this process is a FastCGI process or not.
1849 * Returns 1 if FastCGI, 0 if not.
1854 *----------------------------------------------------------------------
1856 int OS_IsFcgi(int sock)
1858 // Touch args to prevent warnings
1861 /* XXX This is broken for sock */
1863 return (listenType != FD_UNUSED);
1867 *----------------------------------------------------------------------
1871 * Sets selected flag bits in an open file descriptor. Currently
1872 * this is only to put a SOCKET into non-blocking mode.
1874 *----------------------------------------------------------------------
1876 void OS_SetFlags(int fd, int flags)
1878 unsigned long pLong = 1L;
1881 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1882 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1884 exit(WSAGetLastError());
1886 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1887 hIoCompPort, fd, 1)) {
1888 err = GetLastError();
1892 fdTable[fd].type = FD_SOCKET_ASYNC;