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.35 2004/01/31 17:47:07 robs Exp $";
23 #define WIN32_LEAN_AND_MEAN
29 #include <sys/timeb.h>
33 #define DLLAPI __declspec(dllexport)
38 #define WIN32_OPEN_MAX 128 /* XXX: Small hack */
41 * millisecs to wait for a client connection before checking the
42 * shutdown flag (then go back to waiting for a connection, etc).
44 #define ACCEPT_TIMEOUT 1000
46 #define MUTEX_VARNAME "_FCGI_MUTEX_"
47 #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
48 #define LOCALHOST "localhost"
50 static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
51 static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
52 static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
54 static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
55 INVALID_HANDLE_VALUE};
57 // This is a nail for listening to more than one port..
58 static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
60 static BOOLEAN shutdownPending = FALSE;
61 static BOOLEAN shutdownNow = FALSE;
64 * An enumeration of the file types
65 * supported by the FD_TABLE structure.
67 * XXX: Not all currently supported. This allows for future
87 * Structure used to map file handle and socket handle
88 * values into values that can be used to create unix-like
89 * select bitmaps, read/write for both sockets/files.
96 unsigned long instance;
98 int offset; /* only valid for async file writes */
99 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
100 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
101 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
102 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
106 * XXX Note there is no dyanmic sizing of this table, so if the
107 * number of open file descriptors exceeds WIN32_OPEN_MAX the
110 static struct FD_TABLE fdTable[WIN32_OPEN_MAX];
112 static CRITICAL_SECTION fdTableCritical;
114 struct OVERLAPPED_REQUEST {
115 OVERLAPPED overlapped;
116 unsigned long instance; /* file instance (won't match after a close) */
117 OS_AsyncProc procPtr; /* callback routine */
118 ClientData clientData; /* callback argument */
119 ClientData clientData1; /* additional clientData */
121 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
123 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
125 static FILE_TYPE listenType = FD_UNUSED;
127 // XXX This should be a DESCRIPTOR
128 static HANDLE hListen = INVALID_HANDLE_VALUE;
130 static BOOLEAN libInitialized = FALSE;
133 *--------------------------------------------------------------
135 * Win32NewDescriptor --
137 * Set up for I/O descriptor masquerading.
140 * Returns "fake id" which masquerades as a UNIX-style "small
141 * non-negative integer" file/socket descriptor.
142 * Win32_* routine below will "do the right thing" based on the
143 * descriptor's actual type. -1 indicates failure.
146 * Entry in fdTable is reserved to represent the socket/file.
148 *--------------------------------------------------------------
150 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
154 EnterCriticalSection(&fdTableCritical);
157 * If desiredFd is set, try to get this entry (this is used for
158 * mapping stdio handles). Otherwise try to get the fd entry.
159 * If this is not available, find a the first empty slot. .
161 if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
163 if (fdTable[desiredFd].type == FD_UNUSED)
170 if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED)
178 for (i = 1; i < WIN32_OPEN_MAX; ++i)
180 if (fdTable[i].type == FD_UNUSED)
191 fdTable[index].fid.value = fd;
192 fdTable[index].type = type;
193 fdTable[index].path = NULL;
194 fdTable[index].Errno = NO_ERROR;
195 fdTable[index].status = 0;
196 fdTable[index].offset = -1;
197 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
198 fdTable[index].hMapMutex = NULL;
199 fdTable[index].ovList = NULL;
202 LeaveCriticalSection(&fdTableCritical);
207 *--------------------------------------------------------------
211 * This thread performs I/O on stadard input. It is needed
212 * because you can't guarantee that all applications will
213 * create standard input with sufficient access to perform
214 * asynchronous I/O. Since we don't want to block the app
215 * reading from stdin we make it look like it's using I/O
216 * completion ports to perform async I/O.
219 * Data is read from stdin and posted to the io completion
225 *--------------------------------------------------------------
227 static void StdinThread(void * startup)
231 unsigned long bytesRead;
232 POVERLAPPED_REQUEST pOv;
234 // Touch the arg to prevent warning
239 * Block until a request to read from stdin comes in or a
240 * request to terminate the thread arrives (fd = -1).
242 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
243 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
248 ASSERT((fd == STDIN_FILENO) || (fd == -1));
253 ASSERT(pOv->clientData1 != NULL);
255 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
257 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
258 STDIN_FILENO, (LPOVERLAPPED)pOv);
268 void OS_ShutdownPending(void)
270 shutdownPending = TRUE;
273 static void ShutdownRequestThread(void * arg)
275 HANDLE shutdownEvent = (HANDLE) arg;
277 WaitForSingleObject(shutdownEvent, INFINITE);
279 shutdownPending = TRUE;
281 // emulate the unix behaviour
284 if (listenType == FD_PIPE_SYNC)
286 // Its a hassle to get ConnectNamedPipe to return early,
287 // so just wack the whole process - yes, this will toast
288 // any requests in progress, but at least its a clean
289 // shutdown (its better than TerminateProcess())
293 // FD_SOCKET_SYNC: When in Accept(), select() is used to poll
294 // the shutdownPending flag - yeah this isn't pretty either
295 // but its only one process doing it if an Accept mutex is used.
296 // This at least buys no toasted requests.
300 *--------------------------------------------------------------
304 * Set up the OS library for use.
307 * Returns 0 if success, -1 if not.
310 * Sockets initialized, pseudo file descriptors setup, etc.
312 *--------------------------------------------------------------
314 int OS_LibInit(int stdioFds[3])
320 char *cLenPtr = NULL;
326 InitializeCriticalSection(&fdTableCritical);
329 * Initialize windows sockets library.
331 wVersion = MAKEWORD(2,0);
332 err = WSAStartup( wVersion, &wsaData );
334 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
340 * Create the I/O completion port to be used for our I/O queue.
342 if (hIoCompPort == INVALID_HANDLE_VALUE) {
343 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
345 if(hIoCompPort == INVALID_HANDLE_VALUE) {
346 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
353 * If a shutdown event is in the env, save it (I don't see any to
354 * remove it from the environment out from under the application).
355 * Spawn a thread to wait on the shutdown request.
357 val = getenv(SHUTDOWN_EVENT_NAME);
360 HANDLE shutdownEvent = (HANDLE) atoi(val);
362 if (_beginthread(ShutdownRequestThread, 0, shutdownEvent) == -1)
368 if (acceptMutex == INVALID_HANDLE_VALUE)
370 /* If an accept mutex is in the env, use it */
371 val = getenv(MUTEX_VARNAME);
374 acceptMutex = (HANDLE) atoi(val);
379 * Determine if this library is being used to listen for FastCGI
380 * connections. This is communicated by STDIN containing a
381 * valid handle to a listener object. In this case, both the
382 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
383 * the starting process.
385 * The trick is determining if this is a pipe or a socket...
387 * XXX: Add the async accept test to determine socket or handle to a
390 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
391 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
392 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
394 DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
395 HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
397 // Move the handle to a "low" number
398 if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
399 GetCurrentProcess(), &hListen,
400 0, TRUE, DUPLICATE_SAME_ACCESS))
405 if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
410 CloseHandle(oldStdIn);
413 * Set the pipe handle state so that it operates in wait mode.
415 * NOTE: The listenFd is not mapped to a pseudo file descriptor
416 * as all work done on it is contained to the OS library.
418 * XXX: Initial assumption is that SetNamedPipeHandleState will
419 * fail if this is an IP socket...
421 if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
423 listenType = FD_PIPE_SYNC;
427 listenType = FD_SOCKET_SYNC;
432 * If there are no stdioFds passed in, we're done.
434 if(stdioFds == NULL) {
440 * Setup standard input asynchronous I/O. There is actually a separate
441 * thread spawned for this purpose. The reason for this is that some
442 * web servers use anonymous pipes for the connection between itself
443 * and a CGI application. Anonymous pipes can't perform asynchronous
444 * I/O or use I/O completion ports. Therefore in order to present a
445 * consistent I/O dispatch model to an application we emulate I/O
446 * completion port behavior by having the standard input thread posting
447 * messages to the hIoCompPort which look like a complete overlapped
448 * I/O structure. This keeps the event dispatching simple from the
449 * application perspective.
451 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
453 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
454 HANDLE_FLAG_INHERIT, 0)) {
456 * XXX: Causes error when run from command line. Check KB
457 err = GetLastError();
463 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
464 (int)stdioHandles[STDIN_FILENO],
465 STDIN_FILENO)) == -1) {
469 * Set stdin equal to our pseudo FD and create the I/O completion
470 * port to be used for async I/O.
472 stdioFds[STDIN_FILENO] = fakeFd;
476 * Create the I/O completion port to be used for communicating with
477 * the thread doing I/O on standard in. This port will carry read
478 * and possibly thread termination requests to the StdinThread.
480 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
481 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
483 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
484 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
491 * Create the thread that will read stdin if the CONTENT_LENGTH
494 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
496 hStdinThread = (HANDLE) _beginthread(StdinThread, 0, NULL);
497 if (hStdinThread == (HANDLE) -1) {
498 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
505 * STDOUT will be used synchronously.
507 * XXX: May want to convert this so that it could be used for OVERLAPPED
508 * I/O later. If so, model it after the Stdin I/O as stdout is
509 * also incapable of async I/O on some servers.
511 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
512 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
513 HANDLE_FLAG_INHERIT, FALSE)) {
518 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
519 (int)stdioHandles[STDOUT_FILENO],
520 STDOUT_FILENO)) == -1) {
524 * Set stdout equal to our pseudo FD
526 stdioFds[STDOUT_FILENO] = fakeFd;
529 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
530 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
531 HANDLE_FLAG_INHERIT, FALSE)) {
535 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
536 (int)stdioHandles[STDERR_FILENO],
537 STDERR_FILENO)) == -1) {
541 * Set stderr equal to our pseudo FD
543 stdioFds[STDERR_FILENO] = fakeFd;
550 *--------------------------------------------------------------
554 * Shutdown the OS library.
560 * Memory freed, handles closed.
562 *--------------------------------------------------------------
564 void OS_LibShutdown()
567 if (hIoCompPort != INVALID_HANDLE_VALUE)
569 CloseHandle(hIoCompPort);
570 hIoCompPort = INVALID_HANDLE_VALUE;
573 if (hStdinCompPort != INVALID_HANDLE_VALUE)
575 CloseHandle(hStdinCompPort);
576 hStdinCompPort = INVALID_HANDLE_VALUE;
579 if (acceptMutex != INVALID_HANDLE_VALUE)
581 ReleaseMutex(acceptMutex);
584 DisconnectNamedPipe(hListen);
593 *--------------------------------------------------------------
595 * Win32FreeDescriptor --
597 * Free I/O descriptor entry in fdTable.
600 * Frees I/O descriptor entry in fdTable.
605 *--------------------------------------------------------------
607 static void Win32FreeDescriptor(int fd)
609 /* Catch it if fd is a bogus value */
610 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
612 EnterCriticalSection(&fdTableCritical);
614 if (fdTable[fd].type != FD_UNUSED)
616 switch (fdTable[fd].type)
621 /* Free file path string */
622 ASSERT(fdTable[fd].path != NULL);
623 free(fdTable[fd].path);
624 fdTable[fd].path = NULL;
631 ASSERT(fdTable[fd].path == NULL);
633 fdTable[fd].type = FD_UNUSED;
634 fdTable[fd].path = NULL;
635 fdTable[fd].Errno = NO_ERROR;
636 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
638 if (fdTable[fd].hMapMutex != NULL)
640 CloseHandle(fdTable[fd].hMapMutex);
641 fdTable[fd].hMapMutex = NULL;
645 LeaveCriticalSection(&fdTableCritical);
650 static short getPort(const char * bindPath)
653 char * p = strchr(bindPath, ':');
662 port = (short) atoi(buf);
669 * OS_CreateLocalIpcFd --
671 * This procedure is responsible for creating the listener pipe
672 * on Windows NT for local process communication. It will create a
673 * named pipe and return a file descriptor to it to the caller.
676 * Listener pipe created. This call returns either a valid
677 * pseudo file descriptor or -1 on error.
680 * Listener pipe and IPC address are stored in the FCGI info
682 * 'errno' will set on errors (-1 is returned).
684 *----------------------------------------------------------------------
686 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
689 short port = getPort(bindPath);
691 if (acceptMutex == INVALID_HANDLE_VALUE)
693 acceptMutex = CreateMutex(NULL, FALSE, NULL);
694 if (acceptMutex == NULL) return -2;
695 if (! SetHandleInformation(acceptMutex, HANDLE_FLAG_INHERIT, TRUE)) return -3;
698 // There's nothing to be gained (at the moment) by a shutdown Event
700 if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST)))
702 fprintf(stderr, "To start a service on a TCP port can not "
703 "specify a host name.\n"
704 "You should either use \"localhost:<port>\" or "
705 " just use \":<port>.\"\n");
709 listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC;
714 struct sockaddr_in sockAddr;
715 int sockLen = sizeof(sockAddr);
717 memset(&sockAddr, 0, sizeof(sockAddr));
718 sockAddr.sin_family = AF_INET;
719 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
720 sockAddr.sin_port = htons(port);
722 listenSock = socket(AF_INET, SOCK_STREAM, 0);
723 if (listenSock == INVALID_SOCKET)
728 if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) )
733 if (listen(listenSock, backlog))
738 pseudoFd = Win32NewDescriptor(listenType, listenSock, -1);
742 closesocket(listenSock);
746 hListen = (HANDLE) listenSock;
750 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
751 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
758 strcpy(pipePath, bindPathPrefix);
759 strcat(pipePath, bindPath);
761 hListenPipe = CreateNamedPipe(pipePath,
763 PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE,
764 PIPE_UNLIMITED_INSTANCES,
765 4096, 4096, 0, NULL);
769 if (hListenPipe == INVALID_HANDLE_VALUE)
774 if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE))
779 pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1);
783 CloseHandle(hListenPipe);
787 hListen = (HANDLE) hListenPipe;
794 *----------------------------------------------------------------------
798 * Create the pipe pathname connect to the remote application if
802 * -1 if fail or a valid handle if connection succeeds.
805 * Remote connection established.
807 *----------------------------------------------------------------------
809 int OS_FcgiConnect(char *bindPath)
811 short port = getPort(bindPath);
818 struct sockaddr_in sockAddr;
819 int sockLen = sizeof(sockAddr);
822 if (*bindPath != ':')
824 char * p = strchr(bindPath, ':');
825 int len = p - bindPath + 1;
828 strncpy(host, bindPath, len);
832 hp = gethostbyname(host ? host : LOCALHOST);
841 fprintf(stderr, "Unknown host: %s\n", bindPath);
845 memset(&sockAddr, 0, sizeof(sockAddr));
846 sockAddr.sin_family = AF_INET;
847 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
848 sockAddr.sin_port = htons(port);
850 sock = socket(AF_INET, SOCK_STREAM, 0);
851 if (sock == INVALID_SOCKET)
856 if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen))
862 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1);
871 char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1);
879 strcpy(pipePath, bindPathPrefix);
880 strcat(pipePath, bindPath);
882 hPipe = CreateFile(pipePath,
883 GENERIC_WRITE | GENERIC_READ,
884 FILE_SHARE_READ | FILE_SHARE_WRITE,
887 FILE_FLAG_OVERLAPPED,
892 if( hPipe == INVALID_HANDLE_VALUE)
897 pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1);
906 * Set stdin equal to our pseudo FD and create the I/O completion
907 * port to be used for async I/O.
909 if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1))
911 Win32FreeDescriptor(pseudoFd);
921 *--------------------------------------------------------------
925 * Pass through to the appropriate NT read function.
928 * Returns number of byes read. Mimics unix read:.
929 * n bytes read, 0 or -1 failure: errno contains actual error
934 *--------------------------------------------------------------
936 int OS_Read(int fd, char * buf, size_t len)
941 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
943 if (shutdownNow) return -1;
945 switch (fdTable[fd].type)
952 if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL))
958 fdTable[fd].Errno = GetLastError();
964 case FD_SOCKET_ASYNC:
966 ret = recv(fdTable[fd].fid.sock, buf, len, 0);
967 if (ret == SOCKET_ERROR)
969 fdTable[fd].Errno = WSAGetLastError();
984 *--------------------------------------------------------------
988 * Perform a synchronous OS write.
991 * Returns number of bytes written. Mimics unix write:
992 * n bytes written, 0 or -1 failure (??? couldn't find man page).
997 *--------------------------------------------------------------
999 int OS_Write(int fd, char * buf, size_t len)
1004 ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX);
1006 if (shutdownNow) return -1;
1008 switch (fdTable[fd].type)
1015 if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL))
1021 fdTable[fd].Errno = GetLastError();
1026 case FD_SOCKET_SYNC:
1027 case FD_SOCKET_ASYNC:
1029 ret = send(fdTable[fd].fid.sock, buf, len, 0);
1030 if (ret == SOCKET_ERROR)
1032 fdTable[fd].Errno = WSAGetLastError();
1047 *----------------------------------------------------------------------
1051 * Spawns a new server listener process, and stores the information
1052 * relating to the child in the supplied record. A wait handler is
1053 * registered on the child's completion. This involves creating
1054 * a process on NT and preparing a command line with the required
1055 * state (currently a -childproc flag and the server socket to use
1056 * for accepting connections).
1059 * 0 if success, -1 if error.
1062 * Child process spawned.
1064 *----------------------------------------------------------------------
1066 int OS_SpawnChild(char *execPath, int listenFd)
1068 STARTUPINFO StartupInfo;
1069 PROCESS_INFORMATION pInfo;
1072 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
1073 StartupInfo.cb = sizeof (STARTUPINFO);
1074 StartupInfo.lpReserved = NULL;
1075 StartupInfo.lpReserved2 = NULL;
1076 StartupInfo.cbReserved2 = 0;
1077 StartupInfo.lpDesktop = NULL;
1080 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
1081 * the new process. The fact that there is a stdin and NULL handles
1082 * for stdout and stderr tells the FastCGI process that this is a
1083 * FastCGI process and not a CGI process.
1085 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1087 * XXX: Do I have to dup the handle before spawning the process or is
1088 * it sufficient to use the handle as it's reference counted
1091 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
1092 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
1093 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
1096 * Make the listener socket inheritable.
1098 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
1105 * XXX: Might want to apply some specific security attributes to the
1108 success = CreateProcess(execPath, /* LPCSTR address of module name */
1109 NULL, /* LPCSTR address of command line */
1110 NULL, /* Process security attributes */
1111 NULL, /* Thread security attributes */
1112 TRUE, /* Inheritable Handes inherited. */
1113 0, /* DWORD creation flags */
1114 NULL, /* Use parent environment block */
1115 NULL, /* Address of current directory name */
1116 &StartupInfo, /* Address of STARTUPINFO */
1117 &pInfo); /* Address of PROCESS_INFORMATION */
1126 *--------------------------------------------------------------
1128 * OS_AsyncReadStdin --
1130 * This initiates an asynchronous read on the standard
1131 * input handle. This handle is not guaranteed to be
1132 * capable of performing asynchronous I/O so we send a
1133 * message to the StdinThread to do the synchronous read.
1136 * -1 if error, 0 otherwise.
1139 * Asynchronous message is queued to the StdinThread and an
1140 * overlapped structure is allocated/initialized.
1142 *--------------------------------------------------------------
1144 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1145 ClientData clientData)
1147 POVERLAPPED_REQUEST pOv;
1149 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1151 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1153 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1154 pOv->clientData1 = (ClientData)buf;
1155 pOv->instance = fdTable[STDIN_FILENO].instance;
1156 pOv->procPtr = procPtr;
1157 pOv->clientData = clientData;
1159 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1165 *--------------------------------------------------------------
1169 * This initiates an asynchronous read on the file
1170 * handle which may be a socket or named pipe.
1172 * We also must save the ProcPtr and ClientData, so later
1173 * when the io completes, we know who to call.
1175 * We don't look at any results here (the ReadFile may
1176 * return data if it is cached) but do all completion
1177 * processing in OS_Select when we get the io completion
1178 * port done notifications. Then we call the callback.
1181 * -1 if error, 0 otherwise.
1184 * Asynchronous I/O operation is queued for completion.
1186 *--------------------------------------------------------------
1188 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1189 OS_AsyncProc procPtr, ClientData clientData)
1192 POVERLAPPED_REQUEST pOv;
1195 * Catch any bogus fd values
1197 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1199 * Confirm that this is an async fd
1201 ASSERT(fdTable[fd].type != FD_UNUSED);
1202 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1203 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1204 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1206 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1208 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1210 * Only file offsets should be non-zero, but make sure.
1212 if (fdTable[fd].type == FD_FILE_ASYNC)
1213 if (fdTable[fd].offset >= 0)
1214 pOv->overlapped.Offset = fdTable[fd].offset;
1216 pOv->overlapped.Offset = offset;
1217 pOv->instance = fdTable[fd].instance;
1218 pOv->procPtr = procPtr;
1219 pOv->clientData = clientData;
1222 * ReadFile returns: TRUE success, FALSE failure
1224 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1225 (LPOVERLAPPED)pOv)) {
1226 fdTable[fd].Errno = GetLastError();
1227 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1228 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1229 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1232 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1233 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1236 fdTable[fd].Errno = 0;
1242 *--------------------------------------------------------------
1246 * This initiates an asynchronous write on the "fake" file
1247 * descriptor (which may be a file, socket, or named pipe).
1248 * We also must save the ProcPtr and ClientData, so later
1249 * when the io completes, we know who to call.
1251 * We don't look at any results here (the WriteFile generally
1252 * completes immediately) but do all completion processing
1253 * in OS_DoIo when we get the io completion port done
1254 * notifications. Then we call the callback.
1257 * -1 if error, 0 otherwise.
1260 * Asynchronous I/O operation is queued for completion.
1262 *--------------------------------------------------------------
1264 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1265 OS_AsyncProc procPtr, ClientData clientData)
1268 POVERLAPPED_REQUEST pOv;
1271 * Catch any bogus fd values
1273 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1275 * Confirm that this is an async fd
1277 ASSERT(fdTable[fd].type != FD_UNUSED);
1278 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1279 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1280 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1282 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1284 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1286 * Only file offsets should be non-zero, but make sure.
1288 if (fdTable[fd].type == FD_FILE_ASYNC)
1290 * Only file opened via OS_AsyncWrite with
1291 * O_APPEND will have an offset != -1.
1293 if (fdTable[fd].offset >= 0)
1295 * If the descriptor has a memory mapped file
1296 * handle, take the offsets from there.
1298 if (fdTable[fd].hMapMutex != NULL) {
1300 * Wait infinitely; this *should* not cause problems.
1302 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1305 * Retrieve the shared offset values.
1307 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1308 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1311 * Update the shared offset values for the next write
1313 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1314 *(fdTable[fd].offsetLowPtr) += len;
1316 ReleaseMutex(fdTable[fd].hMapMutex);
1318 pOv->overlapped.Offset = fdTable[fd].offset;
1320 pOv->overlapped.Offset = offset;
1321 pOv->instance = fdTable[fd].instance;
1322 pOv->procPtr = procPtr;
1323 pOv->clientData = clientData;
1326 * WriteFile returns: TRUE success, FALSE failure
1328 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1329 (LPOVERLAPPED)pOv)) {
1330 fdTable[fd].Errno = GetLastError();
1331 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1332 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1335 fdTable[fd].Errno = 0;
1337 if (fdTable[fd].offset >= 0)
1338 fdTable[fd].offset += len;
1343 *--------------------------------------------------------------
1347 * Closes the descriptor with routine appropriate for
1348 * descriptor's type.
1351 * Socket or file is closed. Return values mimic Unix close:
1352 * 0 success, -1 failure
1355 * Entry in fdTable is marked as free.
1357 *--------------------------------------------------------------
1359 int OS_Close(int fd, int shutdown_ok)
1364 * Catch it if fd is a bogus value
1366 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1367 ASSERT(fdTable[fd].type != FD_UNUSED);
1369 switch (fdTable[fd].type) {
1377 case FD_SOCKET_SYNC:
1378 case FD_SOCKET_ASYNC:
1381 * shutdown() the send side and then read() from client until EOF
1382 * or a timeout expires. This is done to minimize the potential
1383 * that a TCP RST will be sent by our TCP stack in response to
1384 * receipt of additional data from the client. The RST would
1385 * cause the client to discard potentially useful response data.
1390 if (shutdown(fdTable[fd].fid.sock, SD_SEND) == 0)
1394 int sock = fdTable[fd].fid.sock;
1402 #pragma warning( disable : 4127 )
1403 FD_SET((unsigned) sock, &rfds);
1404 #pragma warning( default : 4127 )
1408 rv = select(sock + 1, &rfds, NULL, NULL, &tv);
1410 while (rv > 0 && recv(sock, trash, sizeof(trash), 0) > 0);
1414 closesocket(fdTable[fd].fid.sock);
1420 ret = -1; /* fake failure */
1423 Win32FreeDescriptor(fd);
1428 *--------------------------------------------------------------
1432 * Cancel outstanding asynchronous reads and prevent subsequent
1433 * reads from completing.
1436 * Socket or file is shutdown. Return values mimic Unix shutdown:
1437 * 0 success, -1 failure
1439 *--------------------------------------------------------------
1441 int OS_CloseRead(int fd)
1446 * Catch it if fd is a bogus value
1448 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1449 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1450 || fdTable[fd].type == FD_SOCKET_SYNC);
1452 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1458 *--------------------------------------------------------------
1462 * This function was formerly OS_Select. It's purpose is
1463 * to pull I/O completion events off the queue and dispatch
1464 * them to the appropriate place.
1470 * Handlers are called.
1472 *--------------------------------------------------------------
1474 int OS_DoIo(struct timeval *tmo)
1477 unsigned long bytes;
1478 POVERLAPPED_REQUEST pOv;
1485 * We can loop in here, but not too long, as wait handlers
1487 * For cgi stdin, apparently select returns when io completion
1488 * ports don't, so don't wait the full timeout.
1491 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1495 ms_last = tb.time*1000 + tb.millitm;
1497 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1499 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1500 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1501 err = WSAGetLastError();
1502 return 0; /* timeout */
1505 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1506 /* call callback if descriptor still valid */
1508 if(pOv->instance == fdTable[fd].instance)
1509 (*pOv->procPtr)(pOv->clientData, bytes);
1513 ms -= (tb.time*1000 + tb.millitm - ms_last);
1514 ms_last = tb.time*1000 + tb.millitm;
1519 static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs)
1521 static const char *token = " ,;:\t";
1525 if (okAddrs == NULL) return TRUE;
1527 ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
1528 p = strstr(okAddrs, ipaddr);
1530 if (p == NULL) return FALSE;
1534 p += strlen(ipaddr);
1535 return (strchr(token, *p) != NULL);
1538 if (strchr(token, *--p) != NULL)
1540 p += strlen(ipaddr) + 1;
1541 return (strchr(token, *p) != NULL);
1548 static int CALLBACK isAddrOKCallback(LPWSABUF lpCallerId,
1557 struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf;
1559 // Touch the args to avoid warnings
1560 dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; dc5 = NULL;
1562 if ((void *) data == NULL) return CF_ACCEPT;
1564 if (sockaddr->sin_family != AF_INET) return CF_ACCEPT;
1566 return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT;
1570 static void printLastError(const char * text)
1575 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1576 FORMAT_MESSAGE_FROM_SYSTEM |
1577 FORMAT_MESSAGE_IGNORE_INSERTS,
1586 fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf);
1590 static int acceptNamedPipe()
1594 if (! ConnectNamedPipe(hListen, NULL))
1596 switch (GetLastError())
1598 case ERROR_PIPE_CONNECTED:
1600 // A client connected after CreateNamedPipe but
1601 // before ConnectNamedPipe. Its a good connection.
1605 case ERROR_IO_PENDING:
1607 // The NamedPipe was opened with an Overlapped structure
1608 // and there is a pending io operation. mod_fastcgi
1609 // did this in 2.2.12 (fcgi_pm.c v1.52).
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};
1652 #pragma warning( disable : 4127 )
1653 FD_SET((unsigned int) hListen, &readfds);
1654 #pragma warning( default : 4127 )
1656 if (select(0, &readfds, NULL, NULL, &timeout) == 0)
1658 if (shutdownPending)
1671 hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen);
1673 if (hSock == INVALID_SOCKET)
1678 if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs))
1685 hSock = WSAAccept((unsigned int) hListen,
1689 (DWORD) webServerAddrs);
1691 if (hSock != INVALID_SOCKET)
1696 if (WSAGetLastError() != WSAECONNREFUSED)
1703 if (hSock == INVALID_SOCKET)
1705 /* Use FormatMessage() */
1706 fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError());
1710 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1720 *----------------------------------------------------------------------
1724 * Accepts a new FastCGI connection. This routine knows whether
1725 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1727 * fail_on_intr is ignored in the Win lib.
1730 * -1 if the operation fails, otherwise this is a valid IPC fd.
1732 *----------------------------------------------------------------------
1734 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1738 // Touch args to prevent warnings
1739 listen_sock = 0; fail_on_intr = 0;
1741 // @todo Muliple listen sockets and sockets other than 0 are not
1742 // supported due to the use of globals.
1744 if (shutdownPending)
1750 // The mutex is to keep other processes (and threads, when supported)
1751 // from going into the accept cycle. The accept cycle needs to
1752 // periodically break out to check the state of the shutdown flag
1753 // and there's no point to having more than one thread do that.
1755 if (acceptMutex != INVALID_HANDLE_VALUE)
1757 if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
1759 printLastError("WaitForSingleObject() failed");
1764 if (shutdownPending)
1768 else if (listenType == FD_PIPE_SYNC)
1770 ipcFd = acceptNamedPipe();
1772 else if (listenType == FD_SOCKET_SYNC)
1774 ipcFd = acceptSocket(webServerAddrs);
1778 fprintf(stderr, "unknown listenType (%d)\n", listenType);
1781 if (acceptMutex != INVALID_HANDLE_VALUE)
1783 ReleaseMutex(acceptMutex);
1790 *----------------------------------------------------------------------
1794 * OS IPC routine to close an IPC connection.
1800 * IPC connection is closed.
1802 *----------------------------------------------------------------------
1804 int OS_IpcClose(int ipcFd, int shutdown)
1806 if (ipcFd == -1) return 0;
1809 * Catch it if fd is a bogus value
1811 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1812 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1818 * Make sure that the client (ie. a Web Server in this case) has
1819 * read all data from the pipe before we disconnect.
1821 if (! FlushFileBuffers(fdTable[ipcFd].fid.fileHandle)) return -1;
1823 if (! DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) return -1;
1827 case FD_SOCKET_SYNC:
1829 OS_Close(ipcFd, shutdown);
1843 *----------------------------------------------------------------------
1847 * Determines whether this process is a FastCGI process or not.
1850 * Returns 1 if FastCGI, 0 if not.
1855 *----------------------------------------------------------------------
1857 int OS_IsFcgi(int sock)
1859 // Touch args to prevent warnings
1862 /* XXX This is broken for sock */
1864 return (listenType != FD_UNUSED);
1868 *----------------------------------------------------------------------
1872 * Sets selected flag bits in an open file descriptor. Currently
1873 * this is only to put a SOCKET into non-blocking mode.
1875 *----------------------------------------------------------------------
1877 void OS_SetFlags(int fd, int flags)
1879 unsigned long pLong = 1L;
1882 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1883 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1885 exit(WSAGetLastError());
1887 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1888 hIoCompPort, fd, 1)) {
1889 err = GetLastError();
1893 fdTable[fd].type = FD_SOCKET_ASYNC;