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.9 2001/03/26 20:04:56 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 */
38 #define MUTEX_VARNAME "_FCGI_MUTEX_"
39 #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_"
41 static HANDLE hIoCompPort = INVALID_HANDLE_VALUE;
42 static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE;
43 static HANDLE hStdinThread = INVALID_HANDLE_VALUE;
45 static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
46 INVALID_HANDLE_VALUE};
48 static HANDLE acceptMutex = INVALID_HANDLE_VALUE;
50 static BOOLEAN shutdownPending = FALSE;
53 * An enumeration of the file types
54 * supported by the FD_TABLE structure.
56 * XXX: Not all currently supported. This allows for future
76 * Structure used to map file handle and socket handle
77 * values into values that can be used to create unix-like
78 * select bitmaps, read/write for both sockets/files.
85 unsigned long instance;
87 int offset; /* only valid for async file writes */
88 LPDWORD offsetHighPtr; /* pointers to offset high and low words */
89 LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */
90 HANDLE hMapMutex; /* mutex handle for multi-proc offset update */
91 LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */
94 typedef struct FD_TABLE *PFD_TABLE;
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 struct OVERLAPPED_REQUEST {
104 OVERLAPPED overlapped;
105 unsigned long instance; /* file instance (won't match after a close) */
106 OS_AsyncProc procPtr; /* callback routine */
107 ClientData clientData; /* callback argument */
108 ClientData clientData1; /* additional clientData */
110 typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST;
112 static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\";
114 static int listenType = FD_UNUSED;
116 // XXX This should be a DESCRIPTOR
117 static HANDLE hListen = INVALID_HANDLE_VALUE;
119 static OVERLAPPED listenOverlapped;
120 static BOOLEAN libInitialized = FALSE;
124 *--------------------------------------------------------------
126 * Win32NewDescriptor --
128 * Set up for I/O descriptor masquerading.
131 * Returns "fake id" which masquerades as a UNIX-style "small
132 * non-negative integer" file/socket descriptor.
133 * Win32_* routine below will "do the right thing" based on the
134 * descriptor's actual type. -1 indicates failure.
137 * Entry in fdTable is reserved to represent the socket/file.
139 *--------------------------------------------------------------
141 static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd)
146 * If the "desiredFd" is not -1, try to get this entry for our
147 * pseudo file descriptor. If this is not available, return -1
148 * as the caller wanted to get this mapping. This is typically
149 * only used for mapping stdio handles.
151 if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX)
153 if (fdTable[desiredFd].type != FD_UNUSED)
161 // See if the entry that matches "fd" is available.
163 if (fd <= 0 || fd >= WIN32_OPEN_MAX)
168 if (fdTable[fd].type == FD_UNUSED)
174 // Find an entry we can use.
175 // Start at 1 (0 fake id fails in some cases).
177 for (index = 1; index < WIN32_OPEN_MAX; index++)
179 if (fdTable[index].type == FD_UNUSED)
185 if (index == WIN32_OPEN_MAX)
187 SetLastError(WSAEMFILE);
193 fdTable[index].fid.value = fd;
194 fdTable[index].type = type;
195 fdTable[index].path = NULL;
196 fdTable[index].Errno = NO_ERROR;
197 fdTable[index].status = 0;
198 fdTable[index].offset = -1;
199 fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL;
200 fdTable[index].hMapMutex = NULL;
201 fdTable[index].ovList = NULL;
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(LPDWORD startup){
232 POVERLAPPED_REQUEST pOv;
236 * Block until a request to read from stdin comes in or a
237 * request to terminate the thread arrives (fd = -1).
239 if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd,
240 (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) {
245 ASSERT((fd == STDIN_FILENO) || (fd == -1));
250 ASSERT(pOv->clientData1 != NULL);
252 if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead,
254 PostQueuedCompletionStatus(hIoCompPort, bytesRead,
255 STDIN_FILENO, (LPOVERLAPPED)pOv);
265 static DWORD WINAPI ShutdownRequestThread(LPVOID arg)
267 HANDLE shutdownEvent = (HANDLE) arg;
269 if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED)
271 // Assuming it will happen again, all we can do is exit the thread
276 // "Simple reads and writes to properly-aligned 32-bit variables are atomic"
277 shutdownPending = TRUE;
279 // Before an accept() is entered the shutdownPending flag is checked.
280 // If set, OS_Accept() will return -1. If not, it waits
281 // on a connection request for one second, checks the flag, & repeats.
282 // Only one process/thread is allowed to do this at time by
283 // wrapping the accept() with mutex.
289 *--------------------------------------------------------------
293 * Set up the OS library for use.
296 * Returns 0 if success, -1 if not.
299 * Sockets initialized, pseudo file descriptors setup, etc.
301 *--------------------------------------------------------------
303 int OS_LibInit(int stdioFds[3])
310 char *cLenPtr = NULL;
317 * Initialize windows sockets library.
319 wVersion = MAKEWORD(2,0);
320 err = WSAStartup( wVersion, &wsaData );
322 fprintf(stderr, "Error starting Windows Sockets. Error: %d",
328 * Create the I/O completion port to be used for our I/O queue.
330 if (hIoCompPort == INVALID_HANDLE_VALUE) {
331 hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
333 if(hIoCompPort == INVALID_HANDLE_VALUE) {
334 printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n",
341 * If a shutdown event is in the env, save it (I don't see any to
342 * remove it from the environment out from under the application).
343 * Spawn a thread to wait on the shutdown request.
345 val = getenv(SHUTDOWN_EVENT_NAME);
348 HANDLE shutdownEvent = (HANDLE) atoi(val);
350 putenv(SHUTDOWN_EVENT_NAME"=");
352 if (! CreateThread(NULL, 0, ShutdownRequestThread,
353 shutdownEvent, 0, NULL))
360 * If an accept mutex is in the env, save it and remove it.
362 val = getenv(MUTEX_VARNAME);
365 acceptMutex = (HANDLE) atoi(val);
370 * Determine if this library is being used to listen for FastCGI
371 * connections. This is communicated by STDIN containing a
372 * valid handle to a listener object. In this case, both the
373 * "stdout" and "stderr" handles will be INVALID (ie. closed) by
374 * the starting process.
376 * The trick is determining if this is a pipe or a socket...
378 * XXX: Add the async accept test to determine socket or handle to a
381 if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) &&
382 (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) &&
383 (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) )
385 DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT;
386 HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE);
388 // Move the handle to a "low" number
389 if (! DuplicateHandle(GetCurrentProcess(), oldStdIn,
390 GetCurrentProcess(), &hListen,
391 0, TRUE, DUPLICATE_SAME_ACCESS))
396 if (! SetStdHandle(STD_INPUT_HANDLE, hListen))
401 CloseHandle(oldStdIn);
404 * Set the pipe handle state so that it operates in wait mode.
406 * NOTE: The listenFd is not mapped to a pseudo file descriptor
407 * as all work done on it is contained to the OS library.
409 * XXX: Initial assumption is that SetNamedPipeHandleState will
410 * fail if this is an IP socket...
412 if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL))
414 listenType = FD_PIPE_SYNC;
415 listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
419 listenType = FD_SOCKET_SYNC;
424 * If there are no stdioFds passed in, we're done.
426 if(stdioFds == NULL) {
432 * Setup standard input asynchronous I/O. There is actually a separate
433 * thread spawned for this purpose. The reason for this is that some
434 * web servers use anonymous pipes for the connection between itself
435 * and a CGI application. Anonymous pipes can't perform asynchronous
436 * I/O or use I/O completion ports. Therefore in order to present a
437 * consistent I/O dispatch model to an application we emulate I/O
438 * completion port behavior by having the standard input thread posting
439 * messages to the hIoCompPort which look like a complete overlapped
440 * I/O structure. This keeps the event dispatching simple from the
441 * application perspective.
443 stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE);
445 if(!SetHandleInformation(stdioHandles[STDIN_FILENO],
446 HANDLE_FLAG_INHERIT, 0)) {
448 * XXX: Causes error when run from command line. Check KB
449 err = GetLastError();
455 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
456 (int)stdioHandles[STDIN_FILENO],
457 STDIN_FILENO)) == -1) {
461 * Set stdin equal to our pseudo FD and create the I/O completion
462 * port to be used for async I/O.
464 stdioFds[STDIN_FILENO] = fakeFd;
468 * Create the I/O completion port to be used for communicating with
469 * the thread doing I/O on standard in. This port will carry read
470 * and possibly thread termination requests to the StdinThread.
472 if (hStdinCompPort == INVALID_HANDLE_VALUE) {
473 hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL,
475 if(hStdinCompPort == INVALID_HANDLE_VALUE) {
476 printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n",
483 * Create the thread that will read stdin if the CONTENT_LENGTH
486 if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL &&
488 hStdinThread = CreateThread(NULL, 8192,
489 (LPTHREAD_START_ROUTINE)&StdinThread,
491 if (hStdinThread == NULL) {
492 printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n",
499 * STDOUT will be used synchronously.
501 * XXX: May want to convert this so that it could be used for OVERLAPPED
502 * I/O later. If so, model it after the Stdin I/O as stdout is
503 * also incapable of async I/O on some servers.
505 stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE);
506 if(!SetHandleInformation(stdioHandles[STDOUT_FILENO],
507 HANDLE_FLAG_INHERIT, FALSE)) {
512 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
513 (int)stdioHandles[STDOUT_FILENO],
514 STDOUT_FILENO)) == -1) {
518 * Set stdout equal to our pseudo FD
520 stdioFds[STDOUT_FILENO] = fakeFd;
523 stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE);
524 if(!SetHandleInformation(stdioHandles[STDERR_FILENO],
525 HANDLE_FLAG_INHERIT, FALSE)) {
529 if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC,
530 (int)stdioHandles[STDERR_FILENO],
531 STDERR_FILENO)) == -1) {
535 * Set stderr equal to our pseudo FD
537 stdioFds[STDERR_FILENO] = fakeFd;
545 *--------------------------------------------------------------
549 * Shutdown the OS library.
555 * Memory freed, handles closed.
557 *--------------------------------------------------------------
559 void OS_LibShutdown()
562 if(hIoCompPort != INVALID_HANDLE_VALUE) {
563 CloseHandle(hIoCompPort);
564 hIoCompPort = INVALID_HANDLE_VALUE;
567 if(hStdinCompPort != INVALID_HANDLE_VALUE) {
568 CloseHandle(hStdinCompPort);
569 hStdinCompPort = INVALID_HANDLE_VALUE;
573 * Shutdown the socket library.
581 *--------------------------------------------------------------
583 * Win32FreeDescriptor --
585 * Free I/O descriptor entry in fdTable.
588 * Frees I/O descriptor entry in fdTable.
593 *--------------------------------------------------------------
595 static void Win32FreeDescriptor(int fd)
597 /* Catch it if fd is a bogus value */
598 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
599 ASSERT(fdTable[fd].type != FD_UNUSED);
601 switch (fdTable[fd].type) {
604 /* Free file path string */
605 ASSERT(fdTable[fd].path != NULL);
606 free(fdTable[fd].path);
607 fdTable[fd].path = NULL;
611 * Break through to generic fdTable free-descriptor code
616 ASSERT(fdTable[fd].path == NULL);
617 fdTable[fd].type = FD_UNUSED;
618 fdTable[fd].path = NULL;
619 fdTable[fd].Errno = NO_ERROR;
620 fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL;
621 if (fdTable[fd].hMapMutex != NULL) {
622 CloseHandle(fdTable[fd].hMapMutex);
623 fdTable[fd].hMapMutex = NULL;
630 * OS_CreateLocalIpcFd --
632 * This procedure is responsible for creating the listener pipe
633 * on Windows NT for local process communication. It will create a
634 * named pipe and return a file descriptor to it to the caller.
637 * Listener pipe created. This call returns either a valid
638 * pseudo file descriptor or -1 on error.
641 * Listener pipe and IPC address are stored in the FCGI info
643 * 'errno' will set on errors (-1 is returned).
645 *----------------------------------------------------------------------
647 int OS_CreateLocalIpcFd(const char *bindPath, int backlog)
650 SECURITY_ATTRIBUTES sa;
651 HANDLE hListenPipe = INVALID_HANDLE_VALUE;
656 struct sockaddr_in sockAddr;
662 HANDLE mutex = CreateMutex(NULL, FALSE, NULL);
669 if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE))
674 // This is a nail for listening to more than one port..
675 // This should really be handled by the caller.
676 _snprintf(buf, 1024, MUTEX_VARNAME "=%d", (int) mutex);
680 // There's nothing to be gained (at the moment) by a shutdown Event
682 strncpy(buf, bindPath, 1024);
685 if((tp = strchr(buf, ':')) != 0) {
687 if((port = atoi(tp)) == 0) {
694 if(tcp && (*buf && strcmp(buf, "localhost") != 0)) {
695 fprintf(stderr, "To start a service on a TCP port can not "
696 "specify a host name.\n"
697 "You should either use \"localhost:<port>\" or "
698 " just use \":<port>.\"\n");
703 listenSock = socket(AF_INET, SOCK_STREAM, 0);
704 if(listenSock == SOCKET_ERROR) {
708 * Bind the listening socket.
710 memset((char *) &sockAddr, 0, sizeof(sockAddr));
711 sockAddr.sin_family = AF_INET;
712 sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
713 sockAddr.sin_port = htons(port);
714 servLen = sizeof(sockAddr);
716 if(bind(listenSock, (struct sockaddr *) &sockAddr, servLen) < 0
717 || listen(listenSock, backlog) < 0) {
718 perror("bind/listen");
722 retFd = Win32NewDescriptor(FD_SOCKET_SYNC, (int)listenSock, -1);
728 * Initialize the SECURITY_ATTRIUBTES structure.
730 sa.nLength = sizeof(sa);
731 sa.lpSecurityDescriptor = NULL;
732 sa.bInheritHandle = TRUE; /* This will be inherited by the
736 * Create a unique name to be used for the socket bind path.
737 * Make sure that this name is unique and that there's no process
740 * Named Pipe Pathname: \\.\pipe\FastCGI\OM_WS.pid.N
741 * Where: N is the pipe instance on the machine.
744 bpLen = (int)strlen(bindPathPrefix);
745 bpLen += strlen(bindPath);
746 localPath = malloc(bpLen+2);
747 strcpy(localPath, bindPathPrefix);
748 strcat(localPath, bindPath);
751 * Create and setup the named pipe to be used by the fcgi server.
753 hListenPipe = CreateNamedPipe(localPath, /* name of pipe */
754 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
755 PIPE_TYPE_BYTE | PIPE_WAIT |
756 PIPE_READMODE_BYTE, /* pipe IO type */
757 PIPE_UNLIMITED_INSTANCES, /* number of instances */
758 4096, /* size of outbuf (0 == allocate as necessary) */
759 4096, /* size of inbuf */
760 0, /*1000,*/ /* default time-out value */
761 &sa); /* security attributes */
764 * Can't create an instance of the pipe, fail...
766 if (hListenPipe == INVALID_HANDLE_VALUE) {
770 retFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)hListenPipe, -1);
776 *----------------------------------------------------------------------
780 * Create the pipe pathname connect to the remote application if
784 * -1 if fail or a valid handle if connection succeeds.
787 * Remote connection established.
789 *----------------------------------------------------------------------
791 int OS_FcgiConnect(char *bindPath)
793 char *pipePath = NULL;
797 struct sockaddr_in sockAddr;
798 int servLen, resultSock;
805 strncpy(host, bindPath, 1024);
808 if ((tp = strchr(host, ':')) != 0) {
810 if((port = atoi(tp)) == 0) {
819 if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) {
820 fprintf(stderr, "Unknown host: %s\n", bindPath);
823 sockAddr.sin_family = AF_INET;
824 memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length);
825 sockAddr.sin_port = htons(port);
826 servLen = sizeof(sockAddr);
827 resultSock = socket(AF_INET, SOCK_STREAM, 0);
829 ASSERT(resultSock >= 0);
830 connectStatus = connect(resultSock, (struct sockaddr *)
832 if(connectStatus < 0) {
834 * Most likely (errno == ENOENT || errno == ECONNREFUSED)
835 * and no FCGI application server is running.
837 closesocket(resultSock);
840 pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, resultSock, -1);
842 closesocket(resultSock);
848 * Not a TCP connection, create and connect to a named pipe.
850 pipePath = malloc((size_t)(strlen(bindPathPrefix) + strlen(bindPath) + 2));
851 if(pipePath == NULL) {
854 strcpy(pipePath, bindPathPrefix);
855 strcat(pipePath, bindPath);
857 hPipe = CreateFile (pipePath,
858 /* Generic access, read/write. */
859 GENERIC_WRITE | GENERIC_READ,
860 /* Share both read and write. */
861 FILE_SHARE_READ | FILE_SHARE_WRITE ,
862 NULL, /* No security.*/
863 OPEN_EXISTING, /* Fail if not existing. */
864 FILE_FLAG_OVERLAPPED, /* Use overlap. */
865 NULL); /* No template. */
868 if(hPipe == INVALID_HANDLE_VALUE) {
872 if ((pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int)hPipe, -1)) == -1) {
877 * Set stdin equal to our pseudo FD and create the I/O completion
878 * port to be used for async I/O.
880 if (!CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) {
881 err = GetLastError();
882 Win32FreeDescriptor(pseudoFd);
892 *--------------------------------------------------------------
896 * Pass through to the appropriate NT read function.
899 * Returns number of byes read. Mimics unix read:.
900 * n bytes read, 0 or -1 failure: errno contains actual error
905 *--------------------------------------------------------------
907 int OS_Read(int fd, char * buf, size_t len)
913 * Catch any bogus fd values
915 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
917 switch (fdTable[fd].type) {
924 * ReadFile returns: TRUE success, FALSE failure
926 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
928 fdTable[fd].Errno = GetLastError();
934 case FD_SOCKET_ASYNC:
935 /* winsock recv returns n bytes recv'ed, SOCKET_ERROR failure */
937 * XXX: Test this with ReadFile. If it works, remove this code
938 * to simplify the routine.
940 if ((ret = recv(fdTable[fd].fid.sock, buf, len, 0)) ==
942 fdTable[fd].Errno = WSAGetLastError();
952 *--------------------------------------------------------------
956 * Perform a synchronous OS write.
959 * Returns number of bytes written. Mimics unix write:
960 * n bytes written, 0 or -1 failure (??? couldn't find man page).
965 *--------------------------------------------------------------
967 int OS_Write(int fd, char * buf, size_t len)
973 * Catch any bogus fd values
975 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
976 ASSERT((fdTable[fd].type > FD_UNUSED) &&
977 (fdTable[fd].type <= FD_PIPE_ASYNC));
979 switch (fdTable[fd].type) {
986 * WriteFile returns: TRUE success, FALSE failure
988 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len,
989 &bytesWritten, NULL)) {
990 fdTable[fd].Errno = GetLastError();
995 case FD_SOCKET_ASYNC:
996 /* winsock send returns n bytes written, SOCKET_ERROR failure */
998 * XXX: Test this with WriteFile. If it works, remove this code
999 * to simplify the routine.
1001 if ((ret = send(fdTable[fd].fid.sock, buf, len, 0)) ==
1003 fdTable[fd].Errno = WSAGetLastError();
1014 *----------------------------------------------------------------------
1018 * Spawns a new server listener process, and stores the information
1019 * relating to the child in the supplied record. A wait handler is
1020 * registered on the child's completion. This involves creating
1021 * a process on NT and preparing a command line with the required
1022 * state (currently a -childproc flag and the server socket to use
1023 * for accepting connections).
1026 * 0 if success, -1 if error.
1029 * Child process spawned.
1031 *----------------------------------------------------------------------
1033 int OS_SpawnChild(char *execPath, int listenFd)
1035 STARTUPINFO StartupInfo;
1036 PROCESS_INFORMATION pInfo;
1039 memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO));
1040 StartupInfo.cb = sizeof (STARTUPINFO);
1041 StartupInfo.lpReserved = NULL;
1042 StartupInfo.lpReserved2 = NULL;
1043 StartupInfo.cbReserved2 = 0;
1044 StartupInfo.lpDesktop = NULL;
1047 * FastCGI on NT will set the listener pipe HANDLE in the stdin of
1048 * the new process. The fact that there is a stdin and NULL handles
1049 * for stdout and stderr tells the FastCGI process that this is a
1050 * FastCGI process and not a CGI process.
1052 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1054 * XXX: Do I have to dup the handle before spawning the process or is
1055 * it sufficient to use the handle as it's reference counted
1058 StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle;
1059 StartupInfo.hStdOutput = INVALID_HANDLE_VALUE;
1060 StartupInfo.hStdError = INVALID_HANDLE_VALUE;
1063 * Make the listener socket inheritable.
1065 success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT,
1072 * XXX: Might want to apply some specific security attributes to the
1075 success = CreateProcess(execPath, /* LPCSTR address of module name */
1076 NULL, /* LPCSTR address of command line */
1077 NULL, /* Process security attributes */
1078 NULL, /* Thread security attributes */
1079 TRUE, /* Inheritable Handes inherited. */
1080 0, /* DWORD creation flags */
1081 NULL, /* Use parent environment block */
1082 NULL, /* Address of current directory name */
1083 &StartupInfo, /* Address of STARTUPINFO */
1084 &pInfo); /* Address of PROCESS_INFORMATION */
1094 *--------------------------------------------------------------
1096 * OS_AsyncReadStdin --
1098 * This initiates an asynchronous read on the standard
1099 * input handle. This handle is not guaranteed to be
1100 * capable of performing asynchronous I/O so we send a
1101 * message to the StdinThread to do the synchronous read.
1104 * -1 if error, 0 otherwise.
1107 * Asynchronous message is queued to the StdinThread and an
1108 * overlapped structure is allocated/initialized.
1110 *--------------------------------------------------------------
1112 int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr,
1113 ClientData clientData)
1115 POVERLAPPED_REQUEST pOv;
1117 ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED);
1119 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1121 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1122 pOv->clientData1 = (ClientData)buf;
1123 pOv->instance = fdTable[STDIN_FILENO].instance;
1124 pOv->procPtr = procPtr;
1125 pOv->clientData = clientData;
1127 PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO,
1134 *--------------------------------------------------------------
1138 * This initiates an asynchronous read on the file
1139 * handle which may be a socket or named pipe.
1141 * We also must save the ProcPtr and ClientData, so later
1142 * when the io completes, we know who to call.
1144 * We don't look at any results here (the ReadFile may
1145 * return data if it is cached) but do all completion
1146 * processing in OS_Select when we get the io completion
1147 * port done notifications. Then we call the callback.
1150 * -1 if error, 0 otherwise.
1153 * Asynchronous I/O operation is queued for completion.
1155 *--------------------------------------------------------------
1157 int OS_AsyncRead(int fd, int offset, void *buf, int len,
1158 OS_AsyncProc procPtr, ClientData clientData)
1161 POVERLAPPED_REQUEST pOv;
1164 * Catch any bogus fd values
1166 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1168 * Confirm that this is an async fd
1170 ASSERT(fdTable[fd].type != FD_UNUSED);
1171 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1172 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1173 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1175 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1177 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1179 * Only file offsets should be non-zero, but make sure.
1181 if (fdTable[fd].type == FD_FILE_ASYNC)
1182 if (fdTable[fd].offset >= 0)
1183 pOv->overlapped.Offset = fdTable[fd].offset;
1185 pOv->overlapped.Offset = offset;
1186 pOv->instance = fdTable[fd].instance;
1187 pOv->procPtr = procPtr;
1188 pOv->clientData = clientData;
1191 * ReadFile returns: TRUE success, FALSE failure
1193 if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead,
1194 (LPOVERLAPPED)pOv)) {
1195 fdTable[fd].Errno = GetLastError();
1196 if(fdTable[fd].Errno == ERROR_NO_DATA ||
1197 fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) {
1198 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1201 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1202 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1205 fdTable[fd].Errno = 0;
1211 *--------------------------------------------------------------
1215 * This initiates an asynchronous write on the "fake" file
1216 * descriptor (which may be a file, socket, or named pipe).
1217 * We also must save the ProcPtr and ClientData, so later
1218 * when the io completes, we know who to call.
1220 * We don't look at any results here (the WriteFile generally
1221 * completes immediately) but do all completion processing
1222 * in OS_DoIo when we get the io completion port done
1223 * notifications. Then we call the callback.
1226 * -1 if error, 0 otherwise.
1229 * Asynchronous I/O operation is queued for completion.
1231 *--------------------------------------------------------------
1233 int OS_AsyncWrite(int fd, int offset, void *buf, int len,
1234 OS_AsyncProc procPtr, ClientData clientData)
1237 POVERLAPPED_REQUEST pOv;
1240 * Catch any bogus fd values
1242 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1244 * Confirm that this is an async fd
1246 ASSERT(fdTable[fd].type != FD_UNUSED);
1247 ASSERT(fdTable[fd].type != FD_FILE_SYNC);
1248 ASSERT(fdTable[fd].type != FD_PIPE_SYNC);
1249 ASSERT(fdTable[fd].type != FD_SOCKET_SYNC);
1251 pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST));
1253 memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST));
1255 * Only file offsets should be non-zero, but make sure.
1257 if (fdTable[fd].type == FD_FILE_ASYNC)
1259 * Only file opened via OS_AsyncWrite with
1260 * O_APPEND will have an offset != -1.
1262 if (fdTable[fd].offset >= 0)
1264 * If the descriptor has a memory mapped file
1265 * handle, take the offsets from there.
1267 if (fdTable[fd].hMapMutex != NULL) {
1269 * Wait infinitely; this *should* not cause problems.
1271 WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE);
1274 * Retrieve the shared offset values.
1276 pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr);
1277 pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr);
1280 * Update the shared offset values for the next write
1282 *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */
1283 *(fdTable[fd].offsetLowPtr) += len;
1285 ReleaseMutex(fdTable[fd].hMapMutex);
1287 pOv->overlapped.Offset = fdTable[fd].offset;
1289 pOv->overlapped.Offset = offset;
1290 pOv->instance = fdTable[fd].instance;
1291 pOv->procPtr = procPtr;
1292 pOv->clientData = clientData;
1295 * WriteFile returns: TRUE success, FALSE failure
1297 if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten,
1298 (LPOVERLAPPED)pOv)) {
1299 fdTable[fd].Errno = GetLastError();
1300 if(fdTable[fd].Errno != ERROR_IO_PENDING) {
1301 PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv);
1304 fdTable[fd].Errno = 0;
1306 if (fdTable[fd].offset >= 0)
1307 fdTable[fd].offset += len;
1313 *--------------------------------------------------------------
1317 * Closes the descriptor with routine appropriate for
1318 * descriptor's type.
1321 * Socket or file is closed. Return values mimic Unix close:
1322 * 0 success, -1 failure
1325 * Entry in fdTable is marked as free.
1327 *--------------------------------------------------------------
1329 int OS_Close(int fd)
1334 * Catch it if fd is a bogus value
1336 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1337 ASSERT(fdTable[fd].type != FD_UNUSED);
1339 switch (fdTable[fd].type) {
1346 case FD_SOCKET_SYNC:
1347 case FD_SOCKET_ASYNC:
1349 * Closing a socket that has an async read outstanding causes a
1350 * tcp reset and possible data loss. The shutdown call seems to
1353 shutdown(fdTable[fd].fid.sock, 2);
1355 * closesocket returns: 0 success, SOCKET_ERROR failure
1357 if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR)
1361 return -1; /* fake failure */
1364 Win32FreeDescriptor(fd);
1369 *--------------------------------------------------------------
1373 * Cancel outstanding asynchronous reads and prevent subsequent
1374 * reads from completing.
1377 * Socket or file is shutdown. Return values mimic Unix shutdown:
1378 * 0 success, -1 failure
1380 *--------------------------------------------------------------
1382 int OS_CloseRead(int fd)
1387 * Catch it if fd is a bogus value
1389 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1390 ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC
1391 || fdTable[fd].type == FD_SOCKET_SYNC);
1393 if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR)
1399 *--------------------------------------------------------------
1403 * This function was formerly OS_Select. It's purpose is
1404 * to pull I/O completion events off the queue and dispatch
1405 * them to the appropriate place.
1411 * Handlers are called.
1413 *--------------------------------------------------------------
1415 int OS_DoIo(struct timeval *tmo)
1419 POVERLAPPED_REQUEST pOv;
1426 * We can loop in here, but not too long, as wait handlers
1428 * For cgi stdin, apparently select returns when io completion
1429 * ports don't, so don't wait the full timeout.
1432 ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2;
1436 ms_last = tb.time*1000 + tb.millitm;
1438 if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100)
1440 if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd,
1441 (LPOVERLAPPED *)&pOv, ms) && !pOv) {
1442 err = WSAGetLastError();
1443 return 0; /* timeout */
1446 ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX));
1447 /* call callback if descriptor still valid */
1449 if(pOv->instance == fdTable[fd].instance)
1450 (*pOv->procPtr)(pOv->clientData, bytes);
1454 ms -= (tb.time*1000 + tb.millitm - ms_last);
1455 ms_last = tb.time*1000 + tb.millitm;
1461 static int CALLBACK isAddrOK(LPWSABUF lpCallerId,
1468 DWORD dwCallbackData)
1470 const char *okAddrs = (char *) dwCallbackData;
1471 struct sockaddr *sockaddr = (struct sockaddr *) lpCallerId->buf;
1473 if (okAddrs == NULL || sockaddr->sa_family != AF_INET)
1479 static const char *token = " ,;:\t";
1480 struct sockaddr_in * inet_sockaddr = (struct sockaddr_in *) sockaddr;
1481 char *ipaddr = inet_ntoa(inet_sockaddr->sin_addr);
1482 char *p = strstr(okAddrs, ipaddr);
1488 else if (p == okAddrs)
1490 p += strlen(ipaddr);
1491 return (strchr(token, *p) != NULL);
1493 else if (strchr(token, *--p))
1495 p += strlen(ipaddr) + 1;
1496 return (strchr(token, *p) != NULL);
1507 *----------------------------------------------------------------------
1511 * Accepts a new FastCGI connection. This routine knows whether
1512 * we're dealing with TCP based sockets or NT Named Pipes for IPC.
1515 * -1 if the operation fails, otherwise this is a valid IPC fd.
1518 * New IPC connection is accepted.
1520 *----------------------------------------------------------------------
1522 int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs)
1524 /* XXX This is broken for listen_sock & fail_on_intr */
1529 if (shutdownPending)
1534 // The mutex is to keep other processes (and threads, when supported)
1535 // from going into the accept cycle. The accept cycle needs to
1536 // periodically break out to check the state of the shutdown flag
1537 // and there's no point to having more than one thread do that.
1539 if (acceptMutex != INVALID_HANDLE_VALUE)
1541 if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED)
1547 if (shutdownPending)
1549 if (acceptMutex != INVALID_HANDLE_VALUE)
1551 ReleaseMutex(acceptMutex);
1556 if (listenType == FD_PIPE_SYNC)
1558 pConnected = ConnectNamedPipe(hListen, &listenOverlapped)
1560 : (GetLastError() == ERROR_PIPE_CONNECTED);
1564 while (WaitForSingleObject(listenOverlapped.hEvent, 1000) == WAIT_TIMEOUT)
1566 if (shutdownPending)
1568 if (acceptMutex != INVALID_HANDLE_VALUE)
1570 ReleaseMutex(acceptMutex);
1578 if (acceptMutex != INVALID_HANDLE_VALUE)
1580 ReleaseMutex(acceptMutex);
1583 ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1);
1586 DisconnectNamedPipe(hListen);
1589 else if (listenType == FD_SOCKET_SYNC)
1591 struct sockaddr sockaddr;
1592 int sockaddrLen = sizeof(sockaddr);
1594 const struct timeval timeout = {1, 0};
1597 FD_SET((unsigned int) hListen, &readfds);
1599 while (select(0, &readfds, NULL, NULL, &timeout) == 0)
1601 if (shutdownPending)
1607 hSock = (webServerAddrs == NULL)
1608 ? accept((SOCKET) hListen,
1611 : WSAAccept((unsigned int) hListen,
1615 (DWORD) webServerAddrs);
1617 if (acceptMutex != INVALID_HANDLE_VALUE)
1619 ReleaseMutex(acceptMutex);
1627 ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1);
1642 *----------------------------------------------------------------------
1646 * OS IPC routine to close an IPC connection.
1652 * IPC connection is closed.
1654 *----------------------------------------------------------------------
1656 int OS_IpcClose(int ipcFd)
1662 * Catch it if fd is a bogus value
1664 ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX));
1665 ASSERT(fdTable[ipcFd].type != FD_UNUSED);
1667 switch(listenType) {
1671 * Make sure that the client (ie. a Web Server in this case) has
1672 * read all data from the pipe before we disconnect.
1674 if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle))
1676 if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) {
1684 case FD_SOCKET_SYNC:
1698 *----------------------------------------------------------------------
1702 * Determines whether this process is a FastCGI process or not.
1705 * Returns 1 if FastCGI, 0 if not.
1710 *----------------------------------------------------------------------
1712 int OS_IsFcgi(int sock)
1714 /* XXX This is broken for sock */
1715 if(listenType == FD_UNUSED) {
1724 *----------------------------------------------------------------------
1728 * Sets selected flag bits in an open file descriptor. Currently
1729 * this is only to put a SOCKET into non-blocking mode.
1731 *----------------------------------------------------------------------
1733 void OS_SetFlags(int fd, int flags)
1735 unsigned long pLong = 1L;
1738 if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) {
1739 if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) ==
1741 exit(WSAGetLastError());
1743 if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock,
1744 hIoCompPort, fd, 1)) {
1745 err = GetLastError();
1749 fdTable[fd].type = FD_SOCKET_ASYNC;